Merge remote-tracking branch 'maraoz/refactor/PeerManager'
Conflicts: Connection.js PeerManager.js
This commit is contained in:
commit
2f6c5ac181
|
@ -1,19 +1,18 @@
|
||||||
var imports = require('soop').imports();
|
var imports = require('soop').imports();
|
||||||
|
|
||||||
var config = imports.config || require('./config');
|
|
||||||
var log = imports.log || require('./util/log');
|
var log = imports.log || require('./util/log');
|
||||||
var network = imports.network || require('./networks')[config.network];
|
|
||||||
|
|
||||||
var MAX_RECEIVE_BUFFER = 10000000;
|
var MAX_RECEIVE_BUFFER = 10000000;
|
||||||
var PROTOCOL_VERSION = 70000;
|
var PROTOCOL_VERSION = 70000;
|
||||||
|
|
||||||
var Binary = imports.Binary || require('binary');
|
|
||||||
var Put = imports.Put || require('bufferput');
|
var Put = imports.Put || require('bufferput');
|
||||||
var Buffers = imports.Buffers || require('buffers');
|
var Buffers = imports.Buffers || require('buffers');
|
||||||
require('./Buffers.monkey').patch(Buffers);
|
require('./Buffers.monkey').patch(Buffers);
|
||||||
|
|
||||||
var Block = require('./Block');
|
var bitcoreDefaults = imports.config || require('./config');
|
||||||
var Transaction = require('./Transaction');
|
var networks = imports.networks || require('./networks');
|
||||||
|
var Block = imports.Block || require('./Block');
|
||||||
|
var Transaction = imports.Transaction || require('./Transaction');
|
||||||
var util = imports.util || require('./util/util');
|
var util = imports.util || require('./util/util');
|
||||||
var Parser = imports.Parser || require('./util/BinaryParser');
|
var Parser = imports.Parser || require('./util/BinaryParser');
|
||||||
var buffertools = imports.buffertools || require('buffertools');
|
var buffertools = imports.buffertools || require('buffertools');
|
||||||
|
@ -25,15 +24,16 @@ var BIP0031_VERSION = 60000;
|
||||||
function Connection(socket, peer, opts) {
|
function Connection(socket, peer, opts) {
|
||||||
Connection.super(this, arguments);
|
Connection.super(this, arguments);
|
||||||
|
|
||||||
this.options = opts || {};
|
this.config = opts || bitcoreDefaults;
|
||||||
|
|
||||||
|
this.network = networks[this.config.network] || networks.livenet;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.peer = peer;
|
this.peer = peer;
|
||||||
|
|
||||||
// check for socks5 proxy options and construct a proxied socket
|
// check for socks5 proxy options and construct a proxied socket
|
||||||
if (this.options.proxy) {
|
if (this.options.proxy) {
|
||||||
var Socks5Client = imports.Socks5Client || require('socks5-client');
|
var Socks5Client = imports.Socks5Client || require('socks5-client');
|
||||||
this.socket = new Socks5Client(opts.proxy.host, opts.proxy.port);
|
this.socket = new Socks5Client(config.proxy.host, config.proxy.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A connection is considered "active" once we have received verack
|
// A connection is considered "active" once we have received verack
|
||||||
|
@ -146,7 +146,7 @@ Connection.prototype.handleMessage = function(message) {
|
||||||
this.recvVer = Math.min(message.version, PROTOCOL_VERSION);
|
this.recvVer = Math.min(message.version, PROTOCOL_VERSION);
|
||||||
} else {
|
} else {
|
||||||
// We won't start expecting a checksum until after we've received
|
// We won't start expecting a checksum until after we've received
|
||||||
// the "verack" message.
|
// the 'verack' message.
|
||||||
this.once('verack', (function () {
|
this.once('verack', (function () {
|
||||||
this.recvVer = message.version;
|
this.recvVer = message.version;
|
||||||
}).bind(this));
|
}).bind(this));
|
||||||
|
@ -160,7 +160,7 @@ Connection.prototype.handleMessage = function(message) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ping':
|
case 'ping':
|
||||||
if ("object" === typeof message.nonce) {
|
if ('object' === typeof message.nonce) {
|
||||||
this.sendPong(message.nonce);
|
this.sendPong(message.nonce);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -300,7 +300,7 @@ Connection.prototype.sendBlock = function (block, txs) {
|
||||||
|
|
||||||
Connection.prototype.sendMessage = function (command, payload) {
|
Connection.prototype.sendMessage = function (command, payload) {
|
||||||
try {
|
try {
|
||||||
var magic = network.magic;
|
var magic = this.network.magic;
|
||||||
var commandBuf = new Buffer(command, 'ascii');
|
var commandBuf = new Buffer(command, 'ascii');
|
||||||
if (commandBuf.length > 12) throw 'Command name too long';
|
if (commandBuf.length > 12) throw 'Command name too long';
|
||||||
|
|
||||||
|
@ -323,13 +323,13 @@ Connection.prototype.sendMessage = function (command, payload) {
|
||||||
var buffer = message.buffer();
|
var buffer = message.buffer();
|
||||||
|
|
||||||
log.debug('['+this.peer+'] '+
|
log.debug('['+this.peer+'] '+
|
||||||
"Sending message "+command+" ("+payload.length+" bytes)");
|
'Sending message '+command+' ('+payload.length+' bytes)');
|
||||||
|
|
||||||
this.socket.write(buffer);
|
this.socket.write(buffer);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// TODO: We should catch this error one level higher in order to better
|
// TODO: We should catch this error one level higher in order to better
|
||||||
// determine how to react to it. For now though, ignoring it will do.
|
// determine how to react to it. For now though, ignoring it will do.
|
||||||
log.err("Error while sending message to peer "+this.peer+": "+
|
log.err('Error while sending message to peer '+this.peer+': '+
|
||||||
(err.stack ? err.stack : err.toString()));
|
(err.stack ? err.stack : err.toString()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -338,7 +338,7 @@ Connection.prototype.handleData = function (data) {
|
||||||
this.buffers.push(data);
|
this.buffers.push(data);
|
||||||
|
|
||||||
if (this.buffers.length > MAX_RECEIVE_BUFFER) {
|
if (this.buffers.length > MAX_RECEIVE_BUFFER) {
|
||||||
log.err("Peer "+this.peer+" exceeded maxreceivebuffer, disconnecting."+
|
log.err('Peer '+this.peer+' exceeded maxreceivebuffer, disconnecting.'+
|
||||||
(err.stack ? err.stack : err.toString()));
|
(err.stack ? err.stack : err.toString()));
|
||||||
this.socket.destroy();
|
this.socket.destroy();
|
||||||
return;
|
return;
|
||||||
|
@ -351,7 +351,7 @@ Connection.prototype.processData = function () {
|
||||||
// If there are less than 20 bytes there can't be a message yet.
|
// If there are less than 20 bytes there can't be a message yet.
|
||||||
if (this.buffers.length < 20) return;
|
if (this.buffers.length < 20) return;
|
||||||
|
|
||||||
var magic = network.magic;
|
var magic = this.network.magic;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (this.buffers.get(i ) === magic[0] &&
|
if (this.buffers.get(i ) === magic[0] &&
|
||||||
|
@ -386,13 +386,13 @@ Connection.prototype.processData = function () {
|
||||||
|
|
||||||
if (this.buffers.length < endPos) return;
|
if (this.buffers.length < endPos) return;
|
||||||
|
|
||||||
var command = this.buffers.slice(4, 16).toString('ascii').replace(/\0+$/,"");
|
var command = this.buffers.slice(4, 16).toString('ascii').replace(/\0+$/,'');
|
||||||
var payload = this.buffers.slice(startPos, endPos);
|
var payload = this.buffers.slice(startPos, endPos);
|
||||||
var checksum = (this.recvVer >= 209) ? this.buffers.slice(20, 24) : null;
|
var checksum = (this.recvVer >= 209) ? this.buffers.slice(20, 24) : null;
|
||||||
|
|
||||||
log.debug('['+this.peer+'] ' +
|
log.debug('['+this.peer+'] ' +
|
||||||
"Received message " + command +
|
'Received message ' + command +
|
||||||
" (" + payloadLen + " bytes)");
|
' (' + payloadLen + ' bytes)');
|
||||||
|
|
||||||
if (checksum !== null) {
|
if (checksum !== null) {
|
||||||
var checksumConfirm = doubleSha256(payload).slice(0, 4);
|
var checksumConfirm = doubleSha256(payload).slice(0, 4);
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
|
|
||||||
var imports = require('soop').imports();
|
var imports = require('soop').imports();
|
||||||
var config = imports.config || require('./config');
|
var log = imports.log || require('./util/log');
|
||||||
var log = imports.log || require('./util/log');
|
var bitcoreDefaults = imports.config || require('./config');
|
||||||
var network = imports.network || require('./networks')[config.network];
|
var Connection = imports.Connection || require ('./Connection');
|
||||||
|
|
||||||
var Connection = imports.Connection ||
|
var Peer = imports.Peer || require('./Peer');
|
||||||
require('soop').load('./Connection', {config: config, network: network}) ||
|
|
||||||
require ('./Connection');
|
|
||||||
|
|
||||||
var Peer = imports.Peer || require('./Peer');
|
|
||||||
|
|
||||||
GetAdjustedTime = imports.GetAdjustedTime || function () {
|
GetAdjustedTime = imports.GetAdjustedTime || function () {
|
||||||
// TODO: Implement actual adjustment
|
// TODO: Implement actual adjustment
|
||||||
return Math.floor(new Date().getTime() / 1000);
|
return Math.floor(new Date().getTime() / 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
function PeerManager() {
|
function PeerManager(config) {
|
||||||
|
this.config = config || bitcoreDefaults;
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
|
|
||||||
|
@ -28,7 +25,7 @@ function PeerManager() {
|
||||||
this.interval = 5000;
|
this.interval = 5000;
|
||||||
this.minConnections = 8;
|
this.minConnections = 8;
|
||||||
this.minKnownPeers = 10;
|
this.minKnownPeers = 10;
|
||||||
};
|
}
|
||||||
|
|
||||||
PeerManager.parent = imports.parent || require('events').EventEmitter;
|
PeerManager.parent = imports.parent || require('events').EventEmitter;
|
||||||
PeerManager.Connection = Connection;
|
PeerManager.Connection = Connection;
|
||||||
|
@ -95,8 +92,8 @@ PeerManager.prototype.connectTo = function(peer) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
PeerManager.prototype.addConnection = function(socketConn, peer, opts) {
|
PeerManager.prototype.addConnection = function(socketConn, peer) {
|
||||||
var conn = new Connection(socketConn, peer, opts);
|
var conn = new Connection(socketConn, peer, this.config);
|
||||||
this.connections.push(conn);
|
this.connections.push(conn);
|
||||||
this.emit('connection', conn);
|
this.emit('connection', conn);
|
||||||
|
|
||||||
|
|
219
README.md
219
README.md
|
@ -62,42 +62,43 @@ addrs.forEach(function(addr) {
|
||||||
For this example you need a running bitcoind instance with RPC enabled.
|
For this example you need a running bitcoind instance with RPC enabled.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('../bitcore');
|
||||||
var networks = bitcore.networks;
|
var Peer = bitcore.Peer;
|
||||||
var Peer = bitcore.Peer;
|
var PeerManager = bitcore.PeerManager;
|
||||||
var PeerManager = require('soop').load('../PeerManager', {
|
|
||||||
network: networks.testnet
|
|
||||||
});
|
|
||||||
|
|
||||||
var handleBlock = function(info) {
|
var handleBlock = function(info) {
|
||||||
console.log('** Block Received **');
|
console.log('** Block Received **');
|
||||||
console.log(info.message);
|
console.log(info.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
var handleTx = function(info) {
|
var handleTx = function(info) {
|
||||||
var tx = info.message.tx.getStandardizedObject();
|
var tx = info.message.tx.getStandardizedObject();
|
||||||
console.log('** TX Received **');
|
|
||||||
console.log(tx);
|
|
||||||
};
|
|
||||||
|
|
||||||
var handleInv = function(info) {
|
console.log('** TX Received **');
|
||||||
console.log('** Inv **');
|
console.log(tx);
|
||||||
console.log(info.message);
|
};
|
||||||
var invs = info.message.invs;
|
|
||||||
info.conn.sendGetData(invs);
|
|
||||||
};
|
|
||||||
|
|
||||||
var peerman = new PeerManager();
|
var handleInv = function(info) {
|
||||||
|
console.log('** Inv **');
|
||||||
|
console.log(info.message);
|
||||||
|
|
||||||
peerman.addPeer(new Peer('127.0.0.1', 18333));
|
var invs = info.message.invs;
|
||||||
|
info.conn.sendGetData(invs);
|
||||||
|
};
|
||||||
|
|
||||||
peerman.on('connection', function(conn) {
|
var peerman = new PeerManager({
|
||||||
conn.on('inv', handleInv);
|
network: 'testnet'
|
||||||
conn.on('block', handleBlock);
|
});
|
||||||
conn.on('tx', handleTx);
|
|
||||||
});
|
|
||||||
|
|
||||||
peerman.start();
|
peerman.addPeer(new Peer('127.0.0.1', 18333));
|
||||||
|
|
||||||
|
peerman.on('connection', function(conn) {
|
||||||
|
conn.on('inv', handleInv);
|
||||||
|
conn.on('block', handleBlock);
|
||||||
|
conn.on('tx', handleTx);
|
||||||
|
});
|
||||||
|
|
||||||
|
peerman.start();
|
||||||
```
|
```
|
||||||
|
|
||||||
PeerManager will emit the following events: 'version', 'verack', 'addr', 'getaddr', 'error' 'disconnect'; and will relay events like: 'tx', 'block', 'inv'. Please see [PeerManager.js](PeerManager.js), [Peer.js](Peer.js) and [Connection.js](Connection.js)
|
PeerManager will emit the following events: 'version', 'verack', 'addr', 'getaddr', 'error' 'disconnect'; and will relay events like: 'tx', 'block', 'inv'. Please see [PeerManager.js](PeerManager.js), [Peer.js](Peer.js) and [Connection.js](Connection.js)
|
||||||
|
@ -140,83 +141,101 @@ by the transaction size. Documentation on the parameters of `TransactionBuilder`
|
||||||
can be found on the source file.
|
can be found on the source file.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var networks = bitcore.networks;
|
var Peer = bitcore.Peer;
|
||||||
var Peer = bitcore.Peer;
|
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||||
var TransactionBuilder = bitcore.TransactionBuilder;
|
var PeerManager = bitcore.PeerManager;
|
||||||
var PeerManager = require('soop').load('../PeerManager', {
|
|
||||||
network: networks.testnet
|
|
||||||
});
|
|
||||||
|
|
||||||
// Unspent transactions can be found via the insight.bitcore.io or blockchain.info APIs
|
// Unspent transactions can be found via the insight.bitcore.io or blockchain.info APIs
|
||||||
var unspent = [
|
var unspent = [{
|
||||||
{
|
'txid': '707108b5ba4f78dc951df4647a03365bf36432ea57fb641676045c5044daaea7',
|
||||||
"address": "n4g2TFaQo8UgedwpkYdcQFF6xE2Ei9Czvy",
|
'vout': 0,
|
||||||
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
'address': 'n3QDC7DzsMmN4mcyp3k7XGPX7zFXXHG387',
|
||||||
"scriptPubKey": "76a914fe021bac469a5c49915b2a8ffa7390a9ce5580f988ac",
|
'scriptPubKey': '76a914f00c4a92ee2314ab08ac0283dc8d07d9bf2be32388ac',
|
||||||
"vout": 1,
|
'amount': 0.12345600,
|
||||||
"amount": 1.0101,
|
'confirmations': 43537
|
||||||
"confirmations":7
|
}, {
|
||||||
},
|
'txid': '87a158d32833cb555aea27b6a21af569ccaeb8f9b19691e05f1e6c2b3440bdb3',
|
||||||
{
|
'vout': 1,
|
||||||
"address": "mhNCT9TwZAGF1tLPpZdqfkTmtBkY282YDW",
|
'address': 'mxdrp9s4mVxS9X4RBYiLe99v59V81XA5C3',
|
||||||
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2",
|
'scriptPubKey': '76a914bbc87986da6b17c7876db4efacf59a95e14f6cf588ac',
|
||||||
"scriptPubKey": "76a9141448534cb1a1ec44665b0eb2326e570814afe3f188ac",
|
'amount': 0.05749800,
|
||||||
"vout": 0,
|
'confirmations': 43536
|
||||||
"confirmations": 1,
|
}
|
||||||
"amount": 10
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// Private keys in WIF format (see TransactionBuilder.js for other options)
|
];
|
||||||
var keys = [
|
|
||||||
"cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV",
|
|
||||||
"cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G",
|
|
||||||
"cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB"
|
|
||||||
];
|
|
||||||
|
|
||||||
var peerman = new PeerManager();
|
// Private keys in WIF format (see TransactionBuilder.js for other options)
|
||||||
peerman.addPeer(new Peer('127.0.0.1', 18333));
|
var keys = [
|
||||||
|
'cQA75LXhV5JkMT8wkkqjR87SnHK4doh3c21p7PAd5tp8tc1tRBAY',
|
||||||
|
'cRz85dz9AiDieRpEwoucfXXQa1jdHHghcv6YnnVVGZ3MQyR1X4u2',
|
||||||
|
'cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV',
|
||||||
|
'cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G',
|
||||||
|
'cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB'
|
||||||
|
];
|
||||||
|
|
||||||
peerman.on('connect', function() {
|
var peerman = new PeerManager({
|
||||||
var conn = peerman.getActiveConnection();
|
network: 'testnet'
|
||||||
if (conn) {
|
|
||||||
var outs = [{address: toAddress, amount: amt}];
|
|
||||||
var opts = {remainderOut: {address: changeAddressString}};
|
|
||||||
var TransactionBuilder = bitcore.TransactionBuilder;
|
|
||||||
|
|
||||||
var tx = new TransactionBuilder(opts)
|
|
||||||
.setUnspent(unspent)
|
|
||||||
.setOutputs(outs)
|
|
||||||
.sign(keys)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
/* Create and signing can be done in multiple steps:
|
|
||||||
*
|
|
||||||
* var builder = new bitcore.TransactionBuilder(opts)
|
|
||||||
* .setUnspent(utxos)
|
|
||||||
* .setOutputs(outs);
|
|
||||||
*
|
|
||||||
* // Sign with the first key
|
|
||||||
* builder.sign(key1);
|
|
||||||
* var tx = builder.build(); // Partially signed transaction
|
|
||||||
*
|
|
||||||
* // Sign with the second key
|
|
||||||
* builder.sign(key2);
|
|
||||||
* if (builder.isFullySigned()){
|
|
||||||
* var tx = builder.build();
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* var selectedUnspent = build.getSelectedUnspent(); // Retrieve selected unspent outputs from the transaction
|
|
||||||
*/
|
|
||||||
conn.sendTx(tx.serialize().toString('hex'));
|
|
||||||
}
|
|
||||||
conn.on('reject', function() {
|
|
||||||
console.log('Transaction Rejected');
|
|
||||||
});
|
});
|
||||||
});
|
peerman.addPeer(new Peer('127.0.0.1', 18333));
|
||||||
|
|
||||||
peerman.start();
|
peerman.on('connect', function() {
|
||||||
|
var conn = peerman.getActiveConnection();
|
||||||
|
if (conn) {
|
||||||
|
// define transaction output
|
||||||
|
var outs = [{
|
||||||
|
address: 'mhNCT9TwZAGF1tLPpZdqfkTmtBkY282YDW',
|
||||||
|
amount: 0.1337
|
||||||
|
}];
|
||||||
|
// set change address
|
||||||
|
var opts = {
|
||||||
|
remainderOut: {
|
||||||
|
address: 'n4g2TFaQo8UgedwpkYdcQFF6xE2Ei9Czvy'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var tx = new TransactionBuilder(opts)
|
||||||
|
.setUnspent(unspent)
|
||||||
|
.setOutputs(outs)
|
||||||
|
.sign(keys)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/* Create and signing can be done in multiple steps:
|
||||||
|
*
|
||||||
|
* var builder = new bitcore.TransactionBuilder(opts)
|
||||||
|
* .setUnspent(utxos)
|
||||||
|
* .setOutputs(outs);
|
||||||
|
*
|
||||||
|
* // Sign with the first key
|
||||||
|
* builder.sign(key1);
|
||||||
|
* var tx = builder.build(); // Partially signed transaction
|
||||||
|
*
|
||||||
|
* // Sign with the second key
|
||||||
|
* builder.sign(key2);
|
||||||
|
* if (builder.isFullySigned()){
|
||||||
|
* var tx = builder.build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* var selectedUnspent = build.getSelectedUnspent(); // Retrieve selected unspent outputs from the transaction
|
||||||
|
*/
|
||||||
|
|
||||||
|
var txid = tx.getHash().toString('hex');
|
||||||
|
console.log('Created transaction with txid '+txid);
|
||||||
|
var raw_tx = tx.serialize().toString('hex');
|
||||||
|
console.log('Transaction raw hex dump:');
|
||||||
|
console.log('-------------------------------------');
|
||||||
|
console.log(raw_tx);
|
||||||
|
console.log('-------------------------------------');
|
||||||
|
// finally, send transaction to the bitcoin network
|
||||||
|
conn.sendTx(tx);
|
||||||
|
|
||||||
|
// for now, the network won't respond in any case
|
||||||
|
// (transaction accepted, transaction rejected)
|
||||||
|
// in the future, we may listen to 'reject' message
|
||||||
|
// see https://gist.github.com/gavinandresen/7079034
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
peerman.start();
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parsing a Script
|
## Parsing a Script
|
||||||
|
|
|
@ -3,11 +3,8 @@
|
||||||
var run = function() {
|
var run = function() {
|
||||||
// Replace '../bitcore' with 'bitcore' if you use this code elsewhere.
|
// Replace '../bitcore' with 'bitcore' if you use this code elsewhere.
|
||||||
var bitcore = require('../bitcore');
|
var bitcore = require('../bitcore');
|
||||||
var networks = bitcore.networks;
|
|
||||||
var Peer = bitcore.Peer;
|
var Peer = bitcore.Peer;
|
||||||
var PeerManager = require('soop').load('../PeerManager', {
|
var PeerManager = bitcore.PeerManager;
|
||||||
network: networks.testnet
|
|
||||||
});
|
|
||||||
|
|
||||||
var handleBlock = function(info) {
|
var handleBlock = function(info) {
|
||||||
console.log('** Block Received **');
|
console.log('** Block Received **');
|
||||||
|
@ -29,7 +26,9 @@ var run = function() {
|
||||||
info.conn.sendGetData(invs);
|
info.conn.sendGetData(invs);
|
||||||
};
|
};
|
||||||
|
|
||||||
var peerman = new PeerManager();
|
var peerman = new PeerManager({
|
||||||
|
network: 'testnet'
|
||||||
|
});
|
||||||
|
|
||||||
peerman.addPeer(new Peer('127.0.0.1', 18333));
|
peerman.addPeer(new Peer('127.0.0.1', 18333));
|
||||||
|
|
||||||
|
|
|
@ -3,71 +3,101 @@
|
||||||
var run = function() {
|
var run = function() {
|
||||||
// Replace '../bitcore' with 'bitcore' if you use this code elsewhere.
|
// Replace '../bitcore' with 'bitcore' if you use this code elsewhere.
|
||||||
var bitcore = require('../bitcore');
|
var bitcore = require('../bitcore');
|
||||||
var networks = bitcore.networks;
|
|
||||||
var Peer = bitcore.Peer;
|
var Peer = bitcore.Peer;
|
||||||
var Transaction = bitcore.Transaction;
|
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||||
var Address = bitcore.Address;
|
var PeerManager = bitcore.PeerManager;
|
||||||
var Script = bitcore.Script;
|
|
||||||
var coinUtil = bitcore.util;
|
// Unspent transactions can be found via the insight.bitcore.io or blockchain.info APIs
|
||||||
var PeerManager = require('soop').load('../PeerManager', {
|
var unspent = [{
|
||||||
network: networks.testnet
|
'txid': '707108b5ba4f78dc951df4647a03365bf36432ea57fb641676045c5044daaea7',
|
||||||
|
'vout': 0,
|
||||||
|
'address': 'n3QDC7DzsMmN4mcyp3k7XGPX7zFXXHG387',
|
||||||
|
'scriptPubKey': '76a914f00c4a92ee2314ab08ac0283dc8d07d9bf2be32388ac',
|
||||||
|
'amount': 0.12345600,
|
||||||
|
'confirmations': 43537
|
||||||
|
}, {
|
||||||
|
'txid': '87a158d32833cb555aea27b6a21af569ccaeb8f9b19691e05f1e6c2b3440bdb3',
|
||||||
|
'vout': 1,
|
||||||
|
'address': 'mxdrp9s4mVxS9X4RBYiLe99v59V81XA5C3',
|
||||||
|
'scriptPubKey': '76a914bbc87986da6b17c7876db4efacf59a95e14f6cf588ac',
|
||||||
|
'amount': 0.05749800,
|
||||||
|
'confirmations': 43536
|
||||||
|
}
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
// Private keys in WIF format (see TransactionBuilder.js for other options)
|
||||||
|
var keys = [
|
||||||
|
'cQA75LXhV5JkMT8wkkqjR87SnHK4doh3c21p7PAd5tp8tc1tRBAY',
|
||||||
|
'cRz85dz9AiDieRpEwoucfXXQa1jdHHghcv6YnnVVGZ3MQyR1X4u2',
|
||||||
|
'cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV',
|
||||||
|
'cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G',
|
||||||
|
'cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB'
|
||||||
|
];
|
||||||
|
|
||||||
|
var peerman = new PeerManager({
|
||||||
|
network: 'testnet'
|
||||||
});
|
});
|
||||||
|
|
||||||
var createTx = function() {
|
|
||||||
var TXIN = 'd05f35e0bbc495f6dcab03e599c8f5e32a07cdb4bc76964de201d06a2a7d8265';
|
|
||||||
var TXIN_N = 0;
|
|
||||||
var ADDR = 'muHct3YZ9Nd5Pq7uLYYhXRAxeW4EnpcaLz';
|
|
||||||
var VAL = '0.001';
|
|
||||||
|
|
||||||
var txobj = {
|
|
||||||
version: 1,
|
|
||||||
lock_time: 0,
|
|
||||||
ins: [],
|
|
||||||
outs: []
|
|
||||||
};
|
|
||||||
|
|
||||||
var txin = {
|
|
||||||
s: coinUtil.EMPTY_BUFFER, // Add signature
|
|
||||||
q: 0xffffffff
|
|
||||||
};
|
|
||||||
|
|
||||||
var hash = new Buffer(TXIN.split('').reverse(), 'hex');
|
|
||||||
var vout = parseInt(TXIN_N);
|
|
||||||
var voutBuf = new Buffer(4);
|
|
||||||
|
|
||||||
voutBuf.writeUInt32LE(vout, 0);
|
|
||||||
txin.o = Buffer.concat([hash, voutBuf]);
|
|
||||||
txobj.ins.push(txin);
|
|
||||||
|
|
||||||
var addr = new Address(ADDR);
|
|
||||||
var script = Script.createPubKeyHashOut(addr.payload());
|
|
||||||
var valueNum = coinUtil.parseValue(VAL);
|
|
||||||
var value = coinUtil.bigIntToValue(valueNum);
|
|
||||||
|
|
||||||
var txout = {
|
|
||||||
v: value,
|
|
||||||
s: script.getBuffer(),
|
|
||||||
};
|
|
||||||
txobj.outs.push(txout);
|
|
||||||
|
|
||||||
return new Transaction(txobj);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
var peerman = new PeerManager();
|
|
||||||
peerman.addPeer(new Peer('127.0.0.1', 18333));
|
peerman.addPeer(new Peer('127.0.0.1', 18333));
|
||||||
|
|
||||||
peerman.on('connect', function() {
|
peerman.on('connect', function() {
|
||||||
var conn = peerman.getActiveConnection();
|
var conn = peerman.getActiveConnection();
|
||||||
if (conn) {
|
if (conn) {
|
||||||
conn.sendTx(createTx());
|
// define transaction output
|
||||||
|
var outs = [{
|
||||||
|
address: 'mhNCT9TwZAGF1tLPpZdqfkTmtBkY282YDW',
|
||||||
|
amount: 0.1337
|
||||||
|
}];
|
||||||
|
// set change address
|
||||||
|
var opts = {
|
||||||
|
remainderOut: {
|
||||||
|
address: 'n4g2TFaQo8UgedwpkYdcQFF6xE2Ei9Czvy'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var tx = new TransactionBuilder(opts)
|
||||||
|
.setUnspent(unspent)
|
||||||
|
.setOutputs(outs)
|
||||||
|
.sign(keys)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/* Create and signing can be done in multiple steps:
|
||||||
|
*
|
||||||
|
* var builder = new bitcore.TransactionBuilder(opts)
|
||||||
|
* .setUnspent(utxos)
|
||||||
|
* .setOutputs(outs);
|
||||||
|
*
|
||||||
|
* // Sign with the first key
|
||||||
|
* builder.sign(key1);
|
||||||
|
* var tx = builder.build(); // Partially signed transaction
|
||||||
|
*
|
||||||
|
* // Sign with the second key
|
||||||
|
* builder.sign(key2);
|
||||||
|
* if (builder.isFullySigned()){
|
||||||
|
* var tx = builder.build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* var selectedUnspent = build.getSelectedUnspent(); // Retrieve selected unspent outputs from the transaction
|
||||||
|
*/
|
||||||
|
|
||||||
|
var txid = tx.getHash().toString('hex');
|
||||||
|
console.log('Created transaction with txid '+txid);
|
||||||
|
var raw_tx = tx.serialize().toString('hex');
|
||||||
|
console.log('Transaction raw hex dump:');
|
||||||
|
console.log('-------------------------------------');
|
||||||
|
console.log(raw_tx);
|
||||||
|
console.log('-------------------------------------');
|
||||||
|
// finally, send transaction to the bitcoin network
|
||||||
|
conn.sendTx(tx);
|
||||||
|
|
||||||
|
// for now, the network won't respond in any case
|
||||||
|
// (transaction accepted, transaction rejected)
|
||||||
|
// in the future, we may listen to 'reject' message
|
||||||
|
// see https://gist.github.com/gavinandresen/7079034
|
||||||
}
|
}
|
||||||
conn.on('reject', function() {
|
|
||||||
console.log('Transaction Rejected');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
peerman.start();
|
peerman.start();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.run = run;
|
module.exports.run = run;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* This is a simple script that will display network messages.
|
* This is a simple script that will display network messages.
|
||||||
* It users the Peer / Connection classes * directly instead of
|
* It users the Peer / Connection classes directly instead of
|
||||||
* relying on PeerManager.
|
* relying on PeerManager.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue