start implementing broadcastTx.
This commit is contained in:
parent
9e49864413
commit
b934088f7a
196
lib/bitcoind.js
196
lib/bitcoind.js
@ -360,6 +360,108 @@ Transaction.prototype.print = function() {
|
|||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Transaction.prototype.toHex = function() {
|
||||||
|
return this.hex = this.hex || Transaction.toHex(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
Transaction.toHex = function(tx) {
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast TX
|
||||||
|
*/
|
||||||
|
|
||||||
|
Bitcoin._broadcastTx = function(tx, options, callback) {
|
||||||
|
if (!callback) {
|
||||||
|
callback = options;
|
||||||
|
options = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
options.overrideFees = options.overrideFees || false;
|
||||||
|
|
||||||
|
return bitcoindjs.broadcastTx(tx, options.overrideFees, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Transaction.binary = function(tx) {
|
||||||
|
var p = [];
|
||||||
|
var off = utils.writeU32(p, tx.nVersion, 0);
|
||||||
|
off += utils.varint(p, tx.vin.length, off);
|
||||||
|
|
||||||
|
for (var i = 0; i < tx.vin.length; i++) {
|
||||||
|
var input = tx.vin[i];
|
||||||
|
|
||||||
|
off += utils.copy(utils.toArray(input.out.hash, 'hex'), p, off, true);
|
||||||
|
off += utils.writeU32(p, input.out.index, off);
|
||||||
|
|
||||||
|
var s = script.encode(input.script);
|
||||||
|
off += utils.varint(p, s.length, off);
|
||||||
|
off += utils.copy(s, p, off, true);
|
||||||
|
|
||||||
|
off += utils.writeU32(p, input.seq, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
off += utils.varint(p, tx.vout.length, off);
|
||||||
|
for (var i = 0; i < tx.vout.length; i++) {
|
||||||
|
var output = tx.vout[i];
|
||||||
|
|
||||||
|
// Put LE value
|
||||||
|
var value = output.value.toArray().slice().reverse();
|
||||||
|
assert(value.length <= 8);
|
||||||
|
off += utils.copy(value, p, off, true);
|
||||||
|
for (var j = value.length; j < 8; j++, off++)
|
||||||
|
p[off] = 0;
|
||||||
|
|
||||||
|
var s = script.encode(output.script);
|
||||||
|
off += utils.varint(p, s.length, off);
|
||||||
|
off += utils.copy(s, p, off, true);
|
||||||
|
}
|
||||||
|
off += utils.writeU32(p, tx.nLockTime, off);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
|
||||||
|
var script = {};
|
||||||
|
|
||||||
|
script.encode = function encode(s) {
|
||||||
|
if (!s)
|
||||||
|
return [];
|
||||||
|
var opcodes = constants.opcodes;
|
||||||
|
var res = [];
|
||||||
|
for (var i = 0; i < s.length; i++) {
|
||||||
|
var instr = s[i];
|
||||||
|
|
||||||
|
// Push value to stack
|
||||||
|
if (Array.isArray(instr)) {
|
||||||
|
if (instr.length === 0) {
|
||||||
|
res.push(0);
|
||||||
|
} else if (instr.length === 1 && 0 < instr[0] && instr[0] <= 16) {
|
||||||
|
res.push(0x50 + instr[0]);
|
||||||
|
} else if (1 <= instr.length && instr.length <= 0x4b) {
|
||||||
|
res = res.concat(instr.length, instr);
|
||||||
|
} else if (instr.length <= 0xff) {
|
||||||
|
res = res.concat(opcodes.pushdata1, instr.length, instr);
|
||||||
|
} else if (instr.length <= 0xffff) {
|
||||||
|
res.push(opcodes.pushdata2);
|
||||||
|
utils.writeU16(res, instr.length, res.length);
|
||||||
|
res = res.concat(instr);
|
||||||
|
} else {
|
||||||
|
res.push(opcodes.pushdata4);
|
||||||
|
utils.writeU32(res, instr.length, res.length);
|
||||||
|
res = res.concat(instr);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push(opcodes[instr] || instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utils
|
* Utils
|
||||||
*/
|
*/
|
||||||
@ -385,6 +487,100 @@ utils.forEach = function(obj, iter, done) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
utils.writeU16 = function writeU16(dst, num, off) {
|
||||||
|
if (!off)
|
||||||
|
off = 0;
|
||||||
|
dst[off] = num & 0xff;
|
||||||
|
dst[off + 1] = (num >>> 8) & 0xff;
|
||||||
|
return 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
utils.writeU32 = function writeU32(dst, num, off) {
|
||||||
|
if (!off)
|
||||||
|
off = 0;
|
||||||
|
dst[off] = num & 0xff;
|
||||||
|
dst[off + 1] = (num >>> 8) & 0xff;
|
||||||
|
dst[off + 2] = (num >>> 16) & 0xff;
|
||||||
|
dst[off + 3] = (num >>> 24) & 0xff;
|
||||||
|
return 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
utils.writeU64 = function writeU64(dst, num, off) {
|
||||||
|
if (!off)
|
||||||
|
off = 0;
|
||||||
|
|
||||||
|
num = new bn(num).maskn(64).toArray();
|
||||||
|
while (num.length < 8)
|
||||||
|
num.unshift(0);
|
||||||
|
|
||||||
|
num.reverse().forEach(function(ch) {
|
||||||
|
dst[off++] = ch;
|
||||||
|
});
|
||||||
|
|
||||||
|
var i = num.length;
|
||||||
|
while (i--)
|
||||||
|
dst[off++] = num[i];
|
||||||
|
|
||||||
|
return 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
utils.writeU16BE = function writeU16BE(dst, num, off) {
|
||||||
|
if (!off)
|
||||||
|
off = 0;
|
||||||
|
dst[off] = (num >>> 8) & 0xff;
|
||||||
|
dst[off + 1] = num & 0xff;
|
||||||
|
return 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
utils.writeU32BE = function writeU32BE(dst, num, off) {
|
||||||
|
if (!off)
|
||||||
|
off = 0;
|
||||||
|
dst[off] = (num >>> 24) & 0xff;
|
||||||
|
dst[off + 1] = (num >>> 16) & 0xff;
|
||||||
|
dst[off + 2] = (num >>> 8) & 0xff;
|
||||||
|
dst[off + 3] = num & 0xff;
|
||||||
|
return 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
utils.writeU64BE = function writeU64BE(dst, num, off) {
|
||||||
|
if (!off)
|
||||||
|
off = 0;
|
||||||
|
|
||||||
|
num = new bn(num).maskn(64).toArray();
|
||||||
|
while (num.length < 8)
|
||||||
|
num.unshift(0);
|
||||||
|
|
||||||
|
for (var i = 0; i < num.length; i++)
|
||||||
|
dst[off++] = num[i];
|
||||||
|
|
||||||
|
return 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
utils.varint = function(arr, value, off) {
|
||||||
|
if (!off)
|
||||||
|
off = 0;
|
||||||
|
if (value < 0xfd) {
|
||||||
|
arr[off] = value;
|
||||||
|
return 1;
|
||||||
|
} else if (value <= 0xffff) {
|
||||||
|
arr[off] = 0xfd;
|
||||||
|
arr[off + 1] = value & 0xff;
|
||||||
|
arr[off + 2] = value >>> 8;
|
||||||
|
return 3;
|
||||||
|
} else if (value <= 0xffffffff) {
|
||||||
|
arr[off] = 0xfe;
|
||||||
|
arr[off + 1] = value & 0xff;
|
||||||
|
arr[off + 2] = (value >>> 8) & 0xff;
|
||||||
|
arr[off + 3] = (value >>> 16) & 0xff;
|
||||||
|
arr[off + 4] = value >>> 24;
|
||||||
|
return 5;
|
||||||
|
} else {
|
||||||
|
arr[off] = 0xff;
|
||||||
|
utils.writeU64(arr, value, off + 1);
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -125,6 +125,7 @@ NAN_METHOD(GetBlock);
|
|||||||
NAN_METHOD(GetTx);
|
NAN_METHOD(GetTx);
|
||||||
NAN_METHOD(PollBlocks);
|
NAN_METHOD(PollBlocks);
|
||||||
NAN_METHOD(PollMempool);
|
NAN_METHOD(PollMempool);
|
||||||
|
NAN_METHOD(BroadcastTx);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
async_start_node_work(uv_work_t *req);
|
async_start_node_work(uv_work_t *req);
|
||||||
@ -168,6 +169,12 @@ async_poll_mempool(uv_work_t *req);
|
|||||||
static void
|
static void
|
||||||
async_poll_mempool_after(uv_work_t *req);
|
async_poll_mempool_after(uv_work_t *req);
|
||||||
|
|
||||||
|
static void
|
||||||
|
async_broadcast_tx(uv_work_t *req);
|
||||||
|
|
||||||
|
static void
|
||||||
|
async_broadcast_tx_after(uv_work_t *req);
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
ctx_to_js(const CTransaction& tx, uint256 hashBlock, Local<Object> entry);
|
ctx_to_js(const CTransaction& tx, uint256 hashBlock, Local<Object> entry);
|
||||||
|
|
||||||
@ -255,6 +262,16 @@ struct async_poll_mempool_data {
|
|||||||
Persistent<Function> callback;
|
Persistent<Function> callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* async_broadcast_tx
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct async_broadcast_tx {
|
||||||
|
std::string err_msg;
|
||||||
|
boost::string tx_hex;
|
||||||
|
bool override_fees;
|
||||||
|
Persistent<Function> callback;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StartBitcoind
|
* StartBitcoind
|
||||||
@ -928,6 +945,138 @@ async_poll_mempool_after(uv_work_t *req) {
|
|||||||
delete req;
|
delete req;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BroadcastTx(tx, override_fees, callback)
|
||||||
|
* bitcoind.broadcastTx(tx, override_fees, callback)
|
||||||
|
*/
|
||||||
|
|
||||||
|
NAN_METHOD(BroadcastTx) {
|
||||||
|
NanScope();
|
||||||
|
|
||||||
|
if (args.Length() < 3
|
||||||
|
|| !args[0]->IsObject()
|
||||||
|
|| !args[1]->IsBool()
|
||||||
|
|| !args[2]->IsFunction()) {
|
||||||
|
return NanThrowError(
|
||||||
|
"Usage: bitcoindjs.broadcastTx(tx, override_fees, callback)");
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Object> js_tx = Local<Object>::Cast(args[0]);
|
||||||
|
Local<Function> callback = Local<Function>::Cast(args[2]);
|
||||||
|
|
||||||
|
String::Utf8Value tx_hex_(js_tx->Get(NanNew<String>("hex"))->ToString());
|
||||||
|
std::string tx_hex = std::string(*tx_hex_);
|
||||||
|
if (tx_hex[1] != 'x') {
|
||||||
|
tx_hex = "0x" + tx_hex;
|
||||||
|
}
|
||||||
|
boost::string strHex(tx_hex);
|
||||||
|
|
||||||
|
async_broadcast_tx *data = new async_broadcast_tx();
|
||||||
|
data->tx_hex = strHex;
|
||||||
|
data->override_fees = args[1]->ToBoolean()->IsTrue();
|
||||||
|
data->err_msg = std::string("");
|
||||||
|
data->callback = Persistent<Function>::New(callback);
|
||||||
|
|
||||||
|
uv_work_t *req = new uv_work_t();
|
||||||
|
req->data = data;
|
||||||
|
|
||||||
|
int status = uv_queue_work(uv_default_loop(),
|
||||||
|
req, async_broadcast_tx,
|
||||||
|
(uv_after_work_cb)async_broadcast_tx_after);
|
||||||
|
|
||||||
|
assert(status == 0);
|
||||||
|
|
||||||
|
NanReturnValue(Undefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
async_broadcast_tx(uv_work_t *req) {
|
||||||
|
async_poll_blocks_data* data = static_cast<async_poll_blocks_data*>(req->data);
|
||||||
|
|
||||||
|
// parse hex string from parameter
|
||||||
|
// vector<unsigned char> txData(ParseHexV(params[0], "parameter"));
|
||||||
|
CDataStream ssData(data->tx_hex, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
CTransaction tx;
|
||||||
|
|
||||||
|
bool fOverrideFees = false;
|
||||||
|
if (data->override_fees) {
|
||||||
|
fOverrideFees = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// deserialize binary data stream
|
||||||
|
try {
|
||||||
|
ssData >> tx;
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
data->err_msg = std::string("TX decode failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 hashTx = tx.GetHash();
|
||||||
|
|
||||||
|
bool fHave = false;
|
||||||
|
CCoinsViewCache &view = *pcoinsTip;
|
||||||
|
CCoins existingCoins;
|
||||||
|
{
|
||||||
|
fHave = view.GetCoins(hashTx, existingCoins);
|
||||||
|
if (!fHave) {
|
||||||
|
// push to local node
|
||||||
|
CValidationState state;
|
||||||
|
if (!AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) {
|
||||||
|
data->err_msg = std::string("TX rejected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fHave) {
|
||||||
|
if (existingCoins.nHeight < 1000000000) {
|
||||||
|
data->err_msg = std::string("transaction already in block chain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Not in block, but already in the memory pool; will drop
|
||||||
|
// through to re-relay it.
|
||||||
|
} else {
|
||||||
|
SyncWithWallets(hashTx, tx, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
RelayTransaction(tx, hashTx);
|
||||||
|
|
||||||
|
data->tx_hash = hashTx.GetHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
async_broadcast_tx_after(uv_work_t *req) {
|
||||||
|
NanScope();
|
||||||
|
async_poll_blocks_data* data = static_cast<async_poll_blocks_data*>(req->data);
|
||||||
|
|
||||||
|
if (!data->err_msg.empty()) {
|
||||||
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
||||||
|
const unsigned argc = 1;
|
||||||
|
Local<Value> argv[argc] = { err };
|
||||||
|
TryCatch try_catch;
|
||||||
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
||||||
|
if (try_catch.HasCaught()) {
|
||||||
|
node::FatalException(try_catch);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const unsigned argc = 2;
|
||||||
|
Local<Value> argv[argc] = {
|
||||||
|
Local<Value>::New(Null()),
|
||||||
|
Local<Value>::New(data->tx_hash)
|
||||||
|
};
|
||||||
|
TryCatch try_catch;
|
||||||
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
||||||
|
if (try_catch.HasCaught()) {
|
||||||
|
node::FatalException(try_catch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data->callback.Dispose();
|
||||||
|
|
||||||
|
delete data;
|
||||||
|
delete req;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conversions
|
* Conversions
|
||||||
*/
|
*/
|
||||||
@ -1139,6 +1288,155 @@ ctx_to_js(const CTransaction& tx, uint256 hashBlock, Local<Object> entry) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static inline void
|
||||||
|
hex_to_ctx(string strHex, const CTransaction& tx) {
|
||||||
|
CDataStream stream(ParseHex(strHex), SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
// CTransaction tx;
|
||||||
|
stream >> tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
js_to_ctx(Local<Object> entry, const CTransaction& tx, uint256 hashBlock) {
|
||||||
|
String::Utf8Value tx_hex_(entry->Get(NanNew<String>("hex"))->ToString());
|
||||||
|
std::string tx_hex = std::string(*txHex_);
|
||||||
|
if (tx_hex[1] != 'x') {
|
||||||
|
tx_hex = "0x" + tx_hex;
|
||||||
|
}
|
||||||
|
// std::string tx_hex = data->tx_hex;
|
||||||
|
// uint256 hash(tx_hex);
|
||||||
|
boost::string strHex(tx_hex);
|
||||||
|
// CTransaction tx;
|
||||||
|
hex_to_ctx(strHex, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
js_to_ctx(Local<Object> entry, const CTransaction& tx, uint256 hashBlock) {
|
||||||
|
// entry->Set(NanNew<String>("hex"), NanNew<String>(strHex));
|
||||||
|
entry->Set(NanNew<String>("txid"), NanNew<String>(tx.GetHash().GetHex()));
|
||||||
|
entry->Set(NanNew<String>("version"), NanNew<Number>(tx.nVersion));
|
||||||
|
entry->Set(NanNew<String>("locktime"), NanNew<Number>(tx.nLockTime));
|
||||||
|
|
||||||
|
Local<Array> vin = NanNew<Array>();
|
||||||
|
int vi = 0;
|
||||||
|
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
||||||
|
Local<Object> in = NanNew<Object>();
|
||||||
|
if (tx.IsCoinBase()) {
|
||||||
|
in->Set(NanNew<String>("coinbase"), NanNew<String>(HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
||||||
|
} else {
|
||||||
|
in->Set(NanNew<String>("txid"), NanNew<String>(txin.prevout.hash.GetHex()));
|
||||||
|
in->Set(NanNew<String>("vout"), NanNew<Number>((boost::int64_t)txin.prevout.n));
|
||||||
|
Local<Object> o = NanNew<Object>();
|
||||||
|
o->Set(NanNew<String>("asm"), NanNew<String>(txin.scriptSig.ToString()));
|
||||||
|
o->Set(NanNew<String>("hex"), NanNew<String>(HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
||||||
|
in->Set(NanNew<String>("scriptSig"), o);
|
||||||
|
}
|
||||||
|
in->Set(NanNew<String>("sequence"), NanNew<Number>((boost::int64_t)txin.nSequence));
|
||||||
|
vin->Set(vi, in);
|
||||||
|
vi++;
|
||||||
|
}
|
||||||
|
entry->Set(NanNew<String>("vin"), vin);
|
||||||
|
|
||||||
|
Local<Array> vout = NanNew<Array>();
|
||||||
|
for (unsigned int vo = 0; vo < tx.vout.size(); vo++) {
|
||||||
|
const CTxOut& txout = tx.vout[vo];
|
||||||
|
Local<Object> out = NanNew<Object>();
|
||||||
|
out->Set(NanNew<String>("value"), NanNew<Number>(txout.nValue));
|
||||||
|
out->Set(NanNew<String>("n"), NanNew<Number>((boost::int64_t)vo));
|
||||||
|
|
||||||
|
Local<Object> o = NanNew<Object>();
|
||||||
|
{
|
||||||
|
const CScript& scriptPubKey = txout.scriptPubKey;
|
||||||
|
Local<Object> out = o;
|
||||||
|
bool fIncludeHex = true;
|
||||||
|
|
||||||
|
txnouttype type;
|
||||||
|
vector<CTxDestination> addresses;
|
||||||
|
int nRequired;
|
||||||
|
out->Set(NanNew<String>("asm"), NanNew<String>(scriptPubKey.ToString()));
|
||||||
|
if (fIncludeHex) {
|
||||||
|
out->Set(NanNew<String>("hex"), NanNew<String>(HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
||||||
|
}
|
||||||
|
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
|
||||||
|
out->Set(NanNew<String>("type"), NanNew<String>(GetTxnOutputType(type)));
|
||||||
|
} else {
|
||||||
|
out->Set(NanNew<String>("reqSigs"), NanNew<Number>(nRequired));
|
||||||
|
out->Set(NanNew<String>("type"), NanNew<String>(GetTxnOutputType(type)));
|
||||||
|
Local<Array> a = NanNew<Array>();
|
||||||
|
int ai = 0;
|
||||||
|
BOOST_FOREACH(const CTxDestination& addr, addresses) {
|
||||||
|
a->Set(ai, NanNew<String>(CBitcoinAddress(addr).ToString()));
|
||||||
|
ai++;
|
||||||
|
}
|
||||||
|
out->Set(NanNew<String>("addresses"), a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out->Set(NanNew<String>("scriptPubKey"), o);
|
||||||
|
|
||||||
|
vout->Set(vo, out);
|
||||||
|
}
|
||||||
|
entry->Set(NanNew<String>("vout"), vout);
|
||||||
|
|
||||||
|
if (hashBlock != 0) {
|
||||||
|
entry->Set(NanNew<String>("blockhash"), NanNew<String>(hashBlock.GetHex()));
|
||||||
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
|
||||||
|
if (mi != mapBlockIndex.end() && (*mi).second) {
|
||||||
|
CBlockIndex* pindex = (*mi).second;
|
||||||
|
if (chainActive.Contains(pindex)) {
|
||||||
|
entry->Set(NanNew<String>("confirmations"),
|
||||||
|
NanNew<Number>(1 + chainActive.Height() - pindex->nHeight));
|
||||||
|
entry->Set(NanNew<String>("time"), NanNew<Number>((boost::int64_t)pindex->nTime));
|
||||||
|
entry->Set(NanNew<String>("blocktime"), NanNew<Number>((boost::int64_t)pindex->nTime));
|
||||||
|
} else {
|
||||||
|
entry->Set(NanNew<String>("confirmations"), NanNew<Number>(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CTransaction rawTx;
|
||||||
|
|
||||||
|
BOOST_FOREACH(const Value& input, inputs) {
|
||||||
|
const Object& o = input.get_obj();
|
||||||
|
|
||||||
|
uint256 txid = ParseHashO(o, "txid");
|
||||||
|
|
||||||
|
const Value& vout_v = find_value(o, "vout");
|
||||||
|
if (vout_v.type() != int_type)
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
|
||||||
|
int nOutput = vout_v.get_int();
|
||||||
|
if (nOutput < 0)
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
|
||||||
|
|
||||||
|
CTxIn in(COutPoint(txid, nOutput));
|
||||||
|
rawTx.vin.push_back(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
set<CBitcoinAddress> setAddress;
|
||||||
|
BOOST_FOREACH(const Pair& s, sendTo) {
|
||||||
|
CBitcoinAddress address(s.name_);
|
||||||
|
if (!address.IsValid())
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
|
||||||
|
|
||||||
|
if (setAddress.count(address))
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
|
||||||
|
setAddress.insert(address);
|
||||||
|
|
||||||
|
CScript scriptPubKey;
|
||||||
|
scriptPubKey.SetDestination(address.Get());
|
||||||
|
int64_t nAmount = AmountFromValue(s.value_);
|
||||||
|
|
||||||
|
CTxOut out(nAmount, scriptPubKey);
|
||||||
|
rawTx.vout.push_back(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ss << rawTx;
|
||||||
|
return HexStr(ss.begin(), ss.end());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init
|
* Init
|
||||||
*/
|
*/
|
||||||
@ -1154,6 +1452,7 @@ init(Handle<Object> target) {
|
|||||||
NODE_SET_METHOD(target, "getTx", GetTx);
|
NODE_SET_METHOD(target, "getTx", GetTx);
|
||||||
NODE_SET_METHOD(target, "pollBlocks", PollBlocks);
|
NODE_SET_METHOD(target, "pollBlocks", PollBlocks);
|
||||||
NODE_SET_METHOD(target, "pollMempool", PollMempool);
|
NODE_SET_METHOD(target, "pollMempool", PollMempool);
|
||||||
|
NODE_SET_METHOD(target, "broadcastTx", BroadcastTx);
|
||||||
}
|
}
|
||||||
|
|
||||||
NODE_MODULE(bitcoindjs, init)
|
NODE_MODULE(bitcoindjs, init)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user