Merge pull request #38 from braydonf/sendtransaction
Add sendTransaction method to the daemon.
This commit is contained in:
commit
8905333e3f
@ -167,7 +167,6 @@ describe('Basic Functionality', function() {
|
|||||||
var expected = tx.toBuffer().toString('hex');
|
var expected = tx.toBuffer().toString('hex');
|
||||||
txBuffer.toString('hex').should.equal(expected);
|
txBuffer.toString('hex').should.equal(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get outputs by address', function() {
|
it('get outputs by address', function() {
|
||||||
|
|||||||
@ -21,12 +21,31 @@ var assert = chai.assert;
|
|||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var BitcoinRPC = require('bitcoind-rpc');
|
var BitcoinRPC = require('bitcoind-rpc');
|
||||||
var blockHashes = [];
|
var blockHashes = [];
|
||||||
|
var unspentTransactions = [];
|
||||||
|
var coinbasePrivateKey;
|
||||||
|
var privateKey = bitcore.PrivateKey();
|
||||||
|
var destKey = bitcore.PrivateKey();
|
||||||
|
|
||||||
describe('Basic Functionality', function() {
|
describe('Daemon Binding Functionality', function() {
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
|
|
||||||
|
// Add the regtest network
|
||||||
|
bitcore.Networks.remove(bitcore.Networks.testnet);
|
||||||
|
bitcore.Networks.add({
|
||||||
|
name: 'regtest',
|
||||||
|
alias: 'regtest',
|
||||||
|
pubkeyhash: 0x6f,
|
||||||
|
privatekey: 0xef,
|
||||||
|
scripthash: 0xc4,
|
||||||
|
xpubkey: 0x043587cf,
|
||||||
|
xprivkey: 0x04358394,
|
||||||
|
networkMagic: 0xfabfb5da,
|
||||||
|
port: 18444,
|
||||||
|
dnsSeeds: [ ]
|
||||||
|
});
|
||||||
|
|
||||||
var datadir = __dirname + '/data';
|
var datadir = __dirname + '/data';
|
||||||
|
|
||||||
rimraf(datadir + '/regtest', function(err) {
|
rimraf(datadir + '/regtest', function(err) {
|
||||||
@ -62,18 +81,63 @@ describe('Basic Functionality', function() {
|
|||||||
|
|
||||||
console.log('Generating 100 blocks...');
|
console.log('Generating 100 blocks...');
|
||||||
|
|
||||||
client.generate(100, function(err, response) {
|
// Generate enough blocks so that the initial coinbase transactions
|
||||||
|
// can be spent.
|
||||||
|
|
||||||
|
client.generate(150, function(err, response) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
blockHashes = response.result;
|
blockHashes = response.result;
|
||||||
done();
|
|
||||||
|
// The original coinbase transactions when using regtest spend to
|
||||||
|
// a non-standard pubkeyout instead of a pubkeyhashout.
|
||||||
|
// We'll construct a new transaction that will send funds
|
||||||
|
// to a new address with pubkeyhashout for later testing.
|
||||||
|
|
||||||
|
console.log('Preparing unspent outputs...');
|
||||||
|
|
||||||
|
client.getBalance(function(err, response) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
var amount = response.result;
|
||||||
|
var fee = 0.01;
|
||||||
|
var network = bitcore.Networks.get('regtest');
|
||||||
|
var address = privateKey.toAddress(network);
|
||||||
|
|
||||||
|
client.sendToAddress(address, amount - fee, '', '', function(err, response) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
var txid = response.result;
|
||||||
|
client.getTransaction(txid, function(err, response) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tx = bitcore.Transaction();
|
||||||
|
tx.fromString(response.result.hex);
|
||||||
|
unspentTransactions.push(tx);
|
||||||
|
|
||||||
|
// Include this transaction in a block so that it can
|
||||||
|
// be spent in tests
|
||||||
|
client.generate(1, function(err, response) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Testing setup complete!');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function(done) {
|
after(function(done) {
|
||||||
@ -169,4 +233,32 @@ describe('Basic Functionality', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('send transaction functionality', function() {
|
||||||
|
|
||||||
|
it('will not error and return the transaction hash', function() {
|
||||||
|
|
||||||
|
var unspentTx = unspentTransactions.shift();
|
||||||
|
|
||||||
|
var utxo = {
|
||||||
|
txid: unspentTx.hash,
|
||||||
|
outputIndex: 1,
|
||||||
|
script: unspentTx.outputs[1].script,
|
||||||
|
satoshis: unspentTx.outputs[1].satoshis
|
||||||
|
};
|
||||||
|
|
||||||
|
// create and sign the transaction
|
||||||
|
var tx = bitcore.Transaction();
|
||||||
|
tx.from(utxo);
|
||||||
|
tx.change(privateKey.toAddress());
|
||||||
|
tx.to(destKey.toAddress(), 100000);
|
||||||
|
tx.sign(privateKey);
|
||||||
|
|
||||||
|
// test sending the transaction
|
||||||
|
var hash = bitcoind.sendTransaction(tx.serialize());
|
||||||
|
hash.should.equal(tx.hash);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -344,6 +344,10 @@ Daemon.prototype.getChainWork = function(blockHash) {
|
|||||||
return bitcoindjs.getChainWork(blockHash);
|
return bitcoindjs.getChainWork(blockHash);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Daemon.prototype.sendTransaction = function(transaction, allowAbsurdFees) {
|
||||||
|
return bitcoindjs.sendTransaction(transaction, allowAbsurdFees);
|
||||||
|
};
|
||||||
|
|
||||||
Daemon.prototype.getTransaction = function(txid, queryMempool, callback) {
|
Daemon.prototype.getTransaction = function(txid, queryMempool, callback) {
|
||||||
return bitcoindjs.getTransaction(txid, queryMempool, callback);
|
return bitcoindjs.getTransaction(txid, queryMempool, callback);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1037,6 +1037,65 @@ NAN_METHOD(GetInfo) {
|
|||||||
NanReturnValue(obj);
|
NanReturnValue(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send Transaction
|
||||||
|
* bitcoindjs.sendTransaction()
|
||||||
|
* Will add a transaction to the mempool and broadcast to connected peers.
|
||||||
|
* @param {string} - The serialized hex string of the transaction.
|
||||||
|
* @param {boolean} - Skip absurdly high fee checks
|
||||||
|
*/
|
||||||
|
NAN_METHOD(SendTransaction) {
|
||||||
|
Isolate* isolate = Isolate::GetCurrent();
|
||||||
|
HandleScope scope(isolate);
|
||||||
|
|
||||||
|
LOCK(cs_main);
|
||||||
|
|
||||||
|
// Decode the transaction
|
||||||
|
v8::String::Utf8Value param1(args[0]->ToString());
|
||||||
|
std::string *input = new std::string(*param1);
|
||||||
|
CTransaction tx;
|
||||||
|
if (!DecodeHexTx(tx, *input)) {
|
||||||
|
return NanThrowError("TX decode failed");
|
||||||
|
}
|
||||||
|
uint256 hashTx = tx.GetHash();
|
||||||
|
|
||||||
|
// Skip absurdly high fee check
|
||||||
|
bool allowAbsurdFees = false;
|
||||||
|
if (args.Length() > 1) {
|
||||||
|
allowAbsurdFees = args[1]->BooleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
CCoinsViewCache &view = *pcoinsTip;
|
||||||
|
const CCoins* existingCoins = view.AccessCoins(hashTx);
|
||||||
|
bool fHaveMempool = mempool.exists(hashTx);
|
||||||
|
bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000;
|
||||||
|
if (!fHaveMempool && !fHaveChain) {
|
||||||
|
CValidationState state;
|
||||||
|
bool fMissingInputs;
|
||||||
|
|
||||||
|
// Attempt to add the transaction to the mempool
|
||||||
|
if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !allowAbsurdFees)) {
|
||||||
|
if (state.IsInvalid()) {
|
||||||
|
char *errorMessage;
|
||||||
|
sprintf(errorMessage, "%i: %s", state.GetRejectCode(), state.GetRejectReason().c_str());
|
||||||
|
return NanThrowError(errorMessage);
|
||||||
|
} else {
|
||||||
|
if (fMissingInputs) {
|
||||||
|
return NanThrowError("Missing inputs");
|
||||||
|
}
|
||||||
|
return NanThrowError(state.GetRejectReason().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (fHaveChain) {
|
||||||
|
return NanThrowError("transaction already in block chain");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relay the transaction connect peers
|
||||||
|
RelayTransaction(tx);
|
||||||
|
|
||||||
|
NanReturnValue(Local<Value>::New(isolate, NanNew<String>(hashTx.GetHex())));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetMempoolOutputs
|
* GetMempoolOutputs
|
||||||
* bitcoindjs.getMempoolOutputs()
|
* bitcoindjs.getMempoolOutputs()
|
||||||
@ -1174,7 +1233,7 @@ init(Handle<Object> target) {
|
|||||||
NODE_SET_METHOD(target, "getMempoolOutputs", GetMempoolOutputs);
|
NODE_SET_METHOD(target, "getMempoolOutputs", GetMempoolOutputs);
|
||||||
NODE_SET_METHOD(target, "addMempoolUncheckedTransaction", AddMempoolUncheckedTransaction);
|
NODE_SET_METHOD(target, "addMempoolUncheckedTransaction", AddMempoolUncheckedTransaction);
|
||||||
NODE_SET_METHOD(target, "verifyScript", VerifyScript);
|
NODE_SET_METHOD(target, "verifyScript", VerifyScript);
|
||||||
|
NODE_SET_METHOD(target, "sendTransaction", SendTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
NODE_MODULE(bitcoindjs, init)
|
NODE_MODULE(bitcoindjs, init)
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
#include "core_io.h"
|
#include "core_io.h"
|
||||||
#include "script/bitcoinconsensus.h"
|
#include "script/bitcoinconsensus.h"
|
||||||
|
#include "consensus/validation.h"
|
||||||
|
|
||||||
NAN_METHOD(StartBitcoind);
|
NAN_METHOD(StartBitcoind);
|
||||||
NAN_METHOD(OnBlocksReady);
|
NAN_METHOD(OnBlocksReady);
|
||||||
@ -26,3 +27,5 @@ NAN_METHOD(GetChainWork);
|
|||||||
NAN_METHOD(GetMempoolOutputs);
|
NAN_METHOD(GetMempoolOutputs);
|
||||||
NAN_METHOD(AddMempoolUncheckedTransaction);
|
NAN_METHOD(AddMempoolUncheckedTransaction);
|
||||||
NAN_METHOD(VerifyScript);
|
NAN_METHOD(VerifyScript);
|
||||||
|
NAN_METHOD(SendTransaction);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user