From 54edc851e0d8e27c9447843959cd4afd1abc8f16 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 17 Jul 2015 15:55:36 -0400 Subject: [PATCH 1/4] Add getMempoolOutputs method. --- integration/index.js | 6 +++++ lib/bitcoind.js | 4 +++ src/bitcoindjs.cc | 58 +++++++++++++++++++++++++++++++++++++++++++- src/bitcoindjs.h | 2 ++ 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/integration/index.js b/integration/index.js index fd89630b..7e51af33 100644 --- a/integration/index.js +++ b/integration/index.js @@ -133,7 +133,13 @@ describe('Basic Functionality', function() { var work = bitcoind.getChainWork(hash); should.equal(work, undefined); }); + }); + describe('get outputs by address from the mempool', function() { + it('will do it', function() { + var outputs = bitcoind.getMempoolOutputs('n28S35tqEMbt6vNad7A5K3mZ7vdn8dZ86X'); + Array.isArray(outputs).should.equal(true); + }); }); }); diff --git a/lib/bitcoind.js b/lib/bitcoind.js index 46218747..6d05f567 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -382,6 +382,10 @@ Bitcoin.prototype.getTransactionWithBlock = function(txid, blockhash, callback) }); }; +Bitcoin.prototype.getMempoolOutputs = function(address) { + return bitcoindjs.getMempoolOutputs(address); +}; + Bitcoin.prototype.getInfo = function() { if (bitcoin.stopping) return []; return bitcoindjs.getInfo(); diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index fcff27a0..7db6ec94 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -983,6 +983,62 @@ NAN_METHOD(GetInfo) { NanReturnValue(obj); } +/** + * GetMempoolOutputs + * bitcoindjs.getMempoolOutputs() + * Will return outputs by address from the mempool. + */ +NAN_METHOD(GetMempoolOutputs) { + Isolate* isolate = Isolate::GetCurrent(); + HandleScope scope(isolate); + + Local outputs = Array::New(isolate); + int arrayIndex = 0; + + std::map mapTx = mempool.mapTx; + + for(std::map::iterator it = mapTx.begin(); it != mapTx.end(); it++) { + uint256 txid = it->first; + CTxMemPoolEntry entry = it->second; + + const CTransaction tx = entry.GetTx(); + + int outputIndex = 0; + + BOOST_FOREACH(const CTxOut& txout, tx.vout) { + + CScript script = txout.scriptPubKey; + + txnouttype type; + vector > hash; + + if (Solver(script, type, hash)) { + if (type == TX_PUBKEYHASH || type == TX_SCRIPTHASH) { + + Local output = NanNew(); + + // todo: include the script + output->Set(NanNew("script"), NanNew("")); + + uint64_t satoshis = txout.nValue; + output->Set(NanNew("satoshis"), NanNew(satoshis)); // can't go above 2 ^ 53 -1 + output->Set(NanNew("txid"), NanNew(txid.GetHex())); + + output->Set(NanNew("outputIndex"), NanNew(outputIndex)); + + outputs->Set(arrayIndex, output); + arrayIndex++; + } + } + + outputIndex++; + } + } + + NanReturnValue(outputs); + +} + /** * Helpers */ @@ -1020,7 +1076,7 @@ init(Handle target) { NODE_SET_METHOD(target, "getInfo", GetInfo); NODE_SET_METHOD(target, "isSpent", IsSpent); NODE_SET_METHOD(target, "getChainWork", GetChainWork); - + NODE_SET_METHOD(target, "getMempoolOutputs", GetMempoolOutputs); } NODE_MODULE(bitcoindjs, init) diff --git a/src/bitcoindjs.h b/src/bitcoindjs.h index ea825a87..3e4c4fed 100644 --- a/src/bitcoindjs.h +++ b/src/bitcoindjs.h @@ -29,3 +29,5 @@ NAN_METHOD(GetTransaction); NAN_METHOD(GetInfo); NAN_METHOD(IsSpent); NAN_METHOD(GetChainWork); +NAN_METHOD(GetMempoolOutputs); + From ef3abbcb6c22007fba6807b21be68cd931ecc9ee Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Fri, 17 Jul 2015 17:03:28 -0400 Subject: [PATCH 2/4] Added a binding for Add to mempool. --- integration/index.js | 35 +++++++++++++++++++++++++++++++++++ src/bitcoindjs.cc | 21 +++++++++++++++++++++ src/bitcoindjs.h | 11 ++--------- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/integration/index.js b/integration/index.js index 7e51af33..2d1ce527 100644 --- a/integration/index.js +++ b/integration/index.js @@ -135,6 +135,39 @@ describe('Basic Functionality', function() { }); }); + describe('add to mempool', function() { + it('will add an uncheckedTransaction', function() { + var fromAddress = 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1'; + var utxo = { + address: fromAddress, + txId: 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458', + outputIndex: 0, + script: Script.buildPublicKeyHashOut(fromAddress).toString(), + satoshis: 100000 + }; + var toAddress = 'mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc'; + var changeAddress = 'mgBCJAsvzgT2qNNeXsoECg2uPKrUsZ76up'; + var changeAddressP2SH = '2N7T3TAetJrSCruQ39aNrJvYLhG1LJosujf'; + var privateKey = 'cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY'; + var private1 = '6ce7e97e317d2af16c33db0b9270ec047a91bff3eff8558afb5014afb2bb5976'; + var private2 = 'c9b26b0f771a0d2dad88a44de90f05f416b3b385ff1d989343005546a0032890'; + var tx = new bitcore.Transaction(); + tx.from(utxo); + tx.to(toAddress, 50000); + tx.change(changeAddress); + tx.sign(privateKey); + var added = bitcoind.addMempoolUncheckedTransaction(tx.toBuffer()); + added.should.equal(true); + bitcoind.getTransaction(tx.hash, true, function(err, tx) { + if(err) { + throw err; + } + tx.toString('hex').should.equal(tx.toBuffer().toString('hex')); + }); + + }); + }); + describe('get outputs by address from the mempool', function() { it('will do it', function() { var outputs = bitcoind.getMempoolOutputs('n28S35tqEMbt6vNad7A5K3mZ7vdn8dZ86X'); @@ -142,4 +175,6 @@ describe('Basic Functionality', function() { }); }); + }); + diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index 7db6ec94..ec71d4ef 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -1039,6 +1039,26 @@ NAN_METHOD(GetMempoolOutputs) { } +/** + * AddMempoolUncheckedTransaction + */ +NAN_METHOD(AddMempoolUncheckedTransaction) { + if (!node::Buffer::HasInstance(args[0])) { + return NanThrowTypeError("First argument should be a Buffer."); + } + + CTransaction tx; + const char *arg = node::Buffer::Data(args[0]); + std::string strArg = std::string(arg); + + if (!DecodeHexTx(tx, strArg)) { + return NanThrowError("could not decode tx"); + } + bool added = mempool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(tx, 0, 0, 0.0, 1)); + NanReturnValue(NanNew(added)); + +} + /** * Helpers */ @@ -1077,6 +1097,7 @@ init(Handle target) { NODE_SET_METHOD(target, "isSpent", IsSpent); NODE_SET_METHOD(target, "getChainWork", GetChainWork); NODE_SET_METHOD(target, "getMempoolOutputs", GetMempoolOutputs); + NODE_SET_METHOD(target, "addMempoolUncheckedTransaction", AddMempoolUncheckedTransaction); } NODE_MODULE(bitcoindjs, init) diff --git a/src/bitcoindjs.h b/src/bitcoindjs.h index 3e4c4fed..28c625e9 100644 --- a/src/bitcoindjs.h +++ b/src/bitcoindjs.h @@ -1,11 +1,3 @@ -/** - * bitcoind.js - * Copyright (c) 2015, BitPay (MIT License) - * - * bitcoindjs.h: - * A bitcoind node.js binding header file. - */ - #include "main.h" #include "addrman.h" #include "alert.h" @@ -18,6 +10,7 @@ #include #include "nan.h" #include "scheduler.h" +#include "core_io.h" NAN_METHOD(StartBitcoind); NAN_METHOD(OnBlocksReady); @@ -30,4 +23,4 @@ NAN_METHOD(GetInfo); NAN_METHOD(IsSpent); NAN_METHOD(GetChainWork); NAN_METHOD(GetMempoolOutputs); - +NAN_METHOD(AddMempoolUncheckedTransaction); From 0b926b67b6116db0925360dac8604ea630f9c18d Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 17 Jul 2015 21:24:59 -0400 Subject: [PATCH 3/4] Completed getMempoolOutputs - Pass ctx to data from the mempool for getTransaction - Use string as input for unchecked transaction to mempool - Only include outputs that match input address - Include script in results --- integration/index.js | 69 +++++++++++++++++++++++++------------------- lib/bitcoind.js | 4 +++ src/bitcoindjs.cc | 57 ++++++++++++++++++++++-------------- 3 files changed, 78 insertions(+), 52 deletions(-) diff --git a/integration/index.js b/integration/index.js index 2d1ce527..34a1435c 100644 --- a/integration/index.js +++ b/integration/index.js @@ -135,46 +135,55 @@ describe('Basic Functionality', function() { }); }); - describe('add to mempool', function() { - it('will add an uncheckedTransaction', function() { - var fromAddress = 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1'; - var utxo = { - address: fromAddress, - txId: 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458', - outputIndex: 0, - script: Script.buildPublicKeyHashOut(fromAddress).toString(), - satoshis: 100000 - }; - var toAddress = 'mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc'; - var changeAddress = 'mgBCJAsvzgT2qNNeXsoECg2uPKrUsZ76up'; - var changeAddressP2SH = '2N7T3TAetJrSCruQ39aNrJvYLhG1LJosujf'; - var privateKey = 'cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY'; - var private1 = '6ce7e97e317d2af16c33db0b9270ec047a91bff3eff8558afb5014afb2bb5976'; - var private2 = 'c9b26b0f771a0d2dad88a44de90f05f416b3b385ff1d989343005546a0032890'; - var tx = new bitcore.Transaction(); - tx.from(utxo); - tx.to(toAddress, 50000); - tx.change(changeAddress); - tx.sign(privateKey); - var added = bitcoind.addMempoolUncheckedTransaction(tx.toBuffer()); + describe('mempool functionality', function() { + + var fromAddress = 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1'; + var utxo = { + address: fromAddress, + txId: 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458', + outputIndex: 0, + script: bitcore.Script.buildPublicKeyHashOut(fromAddress).toString(), + satoshis: 100000 + }; + var toAddress = 'mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc'; + var changeAddress = 'mgBCJAsvzgT2qNNeXsoECg2uPKrUsZ76up'; + var changeAddressP2SH = '2N7T3TAetJrSCruQ39aNrJvYLhG1LJosujf'; + var privateKey = 'cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY'; + var private1 = '6ce7e97e317d2af16c33db0b9270ec047a91bff3eff8558afb5014afb2bb5976'; + var private2 = 'c9b26b0f771a0d2dad88a44de90f05f416b3b385ff1d989343005546a0032890'; + var tx = new bitcore.Transaction(); + tx.from(utxo); + tx.to(toAddress, 50000); + tx.change(changeAddress); + tx.sign(privateKey); + + it('will add an unchecked transaction', function() { + var added = bitcoind.addMempoolUncheckedTransaction(tx.serialize()); added.should.equal(true); - bitcoind.getTransaction(tx.hash, true, function(err, tx) { + bitcoind.getTransaction(tx.hash, true, function(err, txBuffer) { if(err) { throw err; } - tx.toString('hex').should.equal(tx.toBuffer().toString('hex')); + var expected = tx.toBuffer().toString('hex'); + txBuffer.toString('hex').should.equal(expected); }); }); - }); - describe('get outputs by address from the mempool', function() { - it('will do it', function() { - var outputs = bitcoind.getMempoolOutputs('n28S35tqEMbt6vNad7A5K3mZ7vdn8dZ86X'); - Array.isArray(outputs).should.equal(true); + it('get outputs by address', function() { + var outputs = bitcoind.getMempoolOutputs(changeAddress); + var expected = [ + { + script: 'OP_DUP OP_HASH160 073b7eae2823efa349e3b9155b8a735526463a0f OP_EQUALVERIFY OP_CHECKSIG', + satoshis: 40000, + txid: tx.hash, + outputIndex: 1 + } + ]; + outputs.should.deep.equal(expected); }); - }); + }); }); diff --git a/lib/bitcoind.js b/lib/bitcoind.js index 6d05f567..83591768 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -386,6 +386,10 @@ Bitcoin.prototype.getMempoolOutputs = function(address) { return bitcoindjs.getMempoolOutputs(address); }; +Bitcoin.prototype.addMempoolUncheckedTransaction = function(txBuffer) { + return bitcoindjs.addMempoolUncheckedTransaction(txBuffer); +}; + Bitcoin.prototype.getInfo = function() { if (bitcoin.stopping) return []; return bitcoindjs.getInfo(); diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index ec71d4ef..9dfffd2e 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -821,6 +821,7 @@ async_get_tx(uv_work_t *req) { { if (mempool.lookup(hash, ctx)) { + data->ctx = ctx; return; } } @@ -995,6 +996,13 @@ NAN_METHOD(GetMempoolOutputs) { Local outputs = Array::New(isolate); int arrayIndex = 0; + v8::String::Utf8Value param1(args[0]->ToString()); + std::string *input = new std::string(*param1); + const char* psz = input->c_str(); + std::vector vAddress; + DecodeBase58(psz, vAddress); + vector hashBytes(vAddress.begin()+1, vAddress.begin()+21); + std::map mapTx = mempool.mapTx; for(std::map::iterator it = mapTx.begin(); it != mapTx.end(); it++) { @@ -1010,24 +1018,31 @@ NAN_METHOD(GetMempoolOutputs) { CScript script = txout.scriptPubKey; txnouttype type; - vector > hash; + std::vector > hashResults; + + if (Solver(script, type, hashResults)) { - if (Solver(script, type, hash)) { if (type == TX_PUBKEYHASH || type == TX_SCRIPTHASH) { - Local output = NanNew(); + vector scripthashBytes = hashResults.front(); - // todo: include the script - output->Set(NanNew("script"), NanNew("")); + if(equal(hashBytes.begin(), hashBytes.end(), scripthashBytes.begin())) { - uint64_t satoshis = txout.nValue; - output->Set(NanNew("satoshis"), NanNew(satoshis)); // can't go above 2 ^ 53 -1 - output->Set(NanNew("txid"), NanNew(txid.GetHex())); + Local output = NanNew(); - output->Set(NanNew("outputIndex"), NanNew(outputIndex)); + // todo: include the script + output->Set(NanNew("script"), NanNew(script.ToString())); - outputs->Set(arrayIndex, output); - arrayIndex++; + uint64_t satoshis = txout.nValue; + output->Set(NanNew("satoshis"), NanNew(satoshis)); // can't go above 2 ^ 53 -1 + output->Set(NanNew("txid"), NanNew(txid.GetHex())); + + output->Set(NanNew("outputIndex"), NanNew(outputIndex)); + + outputs->Set(arrayIndex, output); + arrayIndex++; + + } } } @@ -1043,19 +1058,17 @@ NAN_METHOD(GetMempoolOutputs) { * AddMempoolUncheckedTransaction */ NAN_METHOD(AddMempoolUncheckedTransaction) { - if (!node::Buffer::HasInstance(args[0])) { - return NanThrowTypeError("First argument should be a Buffer."); - } + NanScope(); - CTransaction tx; - const char *arg = node::Buffer::Data(args[0]); - std::string strArg = std::string(arg); + v8::String::Utf8Value param1(args[0]->ToString()); + std::string *input = new std::string(*param1); - if (!DecodeHexTx(tx, strArg)) { - return NanThrowError("could not decode tx"); - } - bool added = mempool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(tx, 0, 0, 0.0, 1)); - NanReturnValue(NanNew(added)); + CTransaction tx; + if (!DecodeHexTx(tx, *input)) { + return NanThrowError("could not decode tx"); + } + bool added = mempool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(tx, 0, 0, 0.0, 1)); + NanReturnValue(NanNew(added)); } From 0fbca8094ef83426e5731d8ecec49bbaff53a60b Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 17 Jul 2015 21:32:38 -0400 Subject: [PATCH 4/4] Added comments to getMempoolOutputs --- src/bitcoindjs.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index 9dfffd2e..78ccd507 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -993,9 +993,13 @@ NAN_METHOD(GetMempoolOutputs) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); + // Instatiate an empty array that we will fill later + // with matching outputs. Local outputs = Array::New(isolate); int arrayIndex = 0; + // Decode the input address into the hash bytes + // that we can then match to the scriptPubKeys data v8::String::Utf8Value param1(args[0]->ToString()); std::string *input = new std::string(*param1); const char* psz = input->c_str(); @@ -1003,16 +1007,18 @@ NAN_METHOD(GetMempoolOutputs) { DecodeBase58(psz, vAddress); vector hashBytes(vAddress.begin()+1, vAddress.begin()+21); + // Iterate through the entire mempool std::map mapTx = mempool.mapTx; for(std::map::iterator it = mapTx.begin(); it != mapTx.end(); it++) { + uint256 txid = it->first; CTxMemPoolEntry entry = it->second; - const CTransaction tx = entry.GetTx(); int outputIndex = 0; + // Iterate through each output BOOST_FOREACH(const CTxOut& txout, tx.vout) { CScript script = txout.scriptPubKey; @@ -1022,15 +1028,16 @@ NAN_METHOD(GetMempoolOutputs) { if (Solver(script, type, hashResults)) { + // See if the script is any of the standard address types if (type == TX_PUBKEYHASH || type == TX_SCRIPTHASH) { vector scripthashBytes = hashResults.front(); + // Compare the hash bytes with the input hash bytes if(equal(hashBytes.begin(), hashBytes.end(), scripthashBytes.begin())) { Local output = NanNew(); - // todo: include the script output->Set(NanNew("script"), NanNew(script.ToString())); uint64_t satoshis = txout.nValue; @@ -1039,6 +1046,8 @@ NAN_METHOD(GetMempoolOutputs) { output->Set(NanNew("outputIndex"), NanNew(outputIndex)); + // We have a match and push the results to the array + // that is returned as the result outputs->Set(arrayIndex, output); arrayIndex++;