all tests passing.
This commit is contained in:
parent
b1232593d8
commit
8f77cf7173
@ -24,10 +24,49 @@ function TXPool(prefix, db) {
|
||||
|
||||
this.db = db;
|
||||
this.prefix = prefix || 'pool';
|
||||
this.busy = false;
|
||||
this.jobs = [];
|
||||
}
|
||||
|
||||
utils.inherits(TXPool, EventEmitter);
|
||||
|
||||
TXPool.prototype._lock = function _lock(func, args, force) {
|
||||
var self = this;
|
||||
var called;
|
||||
|
||||
if (force) {
|
||||
assert(this.busy);
|
||||
return function unlock() {
|
||||
assert(!called);
|
||||
called = true;
|
||||
};
|
||||
}
|
||||
|
||||
if (this.busy) {
|
||||
this.jobs.push([func, args]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.busy = true;
|
||||
|
||||
return function unlock() {
|
||||
var item, block;
|
||||
|
||||
assert(!called);
|
||||
called = true;
|
||||
|
||||
self.busy = false;
|
||||
|
||||
if (self.jobs.length === 0) {
|
||||
self.emit('flush');
|
||||
return;
|
||||
}
|
||||
|
||||
item = self.jobs.shift();
|
||||
item[0].apply(self, item[1]);
|
||||
};
|
||||
};
|
||||
|
||||
TXPool.prototype.add = function add(tx, callback) {
|
||||
var self = this;
|
||||
|
||||
@ -37,106 +76,33 @@ TXPool.prototype.add = function add(tx, callback) {
|
||||
}, callback);
|
||||
}
|
||||
|
||||
this._filter(tx, function(err, result) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!result)
|
||||
return callback(null, false);
|
||||
|
||||
return self._add(tx, callback);
|
||||
});
|
||||
};
|
||||
|
||||
TXPool.prototype._filter = function _filter(tx, callback) {
|
||||
return callback(null, true);
|
||||
return self._add(tx, callback);
|
||||
};
|
||||
|
||||
TXPool.prototype._hasAddress = function _hasAddress(address, callback) {
|
||||
var p = this.prefix + '/';
|
||||
var self = this;
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (!address)
|
||||
return callback(null, false);
|
||||
|
||||
var iter = this.db.db.iterator({
|
||||
gte: p + 'a/' + address,
|
||||
lte: p + 'a/' + address + '~',
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
keyAsBuffer: false
|
||||
});
|
||||
|
||||
(function next() {
|
||||
iter.next(function(err, key, value) {
|
||||
if (err) {
|
||||
return iter.end(function() {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (key === undefined) {
|
||||
return iter.end(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return callback(null, ids);
|
||||
});
|
||||
}
|
||||
|
||||
return iter.end(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
callback(null, true);
|
||||
});
|
||||
});
|
||||
})();
|
||||
};
|
||||
|
||||
TXPool.prototype._addOrphan = function _addOrphan(orphans, orphan) {
|
||||
var tx = orphan.tx.toExtended(true);
|
||||
var buf;
|
||||
|
||||
buf = new Buffer(tx.length + 4);
|
||||
utils.copy(tx, buf, 0);
|
||||
utils.writeU32(buf, orphan.index, tx.length);
|
||||
|
||||
if (!orphans)
|
||||
return buf;
|
||||
|
||||
return Buffer.concat([orphans, buf]);
|
||||
};
|
||||
|
||||
TXPool.prototype._fromOrphans = function _fromOrphans(buf) {
|
||||
var txs = [];
|
||||
var tx, index, buf;
|
||||
|
||||
if (!buf)
|
||||
return txs;
|
||||
|
||||
while (buf.length) {
|
||||
tx = bcoin.tx.fromExtended(buf, true);
|
||||
index = utils.readU32(buf, tx._extendedSize);
|
||||
buf = buf.slice(tx._extendedSize + 4);
|
||||
txs.push({ tx: tx, index: index });
|
||||
}
|
||||
|
||||
return txs;
|
||||
return callback(null, true);
|
||||
};
|
||||
|
||||
// This big scary function is what a persistent tx pool
|
||||
// looks like. It's a semi mempool in that it can handle
|
||||
// receiving txs out of order.
|
||||
TXPool.prototype._add = function add(tx, callback) {
|
||||
var self = this;
|
||||
var p = this.prefix + '/';
|
||||
var hash = tx.hash('hex');
|
||||
var updated = false;
|
||||
var batch;
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
var batch = this.db.batch();
|
||||
batch = this.db.batch();
|
||||
|
||||
self.getTX(hash, function(err, existing) {
|
||||
this.getTX(hash, function(err, existing) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -219,6 +185,8 @@ TXPool.prototype._add = function add(tx, callback) {
|
||||
utils.forEachSerial(tx.inputs, function(input, next, i) {
|
||||
var key = input.prevout.hash + '/' + input.prevout.index;
|
||||
self.getCoin(input.prevout.hash, input.prevout.index, function(err, coin) {
|
||||
var type, address;
|
||||
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
@ -235,8 +203,8 @@ TXPool.prototype._add = function add(tx, callback) {
|
||||
|
||||
updated = true;
|
||||
|
||||
var type = input.getType();
|
||||
var address = input.getAddress();
|
||||
type = input.getType();
|
||||
address = input.getAddress();
|
||||
|
||||
if (type === 'pubkey' || type === 'multisig')
|
||||
address = null;
|
||||
@ -367,16 +335,21 @@ TXPool.prototype._add = function add(tx, callback) {
|
||||
}
|
||||
|
||||
function finish(err) {
|
||||
var type, adddress;
|
||||
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!orphans) {
|
||||
var type = output.getType();
|
||||
var address = output.getAddress();
|
||||
type = output.getType();
|
||||
address = output.getAddress();
|
||||
|
||||
if (type === 'pubkey' || type === 'multisig')
|
||||
address = null;
|
||||
|
||||
if (address)
|
||||
batch.put(p + 'u/a/' + address + '/' + hash + '/' + i, new Buffer([]));
|
||||
|
||||
batch.put(p + 'u/t/' + hash + '/' + i, coin.toRaw());
|
||||
updated = true;
|
||||
}
|
||||
@ -653,8 +626,6 @@ TXPool.prototype.remove = function remove(hash, callback) {
|
||||
uaddr = null;
|
||||
}
|
||||
|
||||
coinRaw = bcoin.protocol.framer.coin(input.output, true);
|
||||
|
||||
if (uaddr) {
|
||||
batch.del(p + 't/a/' + uaddr + '/' + hash);
|
||||
if (tx.ts === 0)
|
||||
@ -668,10 +639,15 @@ TXPool.prototype.remove = function remove(hash, callback) {
|
||||
new Buffer([]));
|
||||
}
|
||||
|
||||
batch.put(p + 'u/t/'
|
||||
+ input.prevout.hash
|
||||
+ '/' + input.prevout.index,
|
||||
coinRaw);
|
||||
if (input.output) {
|
||||
coinRaw = bcoin.protocol.framer.coin(input.output, true);
|
||||
batch.put(p + 'u/t/'
|
||||
+ input.prevout.hash
|
||||
+ '/' + input.prevout.index,
|
||||
coinRaw);
|
||||
}
|
||||
|
||||
batch.del(p + 'o/' + input.prevout.hash + '/' + input.prevout.index);
|
||||
});
|
||||
|
||||
tx.outputs.forEach(function(output, i) {
|
||||
@ -1148,139 +1124,6 @@ TXPool.prototype.getBalanceByAddress = function getBalanceByAddress(address, cal
|
||||
});
|
||||
};
|
||||
|
||||
function WalletPool(db) {
|
||||
TXPool.call(this, 'w', db);
|
||||
}
|
||||
|
||||
utils.inherits(WalletPool, TXPool);
|
||||
|
||||
WalletPool.prototype._filter = function _filter(tx, callback) {
|
||||
return this.testTX(tx, callback);
|
||||
};
|
||||
|
||||
WalletPool.prototype.getAll = function getAll(id, callback) {
|
||||
var self = this;
|
||||
return this.getAddresses(id, function(err, addresses) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return self.getAllByAddress(addresses, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletPool.prototype.getUnspent = function getUnspent(id, callback) {
|
||||
var self = this;
|
||||
return this.getAddresses(id, function(err, addresses) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return self.getUnspentByAddress(addresses, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletPool.prototype.getPending = function getPending(id, callback) {
|
||||
var self = this;
|
||||
return this.getAddresses(id, function(err, addresses) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return self.getPendingByAddress(addresses, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletPool.prototype.getBalance = function getBalance(id, callback) {
|
||||
var self = this;
|
||||
return this.getAddresses(id, function(err, addresses) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return self.getBalanceByAddress(addresses, callback);
|
||||
});
|
||||
};
|
||||
|
||||
WalletPool.prototype.getAddresses = function getAddresses(id, callback) {
|
||||
if (typeof id === 'string')
|
||||
return callback(null, [id]);
|
||||
|
||||
if (Array.isArray(id))
|
||||
return callback(null, id);
|
||||
|
||||
if (id.addressMap)
|
||||
return callback(null, Object.keys(id.addressMap));
|
||||
|
||||
if (typeof id === 'object')
|
||||
return callback(null, Object.keys(id));
|
||||
|
||||
return this.db.get('w/w/' + id, function(err, buf) {
|
||||
var json;
|
||||
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
try {
|
||||
json = JSON.parse(buf.toString('utf8'));
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
return callback(null, Object.keys(json.addressMap));
|
||||
});
|
||||
};
|
||||
|
||||
WalletPool.prototype.getIDs = function _getIDs(address, callback) {
|
||||
var self = this;
|
||||
var ids = [];
|
||||
|
||||
var iter = this.db.db.iterator({
|
||||
gte: 'w/a/' + address,
|
||||
lte: 'w/a/' + address + '~',
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
keyAsBuffer: false
|
||||
});
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
(function next() {
|
||||
iter.next(function(err, key, value) {
|
||||
if (err) {
|
||||
return iter.end(function() {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (key === undefined) {
|
||||
return iter.end(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return callback(null, ids);
|
||||
});
|
||||
}
|
||||
|
||||
ids.push(key.split('/')[3]);
|
||||
|
||||
next();
|
||||
});
|
||||
})();
|
||||
};
|
||||
|
||||
WalletPool.prototype.testTX = function test(tx, callback) {
|
||||
var self = this;
|
||||
|
||||
utils.forEachSerial(tx.getAddresses(), function(address, next) {
|
||||
self.getIDs(address, function(err, ids) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (ids.length > 0)
|
||||
return callback(null, true);
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return callback(null, false);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -515,23 +515,39 @@ Wallet.prototype.ownOutput = function ownOutput(tx, index) {
|
||||
return tx.testOutputs(this.addressMap, index);
|
||||
};
|
||||
|
||||
Wallet.prototype.fill = function fill(tx, options) {
|
||||
Wallet.prototype.fill = function fill(tx, options, callback) {
|
||||
var self = this;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
assert(this._initialized);
|
||||
|
||||
tx.fill(this.getUnspent(), {
|
||||
selection: options.selection || 'age',
|
||||
fee: options.fee,
|
||||
subtractFee: options.subtractFee,
|
||||
changeAddress: this.changeAddress.getAddress(),
|
||||
wallet: this,
|
||||
m: this.m,
|
||||
n: this.n
|
||||
});
|
||||
this.getUnspent(function(err, unspent) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return true;
|
||||
try {
|
||||
tx.fill(unspent, {
|
||||
selection: options.selection || 'age',
|
||||
fee: options.fee,
|
||||
subtractFee: options.subtractFee,
|
||||
changeAddress: self.changeAddress.getAddress(),
|
||||
wallet: self,
|
||||
m: self.m,
|
||||
n: self.n
|
||||
});
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
Wallet.prototype.fillPrevout = function fillPrevout(tx, callback) {
|
||||
|
||||
@ -117,6 +117,7 @@ WalletDB.prototype._init = function _init() {
|
||||
this.db = WalletDB._db[this.file];
|
||||
|
||||
this.tx = new bcoin.txdb('w', this.db);
|
||||
this.tx._hasAddress = this.hasAddress.bind(this);
|
||||
};
|
||||
|
||||
WalletDB.prototype.getJSON = function getJSON(id, callback) {
|
||||
@ -602,6 +603,48 @@ WalletDB.prototype.testTX = function test(tx, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
WalletDB.prototype.hasAddress = function hasAddress(address, callback) {
|
||||
var self = this;
|
||||
|
||||
callback = utils.ensure(callback);
|
||||
|
||||
if (!address)
|
||||
return callback(null, false);
|
||||
|
||||
var iter = this.db.db.iterator({
|
||||
gte: 'w/a/' + address,
|
||||
lte: 'w/a/' + address + '~',
|
||||
keys: true,
|
||||
values: false,
|
||||
fillCache: false,
|
||||
keyAsBuffer: false
|
||||
});
|
||||
|
||||
(function next() {
|
||||
iter.next(function(err, key, value) {
|
||||
if (err) {
|
||||
return iter.end(function() {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (key === undefined) {
|
||||
return iter.end(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return callback(null, false);
|
||||
});
|
||||
}
|
||||
|
||||
return iter.end(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
callback(null, true);
|
||||
});
|
||||
});
|
||||
})();
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -174,69 +174,44 @@ describe('Wallet', function() {
|
||||
// Fake TX should temporarly change output
|
||||
// w.addTX(fake);
|
||||
|
||||
process.on('uncaughtException', function() {
|
||||
return;
|
||||
w.db.dump(function(err, records) {
|
||||
console.log(records);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
w.addTX(fake, function(err) {
|
||||
if (err) throw err;
|
||||
assert(!err);
|
||||
w.addTX(t4, function(err) {
|
||||
if (err) throw err;
|
||||
assert(!err);
|
||||
if (0) {
|
||||
w.db.tx.getCoinByAddress(Object.keys(w.addressMap), function(err, coins) {
|
||||
console.log(coins);
|
||||
cb();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (0) {
|
||||
w.db.dump(function(err, records) {
|
||||
console.log(records);
|
||||
//process.exit(1);
|
||||
return cb();
|
||||
});
|
||||
return;
|
||||
}
|
||||
w.getBalance(function(err, balance) {
|
||||
w.addTX(t4, function(err) {
|
||||
assert(!err);
|
||||
assert.equal(balance.toString(10), '22500');
|
||||
// assert.equal(balance.toString(10), '22000');
|
||||
w.addTX(t1, function(err) {
|
||||
w.getBalance(function(err, balance) {
|
||||
assert(!err);
|
||||
assert.equal(balance.toString(10), '73000');
|
||||
w.addTX(t2, function(err) {
|
||||
w.getBalance(function(err, balance) {
|
||||
assert(!err);
|
||||
assert.equal(balance.toString(10), '22500');
|
||||
w.addTX(t1, function(err) {
|
||||
w.getBalance(function(err, balance) {
|
||||
assert(!err);
|
||||
w.getBalance(function(err, balance) {
|
||||
assert.equal(balance.toString(10), '73000');
|
||||
w.addTX(t2, function(err) {
|
||||
assert(!err);
|
||||
assert.equal(balance.toString(10), '47000');
|
||||
w.addTX(t3, function(err) {
|
||||
w.getBalance(function(err, balance) {
|
||||
assert(!err);
|
||||
w.getBalance(function(err, balance) {
|
||||
assert.equal(balance.toString(10), '47000');
|
||||
w.addTX(t3, function(err) {
|
||||
assert(!err);
|
||||
assert.equal(balance.toString(10), '22000');
|
||||
w.addTX(f1, function(err) {
|
||||
w.getBalance(function(err, balance) {
|
||||
assert(!err);
|
||||
w.getBalance(function(err, balance) {
|
||||
assert.equal(balance.toString(10), '22000');
|
||||
w.addTX(f1, function(err) {
|
||||
assert(!err);
|
||||
assert.equal(balance.toString(10), '11000');
|
||||
w.getAll(function(err, txs) {
|
||||
assert(txs.some(function(tx) {
|
||||
return tx.hash('hex') === f1.hash('hex');
|
||||
}));
|
||||
w.getBalance(function(err, balance) {
|
||||
assert(!err);
|
||||
assert.equal(balance.toString(10), '11000');
|
||||
w.getAll(function(err, txs) {
|
||||
assert(txs.some(function(tx) {
|
||||
return tx.hash('hex') === f1.hash('hex');
|
||||
}));
|
||||
|
||||
var w2 = bcoin.wallet.fromJSON(w.toJSON());
|
||||
// assert.equal(w2.getBalance().toString(10), '11000');
|
||||
// assert(w2.getAll().some(function(tx) {
|
||||
// return tx.hash('hex') === f1.hash('hex');
|
||||
// }));
|
||||
cb();
|
||||
var w2 = bcoin.wallet.fromJSON(w.toJSON());
|
||||
// assert.equal(w2.getBalance().toString(10), '11000');
|
||||
// assert(w2.getAll().some(function(tx) {
|
||||
// return tx.hash('hex') === f1.hash('hex');
|
||||
// }));
|
||||
cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -250,7 +225,6 @@ describe('Wallet', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fill tx with inputs', function(cb) {
|
||||
@ -259,33 +233,34 @@ describe('Wallet', function() {
|
||||
|
||||
// Coinbase
|
||||
var t1 = bcoin.mtx().addOutput(w1, 5460).addOutput(w1, 5460).addOutput(w1, 5460).addOutput(w1, 5460);
|
||||
t1.addInput(dummyInput);
|
||||
|
||||
// Fake TX should temporarly change output
|
||||
w1.addTX(t1);
|
||||
w1.addTX(t1, function(err) {
|
||||
assert(!err);
|
||||
|
||||
// Create new transaction
|
||||
var t2 = bcoin.mtx().addOutput(w2, 5460);
|
||||
assert(w1.fill(t2));
|
||||
w1.sign(t2);
|
||||
assert(t2.verify());
|
||||
// Create new transaction
|
||||
var t2 = bcoin.mtx().addOutput(w2, 5460);
|
||||
w1.fill(t2, function(err) {
|
||||
assert(!err);
|
||||
w1.sign(t2);
|
||||
assert(t2.verify());
|
||||
|
||||
assert.equal(t2.getInputValue().toString(10), 16380);
|
||||
// If change < dust and is added to outputs:
|
||||
// assert.equal(t2.getOutputValue().toString(10), 6380);
|
||||
// If change < dust and is added to fee:
|
||||
assert.equal(t2.getOutputValue().toString(10), 5460);
|
||||
assert.equal(t2.getInputValue().toString(10), 16380);
|
||||
// If change < dust and is added to outputs:
|
||||
// assert.equal(t2.getOutputValue().toString(10), 6380);
|
||||
// If change < dust and is added to fee:
|
||||
assert.equal(t2.getOutputValue().toString(10), 5460);
|
||||
|
||||
// Create new transaction
|
||||
var t3 = bcoin.mtx().addOutput(w2, 15000);
|
||||
try {
|
||||
w1.fill(t3);
|
||||
} catch (e) {
|
||||
var err = e;
|
||||
}
|
||||
assert(err);
|
||||
assert.equal(err.requiredFunds.toString(10), 25000);
|
||||
|
||||
cb();
|
||||
// Create new transaction
|
||||
var t3 = bcoin.mtx().addOutput(w2, 15000);
|
||||
w1.fill(t3, function(err) {
|
||||
assert(err);
|
||||
assert.equal(err.requiredFunds.toString(10), 25000);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should sign multiple inputs using different keys', function(cb) {
|
||||
@ -297,60 +272,69 @@ describe('Wallet', function() {
|
||||
var t1 = bcoin.mtx().addOutput(w1, 5460).addOutput(w1, 5460).addOutput(w1, 5460).addOutput(w1, 5460);
|
||||
t1.addInput(dummyInput);
|
||||
// Fake TX should temporarly change output
|
||||
w1.addTX(t1);
|
||||
// Coinbase
|
||||
var t2 = bcoin.mtx().addOutput(w2, 5460).addOutput(w2, 5460).addOutput(w2, 5460).addOutput(w2, 5460);
|
||||
t2.addInput(dummyInput);
|
||||
// Fake TX should temporarly change output
|
||||
w2.addTX(t2);
|
||||
|
||||
// Create our tx with an output
|
||||
var tx = bcoin.mtx();
|
||||
tx.addOutput(to, 5460);
|
||||
w1.addTX(t1, function(err) {
|
||||
assert(!err);
|
||||
w2.addTX(t2, function(err) {
|
||||
assert(!err);
|
||||
|
||||
var cost = tx.getOutputValue();
|
||||
var total = cost.add(new bn(constants.tx.minFee));
|
||||
// Create our tx with an output
|
||||
var tx = bcoin.mtx();
|
||||
tx.addOutput(to, 5460);
|
||||
|
||||
var unspent1 = w1.getUnspent();
|
||||
var unspent2 = w2.getUnspent();
|
||||
var cost = tx.getOutputValue();
|
||||
var total = cost.add(new bn(constants.tx.minFee));
|
||||
|
||||
// Add dummy output (for `left`) to calculate maximum TX size
|
||||
tx.addOutput(w1, new bn(0));
|
||||
w1.getUnspent(function(err, unspent1) {
|
||||
assert(!err);
|
||||
w2.getUnspent(function(err, unspent2) {
|
||||
assert(!err);
|
||||
|
||||
// Add our unspent inputs to sign
|
||||
tx.addInput(unspent1[0]);
|
||||
tx.addInput(unspent1[1]);
|
||||
tx.addInput(unspent2[0]);
|
||||
// Add dummy output (for `left`) to calculate maximum TX size
|
||||
tx.addOutput(w1, new bn(0));
|
||||
|
||||
var left = tx.getInputValue().sub(total);
|
||||
if (left.cmpn(constants.tx.dustThreshold) < 0) {
|
||||
tx.outputs[tx.outputs.length - 2].value.iadd(left);
|
||||
left = new bn(0);
|
||||
}
|
||||
if (left.cmpn(0) === 0)
|
||||
tx.outputs.pop();
|
||||
else
|
||||
tx.outputs[tx.outputs.length - 1].value = left;
|
||||
// Add our unspent inputs to sign
|
||||
tx.addInput(unspent1[0]);
|
||||
tx.addInput(unspent1[1]);
|
||||
tx.addInput(unspent2[0]);
|
||||
|
||||
// Sign transaction
|
||||
assert.equal(w1.sign(tx), 2);
|
||||
assert.equal(w2.sign(tx), 1);
|
||||
var left = tx.getInputValue().sub(total);
|
||||
if (left.cmpn(constants.tx.dustThreshold) < 0) {
|
||||
tx.outputs[tx.outputs.length - 2].value.iadd(left);
|
||||
left = new bn(0);
|
||||
}
|
||||
if (left.cmpn(0) === 0)
|
||||
tx.outputs.pop();
|
||||
else
|
||||
tx.outputs[tx.outputs.length - 1].value = left;
|
||||
|
||||
// Verify
|
||||
assert.equal(tx.verify(), true);
|
||||
// Sign transaction
|
||||
assert.equal(w1.sign(tx), 2);
|
||||
assert.equal(w2.sign(tx), 1);
|
||||
|
||||
// Sign transaction using `inputs` and `off` params.
|
||||
tx.inputs.length = 0;
|
||||
tx.addInput(unspent1[1]);
|
||||
tx.addInput(unspent1[2]);
|
||||
tx.addInput(unspent2[1]);
|
||||
assert.equal(w1.sign(tx, 'all'), 2);
|
||||
assert.equal(w2.sign(tx, 'all'), 1);
|
||||
// Verify
|
||||
assert.equal(tx.verify(), true);
|
||||
|
||||
// Verify
|
||||
assert.equal(tx.verify(), true);
|
||||
// Sign transaction using `inputs` and `off` params.
|
||||
tx.inputs.length = 0;
|
||||
tx.addInput(unspent1[1]);
|
||||
tx.addInput(unspent1[2]);
|
||||
tx.addInput(unspent2[1]);
|
||||
assert.equal(w1.sign(tx, 'all'), 2);
|
||||
assert.equal(w2.sign(tx, 'all'), 1);
|
||||
|
||||
cb();
|
||||
// Verify
|
||||
assert.equal(tx.verify(), true);
|
||||
|
||||
cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function multisig(witness, bullshitNesting, cb) {
|
||||
@ -430,72 +414,86 @@ describe('Wallet', function() {
|
||||
|
||||
assert.equal(w1.receiveDepth, 1);
|
||||
|
||||
w1.addTX(utx);
|
||||
w2.addTX(utx);
|
||||
w3.addTX(utx);
|
||||
w1.addTX(utx, function(err) {
|
||||
assert(!err);
|
||||
w2.addTX(utx, function(err) {
|
||||
assert(!err);
|
||||
w3.addTX(utx, function(err) {
|
||||
assert(!err);
|
||||
|
||||
assert.equal(w1.receiveDepth, 2);
|
||||
assert.equal(w1.changeDepth, 1);
|
||||
assert.equal(w1.receiveDepth, 2);
|
||||
assert.equal(w1.changeDepth, 1);
|
||||
|
||||
assert(w1.getAddress() !== addr);
|
||||
addr = w1.getAddress();
|
||||
assert.equal(w1.getAddress(), addr);
|
||||
assert.equal(w2.getAddress(), addr);
|
||||
assert.equal(w3.getAddress(), addr);
|
||||
assert(w1.getAddress() !== addr);
|
||||
addr = w1.getAddress();
|
||||
assert.equal(w1.getAddress(), addr);
|
||||
assert.equal(w2.getAddress(), addr);
|
||||
assert.equal(w3.getAddress(), addr);
|
||||
|
||||
// Create a tx requiring 2 signatures
|
||||
var send = bcoin.mtx();
|
||||
send.addOutput({ address: receive.getAddress(), value: 5460 });
|
||||
assert(!send.verify(null, true, flags));
|
||||
var result = w1.fill(send, { m: w1.m, n: w1.n });
|
||||
assert(result);
|
||||
w1.sign(send);
|
||||
// Create a tx requiring 2 signatures
|
||||
var send = bcoin.mtx();
|
||||
send.addOutput({ address: receive.getAddress(), value: 5460 });
|
||||
assert(!send.verify(null, true, flags));
|
||||
w1.fill(send, { m: w1.m, n: w1.n }, function(err) {
|
||||
assert(!err);
|
||||
|
||||
assert(!send.verify(null, true, flags));
|
||||
w2.sign(send);
|
||||
w1.sign(send);
|
||||
|
||||
assert(send.verify(null, true, flags));
|
||||
assert(!send.verify(null, true, flags));
|
||||
w2.sign(send);
|
||||
|
||||
assert.equal(w1.changeDepth, 1);
|
||||
var change = w1.changeAddress.getAddress();
|
||||
assert.equal(w1.changeAddress.getAddress(), change);
|
||||
assert.equal(w2.changeAddress.getAddress(), change);
|
||||
assert.equal(w3.changeAddress.getAddress(), change);
|
||||
assert(send.verify(null, true, flags));
|
||||
|
||||
// Simulate a confirmation
|
||||
send.ps = 0;
|
||||
send.ts = 1;
|
||||
send.height = 1;
|
||||
assert.equal(w1.changeDepth, 1);
|
||||
var change = w1.changeAddress.getAddress();
|
||||
assert.equal(w1.changeAddress.getAddress(), change);
|
||||
assert.equal(w2.changeAddress.getAddress(), change);
|
||||
assert.equal(w3.changeAddress.getAddress(), change);
|
||||
|
||||
w1.addTX(send);
|
||||
w2.addTX(send);
|
||||
w3.addTX(send);
|
||||
// Simulate a confirmation
|
||||
send.ps = 0;
|
||||
send.ts = 1;
|
||||
send.height = 1;
|
||||
|
||||
assert.equal(w1.receiveDepth, 2);
|
||||
assert.equal(w1.changeDepth, 2);
|
||||
w1.addTX(send, function(err) {
|
||||
assert(!err);
|
||||
w2.addTX(send, function(err) {
|
||||
assert(!err);
|
||||
w3.addTX(send, function(err) {
|
||||
assert(!err);
|
||||
|
||||
assert(w1.getAddress() === addr);
|
||||
assert(w1.changeAddress.getAddress() !== change);
|
||||
change = w1.changeAddress.getAddress();
|
||||
assert.equal(w1.changeAddress.getAddress(), change);
|
||||
assert.equal(w2.changeAddress.getAddress(), change);
|
||||
assert.equal(w3.changeAddress.getAddress(), change);
|
||||
assert.equal(w1.receiveDepth, 2);
|
||||
assert.equal(w1.changeDepth, 2);
|
||||
|
||||
if (witness)
|
||||
send.inputs[0].witness[2] = new Buffer([]);
|
||||
else
|
||||
send.inputs[0].script[2] = 0;
|
||||
assert(w1.getAddress() === addr);
|
||||
assert(w1.changeAddress.getAddress() !== change);
|
||||
change = w1.changeAddress.getAddress();
|
||||
assert.equal(w1.changeAddress.getAddress(), change);
|
||||
assert.equal(w2.changeAddress.getAddress(), change);
|
||||
assert.equal(w3.changeAddress.getAddress(), change);
|
||||
|
||||
assert(!send.verify(null, true, flags));
|
||||
assert.equal(send.getFee().toNumber(), 10000);
|
||||
if (witness)
|
||||
send.inputs[0].witness[2] = new Buffer([]);
|
||||
else
|
||||
send.inputs[0].script[2] = 0;
|
||||
|
||||
w3 = bcoin.wallet.fromJSON(w3.toJSON());
|
||||
assert.equal(w3.receiveDepth, 2);
|
||||
assert.equal(w3.changeDepth, 2);
|
||||
assert.equal(w3.getAddress(), addr);
|
||||
assert.equal(w3.changeAddress.getAddress(), change);
|
||||
assert(!send.verify(null, true, flags));
|
||||
assert.equal(send.getFee().toNumber(), 10000);
|
||||
|
||||
cb();
|
||||
w3 = bcoin.wallet.fromJSON(w3.toJSON());
|
||||
assert.equal(w3.receiveDepth, 2);
|
||||
assert.equal(w3.changeDepth, 2);
|
||||
assert.equal(w3.getAddress(), addr);
|
||||
assert.equal(w3.changeAddress.getAddress(), change);
|
||||
|
||||
cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it('should verify 2-of-3 scripthash tx', function(cb) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user