Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Domain Manager Multi-select and Portfolio Actions Preview #631

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
167 changes: 167 additions & 0 deletions app/background/wallet/bulk-actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
const { states } = require('hsd/lib/covenants/namestate');

// From hsd/lib/wallet/wallet.js make*()

/** @param {import('hsd/lib/wallet/wallet')} wallet */
export const getNamesForRegisterAll = async (wallet) => {
const height = wallet.wdb.height + 1;
const network = wallet.network;
const names = await wallet.txdb.getNames();
const namesToRegister = [];

for (let i = 0; i < names.length; i++) {
const ns = names[i];
const { owner } = ns;

const coin = await wallet.getUnspentCoin(owner.hash, owner.index);

if (coin) {
if (!coin)
continue;
Comment on lines +18 to +20
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found by @Falci


if (ns.isExpired(height, network))
continue;

// Is local?
if (coin.height < ns.height)
continue;

if (!coin.covenant.isReveal() && !coin.covenant.isClaim())
continue;

if (coin.covenant.isClaim()) {
if (height < coin.height + network.coinbaseMaturity)
continue;
}

const state = ns.state(height, network);

if (state !== states.CLOSED)
continue;

namesToRegister.push({
name: ns.name.toString('binary'),
});
}
}

return namesToRegister;
}

/** @param {import('hsd/lib/wallet/wallet')} wallet */
export const getNamesForFinalizeAll = async (wallet) => {
const height = wallet.wdb.height + 1;
const network = wallet.network;
const names = await wallet.txdb.getNames();
const namesToFinalize = [];

for (const ns of names) {
// Easiest check is for transfer state, do that first
if (!ns.transfer)
continue;

const blocksLeft = (ns.transfer + network.names.transferLockup) - height;
if (blocksLeft > 0)
continue;

// Then check for expiration
if (ns.isExpired(height, network))
continue;

// Now do the db lookups to see if we own the name
const { hash, index } = ns.owner;
const coin = await wallet.getUnspentCoin(hash, index);
if (!coin)
continue;

namesToFinalize.push({
name: ns.name.toString('binary'),
finalizableSinceBlocks: -blocksLeft,
});
}

return namesToFinalize;
}

/** @param {import('hsd/lib/wallet/wallet')} wallet */
export const getNamesForRenewAll = async (wallet) => {
const height = wallet.wdb.height + 1;
const network = wallet.network;
const names = await wallet.txdb.getNames();
const namesToRenew = [];

for (const ns of names) {
// Easiest check is for expiring time, do that first
if (ns.isExpired(height, network))
continue;

// About 90 days on main (1.75 years after REGISTER)
// 625 blocks on regtest (4375 blocks after REGISTER)
const blocksLeft = (ns.renewal + network.names.renewalWindow) - height;
if (blocksLeft >= network.names.renewalWindow / 8)
continue;

if (height < ns.renewal + network.names.treeInterval)
continue; // Can not renew yet

// Now do the db lookups to see if we own the name
const { hash, index } = ns.owner;
const coin = await wallet.getUnspentCoin(hash, index);
if (!coin)
continue;

if (!coin.covenant.isRegister()
&& !coin.covenant.isUpdate()
&& !coin.covenant.isRenew()
&& !coin.covenant.isFinalize()) {
continue; // Name is not yet registered
}

namesToRenew.push({
name: ns.name.toString('binary'),
renewInBlocks: blocksLeft,
});
}

// Sort by urgency, oldest/lowest renewal heights go first
namesToRenew.sort((a, b) => {
return a.blocksLeft - b.blocksLeft;
});

return namesToRenew;
}

/** @param {import('hsd/lib/wallet/wallet')} wallet */
export const getNamesInTransfer = async (wallet) => {
const height = wallet.wdb.height + 1;
const network = wallet.network;
const names = await wallet.txdb.getNames();
const namesInTransfer = [];

for (const ns of names) {
// Easiest check is for transfer state, do that first
if (!ns.transfer)
continue;

const blocksLeft = (ns.transfer + network.names.transferLockup) - height;
if (blocksLeft <= 0)
continue;

// Then check for expiration
if (ns.isExpired(height, network))
continue;

// Now do the db lookups to see if we own the name
const { hash, index } = ns.owner;
const coin = await wallet.getUnspentCoin(hash, index);
if (!coin)
continue;

namesInTransfer.push({
name: ns.name.toString('binary'),
finalizableInBlocks: blocksLeft,
});
}

return namesInTransfer;
}
2 changes: 2 additions & 0 deletions app/background/wallet/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export const clientStub = ipcRendererInjector => makeClient(ipcRendererInjector,
'finalizeWithPayment',
'claimPaidTransfer',
'revokeName',
'sendBatch',
'getNamesForBulkAction',
'send',
'lock',
'unlock',
Expand Down
46 changes: 0 additions & 46 deletions app/background/wallet/create-register-all.js

This file was deleted.

34 changes: 31 additions & 3 deletions app/background/wallet/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from '../../ducks/walletReducer';
import {STOP, SET_CUSTOM_RPC_STATUS} from '../../ducks/nodeReducer';
import {showSuccess, showError} from '../../ducks/notifications';
import {getNamesForRegisterAll} from "./create-register-all";
import * as bulkActions from "./bulk-actions";
import {getStats} from "./stats";
import {get, put} from "../db/service";
import hsdLedger from 'hsd-ledger';
Expand Down Expand Up @@ -682,11 +682,11 @@ class WalletService {
const {wdb} = this.node;
const wallet = await wdb.get(this.name);

const names = await getNamesForRegisterAll(wallet);
const names = (await bulkActions.getNamesForRegisterAll(wallet));
if (!names.length) {
throw new Error('Nothing to do.');
}
const actions = names.map(name => ['UPDATE', name, {records: []}]);
const actions = names.map(name => ['UPDATE', name.name, {records: []}]);

// Chunk into multiple batches to stay within consensus limits
const chunkedActions = [];
Expand Down Expand Up @@ -808,6 +808,32 @@ class WalletService {
() => this._executeRPC('createrevoke', [name]),
);

sendBatch = (actions) => this._walletProxy(
() => this._executeRPC('createbatch', [actions, {paths: true}]),
);

getNamesForBulkAction = async (action) => {
const {wdb} = this.node;
const wallet = await wdb.get(this.name);

switch (action) {
case 'register':
return bulkActions.getNamesForRegisterAll(wallet);

case 'finalize':
return bulkActions.getNamesForFinalizeAll(wallet);

case 'renew':
return bulkActions.getNamesForRenewAll(wallet);

case 'transferring':
return bulkActions.getNamesInTransfer(wallet);

default:
throw new Error('Invalid action.');
}
}

send = (to, amount, fee) => this._walletProxy(
async () => {
await this._executeRPC('settxfee', [Number(fee)]);
Expand Down Expand Up @@ -2181,6 +2207,8 @@ const methods = {
claimPaidTransfer: service.claimPaidTransfer,
signMessageWithName: service.signMessageWithName,
revokeName: service.revokeName,
sendBatch: service.sendBatch,
getNamesForBulkAction: service.getNamesForBulkAction,
send: service.send,
lock: service.lock,
unlock: service.unlock,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
@import '../../variables.scss';

.bulk-action {
.mini-modal__content {
flex-grow: 1;
display: flex;
flex-direction: column;
overflow: auto;
padding-left: 1px;
padding-right: 1px;
}

p {
&:nth-of-type(1) {
margin-top: 0;
}
}

&__label {
color: rgba($black, 0.4);
font-weight: 600;
font-size: 0.8rem;
margin-bottom: 0.75rem;
}

&__table {
flex-grow: 1;
overflow: auto;

.table {
&__header {
min-height: 28px;

&__item {
&--checkbox {
flex: 0 0 2rem;
}
&--domain {
flex: 0 0 8rem;
flex-grow: 1;
}
&--time {
text-align: right;
flex: 0 0 10rem;
}
}
}

&__row {
transition: background-color 250ms ease-in-out;

&:hover {
background-color: rgba($black, .025);
}

&:active {
background-color: rgba($black, .05);
}

&__item {
&--checkbox {
flex: 0 0 2rem;
}
&--domain {
flex: 0 0 8rem;
flex-grow: 1;
}
&--time {
text-align: right;
flex: 0 0 10rem;
}
}
}
}

&__body {
overflow: auto;
flex-grow: 1;
}
}

&__actions {
@extend %row-nowrap;
justify-content: center;
width: 100%;
margin-top: 1rem;

button {
@extend %btn-primary;
flex: 1 0 auto;
}
}
}
Loading