txdb: new orphan resolution.
This commit is contained in:
parent
321f4f6df6
commit
557c1044cc
@ -514,7 +514,9 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx) {
|
|||||||
|
|
||||||
if (spent) {
|
if (spent) {
|
||||||
coin = yield this.getSpentCoin(spent, prevout);
|
coin = yield this.getSpentCoin(spent, prevout);
|
||||||
assert(coin);
|
|
||||||
|
if (!coin)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (this.options.verify && tx.height === -1) {
|
if (this.options.verify && tx.height === -1) {
|
||||||
input.coin = coin;
|
input.coin = coin;
|
||||||
@ -681,6 +683,66 @@ TXDB.prototype.unspendCredit = function unspendCredit(tx, i) {
|
|||||||
this.del(layout.d(spender.hash, spender.index));
|
this.del(layout.d(spender.hash, spender.index));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write input record.
|
||||||
|
* @param {TX} tx
|
||||||
|
* @param {Number} i
|
||||||
|
*/
|
||||||
|
|
||||||
|
TXDB.prototype.writeInput = function writeInput(tx, i) {
|
||||||
|
var prevout = tx.inputs[i].prevout;
|
||||||
|
var spender = Outpoint.fromTX(tx, i);
|
||||||
|
this.put(layout.s(prevout.hash, prevout.index), spender.toRaw());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove input record.
|
||||||
|
* @param {TX} tx
|
||||||
|
* @param {Number} i
|
||||||
|
*/
|
||||||
|
|
||||||
|
TXDB.prototype.removeInput = function removeInput(tx, i) {
|
||||||
|
var prevout = tx.inputs[i].prevout;
|
||||||
|
this.del(layout.s(prevout.hash, prevout.index));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve orphan input.
|
||||||
|
* @param {TX} tx
|
||||||
|
* @param {Number} i
|
||||||
|
* @param {Path} path
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
TXDB.prototype.resolveInput = co(function* resolveInput(tx, i, path) {
|
||||||
|
var hash = tx.hash('hex');
|
||||||
|
var spent = yield this.getSpent(hash, i);
|
||||||
|
var stx, credit;
|
||||||
|
|
||||||
|
if (!spent)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (yield this.hasSpentCoin(spent))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stx = yield this.getTX(spent.hash);
|
||||||
|
assert(stx);
|
||||||
|
|
||||||
|
credit = Credit.fromTX(tx, i);
|
||||||
|
|
||||||
|
this.spendCredit(credit, stx, spent.index);
|
||||||
|
|
||||||
|
if (stx.height === -1) {
|
||||||
|
credit.spent = true;
|
||||||
|
this.saveCredit(credit, path);
|
||||||
|
this.pending.coin++;
|
||||||
|
if (tx.height !== -1)
|
||||||
|
this.pending.confirmed += credit.coin.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test an entire transaction to see
|
* Test an entire transaction to see
|
||||||
* if any of its outpoints are a double-spend.
|
* if any of its outpoints are a double-spend.
|
||||||
@ -847,8 +909,10 @@ TXDB.prototype._insert = co(function* insert(tx) {
|
|||||||
prevout = input.prevout;
|
prevout = input.prevout;
|
||||||
credit = yield this.getCredit(prevout.hash, prevout.index);
|
credit = yield this.getCredit(prevout.hash, prevout.index);
|
||||||
|
|
||||||
if (!credit)
|
if (!credit) {
|
||||||
|
this.writeInput(tx, i);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
coin = credit.coin;
|
coin = credit.coin;
|
||||||
path = yield this.getPath(coin);
|
path = yield this.getPath(coin);
|
||||||
@ -880,10 +944,12 @@ TXDB.prototype._insert = co(function* insert(tx) {
|
|||||||
|
|
||||||
details.setOutput(i, path);
|
details.setOutput(i, path);
|
||||||
|
|
||||||
|
if (yield this.resolveInput(tx, i, path))
|
||||||
|
continue;
|
||||||
|
|
||||||
credit = Credit.fromTX(tx, i);
|
credit = Credit.fromTX(tx, i);
|
||||||
|
|
||||||
this.pending.coin++;
|
this.pending.coin++;
|
||||||
|
|
||||||
this.pending.unconfirmed += output.value;
|
this.pending.unconfirmed += output.value;
|
||||||
|
|
||||||
if (tx.height !== -1)
|
if (tx.height !== -1)
|
||||||
@ -893,25 +959,23 @@ TXDB.prototype._insert = co(function* insert(tx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.put(layout.t(hash), tx.toExtended());
|
this.put(layout.t(hash), tx.toExtended());
|
||||||
|
this.put(layout.m(tx.ps, hash), DUMMY);
|
||||||
|
|
||||||
if (tx.height === -1)
|
if (tx.height === -1)
|
||||||
this.put(layout.p(hash), DUMMY);
|
this.put(layout.p(hash), DUMMY);
|
||||||
else
|
else
|
||||||
this.put(layout.h(tx.height, hash), DUMMY);
|
this.put(layout.h(tx.height, hash), DUMMY);
|
||||||
|
|
||||||
this.put(layout.m(tx.ps, hash), DUMMY);
|
|
||||||
|
|
||||||
for (i = 0; i < details.accounts.length; i++) {
|
for (i = 0; i < details.accounts.length; i++) {
|
||||||
account = details.accounts[i];
|
account = details.accounts[i];
|
||||||
|
|
||||||
this.put(layout.T(account, hash), DUMMY);
|
this.put(layout.T(account, hash), DUMMY);
|
||||||
|
this.put(layout.M(account, tx.ps, hash), DUMMY);
|
||||||
|
|
||||||
if (tx.height === -1)
|
if (tx.height === -1)
|
||||||
this.put(layout.P(account, hash), DUMMY);
|
this.put(layout.P(account, hash), DUMMY);
|
||||||
else
|
else
|
||||||
this.put(layout.H(account, tx.height, hash), DUMMY);
|
this.put(layout.H(account, tx.height, hash), DUMMY);
|
||||||
|
|
||||||
this.put(layout.M(account, tx.ps, hash), DUMMY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pending.tx++;
|
this.pending.tx++;
|
||||||
@ -990,6 +1054,9 @@ TXDB.prototype.confirm = co(function* confirm(tx) {
|
|||||||
|
|
||||||
details.setOutput(i, path);
|
details.setOutput(i, path);
|
||||||
|
|
||||||
|
if (yield this.resolveInput(tx, i, path))
|
||||||
|
continue;
|
||||||
|
|
||||||
credit = yield this.getCredit(hash, i);
|
credit = yield this.getCredit(hash, i);
|
||||||
assert(credit);
|
assert(credit);
|
||||||
|
|
||||||
@ -1105,8 +1172,10 @@ TXDB.prototype._remove = co(function* remove(tx) {
|
|||||||
input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
credit = credits[i];
|
credit = credits[i];
|
||||||
|
|
||||||
if (!credit)
|
if (!credit) {
|
||||||
|
this.removeInput(tx, i);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
coin = credit.coin;
|
coin = credit.coin;
|
||||||
path = yield this.getPath(coin);
|
path = yield this.getPath(coin);
|
||||||
@ -1147,25 +1216,23 @@ TXDB.prototype._remove = co(function* remove(tx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.del(layout.t(hash));
|
this.del(layout.t(hash));
|
||||||
|
this.del(layout.m(tx.ps, hash));
|
||||||
|
|
||||||
if (tx.height === -1)
|
if (tx.height === -1)
|
||||||
this.del(layout.p(hash));
|
this.del(layout.p(hash));
|
||||||
else
|
else
|
||||||
this.del(layout.h(tx.height, hash));
|
this.del(layout.h(tx.height, hash));
|
||||||
|
|
||||||
this.del(layout.m(tx.ps, hash));
|
|
||||||
|
|
||||||
for (i = 0; i < details.accounts.length; i++) {
|
for (i = 0; i < details.accounts.length; i++) {
|
||||||
account = details.accounts[i];
|
account = details.accounts[i];
|
||||||
|
|
||||||
this.del(layout.T(account, hash));
|
this.del(layout.T(account, hash));
|
||||||
|
this.del(layout.M(account, tx.ps, hash));
|
||||||
|
|
||||||
if (tx.height === -1)
|
if (tx.height === -1)
|
||||||
this.del(layout.P(account, hash));
|
this.del(layout.P(account, hash));
|
||||||
else
|
else
|
||||||
this.del(layout.H(account, tx.height, hash));
|
this.del(layout.H(account, tx.height, hash));
|
||||||
|
|
||||||
this.del(layout.M(account, tx.ps, hash));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pending.tx--;
|
this.pending.tx--;
|
||||||
@ -2216,6 +2283,16 @@ TXDB.prototype.getSpentCoin = co(function* getSpentCoin(spent, prevout) {
|
|||||||
return coin;
|
return coin;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether the database has a spent coin.
|
||||||
|
* @param {Outpoint} spent
|
||||||
|
* @returns {Promise} - Returns {@link Coin}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
TXDB.prototype.hasSpentCoin = function hasSpentCoin(spent) {
|
||||||
|
return this.has(layout.d(spent.hash, spent.index));
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update spent coin height in storage.
|
* Update spent coin height in storage.
|
||||||
* @param {TX} tx - Sending transaction.
|
* @param {TX} tx - Sending transaction.
|
||||||
|
|||||||
@ -17,24 +17,27 @@ var KEY1 = 'xprv9s21ZrQH143K3Aj6xQBymM31Zb4BVc7wxqfUhMZrzewdDVCt'
|
|||||||
var KEY2 = 'xprv9s21ZrQH143K3mqiSThzPtWAabQ22Pjp3uSNnZ53A5bQ4udp'
|
var KEY2 = 'xprv9s21ZrQH143K3mqiSThzPtWAabQ22Pjp3uSNnZ53A5bQ4udp'
|
||||||
+ 'faKekc2m4AChLYH1XDzANhrSdxHYWUeTWjYJwFwWFyHkTMnMeAcW4JyRCZa';
|
+ 'faKekc2m4AChLYH1XDzANhrSdxHYWUeTWjYJwFwWFyHkTMnMeAcW4JyRCZa';
|
||||||
|
|
||||||
var dummyInput = {
|
function dummy() {
|
||||||
prevout: {
|
var hash = crypto.randomBytes(32).toString('hex');
|
||||||
hash: constants.NULL_HASH,
|
return {
|
||||||
index: 0
|
prevout: {
|
||||||
},
|
hash: hash,
|
||||||
coin: {
|
index: 0
|
||||||
version: 1,
|
},
|
||||||
height: 0,
|
coin: {
|
||||||
value: constants.MAX_MONEY,
|
version: 1,
|
||||||
script: new bcoin.script([]),
|
height: 0,
|
||||||
coinbase: false,
|
value: constants.MAX_MONEY,
|
||||||
hash: constants.NULL_HASH,
|
script: new bcoin.script(),
|
||||||
index: 0
|
coinbase: false,
|
||||||
},
|
hash: hash,
|
||||||
script: new bcoin.script([]),
|
index: 0
|
||||||
witness: new bcoin.witness([]),
|
},
|
||||||
sequence: 0xffffffff
|
script: new bcoin.script(),
|
||||||
};
|
witness: new bcoin.witness(),
|
||||||
|
sequence: 0xffffffff
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe('Wallet', function() {
|
describe('Wallet', function() {
|
||||||
var walletdb, wallet, ewallet, ekey, doubleSpendWallet, doubleSpend;
|
var walletdb, wallet, ewallet, ekey, doubleSpendWallet, doubleSpend;
|
||||||
@ -110,7 +113,7 @@ describe('Wallet', function() {
|
|||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
src.addInput(dummyInput);
|
src.addInput(dummy());
|
||||||
|
|
||||||
tx = bcoin.mtx()
|
tx = bcoin.mtx()
|
||||||
.addInput(src, 0)
|
.addInput(src, 0)
|
||||||
@ -162,7 +165,7 @@ describe('Wallet', function() {
|
|||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
src.addInput(dummyInput);
|
src.addInput(dummy());
|
||||||
|
|
||||||
tx = bcoin.mtx()
|
tx = bcoin.mtx()
|
||||||
.addInput(src, 0)
|
.addInput(src, 0)
|
||||||
@ -187,7 +190,7 @@ describe('Wallet', function() {
|
|||||||
t1 = bcoin.mtx()
|
t1 = bcoin.mtx()
|
||||||
.addOutput(w.getAddress(), 50000)
|
.addOutput(w.getAddress(), 50000)
|
||||||
.addOutput(w.getAddress(), 1000);
|
.addOutput(w.getAddress(), 1000);
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1.ts = utils.now();
|
t1.ts = utils.now();
|
||||||
t1.height = 1;
|
t1.height = 1;
|
||||||
|
|
||||||
@ -347,7 +350,7 @@ describe('Wallet', function() {
|
|||||||
t1 = bcoin.mtx()
|
t1 = bcoin.mtx()
|
||||||
.addOutput(w.getAddress(), 50000)
|
.addOutput(w.getAddress(), 50000)
|
||||||
.addOutput(w.getAddress(), 1000);
|
.addOutput(w.getAddress(), 1000);
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1.ts = utils.now();
|
t1.ts = utils.now();
|
||||||
t1.height = 1;
|
t1.height = 1;
|
||||||
|
|
||||||
@ -416,17 +419,20 @@ describe('Wallet', function() {
|
|||||||
yield walletdb.addTX(t2);
|
yield walletdb.addTX(t2);
|
||||||
|
|
||||||
balance = yield w.getBalance();
|
balance = yield w.getBalance();
|
||||||
assert.equal(balance.unconfirmed, 71000);
|
//assert.equal(balance.unconfirmed, 71000);
|
||||||
|
assert.equal(balance.unconfirmed, 47000);
|
||||||
|
|
||||||
yield walletdb.addTX(t3);
|
yield walletdb.addTX(t3);
|
||||||
|
|
||||||
balance = yield w.getBalance();
|
balance = yield w.getBalance();
|
||||||
assert.equal(balance.unconfirmed, 69000);
|
//assert.equal(balance.unconfirmed, 69000);
|
||||||
|
assert.equal(balance.unconfirmed, 22000);
|
||||||
|
|
||||||
yield walletdb.addTX(f1);
|
yield walletdb.addTX(f1);
|
||||||
|
|
||||||
balance = yield w.getBalance();
|
balance = yield w.getBalance();
|
||||||
assert.equal(balance.unconfirmed, 58000);
|
//assert.equal(balance.unconfirmed, 58000);
|
||||||
|
assert.equal(balance.unconfirmed, 11000);
|
||||||
|
|
||||||
txs = yield w.getHistory();
|
txs = yield w.getHistory();
|
||||||
assert(txs.some(function(tx) {
|
assert(txs.some(function(tx) {
|
||||||
@ -478,7 +484,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(w1.getAddress(), 5460)
|
.addOutput(w1.getAddress(), 5460)
|
||||||
.addOutput(w1.getAddress(), 5460);
|
.addOutput(w1.getAddress(), 5460);
|
||||||
|
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1 = t1.toTX();
|
t1 = t1.toTX();
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
@ -520,7 +526,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(w1.getAddress(), 5460)
|
.addOutput(w1.getAddress(), 5460)
|
||||||
.addOutput(w1.getAddress(), 5460);
|
.addOutput(w1.getAddress(), 5460);
|
||||||
|
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1 = t1.toTX();
|
t1 = t1.toTX();
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
@ -578,7 +584,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(w1.getAddress(), 5460)
|
.addOutput(w1.getAddress(), 5460)
|
||||||
.addOutput(w1.getAddress(), 5460);
|
.addOutput(w1.getAddress(), 5460);
|
||||||
|
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1 = t1.toTX();
|
t1 = t1.toTX();
|
||||||
|
|
||||||
// Coinbase
|
// Coinbase
|
||||||
@ -588,7 +594,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(w2.getAddress(), 5460)
|
.addOutput(w2.getAddress(), 5460)
|
||||||
.addOutput(w2.getAddress(), 5460);
|
.addOutput(w2.getAddress(), 5460);
|
||||||
|
|
||||||
t2.addInput(dummyInput);
|
t2.addInput(dummy());
|
||||||
t2 = t2.toTX();
|
t2 = t2.toTX();
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
@ -707,7 +713,7 @@ describe('Wallet', function() {
|
|||||||
else
|
else
|
||||||
utx.addOutput({ address: addr, value: 5460 * 10 });
|
utx.addOutput({ address: addr, value: 5460 * 10 });
|
||||||
|
|
||||||
utx.addInput(dummyInput);
|
utx.addInput(dummy());
|
||||||
utx = utx.toTX();
|
utx = utx.toTX();
|
||||||
|
|
||||||
// Simulate a confirmation
|
// Simulate a confirmation
|
||||||
@ -816,7 +822,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(rec.getAddress(), 5460)
|
.addOutput(rec.getAddress(), 5460)
|
||||||
.addOutput(rec.getAddress(), 5460);
|
.addOutput(rec.getAddress(), 5460);
|
||||||
|
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1 = t1.toTX();
|
t1 = t1.toTX();
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
@ -878,7 +884,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(w.getAddress(), 5460)
|
.addOutput(w.getAddress(), 5460)
|
||||||
.addOutput(account.receive.getAddress(), 5460);
|
.addOutput(account.receive.getAddress(), 5460);
|
||||||
|
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1 = t1.toTX();
|
t1 = t1.toTX();
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
@ -903,7 +909,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(account.receive.getAddress(), 5460);
|
.addOutput(account.receive.getAddress(), 5460);
|
||||||
|
|
||||||
t1.ps = 0xdeadbeef;
|
t1.ps = 0xdeadbeef;
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1 = t1.toTX();
|
t1 = t1.toTX();
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
@ -927,7 +933,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(w.getAddress(), 5460)
|
.addOutput(w.getAddress(), 5460)
|
||||||
.addOutput(w.getAddress(), 5460);
|
.addOutput(w.getAddress(), 5460);
|
||||||
|
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1 = t1.toTX();
|
t1 = t1.toTX();
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
@ -963,7 +969,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(w1.getAddress(), 5460)
|
.addOutput(w1.getAddress(), 5460)
|
||||||
.addOutput(w1.getAddress(), 5460);
|
.addOutput(w1.getAddress(), 5460);
|
||||||
|
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1 = t1.toTX();
|
t1 = t1.toTX();
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
@ -992,7 +998,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(w1.getAddress(), 5460)
|
.addOutput(w1.getAddress(), 5460)
|
||||||
.addOutput(w1.getAddress(), 5460);
|
.addOutput(w1.getAddress(), 5460);
|
||||||
|
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1 = t1.toTX();
|
t1 = t1.toTX();
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
@ -1065,7 +1071,7 @@ describe('Wallet', function() {
|
|||||||
.addOutput(key.getAddress(), 5460)
|
.addOutput(key.getAddress(), 5460)
|
||||||
.addOutput(key.getAddress(), 5460);
|
.addOutput(key.getAddress(), 5460);
|
||||||
|
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1 = t1.toTX();
|
t1 = t1.toTX();
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
@ -1194,7 +1200,7 @@ describe('Wallet', function() {
|
|||||||
// Coinbase
|
// Coinbase
|
||||||
t1 = bcoin.mtx()
|
t1 = bcoin.mtx()
|
||||||
.addOutput(addr, 50000);
|
.addOutput(addr, 50000);
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1.height = 1;
|
t1.height = 1;
|
||||||
|
|
||||||
yield alice.sign(t1);
|
yield alice.sign(t1);
|
||||||
@ -1267,7 +1273,7 @@ describe('Wallet', function() {
|
|||||||
// Coinbase
|
// Coinbase
|
||||||
t1 = bcoin.mtx()
|
t1 = bcoin.mtx()
|
||||||
.addOutput(addr, 50000);
|
.addOutput(addr, 50000);
|
||||||
t1.addInput(dummyInput);
|
t1.addInput(dummy());
|
||||||
t1.height = 1;
|
t1.height = 1;
|
||||||
|
|
||||||
yield alice.sign(t1);
|
yield alice.sign(t1);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user