Added bindings to be able to listen to tx leaving mempool.

This commit is contained in:
Braydon Fuller 2015-10-29 15:27:35 -04:00
parent 275a0b57ea
commit f0ec424161
5 changed files with 124 additions and 5 deletions

View File

@ -367,14 +367,27 @@ index f94771a..72ee00e 100644
diff --git a/src/net.h b/src/net.h
index 17502b9..e181d68 100644
index 17502b9..c9ae1b2 100644
--- a/src/net.h
+++ b/src/net.h
@@ -99,6 +99,7 @@ struct CNodeSignals
@@ -99,6 +99,8 @@ struct CNodeSignals
{
boost::signals2::signal<int ()> GetHeight;
boost::signals2::signal<bool (CNode*), CombinerAll> ProcessMessages;
+ boost::signals2::signal<bool (const CTransaction&)> TxToMemPool;
+ boost::signals2::signal<bool (const CTransaction&)> TxLeaveMemPool;
boost::signals2::signal<bool (CNode*, bool), CombinerAll> SendMessages;
boost::signals2::signal<void (NodeId, const CNode*)> InitializeNode;
boost::signals2::signal<void (NodeId)> FinalizeNode;
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index c3d1b60..03e265d 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -133,6 +133,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
if (!mapTx.count(hash))
continue;
const CTransaction& tx = mapTx[hash].GetTx();
+ GetNodeSignals().TxLeaveMemPool(tx);
if (fRecursive) {
for (unsigned int i = 0; i < tx.vout.size(); i++) {
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i));

View File

@ -360,6 +360,30 @@ describe('Daemon Binding Functionality', function() {
});
});
describe('transactions leaving the mempool', function() {
it('receive event when transaction leaves', function(done) {
// add transaction to build a new block
var tx = bitcore.Transaction();
tx.from(utxos[4]);
tx.change(privateKey.toAddress());
tx.to(destKey.toAddress(), utxos[4].amount * 1e8 - 1000);
tx.sign(bitcore.PrivateKey.fromWIF(utxos[4].privateKeyWIF));
bitcoind.sendTransaction(tx.serialize());
bitcoind.once('txleave', function(txInfo) {
txInfo.hash.should.equal(tx.hash);
done();
});
client.generate(1, function(err, response) {
if (err) {
throw err;
}
});
});
});
describe('mempool functionality', function() {
var fromAddress = 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1';

View File

@ -110,12 +110,19 @@ Bitcoin.prototype._registerEventHandlers = function() {
// Set the height and emit a new tip
bindings.onTipUpdate(self._onTipUpdate.bind(this));
// Register callback function to handle incoming transactions
// Register callback function to handle transactions entering the mempool
bindings.startTxMon(function(txs) {
for(var i = 0; i < txs.length; i++) {
self.emit('tx', txs[i]);
}
});
// Register callback function to handle transactions leaving the mempool
bindings.startTxMonLeave(function(txs) {
for(var i = 0; i < txs.length; i++) {
self.emit('txleave', txs[i]);
}
});
};
Bitcoin.prototype._onReady = function(result, callback) {

View File

@ -39,6 +39,9 @@ extern int64_t nTimeBestReceived;
static void
tx_notifier(uv_async_t *handle);
static void
txleave_notifier(uv_async_t *handle);
static void
async_tip_update(uv_work_t *req);
@ -90,6 +93,9 @@ async_get_tx_and_info_after(uv_work_t *req);
static bool
queueTx(const CTransaction&);
static bool
queueTxLeave(const CTransaction&);
extern "C" void
init(Handle<Object>);
@ -98,9 +104,13 @@ init(Handle<Object>);
* Used only by bitcoind functions.
*/
static std::vector<CTransaction> txQueue;
static std::vector<CTransaction> txQueueLeave;
static uv_async_t txmon_async;
static uv_async_t txmonleave_async;
static Eternal<Function> txmon_callback;
static Eternal<Function> txmonleave_callback;
static bool txmon_callback_available;
static bool txmonleave_callback_available;
static volatile bool shutdown_complete = false;
static char *g_data_dir = NULL;
@ -259,6 +269,21 @@ NAN_METHOD(StartTxMon) {
info.GetReturnValue().Set(Null());
};
NAN_METHOD(StartTxMonLeave) {
Isolate* isolate = info.GetIsolate();
Local<Function> callback = Local<Function>::Cast(info[0]);
Eternal<Function> cb(isolate, callback);
txmonleave_callback = cb;
txmonleave_callback_available = true;
CNodeSignals& nodeSignals = GetNodeSignals();
nodeSignals.TxLeaveMemPool.connect(&queueTxLeave);
uv_async_init(uv_default_loop(), &txmonleave_async, txleave_notifier);
info.GetReturnValue().Set(Null());
};
static void
tx_notifier(uv_async_t *handle) {
Isolate* isolate = Isolate::GetCurrent();
@ -307,6 +332,53 @@ queueTx(const CTransaction& tx) {
return true;
}
static void
txleave_notifier(uv_async_t *handle) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
Local<Array> results = Array::New(isolate);
int arrayIndex = 0;
LOCK(cs_main);
BOOST_FOREACH(const CTransaction& tx, txQueueLeave) {
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << tx;
std::string stx = ssTx.str();
Nan::MaybeLocal<v8::Object> txBuffer = Nan::CopyBuffer((char *)stx.c_str(), stx.size());
uint256 hash = tx.GetHash();
Local<Object> obj = New<Object>();
Nan::Set(obj, New("buffer").ToLocalChecked(), txBuffer.ToLocalChecked());
Nan::Set(obj, New("hash").ToLocalChecked(), New(hash.GetHex()).ToLocalChecked());
results->Set(arrayIndex, obj);
arrayIndex++;
}
const unsigned argc = 1;
Local<Value> argv[argc] = {
Local<Value>::New(isolate, results)
};
Local<Function> cb = txmonleave_callback.Get(isolate);
cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);
txQueueLeave.clear();
}
static bool
queueTxLeave(const CTransaction& tx) {
LOCK(cs_main);
txQueueLeave.push_back(tx);
uv_async_send(&txmonleave_async);
return true;
}
/**
* Functions
*/
@ -1527,6 +1599,7 @@ NAN_MODULE_INIT(init) {
Nan::Set(target, New("sendTransaction").ToLocalChecked(), GetFunction(New<FunctionTemplate>(SendTransaction)).ToLocalChecked());
Nan::Set(target, New("estimateFee").ToLocalChecked(), GetFunction(New<FunctionTemplate>(EstimateFee)).ToLocalChecked());
Nan::Set(target, New("startTxMon").ToLocalChecked(), GetFunction(New<FunctionTemplate>(StartTxMon)).ToLocalChecked());
Nan::Set(target, New("startTxMonLeave").ToLocalChecked(), GetFunction(New<FunctionTemplate>(StartTxMonLeave)).ToLocalChecked());
Nan::Set(target, New("syncPercentage").ToLocalChecked(), GetFunction(New<FunctionTemplate>(SyncPercentage)).ToLocalChecked());
Nan::Set(target, New("isSynced").ToLocalChecked(), GetFunction(New<FunctionTemplate>(IsSynced)).ToLocalChecked());
Nan::Set(target, New("getBestBlockHash").ToLocalChecked(), GetFunction(New<FunctionTemplate>(GetBestBlockHash)).ToLocalChecked());

View File

@ -144,7 +144,8 @@ describe('Bitcoin Service', function() {
name.should.equal('bitcoind.node');
return {
onTipUpdate: sinon.stub(),
startTxMon: sinon.stub().callsArgWith(0, [transaction])
startTxMon: sinon.stub().callsArgWith(0, [transaction]),
startTxMonLeave: sinon.stub().callsArgWith(0, [transaction])
};
}
});
@ -175,7 +176,8 @@ describe('Bitcoin Service', function() {
callback(height++);
});
},
startTxMon: sinon.stub()
startTxMon: sinon.stub(),
startTxMonLeave: sinon.stub()
};
}
});