walletdb. tests. refactor.
This commit is contained in:
parent
8f77cf7173
commit
e31e485553
@ -161,9 +161,7 @@ Fullnode.prototype._init = function _init() {
|
|||||||
if (!self.miner.address)
|
if (!self.miner.address)
|
||||||
self.miner.address = wallet.getAddress();
|
self.miner.address = wallet.getAddress();
|
||||||
|
|
||||||
wallet.on('load', function() {
|
load();
|
||||||
load();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.chain.once('load', function() {
|
this.chain.once('load', function() {
|
||||||
|
|||||||
@ -1224,44 +1224,29 @@ Pool.prototype.addWallet = function addWallet(wallet, callback) {
|
|||||||
if (this.loading)
|
if (this.loading)
|
||||||
return this.once('load', this.addWallet.bind(this, wallet, callback));
|
return this.once('load', this.addWallet.bind(this, wallet, callback));
|
||||||
|
|
||||||
if (!this.options.spv) {
|
callback = utils.asyncify(callback);
|
||||||
wallet.getPending().forEach(function(tx) {
|
|
||||||
self.sendTX(tx);
|
|
||||||
});
|
|
||||||
return utils.nextTick(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.wallets.indexOf(wallet) !== -1)
|
if (this.options.spv) {
|
||||||
return false;
|
if (this.wallets.indexOf(wallet) !== -1)
|
||||||
|
|
||||||
this.watchWallet(wallet);
|
|
||||||
this.wallets.push(wallet);
|
|
||||||
|
|
||||||
function search() {
|
|
||||||
// Relay pending TXs
|
|
||||||
// NOTE: It is important to do it after
|
|
||||||
// search, because search could add TS
|
|
||||||
// to pending TXs, thus making them
|
|
||||||
// confirmed.
|
|
||||||
wallet.getPending().forEach(function(tx) {
|
|
||||||
self.sendTX(tx);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (self._pendingSearch)
|
|
||||||
return callback();
|
return callback();
|
||||||
|
|
||||||
self._pendingSearch = true;
|
this.watchWallet(wallet);
|
||||||
|
this.wallets.push(wallet);
|
||||||
utils.nextTick(function() {
|
|
||||||
self._pendingSearch = false;
|
|
||||||
self.searchWallet(callback);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wallet.loading)
|
wallet.getPending(function(err, txs) {
|
||||||
wallet.once('load', search);
|
if (err)
|
||||||
else
|
return callback(err);
|
||||||
search();
|
|
||||||
|
txs.forEach(function(tx) {
|
||||||
|
self.sendTX(tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!self.options.spv)
|
||||||
|
return callback();
|
||||||
|
|
||||||
|
self.searchWallet(wallet, callback);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.removeWallet = function removeWallet(wallet) {
|
Pool.prototype.removeWallet = function removeWallet(wallet) {
|
||||||
@ -1303,57 +1288,56 @@ Pool.prototype.unwatchWallet = function unwatchWallet(wallet) {
|
|||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.searchWallet = function(ts, height, callback) {
|
Pool.prototype.searchWallet = function(wallet, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var wallet;
|
var wallet;
|
||||||
|
|
||||||
assert(!this.loading);
|
assert(!this.loading);
|
||||||
|
|
||||||
if (!this.options.spv)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (ts == null || typeof ts === 'function') {
|
|
||||||
callback = ts;
|
|
||||||
height = this.wallets.reduce(function(height, wallet) {
|
|
||||||
if (wallet.lastHeight < height)
|
|
||||||
return wallet.lastHeight;
|
|
||||||
return height;
|
|
||||||
}, Infinity);
|
|
||||||
assert(height !== Infinity);
|
|
||||||
ts = this.wallets.reduce(function(ts, wallet) {
|
|
||||||
if (wallet.lastTs < ts)
|
|
||||||
return wallet.lastTs;
|
|
||||||
return ts;
|
|
||||||
}, Infinity);
|
|
||||||
assert(ts !== Infinity);
|
|
||||||
} else if (typeof ts !== 'number') {
|
|
||||||
callback = height;
|
|
||||||
wallet = ts;
|
|
||||||
if (wallet.loading) {
|
|
||||||
wallet.once('load', function() {
|
|
||||||
self.searchWallet(wallet);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ts = wallet.lastTs;
|
|
||||||
height = wallet.lastHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback = utils.asyncify(callback);
|
callback = utils.asyncify(callback);
|
||||||
|
|
||||||
// Always prefer height
|
if (!this.options.spv)
|
||||||
if (height > 0) {
|
return callback();
|
||||||
// Back one week
|
|
||||||
if (!height || height === -1)
|
|
||||||
height = this.chain.height - (7 * 24 * 6);
|
|
||||||
|
|
||||||
this.chain.resetHeightAsync(height, function(err) {
|
wallet.getLast(function(err, ts, height) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
// Always prefer height
|
||||||
|
if (height > 0) {
|
||||||
|
// Back one week
|
||||||
|
if (!height || height === -1)
|
||||||
|
height = self.chain.height - (7 * 24 * 6);
|
||||||
|
|
||||||
|
self.chain.resetHeightAsync(height, function(err) {
|
||||||
|
if (err) {
|
||||||
|
utils.debug('Failed to reset height: %s', err.stack + '');
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.debug('Wallet height: %s', height);
|
||||||
|
utils.debug(
|
||||||
|
'Reverted chain to height=%d (%s)',
|
||||||
|
self.chain.height,
|
||||||
|
new Date(self.chain.tip.ts * 1000)
|
||||||
|
);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ts)
|
||||||
|
ts = utils.now() - 7 * 24 * 3600;
|
||||||
|
|
||||||
|
self.chain.resetTimeAsync(ts, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
utils.debug('Failed to reset height: %s', err.stack + '');
|
utils.debug('Failed to reset time: %s', err.stack + '');
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.debug('Wallet height: %s', height);
|
utils.debug('Wallet time: %s', new Date(ts * 1000));
|
||||||
utils.debug(
|
utils.debug(
|
||||||
'Reverted chain to height=%d (%s)',
|
'Reverted chain to height=%d (%s)',
|
||||||
self.chain.height,
|
self.chain.height,
|
||||||
@ -1362,27 +1346,6 @@ Pool.prototype.searchWallet = function(ts, height, callback) {
|
|||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ts)
|
|
||||||
ts = utils.now() - 7 * 24 * 3600;
|
|
||||||
|
|
||||||
this.chain.resetTimeAsync(ts, function(err) {
|
|
||||||
if (err) {
|
|
||||||
utils.debug('Failed to reset time: %s', err.stack + '');
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.debug('Wallet time: %s', new Date(ts * 1000));
|
|
||||||
utils.debug(
|
|
||||||
'Reverted chain to height=%d (%s)',
|
|
||||||
self.chain.height,
|
|
||||||
new Date(self.chain.tip.ts * 1000)
|
|
||||||
);
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -601,7 +601,7 @@ TX.prototype.fillPrevout = function fillPrevout(txs, unspent) {
|
|||||||
} else if (txs instanceof bcoin.txpool) {
|
} else if (txs instanceof bcoin.txpool) {
|
||||||
unspent = txs._unspent;
|
unspent = txs._unspent;
|
||||||
txs = txs._all;
|
txs = txs._all;
|
||||||
} else if (txs instanceof bcoin.wallet) {
|
} else if (txs instanceof bcoin.wallet && txs.tx) {
|
||||||
unspent = txs.tx._unspent;
|
unspent = txs.tx._unspent;
|
||||||
txs = txs.tx._all;
|
txs = txs.tx._all;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,31 +80,36 @@ TXPool.prototype.add = function add(tx, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TXPool.prototype._hasAddress = function _hasAddress(address, callback) {
|
TXPool.prototype._hasAddress = function _hasAddress(address, callback) {
|
||||||
callback = utils.ensure(callback);
|
|
||||||
|
|
||||||
if (!address)
|
if (!address)
|
||||||
return callback(null, false);
|
return callback(null, false);
|
||||||
|
|
||||||
return callback(null, true);
|
return callback(null, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// This big scary function is what a persistent tx pool
|
// This big scary function is what a persistent tx pool
|
||||||
// looks like. It's a semi mempool in that it can handle
|
// looks like. It's a semi mempool in that it can handle
|
||||||
// receiving txs out of order.
|
// receiving txs out of order.
|
||||||
TXPool.prototype._add = function add(tx, callback) {
|
TXPool.prototype._add = function add(tx, callback, force) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var p = this.prefix + '/';
|
var p = this.prefix + '/';
|
||||||
var hash = tx.hash('hex');
|
var hash = tx.hash('hex');
|
||||||
var updated = false;
|
var updated = false;
|
||||||
var batch;
|
var batch;
|
||||||
|
|
||||||
callback = utils.ensure(callback);
|
var unlock = this._lock(add, [tx, callback], force);
|
||||||
|
if (!unlock)
|
||||||
|
return;
|
||||||
|
|
||||||
batch = this.db.batch();
|
function done(err, result) {
|
||||||
|
unlock();
|
||||||
|
if (callback)
|
||||||
|
callback(err, result);
|
||||||
|
};
|
||||||
|
|
||||||
this.getTX(hash, function(err, existing) {
|
this.getTX(hash, function(err, existing) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return done(err);
|
||||||
|
|
||||||
|
batch = self.db.batch();
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
// Tricky - update the tx and coin in storage,
|
// Tricky - update the tx and coin in storage,
|
||||||
@ -137,10 +142,10 @@ TXPool.prototype._add = function add(tx, callback) {
|
|||||||
batch.del(p + 'p/a/' + uaddr + '/' + hash);
|
batch.del(p + 'p/a/' + uaddr + '/' + hash);
|
||||||
});
|
});
|
||||||
|
|
||||||
tx.outputs.forEach(function(output) {
|
utils.forEachSerial(tx.outputs, function(output, next, i) {
|
||||||
var type = output.getType();
|
var type = output.getType();
|
||||||
var address = output.getAddress();
|
var address = output.getAddress();
|
||||||
var uaddr, coinRaw;
|
var uaddr;
|
||||||
|
|
||||||
if (type === 'pubkey' || type === 'multisig')
|
if (type === 'pubkey' || type === 'multisig')
|
||||||
address = null;
|
address = null;
|
||||||
@ -157,28 +162,37 @@ TXPool.prototype._add = function add(tx, callback) {
|
|||||||
if (uaddr)
|
if (uaddr)
|
||||||
batch.del(p + 'p/a/' + uaddr + '/' + hash);
|
batch.del(p + 'p/a/' + uaddr + '/' + hash);
|
||||||
|
|
||||||
coinRaw = bcoin.protocol.framer.coin({
|
self.getCoin(hash, i, function(err, coin) {
|
||||||
version: tx.version,
|
if (err)
|
||||||
height: tx.height,
|
return next(err);
|
||||||
value: output.value,
|
|
||||||
script: output.script,
|
|
||||||
hash: hash,
|
|
||||||
index: i,
|
|
||||||
spent: false
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
batch.put(p + 'u/t/' + hash + '/' + i, coinRaw);
|
if (!coin)
|
||||||
});
|
return next();
|
||||||
|
|
||||||
batch.write(function(err) {
|
coin.height = tx.height;
|
||||||
|
|
||||||
|
batch.put(p + 'u/t/' + hash + '/' + i, coin.toRaw());
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return done(err);
|
||||||
self.emit('confirmed', tx);
|
|
||||||
self.emit('tx', tx);
|
batch.write(function(err) {
|
||||||
return callback(null, true);
|
if (err)
|
||||||
|
return done(err);
|
||||||
|
|
||||||
|
self.emit('confirmed', tx);
|
||||||
|
self.emit('tx', tx);
|
||||||
|
|
||||||
|
return done(null, true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return callback(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consume unspent money or add orphans
|
// Consume unspent money or add orphans
|
||||||
@ -199,7 +213,7 @@ TXPool.prototype._add = function add(tx, callback) {
|
|||||||
|
|
||||||
// Skip invalid transactions
|
// Skip invalid transactions
|
||||||
if (!tx.verify(i))
|
if (!tx.verify(i))
|
||||||
return callback(null, false);
|
return done(null, false);
|
||||||
|
|
||||||
updated = true;
|
updated = true;
|
||||||
|
|
||||||
@ -226,7 +240,7 @@ TXPool.prototype._add = function add(tx, callback) {
|
|||||||
// Only add orphans if this input is ours.
|
// Only add orphans if this input is ours.
|
||||||
self._hasAddress(input.getAddress(), function(err, result) {
|
self._hasAddress(input.getAddress(), function(err, result) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return done(err);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return next();
|
return next();
|
||||||
@ -234,15 +248,13 @@ TXPool.prototype._add = function add(tx, callback) {
|
|||||||
// Add orphan, if no parent transaction is yet known
|
// Add orphan, if no parent transaction is yet known
|
||||||
self.db.get(p + 'o/' + key, function(err, orphans) {
|
self.db.get(p + 'o/' + key, function(err, orphans) {
|
||||||
if (err && err.type !== 'NotFoundError')
|
if (err && err.type !== 'NotFoundError')
|
||||||
return callback(err);
|
return done(err);
|
||||||
|
|
||||||
// orphans = self._addOrphan(orphans, { tx: tx, index: i });
|
|
||||||
|
|
||||||
if (orphans) {
|
if (orphans) {
|
||||||
try {
|
try {
|
||||||
orphans = JSON.parse(orphans.toString('utf8'));
|
orphans = JSON.parse(orphans.toString('utf8'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return callback(e);
|
return done(e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
orphans = [];
|
orphans = [];
|
||||||
@ -263,32 +275,35 @@ TXPool.prototype._add = function add(tx, callback) {
|
|||||||
});
|
});
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return done(err);
|
||||||
|
|
||||||
// Add unspent outputs or resolve orphans
|
// Add unspent outputs or resolve orphans
|
||||||
utils.forEachSerial(tx.outputs, function(output, next, i) {
|
utils.forEachSerial(tx.outputs, function(output, next, i) {
|
||||||
// Do not add unspents for outputs that aren't ours.
|
// Do not add unspents for outputs that aren't ours.
|
||||||
self._hasAddress(output.getAddress(), function(err, result) {
|
self._hasAddress(output.getAddress(), function(err, result) {
|
||||||
|
var key, coin;
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return done(err);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return next();
|
return next();
|
||||||
|
|
||||||
var coin = bcoin.coin(tx, i);
|
key = hash + '/' + i;
|
||||||
|
coin = bcoin.coin(tx, i);
|
||||||
var key = hash + '/' + i;
|
|
||||||
|
|
||||||
self.db.get(p + 'o/' + key, function(err, orphans) {
|
self.db.get(p + 'o/' + key, function(err, orphans) {
|
||||||
var some;
|
var some;
|
||||||
|
|
||||||
if (err && err.type !== 'NotFoundError')
|
if (err && err.type !== 'NotFoundError')
|
||||||
return callback(err);
|
return done(err);
|
||||||
|
|
||||||
if (orphans) {
|
if (orphans) {
|
||||||
try {
|
try {
|
||||||
orphans = JSON.parse(orphans.toString('utf8')).map(function(orphan) {
|
orphans = JSON.parse(orphans.toString('utf8'));
|
||||||
orphan.tx = bcoin.tx.fromExtended(new Buffer(orphan.tx, 'hex'), true);
|
orphans = orphans.map(function(orphan) {
|
||||||
|
var tx = new Buffer(orphan.tx, 'hex');
|
||||||
|
orphan.tx = bcoin.tx.fromExtended(tx, true);
|
||||||
return orphan;
|
return orphan;
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -362,7 +377,7 @@ TXPool.prototype._add = function add(tx, callback) {
|
|||||||
});
|
});
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return done(err);
|
||||||
|
|
||||||
batch.put(p + 't/t/' + hash, tx.toExtended());
|
batch.put(p + 't/t/' + hash, tx.toExtended());
|
||||||
if (tx.ts === 0)
|
if (tx.ts === 0)
|
||||||
@ -376,204 +391,23 @@ TXPool.prototype._add = function add(tx, callback) {
|
|||||||
|
|
||||||
batch.write(function(err) {
|
batch.write(function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return done(err);
|
||||||
|
|
||||||
self.emit('tx', tx);
|
self.emit('tx', tx);
|
||||||
|
|
||||||
if (tx.ts !== 0)
|
if (tx.ts !== 0)
|
||||||
self.emit('confirmed', tx);
|
self.emit('confirmed', tx);
|
||||||
|
|
||||||
return callback(null, true);
|
if (updated)
|
||||||
|
self.emit('updated', tx);
|
||||||
|
|
||||||
|
return done(null, true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
TXPool.prototype._add_ = function _add(tx, callback) {
|
|
||||||
var self = this;
|
|
||||||
var p = this.prefix + '/';
|
|
||||||
var hash = tx.hash('hex');
|
|
||||||
var uniq = {};
|
|
||||||
|
|
||||||
callback = utils.ensure(callback);
|
|
||||||
|
|
||||||
this.getTX(hash, function(err, existing) {
|
|
||||||
var batch;
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
batch = self.db.batch();
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
// Tricky - update the tx and coin in storage,
|
|
||||||
// and remove pending flag to mark as confirmed.
|
|
||||||
if (existing.ts === 0 && tx.ts !== 0) {
|
|
||||||
batch.put(p + 't/t/' + hash, tx.toExtended());
|
|
||||||
batch.del(p + 'p/' + hash);
|
|
||||||
|
|
||||||
tx.inputs.forEach(function(input) {
|
|
||||||
var type = input.getType();
|
|
||||||
var address = input.getAddress();
|
|
||||||
var uaddr;
|
|
||||||
|
|
||||||
if (input.isCoinbase())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (type === 'pubkey' || type === 'multisig')
|
|
||||||
address = null;
|
|
||||||
|
|
||||||
uaddr = address;
|
|
||||||
|
|
||||||
if (uaddr) {
|
|
||||||
if (!uniq[uaddr])
|
|
||||||
uniq[uaddr] = true;
|
|
||||||
else
|
|
||||||
uaddr = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uaddr)
|
|
||||||
batch.del(p + 'p/a/' + uaddr + '/' + hash);
|
|
||||||
});
|
|
||||||
|
|
||||||
tx.outputs.forEach(function(output) {
|
|
||||||
var type = output.getType();
|
|
||||||
var address = output.getAddress();
|
|
||||||
var uaddr, coinRaw;
|
|
||||||
|
|
||||||
if (type === 'pubkey' || type === 'multisig')
|
|
||||||
address = null;
|
|
||||||
|
|
||||||
uaddr = address;
|
|
||||||
|
|
||||||
if (uaddr) {
|
|
||||||
if (!uniq[uaddr])
|
|
||||||
uniq[uaddr] = true;
|
|
||||||
else
|
|
||||||
uaddr = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uaddr)
|
|
||||||
batch.del(p + 'p/a/' + uaddr + '/' + hash);
|
|
||||||
|
|
||||||
coinRaw = bcoin.protocol.framer.coin({
|
|
||||||
version: tx.version,
|
|
||||||
height: tx.height,
|
|
||||||
value: output.value,
|
|
||||||
script: output.script,
|
|
||||||
hash: hash,
|
|
||||||
index: i,
|
|
||||||
spent: false
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
batch.put(p + 'u/t/' + hash + '/' + i, coinRaw);
|
|
||||||
});
|
|
||||||
|
|
||||||
batch.write(function(err) {
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
self.emit('confirmed', tx);
|
|
||||||
self.emit('tx', tx);
|
|
||||||
return callback(null, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return callback(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.put(p + 't/t/' + hash, tx.toExtended());
|
|
||||||
if (tx.ts === 0)
|
|
||||||
batch.put(p + 'p/' + hash, new Buffer([]));
|
|
||||||
|
|
||||||
tx.inputs.forEach(function(input) {
|
|
||||||
var type = input.getType();
|
|
||||||
var address = input.getAddress();
|
|
||||||
var uaddr;
|
|
||||||
|
|
||||||
if (input.isCoinbase())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (type === 'pubkey' || type === 'multisig')
|
|
||||||
address = null;
|
|
||||||
|
|
||||||
uaddr = address;
|
|
||||||
|
|
||||||
if (uaddr) {
|
|
||||||
if (!uniq[uaddr])
|
|
||||||
uniq[uaddr] = true;
|
|
||||||
else
|
|
||||||
uaddr = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uaddr) {
|
|
||||||
batch.put(p + 't/a/' + uaddr + '/' + hash, new Buffer([]));
|
|
||||||
if (tx.ts === 0)
|
|
||||||
batch.put(p + 'p/a/' + uaddr + '/' + hash, new Buffer([]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address) {
|
|
||||||
batch.del(
|
|
||||||
p + 'u/a/' + address
|
|
||||||
+ '/' + input.prevout.hash
|
|
||||||
+ '/' + input.prevout.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.del(p + 'u/t/' + input.prevout.hash + '/' + input.prevout.index);
|
|
||||||
});
|
|
||||||
|
|
||||||
tx.outputs.forEach(function(output, i) {
|
|
||||||
var type = output.getType();
|
|
||||||
var address = output.getAddress();
|
|
||||||
var uaddr, coinRaw;
|
|
||||||
|
|
||||||
if (type === 'pubkey' || type === 'multisig')
|
|
||||||
address = null;
|
|
||||||
|
|
||||||
uaddr = address;
|
|
||||||
|
|
||||||
if (uaddr) {
|
|
||||||
if (!uniq[uaddr])
|
|
||||||
uniq[uaddr] = true;
|
|
||||||
else
|
|
||||||
uaddr = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
coinRaw = bcoin.protocol.framer.coin({
|
|
||||||
version: tx.version,
|
|
||||||
height: tx.height,
|
|
||||||
value: output.value,
|
|
||||||
script: output.script,
|
|
||||||
hash: hash,
|
|
||||||
index: i,
|
|
||||||
spent: false
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
if (uaddr) {
|
|
||||||
batch.put(p + 't/a/' + uaddr + '/' + hash, new Buffer([]));
|
|
||||||
if (tx.ts === 0)
|
|
||||||
batch.put(p + 'p/a/' + uaddr + '/' + hash, new Buffer([]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address)
|
|
||||||
batch.put(p + 'u/a/' + address + '/' + hash + '/' + i, new Buffer([]));
|
|
||||||
|
|
||||||
batch.put(p + 'u/t/' + hash + '/' + i, coinRaw);
|
|
||||||
});
|
|
||||||
|
|
||||||
batch.write(function(err) {
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
self.emit('tx', tx);
|
|
||||||
|
|
||||||
if (tx.ts !== 0)
|
|
||||||
self.emit('confirmed', tx);
|
|
||||||
|
|
||||||
return callback(null, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
TXPool.prototype.remove = function remove(hash, callback) {
|
TXPool.prototype.remove = function remove(hash, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var p = this.prefix + '/';
|
var p = this.prefix + '/';
|
||||||
@ -606,7 +440,7 @@ TXPool.prototype.remove = function remove(hash, callback) {
|
|||||||
tx.inputs.forEach(function(input) {
|
tx.inputs.forEach(function(input) {
|
||||||
var type = input.getType();
|
var type = input.getType();
|
||||||
var address = input.getAddress();
|
var address = input.getAddress();
|
||||||
var uaddr, coinRaw;
|
var uaddr;
|
||||||
|
|
||||||
if (input.isCoinbase())
|
if (input.isCoinbase())
|
||||||
return;
|
return;
|
||||||
@ -640,11 +474,10 @@ TXPool.prototype.remove = function remove(hash, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (input.output) {
|
if (input.output) {
|
||||||
coinRaw = bcoin.protocol.framer.coin(input.output, true);
|
|
||||||
batch.put(p + 'u/t/'
|
batch.put(p + 'u/t/'
|
||||||
+ input.prevout.hash
|
+ input.prevout.hash
|
||||||
+ '/' + input.prevout.index,
|
+ '/' + input.prevout.index,
|
||||||
coinRaw);
|
input.output.toRaw());
|
||||||
}
|
}
|
||||||
|
|
||||||
batch.del(p + 'o/' + input.prevout.hash + '/' + input.prevout.index);
|
batch.del(p + 'o/' + input.prevout.hash + '/' + input.prevout.index);
|
||||||
@ -690,6 +523,103 @@ TXPool.prototype.remove = function remove(hash, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TXPool.prototype.unconfirm = function unconfirm(hash, callback) {
|
||||||
|
var self = this;
|
||||||
|
var p = this.prefix + '/';
|
||||||
|
var uniq = {};
|
||||||
|
|
||||||
|
if (hash.hash)
|
||||||
|
hash = hash.hash('hex');
|
||||||
|
|
||||||
|
this.getTX(hash, function(err, tx) {
|
||||||
|
var batch;
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (!tx)
|
||||||
|
return callback(null, true);
|
||||||
|
|
||||||
|
tx.height = -1;
|
||||||
|
tx.ps = utils.now();
|
||||||
|
tx.ts = 0;
|
||||||
|
tx.index = -1;
|
||||||
|
tx.block = null;
|
||||||
|
|
||||||
|
batch.put(p + 't/t/' + hash, tx.toExtended());
|
||||||
|
batch.put(p + 'p/' + hash, new Buffer([]));
|
||||||
|
|
||||||
|
tx.inputs.forEach(function(input) {
|
||||||
|
var type = input.getType();
|
||||||
|
var address = input.getAddress();
|
||||||
|
var uaddr;
|
||||||
|
|
||||||
|
if (input.isCoinbase())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (type === 'pubkey' || type === 'multisig')
|
||||||
|
address = null;
|
||||||
|
|
||||||
|
uaddr = address;
|
||||||
|
|
||||||
|
if (uaddr) {
|
||||||
|
if (!uniq[uaddr])
|
||||||
|
uniq[uaddr] = true;
|
||||||
|
else
|
||||||
|
uaddr = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uaddr)
|
||||||
|
batch.put(p + 'p/a/' + uaddr + '/' + hash, new Buffer([]));
|
||||||
|
});
|
||||||
|
|
||||||
|
utils.forEachSerial(tx.outputs, function(output, next, i) {
|
||||||
|
var type = output.getType();
|
||||||
|
var address = output.getAddress();
|
||||||
|
var uaddr;
|
||||||
|
|
||||||
|
if (type === 'pubkey' || type === 'multisig')
|
||||||
|
address = null;
|
||||||
|
|
||||||
|
uaddr = address;
|
||||||
|
|
||||||
|
if (uaddr) {
|
||||||
|
if (!uniq[uaddr])
|
||||||
|
uniq[uaddr] = true;
|
||||||
|
else
|
||||||
|
uaddr = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uaddr)
|
||||||
|
batch.put(p + 'p/a/' + uaddr + '/' + hash, new Buffer([]));
|
||||||
|
|
||||||
|
self.getCoin(hash, i, function(err, coin) {
|
||||||
|
if (err)
|
||||||
|
return next(err);
|
||||||
|
|
||||||
|
if (!coin)
|
||||||
|
return next();
|
||||||
|
|
||||||
|
coin.height = tx.height;
|
||||||
|
|
||||||
|
batch.put(p + 'u/t/' + hash + '/' + i, coin.toRaw());
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}, function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
batch.write(function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
self.emit('unconfirmed', tx);
|
||||||
|
return callback(null, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
TXPool.prototype.getTXHashes = function getTXHashes(address, callback) {
|
TXPool.prototype.getTXHashes = function getTXHashes(address, callback) {
|
||||||
var p = this.prefix + '/';
|
var p = this.prefix + '/';
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|||||||
@ -44,7 +44,7 @@ function Wallet(options) {
|
|||||||
options.master = bcoin.hd.fromSeed();
|
options.master = bcoin.hd.fromSeed();
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.db = options.db || new bcoin.walletdb();
|
this.db = options.db;
|
||||||
this.addresses = [];
|
this.addresses = [];
|
||||||
this.master = options.master || null;
|
this.master = options.master || null;
|
||||||
this.addressMap = options.addressMap || {};
|
this.addressMap = options.addressMap || {};
|
||||||
@ -85,10 +85,6 @@ function Wallet(options) {
|
|||||||
: this.master.deriveAccount44(this.accountIndex);
|
: this.master.deriveAccount44(this.accountIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
this.lastTs = 0;
|
|
||||||
this.lastHeight = 0;
|
|
||||||
|
|
||||||
this.addKey(this.accountKey);
|
this.addKey(this.accountKey);
|
||||||
|
|
||||||
(options.keys || []).forEach(function(key) {
|
(options.keys || []).forEach(function(key) {
|
||||||
@ -100,12 +96,13 @@ utils.inherits(Wallet, EventEmitter);
|
|||||||
|
|
||||||
Wallet.prototype._init = function _init() {
|
Wallet.prototype._init = function _init() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var prevBalance = null;
|
|
||||||
var options = this.options;
|
var options = this.options;
|
||||||
var addr, i;
|
var addr, i;
|
||||||
|
|
||||||
assert(!this._initialized);
|
assert(!this._initialized);
|
||||||
|
|
||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
|
|
||||||
this.id = this.getID();
|
this.id = this.getID();
|
||||||
|
|
||||||
if (Object.keys(this.addressMap).length === 0) {
|
if (Object.keys(this.addressMap).length === 0) {
|
||||||
@ -128,31 +125,6 @@ Wallet.prototype._init = function _init() {
|
|||||||
assert(this.receiveAddress);
|
assert(this.receiveAddress);
|
||||||
assert(!this.receiveAddress.change);
|
assert(!this.receiveAddress.change);
|
||||||
assert(this.changeAddress.change);
|
assert(this.changeAddress.change);
|
||||||
|
|
||||||
this.tx = this.db.tx;
|
|
||||||
|
|
||||||
this.tx.on('tx', function(tx) {
|
|
||||||
self.emit('tx', tx);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tx.on('confirmed', function(tx) {
|
|
||||||
// TX using this address was confirmed.
|
|
||||||
// Allocate a new address.
|
|
||||||
self.syncOutputDepth(tx);
|
|
||||||
self.emit('confirmed', tx);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tx.on('error', function(err) {
|
|
||||||
self.emit('error', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.save(function(err) {
|
|
||||||
if (err)
|
|
||||||
throw err;
|
|
||||||
|
|
||||||
self.loading = false;
|
|
||||||
self.emit('load', self.lastTs);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.addKey = function addKey(key) {
|
Wallet.prototype.addKey = function addKey(key) {
|
||||||
@ -355,17 +327,12 @@ Wallet.prototype.deriveAddress = function deriveAddress(change, index) {
|
|||||||
address = new bcoin.address(options);
|
address = new bcoin.address(options);
|
||||||
|
|
||||||
this.addressMap[address.getKeyAddress()] = data.path;
|
this.addressMap[address.getKeyAddress()] = data.path;
|
||||||
this._saveAddress(address.getKeyAddress());
|
|
||||||
|
|
||||||
if (this.type === 'multisig') {
|
if (this.type === 'multisig')
|
||||||
this.addressMap[address.getScriptAddress()] = data.path;
|
this.addressMap[address.getScriptAddress()] = data.path;
|
||||||
this._saveAddress(address.getScriptAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.witness) {
|
if (this.witness)
|
||||||
this.addressMap[address.getProgramAddress()] = data.path;
|
this.addressMap[address.getProgramAddress()] = data.path;
|
||||||
this._saveAddress(address.getProgramAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emit('add address', address);
|
this.emit('add address', address);
|
||||||
|
|
||||||
@ -503,8 +470,6 @@ Wallet.prototype.ownInput = function ownInput(tx, index) {
|
|||||||
if (tx instanceof bcoin.input)
|
if (tx instanceof bcoin.input)
|
||||||
return tx.test(this.addressMap);
|
return tx.test(this.addressMap);
|
||||||
|
|
||||||
this.fillPrevout(tx);
|
|
||||||
|
|
||||||
return tx.testInputs(this.addressMap, index);
|
return tx.testInputs(this.addressMap, index);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -551,14 +516,39 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.fillPrevout = function fillPrevout(tx, callback) {
|
Wallet.prototype.fillPrevout = function fillPrevout(tx, callback) {
|
||||||
return tx.fillPrevout(this);
|
if (!this.db)
|
||||||
return this.db.fillTX(tx, callback);
|
return callback(new Error('No wallet db available.'));
|
||||||
|
|
||||||
|
return this.db.fillCoin(tx, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.createTX = function createTX(options, outputs) {
|
Wallet.prototype.sync = function sync(callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
callback = utils.ensure(callback);
|
||||||
|
|
||||||
|
this.getUnspent(function(err, unspent) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
unspent.forEach(function(coin) {
|
||||||
|
self.syncOutputDepth(coin);
|
||||||
|
});
|
||||||
|
|
||||||
|
return callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.tx = function _tx(options, outputs, callback) {
|
||||||
|
var self = this;
|
||||||
var tx = bcoin.mtx();
|
var tx = bcoin.mtx();
|
||||||
var target;
|
var target;
|
||||||
|
|
||||||
|
if (typeof outputs === 'function') {
|
||||||
|
callback = outputs;
|
||||||
|
outputs = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!outputs) {
|
if (!outputs) {
|
||||||
outputs = options;
|
outputs = options;
|
||||||
options = {};
|
options = {};
|
||||||
@ -573,28 +563,44 @@ Wallet.prototype.createTX = function createTX(options, outputs) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Fill the inputs with unspents
|
// Fill the inputs with unspents
|
||||||
this.fill(tx, options);
|
this.fill(tx, options, function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
// Sort members a la BIP69
|
// Sort members a la BIP69
|
||||||
tx.sortMembers();
|
tx.sortMembers();
|
||||||
|
|
||||||
// Find the necessary locktime if there is
|
// Find the necessary locktime if there is
|
||||||
// a checklocktimeverify script in the unspents.
|
// a checklocktimeverify script in the unspents.
|
||||||
target = tx.getTargetLocktime();
|
target = tx.getTargetLocktime();
|
||||||
|
|
||||||
// No target value. The unspents have an
|
// No target value. The unspents have an
|
||||||
// incompatible locktime type.
|
// incompatible locktime type.
|
||||||
if (!target)
|
if (!target)
|
||||||
return;
|
return callback(new Error('Incompatible locktime.'));
|
||||||
|
|
||||||
// Set the locktime to target value or
|
// Set the locktime to target value or
|
||||||
// `height - whatever` to avoid fee snipping.
|
// `height - whatever` to avoid fee snipping.
|
||||||
if (target.value > 0)
|
if (target.value > 0)
|
||||||
tx.setLocktime(target.value);
|
tx.setLocktime(target.value);
|
||||||
else
|
else
|
||||||
tx.avoidFeeSniping();
|
tx.avoidFeeSniping();
|
||||||
|
|
||||||
return tx;
|
// Sign the transaction
|
||||||
|
if (!self.sign(tx))
|
||||||
|
return callback(new Error('Could not sign transaction.'));
|
||||||
|
|
||||||
|
if (self.m > 1)
|
||||||
|
return callback(null, tx);
|
||||||
|
|
||||||
|
// Add to pool
|
||||||
|
self.addTX(tx, function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
callback(null, tx);
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.deriveInputs = function deriveInputs(tx) {
|
Wallet.prototype.deriveInputs = function deriveInputs(tx) {
|
||||||
@ -618,6 +624,13 @@ Wallet.prototype.getInputPaths = function getInputPaths(tx) {
|
|||||||
var paths = [];
|
var paths = [];
|
||||||
var i, input, output, address, path;
|
var i, input, output, address, path;
|
||||||
|
|
||||||
|
if (tx instanceof bcoin.input) {
|
||||||
|
path = this.getPath(tx.output.getAddress());
|
||||||
|
if (path)
|
||||||
|
paths.push(path);
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
output = input.output;
|
output = input.output;
|
||||||
@ -639,6 +652,13 @@ Wallet.prototype.getOutputPaths = function getOutputPaths(tx) {
|
|||||||
var paths = [];
|
var paths = [];
|
||||||
var i, output, address, path;
|
var i, output, address, path;
|
||||||
|
|
||||||
|
if (tx instanceof bcoin.output) {
|
||||||
|
path = this.getPath(tx.getAddress());
|
||||||
|
if (path)
|
||||||
|
paths.push(path);
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < tx.outputs.length; i++) {
|
for (i = 0; i < tx.outputs.length; i++) {
|
||||||
output = tx.outputs[i];
|
output = tx.outputs[i];
|
||||||
|
|
||||||
@ -678,10 +698,19 @@ Wallet.prototype.getOutputDepth = function getOutputDepth(tx) {
|
|||||||
|
|
||||||
Wallet.prototype.syncOutputDepth = function syncOutputDepth(tx) {
|
Wallet.prototype.syncOutputDepth = function syncOutputDepth(tx) {
|
||||||
var depth = this.getOutputDepth(tx);
|
var depth = this.getOutputDepth(tx);
|
||||||
if (depth.changeDepth >= this.changeDepth)
|
var res = false;
|
||||||
|
|
||||||
|
if (depth.changeDepth >= this.changeDepth) {
|
||||||
this.setChangeDepth(depth.changeDepth + 1);
|
this.setChangeDepth(depth.changeDepth + 1);
|
||||||
if (depth.receiveDepth >= this.receiveDepth)
|
res = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth.receiveDepth >= this.receiveDepth) {
|
||||||
this.setReceiveDepth(depth.receiveDepth + 1);
|
this.setReceiveDepth(depth.receiveDepth + 1);
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.scan = function scan(txByAddress, callback) {
|
Wallet.prototype.scan = function scan(txByAddress, callback) {
|
||||||
@ -761,11 +790,7 @@ Wallet.prototype._scan = function _scan(options, txByAddress, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.scriptInputs = function scriptInputs(tx, index) {
|
Wallet.prototype.scriptInputs = function scriptInputs(tx, index) {
|
||||||
var addresses;
|
var addresses = this.deriveInputs(tx);
|
||||||
|
|
||||||
this.fillPrevout(tx);
|
|
||||||
|
|
||||||
addresses = this.deriveInputs(tx);
|
|
||||||
|
|
||||||
return addresses.reduce(function(total, address) {
|
return addresses.reduce(function(total, address) {
|
||||||
return total + address.scriptInputs(tx, index);
|
return total + address.scriptInputs(tx, index);
|
||||||
@ -773,11 +798,7 @@ Wallet.prototype.scriptInputs = function scriptInputs(tx, index) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.signInputs = function signInputs(tx, type, index) {
|
Wallet.prototype.signInputs = function signInputs(tx, type, index) {
|
||||||
var addresses;
|
var addresses = this.deriveInputs(tx);
|
||||||
|
|
||||||
this.fillPrevout(tx);
|
|
||||||
|
|
||||||
addresses = this.deriveInputs(tx);
|
|
||||||
|
|
||||||
return addresses.reduce(function(total, address) {
|
return addresses.reduce(function(total, address) {
|
||||||
return total + address.signInputs(tx, type, index);
|
return total + address.signInputs(tx, type, index);
|
||||||
@ -785,11 +806,7 @@ Wallet.prototype.signInputs = function signInputs(tx, type, index) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.sign = function sign(tx, type, index) {
|
Wallet.prototype.sign = function sign(tx, type, index) {
|
||||||
var addresses;
|
var addresses = this.deriveInputs(tx);
|
||||||
|
|
||||||
this.fillPrevout(tx);
|
|
||||||
|
|
||||||
addresses = this.deriveInputs(tx);
|
|
||||||
|
|
||||||
return addresses.reduce(function(total, address) {
|
return addresses.reduce(function(total, address) {
|
||||||
return total + address.sign(tx, type, index);
|
return total + address.sign(tx, type, index);
|
||||||
@ -797,35 +814,49 @@ Wallet.prototype.sign = function sign(tx, type, index) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.addTX = function addTX(tx, callback) {
|
Wallet.prototype.addTX = function addTX(tx, callback) {
|
||||||
|
this.syncOutputDepth(tx);
|
||||||
|
|
||||||
if (!this.db)
|
if (!this.db)
|
||||||
return;
|
return callback(new Error('No wallet db available.'));
|
||||||
|
|
||||||
return this.db.addTX(tx, callback);
|
return this.db.addTX(tx, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.getAll = function getAll(callback) {
|
Wallet.prototype.getAll = function getAll(callback) {
|
||||||
if (!this.db)
|
if (!this.db)
|
||||||
return;
|
return callback(new Error('No wallet db available.'));
|
||||||
|
|
||||||
return this.db.getAll(this, callback);
|
return this.db.getAll(this, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.getUnspent = function getUnspent(callback) {
|
Wallet.prototype.getUnspent = function getUnspent(callback) {
|
||||||
if (!this.db)
|
if (!this.db)
|
||||||
return;
|
return callback(new Error('No wallet db available.'));
|
||||||
|
|
||||||
return this.db.getUnspent(this, callback);
|
return this.db.getUnspent(this, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.getPending = function getPending(callback) {
|
Wallet.prototype.getPending = function getPending(callback) {
|
||||||
if (!this.db)
|
if (!this.db)
|
||||||
return;
|
return callback(new Error('No wallet db available.'));
|
||||||
|
|
||||||
return this.db.getPending(this, callback);
|
return this.db.getPending(this, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.getBalance = function getBalance(callback) {
|
Wallet.prototype.getBalance = function getBalance(callback) {
|
||||||
if (!this.db)
|
if (!this.db)
|
||||||
return;
|
return callback(new Error('No wallet db available.'));
|
||||||
|
|
||||||
return this.db.getBalance(this, callback);
|
return this.db.getBalance(this, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.getLast = function getLast(callback) {
|
||||||
|
if (!this.db)
|
||||||
|
return callback(new Error('No wallet db available.'));
|
||||||
|
|
||||||
|
return this.db.getLast(this, callback);
|
||||||
|
};
|
||||||
|
|
||||||
Wallet.prototype.__defineGetter__('script', function() {
|
Wallet.prototype.__defineGetter__('script', function() {
|
||||||
return this.getScript();
|
return this.getScript();
|
||||||
});
|
});
|
||||||
@ -901,14 +932,11 @@ Wallet.prototype.toJSON = function toJSON() {
|
|||||||
addressMap: this.addressMap,
|
addressMap: this.addressMap,
|
||||||
keys: this.keys.map(function(key) {
|
keys: this.keys.map(function(key) {
|
||||||
return key.xpubkey;
|
return key.xpubkey;
|
||||||
}),
|
})
|
||||||
txs: []
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet._fromJSON = function _fromJSON(json, passphrase) {
|
Wallet._fromJSON = function _fromJSON(json, passphrase) {
|
||||||
var wallet;
|
|
||||||
|
|
||||||
assert.equal(json.v, 3);
|
assert.equal(json.v, 3);
|
||||||
assert.equal(json.name, 'wallet');
|
assert.equal(json.name, 'wallet');
|
||||||
|
|
||||||
@ -929,10 +957,7 @@ Wallet._fromJSON = function _fromJSON(json, passphrase) {
|
|||||||
master: bcoin.hd.fromJSON(json.master, passphrase),
|
master: bcoin.hd.fromJSON(json.master, passphrase),
|
||||||
addressMap: json.addressMap,
|
addressMap: json.addressMap,
|
||||||
keys: json.keys,
|
keys: json.keys,
|
||||||
passphrase: passphrase,
|
passphrase: passphrase
|
||||||
txs: json.txs.map(function(json) {
|
|
||||||
return bcoin.tx.fromCompact(json);
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -940,24 +965,6 @@ Wallet.fromJSON = function fromJSON(json, passphrase) {
|
|||||||
return new Wallet(Wallet._fromJSON(json, passphrase));
|
return new Wallet(Wallet._fromJSON(json, passphrase));
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype._saveAddress = function _saveAddress(address, callback) {
|
|
||||||
callback = utils.ensure(callback);
|
|
||||||
|
|
||||||
if (!this.db)
|
|
||||||
return utils.nextTick(callback);
|
|
||||||
|
|
||||||
return this.db.saveAddress(this.id, address, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Wallet.prototype.save = function save(callback) {
|
|
||||||
callback = utils.ensure(callback);
|
|
||||||
|
|
||||||
if (!this.db)
|
|
||||||
return utils.nextTick(callback);
|
|
||||||
|
|
||||||
return this.db.save(this, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -53,7 +53,7 @@ WalletDB._db = {};
|
|||||||
|
|
||||||
WalletDB.prototype.dump = function dump(callback) {
|
WalletDB.prototype.dump = function dump(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var records = [];
|
var records = {};
|
||||||
|
|
||||||
var iter = this.db.db.iterator({
|
var iter = this.db.db.iterator({
|
||||||
gte: 'w',
|
gte: 'w',
|
||||||
@ -83,7 +83,7 @@ WalletDB.prototype.dump = function dump(callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
records.push([key, value.slice(0, 200).toString('hex')]);
|
records[key] = value.slice(0, 50).toString('hex');
|
||||||
|
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
@ -134,19 +134,8 @@ WalletDB.prototype.saveJSON = function saveJSON(id, json, callback) {
|
|||||||
|
|
||||||
callback = utils.ensure(callback);
|
callback = utils.ensure(callback);
|
||||||
|
|
||||||
if (json instanceof bcoin.wallet) {
|
if (json instanceof bcoin.wallet)
|
||||||
json = json.toJSON();
|
json = json.toJSON();
|
||||||
} else {
|
|
||||||
if (typeof json.v !== 'number') {
|
|
||||||
json = utils.merge({}, json);
|
|
||||||
delete json.store;
|
|
||||||
delete json.db;
|
|
||||||
var save = bcoin.wallet.prototype.save;
|
|
||||||
bcoin.wallet.prototype.save = function() {};
|
|
||||||
json = new bcoin.wallet(json).toJSON();
|
|
||||||
bcoin.wallet.prototype.save = save;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cb(err, json) {
|
function cb(err, json) {
|
||||||
var batch;
|
var batch;
|
||||||
@ -204,20 +193,6 @@ WalletDB.prototype.removeJSON = function removeJSON(id, callback) {
|
|||||||
return this._removeDB(id, cb);
|
return this._removeDB(id, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
WalletDB.prototype.createJSON = function createJSON(id, options, callback) {
|
|
||||||
var self = this;
|
|
||||||
callback = utils.ensure(callback);
|
|
||||||
return this.getJSON(id, function(err, json) {
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
if (!json)
|
|
||||||
return self.saveJSON(options.id, options, callback);
|
|
||||||
|
|
||||||
return callback(null, json);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
WalletDB.prototype._getDB = function _getDB(id, callback) {
|
WalletDB.prototype._getDB = function _getDB(id, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var key;
|
var key;
|
||||||
@ -295,9 +270,15 @@ WalletDB.prototype.get = function get(id, passphrase, callback) {
|
|||||||
if (!options)
|
if (!options)
|
||||||
return callback();
|
return callback();
|
||||||
|
|
||||||
wallet = bcoin.wallet.fromJSON(options, passphrase);
|
try {
|
||||||
wallet.store = true;
|
wallet = bcoin.wallet.fromJSON(options, passphrase);
|
||||||
wallet.db = self;
|
wallet.store = true;
|
||||||
|
wallet.db = self;
|
||||||
|
} catch (e) {
|
||||||
|
return callback(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
wallet.on('add address', self._onAddress(wallet, wallet.id));
|
||||||
|
|
||||||
return callback(null, wallet);
|
return callback(null, wallet);
|
||||||
});
|
});
|
||||||
@ -332,148 +313,82 @@ WalletDB.prototype.create = function create(options, callback) {
|
|||||||
|
|
||||||
callback = utils.ensure(callback);
|
callback = utils.ensure(callback);
|
||||||
|
|
||||||
if (options instanceof bcoin.wallet) {
|
function getJSON(id, callback) {
|
||||||
options.store = true;
|
if (!id)
|
||||||
options.db = this;
|
return callback();
|
||||||
|
|
||||||
|
return self.getJSON(id, function(err, json) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
return callback(null, json);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.createJSON(options.id, options, function(err, json) {
|
return getJSON(options.id, function(err, json) {
|
||||||
var wallet;
|
var wallet;
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
wallet = bcoin.wallet.fromJSON(json, options.passphrase);
|
if (json) {
|
||||||
wallet.store = true;
|
try {
|
||||||
wallet.db = self;
|
wallet = bcoin.wallet.fromJSON(json, options.passphrase);
|
||||||
|
wallet.store = true;
|
||||||
return callback(null, wallet);
|
wallet.db = self;
|
||||||
});
|
} catch (e) {
|
||||||
};
|
return callback(e);
|
||||||
|
|
||||||
WalletDB.prototype.saveAddress = function saveAddress(id, address, callback) {
|
|
||||||
callback = utils.ensure(callback);
|
|
||||||
this.db.put('w/a/' + address + '/' + id, new Buffer([]), callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
WalletDB.prototype.removeAddress = function removeAddress(id, address, callback) {
|
|
||||||
callback = utils.ensure(callback);
|
|
||||||
this.db.del('w/a/' + address + '/' + id, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
WalletDB.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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
options.store = true;
|
||||||
|
options.db = self;
|
||||||
|
wallet = new bcoin.wallet(options);
|
||||||
|
self._saveDB(wallet.id, wallet.toJSON(), done);
|
||||||
|
}
|
||||||
|
|
||||||
if (key === undefined) {
|
function done(err) {
|
||||||
return iter.end(function(err) {
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
return callback(null, ids);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ids.push(key.split('/')[2]);
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
|
|
||||||
WalletDB.prototype.test = function test(addresses, callback) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
utils.forEachSerial(addresses, function(address, next) {
|
|
||||||
self._getIDs(address, function(err, ids) {
|
|
||||||
if (err)
|
|
||||||
return next(err);
|
|
||||||
|
|
||||||
if (ids.length > 0)
|
|
||||||
return callback(null, ids);
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}, function(err) {
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
return callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
WalletDB.prototype.ownInput = function ownInput(tx, callback) {
|
|
||||||
var self = this;
|
|
||||||
var addresses;
|
|
||||||
|
|
||||||
if (tx.getAddress) {
|
|
||||||
assert(tx instanceof bcoin.input);
|
|
||||||
addresses = tx.getAddress();
|
|
||||||
if (addresses)
|
|
||||||
addresses = [addresses];
|
|
||||||
else
|
|
||||||
addresses = [];
|
|
||||||
} else {
|
|
||||||
addresses = tx.getInputAddresses();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.test(addresses, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
WalletDB.prototype.ownOutput = function ownOutput(tx, callback) {
|
|
||||||
var self = this;
|
|
||||||
var addresses;
|
|
||||||
|
|
||||||
if (tx.getAddress) {
|
|
||||||
assert(tx instanceof bcoin.output);
|
|
||||||
addresses = tx.getAddress();
|
|
||||||
if (addresses)
|
|
||||||
addresses = [addresses];
|
|
||||||
else
|
|
||||||
addresses = [];
|
|
||||||
} else {
|
|
||||||
addresses = tx.getOutputAddresses();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.test(addresses, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
WalletDB.prototype.ownTX = function ownTX(tx, callback) {
|
|
||||||
var self = this;
|
|
||||||
return this.ownInput(tx, function(err, input) {
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
return self.ownOutput(tx, function(err, output) {
|
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (input || output)
|
wallet.on('add address', self._onAddress(wallet, wallet.id));
|
||||||
return callback(null, input, output);
|
|
||||||
|
|
||||||
return callback();
|
return callback(null, wallet);
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
*/
|
|
||||||
|
WalletDB.prototype._onAddress = function _onAddress(wallet, id) {
|
||||||
|
var self = this;
|
||||||
|
return function(address) {
|
||||||
|
var batch = self.db.batch();
|
||||||
|
|
||||||
|
batch.put(
|
||||||
|
'w/a/' + address.getKeyAddress() + '/' + id,
|
||||||
|
new Buffer([]));
|
||||||
|
|
||||||
|
if (address.type === 'multisig') {
|
||||||
|
batch.put(
|
||||||
|
'w/a/' + address.getScriptAddress() + '/' + id,
|
||||||
|
new Buffer([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address.witness) {
|
||||||
|
batch.put(
|
||||||
|
'w/a/' + address.getProgramAddress() + '/' + id,
|
||||||
|
new Buffer([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.write(function(err) {
|
||||||
|
if (err)
|
||||||
|
self.emit('error', err);
|
||||||
|
self.saveJSON(wallet.id, wallet.toJSON(), function(err) {
|
||||||
|
if (err)
|
||||||
|
self.emit('error', err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
WalletDB.prototype.addTX = function addTX(tx, callback) {
|
WalletDB.prototype.addTX = function addTX(tx, callback) {
|
||||||
return this.tx.add(tx, callback);
|
return this.tx.add(tx, callback);
|
||||||
@ -493,7 +408,36 @@ WalletDB.prototype.getUnspent = function getUnspent(id, callback) {
|
|||||||
return this.getAddresses(id, function(err, addresses) {
|
return this.getAddresses(id, function(err, addresses) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
return self.tx.getUnspentByAddress(addresses, callback);
|
|
||||||
|
return self.tx.getUnspentByAddress(addresses, function(err, unspent) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (unspent.length === 0)
|
||||||
|
return callback(null, unspent);
|
||||||
|
|
||||||
|
self.get(id, function(err, wallet) {
|
||||||
|
var res = false;
|
||||||
|
|
||||||
|
if (!wallet)
|
||||||
|
return callback(null, unspent);
|
||||||
|
|
||||||
|
unspent.forEach(function(coin) {
|
||||||
|
if (wallet.syncOutputDepth(coin))
|
||||||
|
res = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
return callback(null, wallet);
|
||||||
|
|
||||||
|
self.save(wallet, function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
return callback(null, unspent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -515,6 +459,15 @@ WalletDB.prototype.getBalance = function getBalance(id, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
WalletDB.prototype.getLast = function getLast(id, callback) {
|
||||||
|
var self = this;
|
||||||
|
return this.getAddresses(id, function(err, addresses) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
return self.tx.getLast(addresses, callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
WalletDB.prototype.getAddresses = function getAddresses(id, callback) {
|
WalletDB.prototype.getAddresses = function getAddresses(id, callback) {
|
||||||
if (typeof id === 'string')
|
if (typeof id === 'string')
|
||||||
return callback(null, [id]);
|
return callback(null, [id]);
|
||||||
@ -582,27 +535,6 @@ WalletDB.prototype.getIDs = function _getIDs(address, callback) {
|
|||||||
})();
|
})();
|
||||||
};
|
};
|
||||||
|
|
||||||
WalletDB.prototype.testTX = function test(tx, callback) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
return callback(null, true);
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
WalletDB.prototype.hasAddress = function hasAddress(address, callback) {
|
WalletDB.prototype.hasAddress = function hasAddress(address, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
"bin": "./bin/node",
|
"bin": "./bin/node",
|
||||||
"preferGlobal": false,
|
"preferGlobal": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha --reporter spec test/*-test.js"
|
"test": "rm -rf ~/.bcoin-test && BCOIN_PREFIX=~/.bcoin-test mocha --reporter spec test/*-test.js"
|
||||||
},
|
},
|
||||||
"repository": "git://github.com/indutny/bcoin.git",
|
"repository": "git://github.com/indutny/bcoin.git",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@ -23,6 +23,8 @@ var dummyInput = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('Wallet', function() {
|
describe('Wallet', function() {
|
||||||
|
var wdb = new bcoin.walletdb();
|
||||||
|
|
||||||
it('should generate new key and address', function() {
|
it('should generate new key and address', function() {
|
||||||
var w = bcoin.wallet();
|
var w = bcoin.wallet();
|
||||||
var addr = w.getAddress();
|
var addr = w.getAddress();
|
||||||
@ -38,54 +40,59 @@ describe('Wallet', function() {
|
|||||||
assert(!bcoin.address.validate('1KQ1wMNwXHUYj1nv2xzsRcKUH8gVFpTFUc'));
|
assert(!bcoin.address.validate('1KQ1wMNwXHUYj1nv2xzsRcKUH8gVFpTFUc'));
|
||||||
});
|
});
|
||||||
|
|
||||||
function p2pkh(witness, bullshitNesting) {
|
function p2pkh(witness, bullshitNesting, cb) {
|
||||||
var flags = bcoin.protocol.constants.flags.STANDARD_VERIFY_FLAGS;
|
var flags = bcoin.protocol.constants.flags.STANDARD_VERIFY_FLAGS;
|
||||||
|
|
||||||
if (witness)
|
if (witness)
|
||||||
flags |= bcoin.protocol.constants.flags.VERIFY_WITNESS;
|
flags |= bcoin.protocol.constants.flags.VERIFY_WITNESS;
|
||||||
|
|
||||||
var w = bcoin.wallet({ witness: witness });
|
wdb.create({ witness: witness }, function(err, w) {
|
||||||
|
assert(!err);
|
||||||
|
|
||||||
if (witness)
|
if (witness)
|
||||||
assert(bcoin.address.parse(w.getAddress()).type === 'witnesspubkeyhash');
|
assert(bcoin.address.parse(w.getAddress()).type === 'witnesspubkeyhash');
|
||||||
else
|
else
|
||||||
assert(bcoin.address.parse(w.getAddress()).type === 'pubkeyhash');
|
assert(bcoin.address.parse(w.getAddress()).type === 'pubkeyhash');
|
||||||
|
|
||||||
// Input transcation
|
// Input transcation
|
||||||
var src = bcoin.mtx({
|
var src = bcoin.mtx({
|
||||||
outputs: [{
|
outputs: [{
|
||||||
value: 5460 * 2,
|
value: 5460 * 2,
|
||||||
address: bullshitNesting
|
address: bullshitNesting
|
||||||
? w.getProgramAddress()
|
? w.getProgramAddress()
|
||||||
: w.getAddress()
|
: w.getAddress()
|
||||||
}, {
|
}, {
|
||||||
value: 5460 * 2,
|
value: 5460 * 2,
|
||||||
address: bcoin.address.compileData(new Buffer([]))
|
address: bcoin.address.compileData(new Buffer([]))
|
||||||
}]
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
src.addInput(dummyInput);
|
||||||
|
assert(w.ownOutput(src));
|
||||||
|
assert(w.ownOutput(src.outputs[0]));
|
||||||
|
assert(!w.ownOutput(src.outputs[1]));
|
||||||
|
|
||||||
|
var tx = bcoin.mtx()
|
||||||
|
.addInput(src, 0)
|
||||||
|
.addOutput(w.getAddress(), 5460);
|
||||||
|
|
||||||
|
w.sign(tx);
|
||||||
|
assert(tx.verify(null, true, flags));
|
||||||
|
|
||||||
|
cb();
|
||||||
});
|
});
|
||||||
src.addInput(dummyInput);
|
|
||||||
assert(w.ownOutput(src));
|
|
||||||
assert(w.ownOutput(src.outputs[0]));
|
|
||||||
assert(!w.ownOutput(src.outputs[1]));
|
|
||||||
|
|
||||||
var tx = bcoin.mtx()
|
|
||||||
.addInput(src, 0)
|
|
||||||
.addOutput(w.getAddress(), 5460);
|
|
||||||
|
|
||||||
w.sign(tx);
|
|
||||||
assert(tx.verify(null, true, flags));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should sign/verify pubkeyhash tx', function() {
|
it('should sign/verify pubkeyhash tx', function(cb) {
|
||||||
p2pkh(false, false);
|
p2pkh(false, false, cb);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sign/verify witnesspubkeyhash tx', function() {
|
it('should sign/verify witnesspubkeyhash tx', function(cb) {
|
||||||
p2pkh(true, false);
|
p2pkh(true, false, cb);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sign/verify witnesspubkeyhash tx with bullshit nesting', function() {
|
it('should sign/verify witnesspubkeyhash tx with bullshit nesting', function(cb) {
|
||||||
p2pkh(true, true);
|
p2pkh(true, true, cb);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should multisign/verify TX', function() {
|
it('should multisign/verify TX', function() {
|
||||||
@ -125,92 +132,94 @@ describe('Wallet', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should have TX pool and be serializable', function(cb) {
|
it('should have TX pool and be serializable', function(cb) {
|
||||||
bcoin.walletdb().create({ id: 'w' }, function(err, w) {
|
wdb.create({}, function(err, w) {
|
||||||
assert(w);
|
|
||||||
bcoin.walletdb().create({ id: 'f' }, function(err, f) {
|
|
||||||
assert(f);
|
|
||||||
|
|
||||||
// Coinbase
|
|
||||||
var t1 = bcoin.mtx().addOutput(w, 50000).addOutput(w, 1000);
|
|
||||||
t1.addInput(dummyInput);
|
|
||||||
// balance: 51000
|
|
||||||
w.sign(t1);
|
|
||||||
var t2 = bcoin.mtx().addInput(t1, 0) // 50000
|
|
||||||
.addOutput(w, 24000)
|
|
||||||
.addOutput(w, 24000);
|
|
||||||
// balance: 49000
|
|
||||||
w.sign(t2);
|
|
||||||
var t3 = bcoin.mtx().addInput(t1, 1) // 1000
|
|
||||||
.addInput(t2, 0) // 24000
|
|
||||||
.addOutput(w, 23000);
|
|
||||||
// balance: 47000
|
|
||||||
w.sign(t3);
|
|
||||||
var t4 = bcoin.mtx().addInput(t2, 1) // 24000
|
|
||||||
.addInput(t3, 0) // 23000
|
|
||||||
.addOutput(w, 11000)
|
|
||||||
.addOutput(w, 11000);
|
|
||||||
// balance: 22000
|
|
||||||
w.sign(t4);
|
|
||||||
var f1 = bcoin.mtx().addInput(t4, 1) // 11000
|
|
||||||
.addOutput(f, 10000);
|
|
||||||
// balance: 11000
|
|
||||||
w.sign(f1);
|
|
||||||
var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed)
|
|
||||||
.addOutput(w, 500);
|
|
||||||
// Script inputs but do not sign
|
|
||||||
w.scriptInputs(fake);
|
|
||||||
// Fake signature
|
|
||||||
fake.inputs[0].script[0] = new Buffer([0,0,0,0,0,0,0,0,0]);
|
|
||||||
// balance: 11000
|
|
||||||
|
|
||||||
// Just for debugging
|
|
||||||
t1.hint = 't1';
|
|
||||||
t2.hint = 't2';
|
|
||||||
t3.hint = 't3';
|
|
||||||
t4.hint = 't4';
|
|
||||||
f1.hint = 'f1';
|
|
||||||
fake.hint = 'fake';
|
|
||||||
|
|
||||||
// Fake TX should temporarly change output
|
|
||||||
// w.addTX(fake);
|
|
||||||
|
|
||||||
w.addTX(fake, function(err) {
|
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w.addTX(t4, function(err) {
|
wdb.create({}, function(err, f) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w.getBalance(function(err, balance) {
|
|
||||||
|
// Coinbase
|
||||||
|
var t1 = bcoin.mtx().addOutput(w, 50000).addOutput(w, 1000);
|
||||||
|
t1.addInput(dummyInput);
|
||||||
|
// balance: 51000
|
||||||
|
w.sign(t1);
|
||||||
|
var t2 = bcoin.mtx().addInput(t1, 0) // 50000
|
||||||
|
.addOutput(w, 24000)
|
||||||
|
.addOutput(w, 24000);
|
||||||
|
// balance: 49000
|
||||||
|
w.sign(t2);
|
||||||
|
var t3 = bcoin.mtx().addInput(t1, 1) // 1000
|
||||||
|
.addInput(t2, 0) // 24000
|
||||||
|
.addOutput(w, 23000);
|
||||||
|
// balance: 47000
|
||||||
|
w.sign(t3);
|
||||||
|
var t4 = bcoin.mtx().addInput(t2, 1) // 24000
|
||||||
|
.addInput(t3, 0) // 23000
|
||||||
|
.addOutput(w, 11000)
|
||||||
|
.addOutput(w, 11000);
|
||||||
|
// balance: 22000
|
||||||
|
w.sign(t4);
|
||||||
|
var f1 = bcoin.mtx().addInput(t4, 1) // 11000
|
||||||
|
.addOutput(f, 10000);
|
||||||
|
// balance: 11000
|
||||||
|
w.sign(f1);
|
||||||
|
var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed)
|
||||||
|
.addOutput(w, 500);
|
||||||
|
// Script inputs but do not sign
|
||||||
|
w.scriptInputs(fake);
|
||||||
|
// Fake signature
|
||||||
|
fake.inputs[0].script[0] = new Buffer([0,0,0,0,0,0,0,0,0]);
|
||||||
|
// balance: 11000
|
||||||
|
|
||||||
|
// Just for debugging
|
||||||
|
t1.hint = 't1';
|
||||||
|
t2.hint = 't2';
|
||||||
|
t3.hint = 't3';
|
||||||
|
t4.hint = 't4';
|
||||||
|
f1.hint = 'f1';
|
||||||
|
fake.hint = 'fake';
|
||||||
|
|
||||||
|
// Fake TX should temporarly change output
|
||||||
|
// w.addTX(fake);
|
||||||
|
|
||||||
|
w.addTX(fake, function(err) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.equal(balance.toString(10), '22500');
|
w.addTX(t4, function(err) {
|
||||||
w.addTX(t1, function(err) {
|
assert(!err);
|
||||||
w.getBalance(function(err, balance) {
|
w.getBalance(function(err, balance) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.equal(balance.toString(10), '73000');
|
assert.equal(balance.toString(10), '22500');
|
||||||
w.addTX(t2, function(err) {
|
w.addTX(t1, function(err) {
|
||||||
assert(!err);
|
|
||||||
w.getBalance(function(err, balance) {
|
w.getBalance(function(err, balance) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.equal(balance.toString(10), '47000');
|
assert.equal(balance.toString(10), '73000');
|
||||||
w.addTX(t3, function(err) {
|
w.addTX(t2, function(err) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w.getBalance(function(err, balance) {
|
w.getBalance(function(err, balance) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.equal(balance.toString(10), '22000');
|
assert.equal(balance.toString(10), '47000');
|
||||||
w.addTX(f1, function(err) {
|
w.addTX(t3, function(err) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w.getBalance(function(err, balance) {
|
w.getBalance(function(err, balance) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.equal(balance.toString(10), '11000');
|
assert.equal(balance.toString(10), '22000');
|
||||||
w.getAll(function(err, txs) {
|
w.addTX(f1, function(err) {
|
||||||
assert(txs.some(function(tx) {
|
assert(!err);
|
||||||
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());
|
var w2 = bcoin.wallet.fromJSON(w.toJSON());
|
||||||
// assert.equal(w2.getBalance().toString(10), '11000');
|
// assert.equal(w2.getBalance().toString(10), '11000');
|
||||||
// assert(w2.getAll().some(function(tx) {
|
// assert(w2.getAll().some(function(tx) {
|
||||||
// return tx.hash('hex') === f1.hash('hex');
|
// return tx.hash('hex') === f1.hash('hex');
|
||||||
// }));
|
// }));
|
||||||
cb();
|
cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -223,114 +232,138 @@ describe('Wallet', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fill tx with inputs', function(cb) {
|
it('should fill tx with inputs', function(cb) {
|
||||||
var w1 = bcoin.wallet();
|
wdb.create({}, function(err, w1) {
|
||||||
var w2 = bcoin.wallet();
|
|
||||||
|
|
||||||
// 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, function(err) {
|
|
||||||
assert(!err);
|
assert(!err);
|
||||||
|
wdb.create({}, function(err, w2) {
|
||||||
// Create new transaction
|
|
||||||
var t2 = bcoin.mtx().addOutput(w2, 5460);
|
|
||||||
w1.fill(t2, function(err) {
|
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w1.sign(t2);
|
|
||||||
assert(t2.verify());
|
|
||||||
|
|
||||||
assert.equal(t2.getInputValue().toString(10), 16380);
|
// Coinbase
|
||||||
// If change < dust and is added to outputs:
|
var t1 = bcoin.mtx()
|
||||||
// assert.equal(t2.getOutputValue().toString(10), 6380);
|
.addOutput(w1, 5460)
|
||||||
// If change < dust and is added to fee:
|
.addOutput(w1, 5460)
|
||||||
assert.equal(t2.getOutputValue().toString(10), 5460);
|
.addOutput(w1, 5460)
|
||||||
|
.addOutput(w1, 5460);
|
||||||
|
|
||||||
// Create new transaction
|
t1.addInput(dummyInput);
|
||||||
var t3 = bcoin.mtx().addOutput(w2, 15000);
|
|
||||||
w1.fill(t3, function(err) {
|
// Fake TX should temporarly change output
|
||||||
assert(err);
|
w1.addTX(t1, function(err) {
|
||||||
assert.equal(err.requiredFunds.toString(10), 25000);
|
assert(!err);
|
||||||
cb();
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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) {
|
it('should sign multiple inputs using different keys', function(cb) {
|
||||||
var w1 = bcoin.wallet();
|
wdb.create({}, function(err, w1) {
|
||||||
var w2 = bcoin.wallet();
|
|
||||||
var to = bcoin.wallet();
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// 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
|
|
||||||
|
|
||||||
w1.addTX(t1, function(err) {
|
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w2.addTX(t2, function(err) {
|
wdb.create({}, function(err, w2) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
|
wdb.create({}, function(err, to) {
|
||||||
// Create our tx with an output
|
|
||||||
var tx = bcoin.mtx();
|
|
||||||
tx.addOutput(to, 5460);
|
|
||||||
|
|
||||||
var cost = tx.getOutputValue();
|
|
||||||
var total = cost.add(new bn(constants.tx.minFee));
|
|
||||||
|
|
||||||
w1.getUnspent(function(err, unspent1) {
|
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w2.getUnspent(function(err, unspent2) {
|
|
||||||
|
// 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
|
||||||
|
// 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
|
||||||
|
|
||||||
|
w1.addTX(t1, function(err) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
|
w2.addTX(t2, function(err) {
|
||||||
|
assert(!err);
|
||||||
|
|
||||||
// Add dummy output (for `left`) to calculate maximum TX size
|
// Create our tx with an output
|
||||||
tx.addOutput(w1, new bn(0));
|
var tx = bcoin.mtx();
|
||||||
|
tx.addOutput(to, 5460);
|
||||||
|
|
||||||
// Add our unspent inputs to sign
|
var cost = tx.getOutputValue();
|
||||||
tx.addInput(unspent1[0]);
|
var total = cost.add(new bn(constants.tx.minFee));
|
||||||
tx.addInput(unspent1[1]);
|
|
||||||
tx.addInput(unspent2[0]);
|
|
||||||
|
|
||||||
var left = tx.getInputValue().sub(total);
|
w1.getUnspent(function(err, unspent1) {
|
||||||
if (left.cmpn(constants.tx.dustThreshold) < 0) {
|
assert(!err);
|
||||||
tx.outputs[tx.outputs.length - 2].value.iadd(left);
|
w2.getUnspent(function(err, unspent2) {
|
||||||
left = new bn(0);
|
assert(!err);
|
||||||
}
|
|
||||||
if (left.cmpn(0) === 0)
|
|
||||||
tx.outputs.pop();
|
|
||||||
else
|
|
||||||
tx.outputs[tx.outputs.length - 1].value = left;
|
|
||||||
|
|
||||||
// Sign transaction
|
// Add dummy output (for `left`) to calculate maximum TX size
|
||||||
assert.equal(w1.sign(tx), 2);
|
tx.addOutput(w1, new bn(0));
|
||||||
assert.equal(w2.sign(tx), 1);
|
|
||||||
|
|
||||||
// Verify
|
// Add our unspent inputs to sign
|
||||||
assert.equal(tx.verify(), true);
|
tx.addInput(unspent1[0]);
|
||||||
|
tx.addInput(unspent1[1]);
|
||||||
|
tx.addInput(unspent2[0]);
|
||||||
|
|
||||||
// Sign transaction using `inputs` and `off` params.
|
var left = tx.getInputValue().sub(total);
|
||||||
tx.inputs.length = 0;
|
if (left.cmpn(constants.tx.dustThreshold) < 0) {
|
||||||
tx.addInput(unspent1[1]);
|
tx.outputs[tx.outputs.length - 2].value.iadd(left);
|
||||||
tx.addInput(unspent1[2]);
|
left = new bn(0);
|
||||||
tx.addInput(unspent2[1]);
|
}
|
||||||
assert.equal(w1.sign(tx, 'all'), 2);
|
if (left.cmpn(0) === 0)
|
||||||
assert.equal(w2.sign(tx, 'all'), 1);
|
tx.outputs.pop();
|
||||||
|
else
|
||||||
|
tx.outputs[tx.outputs.length - 1].value = left;
|
||||||
|
|
||||||
// Verify
|
// Sign transaction
|
||||||
assert.equal(tx.verify(), true);
|
assert.equal(w1.sign(tx), 2);
|
||||||
|
assert.equal(w2.sign(tx), 1);
|
||||||
|
|
||||||
cb();
|
// 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);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assert.equal(tx.verify(), true);
|
||||||
|
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -344,149 +377,144 @@ describe('Wallet', function() {
|
|||||||
flags |= bcoin.protocol.constants.flags.VERIFY_WITNESS;
|
flags |= bcoin.protocol.constants.flags.VERIFY_WITNESS;
|
||||||
|
|
||||||
// Create 3 2-of-3 wallets with our pubkeys as "shared keys"
|
// Create 3 2-of-3 wallets with our pubkeys as "shared keys"
|
||||||
var w1 = bcoin.wallet({
|
var options = {
|
||||||
witness: witness,
|
witness: witness,
|
||||||
derivation: 'bip44',
|
derivation: 'bip44',
|
||||||
type: 'multisig',
|
type: 'multisig',
|
||||||
m: 2,
|
m: 2,
|
||||||
n: 3
|
n: 3
|
||||||
});
|
};
|
||||||
|
|
||||||
var w2 = bcoin.wallet({
|
wdb.create(utils.merge({}, options), function(err, w1) {
|
||||||
witness: witness,
|
|
||||||
derivation: 'bip44',
|
|
||||||
type: 'multisig',
|
|
||||||
m: 2,
|
|
||||||
n: 3
|
|
||||||
});
|
|
||||||
|
|
||||||
var w3 = bcoin.wallet({
|
|
||||||
witness: witness,
|
|
||||||
derivation: 'bip44',
|
|
||||||
type: 'multisig',
|
|
||||||
m: 2,
|
|
||||||
n: 3
|
|
||||||
});
|
|
||||||
|
|
||||||
var receive = bcoin.wallet();
|
|
||||||
|
|
||||||
w1.addKey(w2);
|
|
||||||
w1.addKey(w3);
|
|
||||||
w2.addKey(w1);
|
|
||||||
w2.addKey(w3);
|
|
||||||
w3.addKey(w1);
|
|
||||||
w3.addKey(w2);
|
|
||||||
|
|
||||||
w3 = bcoin.wallet.fromJSON(w3.toJSON());
|
|
||||||
|
|
||||||
// Our p2sh address
|
|
||||||
var addr = w1.getAddress();
|
|
||||||
|
|
||||||
if (witness)
|
|
||||||
assert(bcoin.address.parse(addr).type === 'witnessscripthash');
|
|
||||||
else
|
|
||||||
assert(bcoin.address.parse(addr).type === 'scripthash');
|
|
||||||
|
|
||||||
assert.equal(w1.getAddress(), addr);
|
|
||||||
assert.equal(w2.getAddress(), addr);
|
|
||||||
assert.equal(w3.getAddress(), addr);
|
|
||||||
|
|
||||||
var paddr = w1.getProgramAddress();
|
|
||||||
assert.equal(w1.getProgramAddress(), paddr);
|
|
||||||
assert.equal(w2.getProgramAddress(), paddr);
|
|
||||||
assert.equal(w3.getProgramAddress(), paddr);
|
|
||||||
|
|
||||||
// Add a shared unspent transaction to our wallets
|
|
||||||
var utx = bcoin.mtx();
|
|
||||||
if (bullshitNesting)
|
|
||||||
utx.addOutput({ address: paddr, value: 5460 * 10 });
|
|
||||||
else
|
|
||||||
utx.addOutput({ address: addr, value: 5460 * 10 });
|
|
||||||
|
|
||||||
utx.addInput(dummyInput);
|
|
||||||
|
|
||||||
assert(w1.ownOutput(utx.outputs[0]));
|
|
||||||
|
|
||||||
// Simulate a confirmation
|
|
||||||
utx.ps = 0;
|
|
||||||
utx.ts = 1;
|
|
||||||
utx.height = 1;
|
|
||||||
|
|
||||||
assert.equal(w1.receiveDepth, 1);
|
|
||||||
|
|
||||||
w1.addTX(utx, function(err) {
|
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w2.addTX(utx, function(err) {
|
wdb.create(utils.merge({}, options), function(err, w2) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w3.addTX(utx, function(err) {
|
wdb.create(utils.merge({}, options), function(err, w3) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
|
wdb.create({}, function(err, receive) {
|
||||||
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);
|
|
||||||
|
|
||||||
// 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(!err);
|
||||||
|
|
||||||
w1.sign(send);
|
w1.addKey(w2);
|
||||||
|
w1.addKey(w3);
|
||||||
|
w2.addKey(w1);
|
||||||
|
w2.addKey(w3);
|
||||||
|
w3.addKey(w1);
|
||||||
|
w3.addKey(w2);
|
||||||
|
|
||||||
assert(!send.verify(null, true, flags));
|
// w3 = bcoin.wallet.fromJSON(w3.toJSON());
|
||||||
w2.sign(send);
|
|
||||||
|
|
||||||
assert(send.verify(null, true, flags));
|
// Our p2sh address
|
||||||
|
var addr = w1.getAddress();
|
||||||
|
|
||||||
assert.equal(w1.changeDepth, 1);
|
if (witness)
|
||||||
var change = w1.changeAddress.getAddress();
|
assert(bcoin.address.parse(addr).type === 'witnessscripthash');
|
||||||
assert.equal(w1.changeAddress.getAddress(), change);
|
else
|
||||||
assert.equal(w2.changeAddress.getAddress(), change);
|
assert(bcoin.address.parse(addr).type === 'scripthash');
|
||||||
assert.equal(w3.changeAddress.getAddress(), change);
|
|
||||||
|
assert.equal(w1.getAddress(), addr);
|
||||||
|
assert.equal(w2.getAddress(), addr);
|
||||||
|
assert.equal(w3.getAddress(), addr);
|
||||||
|
|
||||||
|
var paddr = w1.getProgramAddress();
|
||||||
|
assert.equal(w1.getProgramAddress(), paddr);
|
||||||
|
assert.equal(w2.getProgramAddress(), paddr);
|
||||||
|
assert.equal(w3.getProgramAddress(), paddr);
|
||||||
|
|
||||||
|
// Add a shared unspent transaction to our wallets
|
||||||
|
var utx = bcoin.mtx();
|
||||||
|
if (bullshitNesting)
|
||||||
|
utx.addOutput({ address: paddr, value: 5460 * 10 });
|
||||||
|
else
|
||||||
|
utx.addOutput({ address: addr, value: 5460 * 10 });
|
||||||
|
|
||||||
|
utx.addInput(dummyInput);
|
||||||
|
|
||||||
|
assert(w1.ownOutput(utx.outputs[0]));
|
||||||
|
|
||||||
// Simulate a confirmation
|
// Simulate a confirmation
|
||||||
send.ps = 0;
|
utx.ps = 0;
|
||||||
send.ts = 1;
|
utx.ts = 1;
|
||||||
send.height = 1;
|
utx.height = 1;
|
||||||
|
|
||||||
w1.addTX(send, function(err) {
|
assert.equal(w1.receiveDepth, 1);
|
||||||
|
|
||||||
|
w1.addTX(utx, function(err) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w2.addTX(send, function(err) {
|
w2.addTX(utx, function(err) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
w3.addTX(send, function(err) {
|
w3.addTX(utx, function(err) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
|
|
||||||
assert.equal(w1.receiveDepth, 2);
|
assert.equal(w1.receiveDepth, 2);
|
||||||
assert.equal(w1.changeDepth, 2);
|
assert.equal(w1.changeDepth, 1);
|
||||||
|
|
||||||
assert(w1.getAddress() === addr);
|
assert(w1.getAddress() !== addr);
|
||||||
assert(w1.changeAddress.getAddress() !== change);
|
addr = w1.getAddress();
|
||||||
change = w1.changeAddress.getAddress();
|
assert.equal(w1.getAddress(), addr);
|
||||||
assert.equal(w1.changeAddress.getAddress(), change);
|
assert.equal(w2.getAddress(), addr);
|
||||||
assert.equal(w2.changeAddress.getAddress(), change);
|
|
||||||
assert.equal(w3.changeAddress.getAddress(), change);
|
|
||||||
|
|
||||||
if (witness)
|
|
||||||
send.inputs[0].witness[2] = new Buffer([]);
|
|
||||||
else
|
|
||||||
send.inputs[0].script[2] = 0;
|
|
||||||
|
|
||||||
assert(!send.verify(null, true, flags));
|
|
||||||
assert.equal(send.getFee().toNumber(), 10000);
|
|
||||||
|
|
||||||
w3 = bcoin.wallet.fromJSON(w3.toJSON());
|
|
||||||
assert.equal(w3.receiveDepth, 2);
|
|
||||||
assert.equal(w3.changeDepth, 2);
|
|
||||||
assert.equal(w3.getAddress(), addr);
|
assert.equal(w3.getAddress(), addr);
|
||||||
assert.equal(w3.changeAddress.getAddress(), change);
|
|
||||||
|
|
||||||
cb();
|
// 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);
|
||||||
|
|
||||||
|
w1.sign(send);
|
||||||
|
|
||||||
|
assert(!send.verify(null, true, flags));
|
||||||
|
w2.sign(send);
|
||||||
|
|
||||||
|
assert(send.verify(null, true, flags));
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Simulate a confirmation
|
||||||
|
send.ps = 0;
|
||||||
|
send.ts = 1;
|
||||||
|
send.height = 1;
|
||||||
|
|
||||||
|
w1.addTX(send, function(err) {
|
||||||
|
assert(!err);
|
||||||
|
w2.addTX(send, function(err) {
|
||||||
|
assert(!err);
|
||||||
|
w3.addTX(send, function(err) {
|
||||||
|
assert(!err);
|
||||||
|
|
||||||
|
assert.equal(w1.receiveDepth, 2);
|
||||||
|
assert.equal(w1.changeDepth, 2);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (witness)
|
||||||
|
send.inputs[0].witness[2] = new Buffer([]);
|
||||||
|
else
|
||||||
|
send.inputs[0].script[2] = 0;
|
||||||
|
|
||||||
|
assert(!send.verify(null, true, flags));
|
||||||
|
assert.equal(send.getFee().toNumber(), 10000);
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -508,28 +536,11 @@ describe('Wallet', function() {
|
|||||||
multisig(true, true, cb);
|
multisig(true, true, cb);
|
||||||
});
|
});
|
||||||
|
|
||||||
var coinbase = '010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2c027156266a24aa21a9edb1e139795984903d6629ddedf3763fb9bc582fd68a46b1f8c7c57f9fbcc7fc900101ffffffff02887d102a0100000023210290dd626747729e1cc445cb9a11cfb7e78ea896db9f5c335e6730491d9ee7474dac0000000000000000266a24aa21a9edb1e139795984903d6629ddedf3763fb9bc582fd68a46b1f8c7c57f9fbcc7fc900120000000000000000000000000000000000000000000000000000000000000000000000000';
|
it('should have gratuitous dump', function(cb) {
|
||||||
var chash = 'ba0cb2bf1aa19e4643208f7b38798a3deaa3320968d2cb1e42c5802a7baaba99';
|
bcoin.walletdb().dump(function(err, records) {
|
||||||
var wpkh = '0100000001fc8f4ccd25b285bcae9f305d2ec3feb79a71384bab0303f810b58089b9c6e084000000006a473044022036548e256acfbc6a77f322d32ae0f11cb20a05a240d72550bda9d8cf169b35e90220303ad1a60d8297a12501dbebc46ec39c7652ac3d75ff394b8d4c3cbdaf3279c7012103f85883e08a3581b636bbafee55f337b6bf4467826a280fda5bf0533368e99b73ffffffff0200ba1dd2050000001976a91443cec67a63867420c0c934ffbbf89f14729304f988acf0cbf01907000000160014e7b8143685eb4eb03810c8ffb7c4a74d5f23161c00000000';
|
assert(!err);
|
||||||
var whash = 'a72943c0131d655ff3d272f202d4f6ad2cf378eba9416c9b8028920d71d8f90a';
|
console.log(records);
|
||||||
var w2hash = 'c532af06b9a81d9171618fb0b30075ddb3a6fca68c9b89536e6e34b0beddcc23';
|
setTimeout(cb, 200);
|
||||||
|
|
||||||
// https://segnet.smartbit.com.au/tx/c532af06b9a81d9171618fb0b30075ddb3a6fca68c9b89536e6e34b0beddcc23
|
|
||||||
var w2pkh = new Buffer(bcoin.fs.readFileSync(__dirname + '/wtx.hex', 'ascii').trim(), 'hex');
|
|
||||||
|
|
||||||
it('should have a wtxid', function(cb) {
|
|
||||||
var src = bcoin.mtx({
|
|
||||||
outputs: [{
|
|
||||||
value: 5460 * 2,
|
|
||||||
address: bcoin.address.compileData(new Buffer([]))
|
|
||||||
}]
|
|
||||||
});
|
});
|
||||||
src.addInput(dummyInput);
|
|
||||||
var t = bcoin.protocol.parser.parseWitnessTX(new Buffer(coinbase, 'hex'));
|
|
||||||
var t = new bcoin.tx(bcoin.protocol.parser.parseWitnessTX(new Buffer(w2pkh, 'hex')));
|
|
||||||
delete t._raw;
|
|
||||||
delete t._hash;
|
|
||||||
delete t._whash;
|
|
||||||
cb();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user