This commit is contained in:
Chris Kleeschulte 2017-02-04 13:42:32 -05:00
parent 3c53b1284c
commit 71ca53f8ba
2 changed files with 112 additions and 48 deletions

View File

@ -5,10 +5,17 @@ var BufferReader = bitcore.encoding.BufferReader;
function Encoding(servicePrefix) { function Encoding(servicePrefix) {
this.servicePrefix = servicePrefix; this.servicePrefix = servicePrefix;
this._subKeyMap = {
transaction: new Buffer('00', 'hex'),
addresses: new Buffer('01', 'hex'),
utxo: new Buffer('02', 'hex'),
utxoSat: new Buffer('03', 'hex'),
balance: new Buffer('04', 'hex')
};
} }
Encoding.prototype.encodeWalletTransactionKey = function(walletId, height) { Encoding.prototype.encodeWalletTransactionKey = function(walletId, height) {
var buffers = [this.servicePrefix]; var buffers = [this.servicePrefix, this._subKeyMap.transaction];
var walletIdSizeBuffer = new Buffer(1); var walletIdSizeBuffer = new Buffer(1);
walletIdSizeBuffer.writeUInt8(walletId.length); walletIdSizeBuffer.writeUInt8(walletId.length);
@ -28,7 +35,7 @@ Encoding.prototype.encodeWalletTransactionKey = function(walletId, height) {
Encoding.prototype.decodeWalletTransactionKey = function(buffer) { Encoding.prototype.decodeWalletTransactionKey = function(buffer) {
var reader = new BufferReader(buffer); var reader = new BufferReader(buffer);
reader.read(1); reader.read(3);
var walletSize = reader.readUInt8(); var walletSize = reader.readUInt8();
var walletId = reader.read(walletSize).toString('utf8'); var walletId = reader.read(walletSize).toString('utf8');
@ -51,7 +58,7 @@ Encoding.prototype.decodeWalletTransactionValue = function(buffer) {
}; };
Encoding.prototype.encodeWalletUtxoKey = function(walletId, txid, outputIndex) { Encoding.prototype.encodeWalletUtxoKey = function(walletId, txid, outputIndex) {
var buffers = [this.servicePrefix]; var buffers = [this.servicePrefix, this._subKeyMap.utxo];
var walletIdSizeBuffer = new Buffer(1); var walletIdSizeBuffer = new Buffer(1);
walletIdSizeBuffer.writeUInt8(walletId.length); walletIdSizeBuffer.writeUInt8(walletId.length);
@ -76,7 +83,7 @@ Encoding.prototype.encodeWalletUtxoKey = function(walletId, txid, outputIndex) {
Encoding.prototype.decodeWalletUtxoKey = function(buffer) { Encoding.prototype.decodeWalletUtxoKey = function(buffer) {
var reader = new BufferReader(buffer); var reader = new BufferReader(buffer);
reader.read(1); reader.read(3);
var walletIdSize = reader.readUInt8(); var walletIdSize = reader.readUInt8();
var walletId = reader.read(walletIdSize).toString('utf8'); var walletId = reader.read(walletIdSize).toString('utf8');
@ -100,8 +107,8 @@ Encoding.prototype.encodeWalletUtxoValue = function(height, satoshis, scriptBuff
Encoding.prototype.decodeWalletUtxoValue = function(buffer) { Encoding.prototype.decodeWalletUtxoValue = function(buffer) {
var reader = new BufferReader(buffer); var reader = new BufferReader(buffer);
var height = reader.readUInt32BE(); var height = reader.readUInt32BE();
var satoshis = reader.readDoubleBE(); var satoshis = buffer.readDoubleBE(4);
var scriptBuffer = reader.read(buffer.length - 12); var scriptBuffer = reader.read(12, buffer.length - 12);
return { return {
height: height, height: height,
satoshis: satoshis, satoshis: satoshis,
@ -110,7 +117,7 @@ Encoding.prototype.decodeWalletUtxoValue = function(buffer) {
}; };
Encoding.prototype.encodeWalletUtxoSatoshisKey = function(walletId, satoshis, txid, outputIndex) { Encoding.prototype.encodeWalletUtxoSatoshisKey = function(walletId, satoshis, txid, outputIndex) {
var buffers = [this.servicePrefix]; var buffers = [this.servicePrefix, this._subKeyMap.utxoSat];
var walletIdSizeBuffer = new Buffer(1); var walletIdSizeBuffer = new Buffer(1);
walletIdSizeBuffer.writeUInt8(walletId.length); walletIdSizeBuffer.writeUInt8(walletId.length);
@ -141,12 +148,12 @@ Encoding.prototype.encodeWalletUtxoSatoshisKey = function(walletId, satoshis, tx
Encoding.prototype.decodeWalletUtxoSatoshisKey = function(buffer) { Encoding.prototype.decodeWalletUtxoSatoshisKey = function(buffer) {
var reader = new BufferReader(buffer); var reader = new BufferReader(buffer);
reader.read(1); reader.read(3);
var walletIdSizeBuffer = reader.readUInt8(); var walletIdSize = reader.readUInt8();
var walletId = reader.read(walletIdSizeBuffer).toString('utf8'); var walletId = reader.read(walletIdSize).toString('utf8');
var satoshis = reader.readDoubleBE(); var satoshis = buffer.readDoubleBE(walletIdSize + 4);
var txid = reader.read(32).toString('hex'); var txid = reader.read(walletIdSize + 12, 32).toString('hex');
var outputIndex = reader.readUInt32BE(); var outputIndex = reader.readUInt32BE();
return { return {
walletId: walletId, walletId: walletId,
@ -173,9 +180,11 @@ Encoding.prototype.decodeWalletUtxoSatoshisValue = function(buffer) {
}; };
Encoding.prototype.encodeWalletAddressesKey = function(walletId) { Encoding.prototype.encodeWalletAddressesKey = function(walletId) {
var prefix = new Buffer('00', 'hex'); var prefix = this._subKeyMap.addresses;
var walletIdBuffer = new Buffer(walletId, 'hex'); var walletIdSizeBuffer = new Buffer(1);
return Buffer.concat([this.servicePrefix, prefix, walletIdBuffer]); walletIdSizeBuffer.writeUInt8(walletId.length);
var walletIdBuffer = new Buffer(walletId, 'utf8');
return Buffer.concat([this.servicePrefix, prefix, walletIdSizeBuffer, walletIdBuffer]);
}; };
Encoding.prototype.decodeWalletAddressesKey = function(buffer) { Encoding.prototype.decodeWalletAddressesKey = function(buffer) {
@ -201,19 +210,19 @@ Encoding.prototype.decodeWalletAddressesValue = function(buffer) {
var reader = new BufferReader(buffer); var reader = new BufferReader(buffer);
var addressesLength = reader.readUInt32BE(); var addressesLength = reader.readUInt32BE();
var addresses = []; var addresses = [];
var addressSize = 0; for(var i = 0; i < addressesLength; i++) {
for(var i = 0; i < addressesLength.length; i++) { var addressSize = reader.readUInt8();
addressSize = reader.readUInt8(addressSize);
addresses.push(reader.read(addressSize).toString('utf8')); addresses.push(reader.read(addressSize).toString('utf8'));
} }
return addresses; return addresses;
}; };
Encoding.prototype.encodeWalletBalanceKey = function(walletId) { Encoding.prototype.encodeWalletBalanceKey = function(walletId) {
var prefix = new Buffer('01', 'hex'); var prefix = this._subKeyMap.balance;
var walletIdBuffer = new Buffer(walletId, 'hex'); var walletIdSizeBuffer = new Buffer(1);
return Buffer.concat([this.servicePrefix, prefix, walletIdBuffer]); walletIdSizeBuffer.writeUInt8(walletId.length);
var walletIdBuffer = new Buffer(walletId, 'utf8');
return Buffer.concat([this.servicePrefix, prefix, walletIdSizeBuffer, walletIdBuffer]);
}; };
Encoding.prototype.decodeWalletBalanceKey = function(buffer) { Encoding.prototype.decodeWalletBalanceKey = function(buffer) {
@ -222,14 +231,12 @@ Encoding.prototype.decodeWalletBalanceKey = function(buffer) {
Encoding.prototype.encodeWalletBalanceValue = function(balance) { Encoding.prototype.encodeWalletBalanceValue = function(balance) {
var balanceBuffer = new Buffer(8); var balanceBuffer = new Buffer(8);
balanceBuffer.writeUInt32BE(balance); balanceBuffer.writeDoubleBE(balance);
return balanceBuffer; return balanceBuffer;
}; };
Encoding.prototype.decodeWalletBalanceValue = function(buffer) { Encoding.prototype.decodeWalletBalanceValue = function(buffer) {
var reader = new BufferReader(buffer); var balance = buffer.readDoubleBE();
var balance = reader.readDoubleBE();
return balance; return balance;
}; };

View File

@ -34,7 +34,8 @@ inherits(WalletService, BaseService);
WalletService.dependencies = [ WalletService.dependencies = [
'bitcoind', 'bitcoind',
'web' 'web',
'address'
]; ];
WalletService.prototype.getAPIMethods = function() { WalletService.prototype.getAPIMethods = function() {
@ -327,8 +328,7 @@ WalletService.prototype._loadAllAddresses = function(callback) {
stream.on('data', function(data) { stream.on('data', function(data) {
var key = self._encoding.decodeWalletAddressesKey(data.key); var key = self._encoding.decodeWalletAddressesKey(data.key);
var value = self._encoding.decodeWalletAddressesValue(data.value); var value = self._encoding.decodeWalletAddressesValue(data.value);
value.forEach(function(address) {
value.addresses.forEach(function(address) {
if(!self._addressMap[address]) { if(!self._addressMap[address]) {
self._addressMap[address] = []; self._addressMap[address] = [];
} }
@ -392,7 +392,7 @@ WalletService.prototype._endpointUTXOs = function() {
var options = { var options = {
queryMempool: queryMempool queryMempool: queryMempool
}; };
self._getUtxos(walletId, height, options, function(err, utxos) { self._getUtxos(walletId, function(err, utxos) {
if(err) { if(err) {
return utils.sendError(err, res); return utils.sendError(err, res);
} }
@ -422,7 +422,7 @@ WalletService.prototype._endpointGetBalance= function() {
byAddress: byAddress byAddress: byAddress
}; };
self._getBalance(walletId, height, options, function(err, result) { self._getBalance(walletId, function(err, result) {
if(err) { if(err) {
return utils.sendError(err, res); return utils.sendError(err, res);
} }
@ -431,6 +431,23 @@ WalletService.prototype._endpointGetBalance= function() {
}; };
}; };
WalletService.prototype._endpointRemoveWallet = function() {
var self = this;
return function(req, res) {
var walletId = req.params.walletId;
self._removeWallet(walletId, function(err) {
if(err) {
return utils.sendError(err, res);
}
res.status(200).jsonp({
walletId: walletId,
result: 'removed'
});
});
};
};
WalletService.prototype._endpointGetAddresses = function() { WalletService.prototype._endpointGetAddresses = function() {
var self = this; var self = this;
return function(req, res) { return function(req, res) {
@ -461,15 +478,31 @@ WalletService.prototype._endpointPostAddresses = function() {
} }
var walletId = utils.getWalletId(); var walletId = utils.getWalletId();
// TODO make imdempotent //if this is a post to /wallets, then it is expected that a new wallet is to be created
self._importAddresses(walletId, addresses, function(err) { async.series([
if(err) { function(next) {
return utils.sendError(err, res); self._createWallet(walletId, [], function(err) {
} if(err) {
res.status(201).jsonp({ return next(err);
walletId: walletId }
next();
});
}, function(next) {
// TODO make imdempotent
self._importAddresses(walletId, addresses, function(err) {
if(err) {
return next(err);
}
next();
});
}], function(err) {
if(err) {
return utils.sendError(err, res);
}
res.status(201).jsonp({
walletId: walletId
});
}); });
});
}; };
}; };
@ -695,9 +728,24 @@ WalletService.prototype._getTransactions = function(walletId, options, callback)
//} //}
}; };
WalletService.prototype._removeWallet = function(walletId, callback) {
var self = this;
var boundary = self._encoding.encodeWalletAddressesKey(walletId);
//wallet tz
// wallet addresses
// utxo
// utxosat
//wallet balance
var txStream = self.store.createReadStream({
gte: boundary
lte: boundary
});
};
WalletService.prototype._getAddresses = function(walletId, callback) { WalletService.prototype._getAddresses = function(walletId, callback) {
var self = this; var self = this;
var key = self._encoding.encodeWalletAddressKey(walletId); var key = self._encoding.encodeWalletAddressesKey(walletId);
self.store.get(key, function(err, value) { self.store.get(key, function(err, value) {
if(err) { if(err) {
return callback(err); return callback(err);
@ -705,10 +753,17 @@ WalletService.prototype._getAddresses = function(walletId, callback) {
if (!value) { if (!value) {
return callback(null, []); return callback(null, []);
} }
callback(null, self._encoding.decodeWalletAddressValue(value)); callback(null, self._encoding.decodeWalletAddressesValue(value));
}); });
}; };
WalletService.prototype._createWallet = function(walletId, addresses, callback) {
var self = this;
var key = self._encoding.encodeWalletAddressesKey(walletId);
var value = self._encoding.encodeWalletAddressesValue(addresses);
this.store.put(key, value, callback);
};
WalletService.prototype._importAddresses = function(walletId, addresses, callback) { WalletService.prototype._importAddresses = function(walletId, addresses, callback) {
var self = this; var self = this;
@ -720,7 +775,7 @@ WalletService.prototype._importAddresses = function(walletId, addresses, callbac
async.parallel( async.parallel(
[ [
self._getUTXOIndexOperations.bind(self, walletId, addresses), self._getUTXOIndexOperations.bind(self, walletId, addresses),
self._getTxidOperations.bind(self, walletId, addresses) self._getTxidIndexOperations.bind(self, walletId, addresses)
], ],
function(err, results) { function(err, results) {
if(err) { if(err) {
@ -730,8 +785,8 @@ WalletService.prototype._importAddresses = function(walletId, addresses, callbac
var operations = results[0].concat(results[1]); var operations = results[0].concat(results[1]);
operations.push({ operations.push({
type: 'put', type: 'put',
key: self._encoding.encodeWalletAddressKey(walletId), key: self._encoding.encodeWalletAddressesKey(walletId),
value: self._encoding.encodeWalletAddressValue(oldAddresses.concat(addresses)) value: self._encoding.encodeWalletAddressesValue(oldAddresses.concat(addresses))
}); });
self.store.batch(operations, function(err) { self.store.batch(operations, function(err) {
@ -772,7 +827,7 @@ WalletService.prototype._getUTXOIndexOperations = function(walletId, addresses,
balance = initialBalance; balance = initialBalance;
} }
self.services.address.getUnspentOutputs(addresses, function(err, utxos) { self.node.services.address.getUnspentOutputs(addresses, false, function(err, utxos) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
@ -814,7 +869,7 @@ WalletService.prototype._getTxidIndexOperations = function(walletId, addresses,
var txids = {}; var txids = {};
async.eachLimit(addresses, 10, function(address, next) { async.eachLimit(addresses, 10, function(address, next) {
self.services.address.getAddressTxidsWithHeights(address, {}, function(err, tmpTxids) { self.node.services.address.getAddressTxidsWithHeights(address, {}, function(err, tmpTxids) {
if(err) { if(err) {
return next(err); return next(err);
} }
@ -840,7 +895,7 @@ WalletService.prototype._getTxidIndexOperations = function(walletId, addresses,
}; };
WalletService.prototype._storeAddresses = function(walletId, addresses, callback) { WalletService.prototype._storeAddresses = function(walletId, addresses, callback) {
var key = this._encoding.encodeWalletAddressKey(walletId); var key = this._encoding.encodeWalletAddressesKey(walletId);
var value = this._encoding.encodeWalletValue(addresses); var value = this._encoding.encodeWalletValue(addresses);
this.store.put(key, value, callback); this.store.put(key, value, callback);
}; };
@ -875,6 +930,9 @@ WalletService.prototype.setupRoutes = function(app) {
app.get('/wallets/:walletId', app.get('/wallets/:walletId',
s._endpointGetAddresses() s._endpointGetAddresses()
); );
app.delete('/wallets/:walletId',
s._endpointRemoveWallet()
);
app.put('/wallets/:walletId/addresses', app.put('/wallets/:walletId/addresses',
s._endpointPutAddresses() s._endpointPutAddresses()
); );
@ -886,7 +944,6 @@ WalletService.prototype.setupRoutes = function(app) {
v.checkAddresses, v.checkAddresses,
s._endpointPostAddresses() s._endpointPostAddresses()
); );
}; };
WalletService.prototype.getRoutePrefix = function() { WalletService.prototype.getRoutePrefix = function() {