Skip to content

Commit

Permalink
txdb: un/lockBalances for every input and output
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz authored and nodech committed Oct 17, 2023
1 parent 5c287c9 commit f785bf5
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 166 deletions.
218 changes: 54 additions & 164 deletions lib/wallet/txdb.js
Original file line number Diff line number Diff line change
Expand Up @@ -958,17 +958,13 @@ class TXDB {
state.coin(path, -1);
state.unconfirmed(path, -coin.value);

// FINALIZE is a special case: locked coins _leave_ the wallet.
if (tx.output(i) && tx.covenant(i).isFinalize()) {
if (!block) {
state.ulocked(path, -tx.outputs[i].value);
} else {
state.clocked(path, -tx.outputs[i].value);
// This is the first time we've seen this tx and it is in a block
// (probably from a rescan). Update unconfirmed locked balance also.
state.ulocked(path, -tx.outputs[i].value);
}
}
// If the first time we see a TX is in a block
// (i.e. during a rescan) update the "unconfirmed" unlocked balance
// before updating the "confirmed" locked balance.
if (height !== -1)
this.unlockBalances(state, coin, path, -1);

this.unlockBalances(state, coin, path, height);

if (!block) {
// If the tx is not mined, we do not
Expand Down Expand Up @@ -1009,9 +1005,9 @@ class TXDB {
// (i.e. during a rescan) update the "unconfirmed" locked balance
// before updating the "confirmed" locked balance.
if (height !== -1)
await this.lockBalances(b, state, tx, i, path, -1);
this.lockBalances(state, output, path, -1);

await this.lockBalances(b, state, tx, i, path, height);
this.lockBalances(state, output, path, height);

details.setOutput(i, path);

Expand Down Expand Up @@ -1162,6 +1158,8 @@ class TXDB {
assert(path);
own = true;

this.unlockBalances(state, coin, path, height);

details.setInput(i, path, coin);

if (resolved) {
Expand All @@ -1174,10 +1172,6 @@ class TXDB {
// been removed on-chain.
state.confirmed(path, -coin.value);

// FINALIZE is a special case: locked coins _leave_ the wallet.
if (tx.output(i) && tx.covenant(i).isFinalize())
state.clocked(path, -tx.outputs[i].value);

await this.removeCredit(b, credit, path);

view.addCoin(coin);
Expand All @@ -1192,7 +1186,7 @@ class TXDB {
if (!path)
continue;

await this.lockBalances(b, state, tx, i, path, height);
this.lockBalances(state, output, path, height);

details.setOutput(i, path);

Expand Down Expand Up @@ -1321,14 +1315,7 @@ class TXDB {
state.coin(path, 1);
state.unconfirmed(path, coin.value);

// FINALIZE is a special case: locked coins _leave_ the wallet.
// In this case a TX is erased, adding them back.
if (tx.output(i) && tx.covenant(i).isFinalize()) {
if (!block)
state.ulocked(path, tx.outputs[i].value);
else
state.clocked(path, tx.outputs[i].value);
}
this.lockBalances(state, coin, path, height);

if (block)
state.confirmed(path, coin.value);
Expand All @@ -1349,7 +1336,7 @@ class TXDB {
if (!path)
continue;

await this.unlockBalances(b, state, tx, i, path, height);
this.unlockBalances(state, output, path, height);

details.setOutput(i, path);

Expand Down Expand Up @@ -1541,15 +1528,12 @@ class TXDB {
const path = await this.getPath(coin);
assert(path);

this.lockBalances(state, coin, path, height);

details.setInput(i, path, coin);

state.confirmed(path, coin.value);

// FINALIZE is a special case: locked coins _leave_ the wallet.
// In this case a TX is reversed, adding them back.
if (tx.output(i) && tx.covenant(i).isFinalize())
state.clocked(path, tx.outputs[i].value);

// Resave the credit and mark it
// as spent in the mempool instead.
credit.spent = true;
Expand All @@ -1566,7 +1550,7 @@ class TXDB {
if (!path)
continue;

await this.unlockBalances(b, state, tx, i, path, height);
this.unlockBalances(state, output, path, height);

const credit = await this.getCredit(hash, i);

Expand Down Expand Up @@ -1703,167 +1687,73 @@ class TXDB {
}

/**
* Lock balances according to covenants.
* @param {Object} b
* Lock balances according to covenant.
* Inserting or confirming: TX outputs.
* Removing or undoing: Coins spent by the wallet in tx inputs.
* @param {State} state
* @param {TX} tx
* @param {Number} i
* @param {Coin|Output} coin
* @param {Path} path
* @param {Number} height
*/

async lockBalances(b, state, tx, i, path, height) {
const output = tx.outputs[i];
const covenant = output.covenant;
lockBalances(state, coin, path, height) {
const {value, covenant} = coin;

switch (covenant.type) {
case types.CLAIM:
case types.BID: {
case types.CLAIM: // output is locked until REGISTER
case types.BID: // output is locked until REVEAL
case types.REVEAL: // output is locked until REDEEM
case types.REGISTER: // output is now locked or "burned"
case types.UPDATE: // output has been locked since REGISTER
case types.RENEW:
case types.TRANSFER:
case types.FINALIZE:
case types.REVOKE:
{
if (height === -1)
state.ulocked(path, output.value);
state.ulocked(path, value);
else
state.clocked(path, output.value);
break;
}

case types.REVEAL: {
assert(i < tx.inputs.length);

const nameHash = covenant.getHash(0);
const prevout = tx.inputs[i].prevout;

const bb = await this.getBid(nameHash, prevout);
if (!bb)
break;

if (height === -1) {
state.ulocked(path, -bb.lockup);
state.ulocked(path, output.value);
} else {
state.clocked(path, -bb.lockup);
state.clocked(path, output.value);
}

break;
}

case types.REDEEM: {
if (height === -1)
state.ulocked(path, -output.value);
else
state.clocked(path, -output.value);
break;
}

case types.REGISTER: {
assert(i < tx.inputs.length);

const prevout = tx.inputs[i].prevout;

const coin = await this.getCoin(prevout.hash, prevout.index);
assert(coin);
assert(coin.covenant.isReveal() || coin.covenant.isClaim());

if (height === -1) {
state.ulocked(path, -coin.value);
state.ulocked(path, output.value);
} else {
state.clocked(path, -coin.value);
state.clocked(path, output.value);
}

state.clocked(path, value);
break;
}

case types.FINALIZE: {
if (height === -1)
state.ulocked(path, output.value);
else
state.clocked(path, output.value);
case types.REDEEM: // noop: already unlocked by the BID in the input
break;
}
}
}

/**
* Unlock balances according to covenants.
* @param {Object} b
* Inserting or confirming: Coins spent by the wallet in TX inputs.
* Removing or undoing: TX outputs.
* @param {State} state
* @param {TX} tx
* @param {Number} i
* @param {Coin|Output} coin
* @param {Path} path
* @param {Number} height
*/

async unlockBalances(b, state, tx, i, path, height) {
const output = tx.outputs[i];
const covenant = output.covenant;
unlockBalances(state, coin, path, height) {
const {value, covenant} = coin;

switch (covenant.type) {
case types.CLAIM:
case types.BID: {
case types.CLAIM: // output is locked until REGISTER
case types.BID: // output is locked until REVEAL
case types.REVEAL: // output is locked until REDEEM
case types.REGISTER: // output is now locked or "burned"
case types.UPDATE: // output has been locked since REGISTER
case types.RENEW:
case types.TRANSFER:
case types.FINALIZE:
case types.REVOKE:
{
if (height === -1)
state.ulocked(path, -output.value);
state.ulocked(path, -value);
else
state.clocked(path, -output.value);
state.clocked(path, -value);
break;
}

case types.REVEAL: {
assert(i < tx.inputs.length);

const nameHash = covenant.getHash(0);
const prevout = tx.inputs[i].prevout;

const bb = await this.getBid(nameHash, prevout);
if (!bb)
break;

if (height === -1) {
state.ulocked(path, bb.lockup);
state.ulocked(path, -output.value);
} else {
state.clocked(path, bb.lockup);
state.clocked(path, -output.value);
}

case types.REDEEM: // noop: already unlocked by the BID in the input
break;
}

case types.REDEEM: {
if (height === -1)
state.ulocked(path, output.value);
else
state.clocked(path, output.value);
break;
}

case types.REGISTER: {
assert(i < tx.inputs.length);

const coins = await this.getSpentCoins(tx);
const coin = coins[i];
assert(coin);
assert(coin.covenant.isReveal() || coin.covenant.isClaim());

if (height === -1) {
state.ulocked(path, coin.value);
state.ulocked(path, -output.value);
} else {
state.clocked(path, coin.value);
state.clocked(path, -output.value);
}

break;
}

case types.FINALIZE: {
if (height === -1)
state.ulocked(path, -output.value);
else
state.clocked(path, -output.value);
break;
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions test/wallet-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2356,7 +2356,7 @@ describe('Wallet', function() {
uTXCount++;

// Check
const senderBal3 = await wallet.getBalance();
const senderBal3 = await wallet.getBalance();
assert.strictEqual(senderBal3.tx, 7);
// One less wallet coin because name UTXO belongs to recip now
assert.strictEqual(senderBal3.coin, 3);
Expand Down Expand Up @@ -3345,7 +3345,7 @@ describe('Wallet', function() {
assert.strictEqual(bal.ulocked, value);
assert.strictEqual(bal.clocked, value + secondHighest);

// Confirm REGISTER
// Confirm REDEEM
const block = {
height: wdb.height + 1,
hash: Buffer.alloc(32),
Expand Down

0 comments on commit f785bf5

Please sign in to comment.