Merge pull request #19 from chjj/satoshi
Allow optional pre BIP-37 behavior (the original satoshi protocol)
This commit is contained in:
commit
d23cca1b4a
@ -23,6 +23,22 @@ function Block(data, subtype) {
|
||||
this.tx = [];
|
||||
this.invalid = false;
|
||||
|
||||
if (this.subtype === 'block') {
|
||||
var self = this;
|
||||
this.txs = data.txs || [];
|
||||
this.txs = this.txs.map(function(tx) {
|
||||
tx = bcoin.tx(tx);
|
||||
tx.block = self.hash('hex');
|
||||
tx.ts = tx.ts || self.ts;
|
||||
return tx;
|
||||
});
|
||||
this.hashes = this.txs.map(function(tx) {
|
||||
return tx.hash('hex');
|
||||
});
|
||||
this.tx = this.hashes.slice();
|
||||
this.invalid = !this._checkBlock();
|
||||
}
|
||||
|
||||
this._hash = null;
|
||||
|
||||
// Verify partial merkle tree and fill `ts` array
|
||||
@ -64,6 +80,10 @@ Block.prototype.hasTX = function hasTX(hash) {
|
||||
Block.prototype._verifyMerkle = function verifyMerkle() {
|
||||
var height = 0;
|
||||
|
||||
if (this.subtype === 'block') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Count leafs
|
||||
for (var i = this.totalTX; i > 0; i >>= 1)
|
||||
height++;
|
||||
@ -138,3 +158,65 @@ Block.fromJSON = function fromJSON(json) {
|
||||
|
||||
return block;
|
||||
};
|
||||
|
||||
Block.prototype._buildMerkle = function buildMerkle() {
|
||||
var merkleTree = [];
|
||||
for (var i = 0; i < this.txs.length; i++) {
|
||||
merkleTree.push(this.txs[i].hash('hex'));
|
||||
}
|
||||
var j = 0;
|
||||
for (var size = this.txs.length; size > 1; size = ((size + 1) / 2) | 0) {
|
||||
for (var i = 0; i < size; i += 2) {
|
||||
var i2 = Math.min(i + 1, size - 1);
|
||||
var hash = utils.dsha256(merkleTree[j+i] + merkleTree[j+i2], 'hex');
|
||||
merkleTree.push(utils.toHex(hash));
|
||||
}
|
||||
j += size;
|
||||
}
|
||||
return merkleTree;
|
||||
};
|
||||
|
||||
// This mimics the behavior of CheckBlockHeader()
|
||||
// and CheckBlock() in bitcoin/src/main.cpp.
|
||||
Block.prototype._checkBlock = function checkBlock() {
|
||||
// Check proof of work matches claimed amount
|
||||
if (!utils.testTarget(this.bits, this.hash())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check timestamp
|
||||
if (this.ts > (Date.now() / 1000) + 2 * 60 * 60) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Size of all txs cant be bigger than MAX_BLOCK_SIZE
|
||||
if (this.txs.length > bcoin.protocol.constants.block.maxSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// First TX must be a coinbase
|
||||
if (this.txs[0].inputs.length !== 1 || +this.txs[0].inputs[0].out.hash !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The rest of the txs must not be coinbases
|
||||
for (var i = 1; i < this.txs.length; i++) {
|
||||
if (this.txs[i].inputs.length === 1 && +this.txs[i].inputs[0].out.hash === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Build MerkleTree
|
||||
this.merkleTree = this._buildMerkle();
|
||||
|
||||
// Check for duplicate tx ids
|
||||
var unique = {};
|
||||
for (var i = 0; i < this.txs.length; i++) {
|
||||
var hash = this.txs[i].hash('hex');
|
||||
if (unique[hash]) return false;
|
||||
unique[hash] = true;
|
||||
}
|
||||
|
||||
// Check merkle root
|
||||
return this.merkleTree[this.merkleTree.length-1] === this.merkleRoot;
|
||||
};
|
||||
|
||||
@ -17,6 +17,7 @@ function Chain(options) {
|
||||
this.prefix = 'bt/chain/';
|
||||
this.storage = this.options.storage;
|
||||
this.strict = this.options.strict || false;
|
||||
this.cacheLimit = this.options.cacheLimit || 2000;
|
||||
this.block = {
|
||||
list: [],
|
||||
|
||||
@ -256,12 +257,12 @@ Chain.prototype.add = function add(block) {
|
||||
};
|
||||
|
||||
Chain.prototype._compress = function compress() {
|
||||
// Keep at least 1000 blocks and at most 2000
|
||||
if (this.block.list.length < 2000)
|
||||
// Keep at least 1000 blocks and at most 2000 by default
|
||||
if (this.block.list.length < this.cacheLimit)
|
||||
return;
|
||||
|
||||
// Bloom filter rebuilt is needed
|
||||
this.block.list = this.block.list.slice(-1000);
|
||||
this.block.list = this.block.list.slice(-(this.cacheLimit / 2 | 0));
|
||||
this.block.bloom.reset();
|
||||
|
||||
for (var i = 0; i < this.block.list.length; i++)
|
||||
|
||||
@ -161,6 +161,19 @@ Peer.prototype.broadcast = function broadcast(items) {
|
||||
};
|
||||
|
||||
Peer.prototype.updateWatch = function updateWatch() {
|
||||
if (!this.pool.options.relay) {
|
||||
if (this.ack) {
|
||||
var self = this;
|
||||
if (this.pool.block.lastHash)
|
||||
this.loadBlocks([ self.pool.block.lastHash ], 0);
|
||||
else
|
||||
this.pool.chain.getLast(function(hash) {
|
||||
self.loadBlocks([ hash ], 0);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ack)
|
||||
this._write(this.framer.filterLoad(this.bloom, 'none'));
|
||||
};
|
||||
@ -417,6 +430,15 @@ Peer.prototype._handleInv = function handleInv(items) {
|
||||
});
|
||||
this.emit('blocks', blocks);
|
||||
|
||||
if (!this.pool.options.relay) {
|
||||
if (txs.length)
|
||||
this.emit('txs', txs.map(function(tx) {
|
||||
return tx.hash;
|
||||
}));
|
||||
this.getData(items);
|
||||
return;
|
||||
}
|
||||
|
||||
if (txs.length === 0)
|
||||
return;
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ function Pool(options) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.options = options || {};
|
||||
this.options.relay = this.options.relay !== false;
|
||||
this.storage = this.options.storage;
|
||||
this.destroyed = false;
|
||||
this.size = options.size || 32;
|
||||
@ -35,7 +36,12 @@ function Pool(options) {
|
||||
};
|
||||
this.maxRetries = options.maxRetries || 42;
|
||||
this.requestTimeout = options.requestTimeout || 10000;
|
||||
this.chain = new bcoin.chain({ storage: this.storage });
|
||||
this.chain = new bcoin.chain({
|
||||
storage: this.storage,
|
||||
// Since regular blocks contain transactions and full merkle
|
||||
// trees, it's risky to cache 2000 blocks. Let's do 100.
|
||||
cacheLimit: !this.options.relay ? 100 : null
|
||||
});
|
||||
this.watchMap = {};
|
||||
this.bloom = new bcoin.bloom(8 * 1024,
|
||||
10,
|
||||
@ -293,6 +299,12 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
||||
self.emit('block', block, peer);
|
||||
});
|
||||
|
||||
if (!this.options.relay) {
|
||||
peer.on('block', function(block) {
|
||||
peer.emit('merkleblock', block);
|
||||
});
|
||||
}
|
||||
|
||||
// Just FYI
|
||||
peer.on('reject', function(payload) {
|
||||
self.emit('reject', payload, peer);
|
||||
|
||||
@ -147,3 +147,9 @@ exports.hashType = {
|
||||
single: 3,
|
||||
anyonecaypay: 0x80
|
||||
};
|
||||
|
||||
exports.block = {
|
||||
maxSize: 1000000,
|
||||
maxSigops: 1000000 / 50,
|
||||
maxOrphanTx: 1000000 / 100
|
||||
};
|
||||
|
||||
@ -128,7 +128,7 @@ function varint(arr, value, off) {
|
||||
arr[off + 4] = value >>> 24;
|
||||
return 5;
|
||||
} else {
|
||||
p[off] = 0xff;
|
||||
arr[off] = 0xff;
|
||||
utils.writeU64(arr, value, off + 1);
|
||||
return 9;
|
||||
}
|
||||
@ -303,11 +303,11 @@ Framer.block = function _block(block, type) {
|
||||
assert.equal(off, 76);
|
||||
off += writeU32(p, block.nonce, off);
|
||||
|
||||
// txn_count (spec says this is a varint for some reason)
|
||||
assert.equal(off, 80);
|
||||
off += writeU32(p, block.totalTX, off);
|
||||
|
||||
if (type === 'merkleblock') {
|
||||
// txn_count
|
||||
off += writeU32(p, block.totalTX, off);
|
||||
// hash count
|
||||
assert.equal(off, 84);
|
||||
off += varint(p, block.hashes.length, off);
|
||||
@ -323,6 +323,15 @@ Framer.block = function _block(block, type) {
|
||||
block.flags.forEach(function(flag) {
|
||||
p[off++] = flag;
|
||||
});
|
||||
} else if (type === 'block') {
|
||||
// txn_count
|
||||
off += varint(p, block.totalTX, off);
|
||||
// txs
|
||||
block.txs.forEach(function(tx) {
|
||||
tx._raw.forEach(function(ch) {
|
||||
p[off++] = ch;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return p;
|
||||
|
||||
@ -218,6 +218,17 @@ Parser.prototype.parseBlock = function parseBlock(p) {
|
||||
if (p.length < 84)
|
||||
return this._error('Invalid block size');
|
||||
|
||||
var result = readIntv(p, 80);
|
||||
var off = result.off;
|
||||
var totalTX = result.r;
|
||||
var txs = [];
|
||||
|
||||
for (var i = 0; i < totalTX; i++) {
|
||||
var tx = this.parseTX(p.slice(off));
|
||||
off += tx._off;
|
||||
txs.push(tx);
|
||||
}
|
||||
|
||||
return {
|
||||
version: readU32(p, 0),
|
||||
prevBlock: p.slice(4, 36),
|
||||
@ -225,7 +236,8 @@ Parser.prototype.parseBlock = function parseBlock(p) {
|
||||
ts: readU32(p, 68),
|
||||
bits: readU32(p, 72),
|
||||
nonce: readU32(p, 76),
|
||||
totalTX: readU32(p, 80)
|
||||
totalTX: totalTX,
|
||||
txs: txs
|
||||
};
|
||||
};
|
||||
|
||||
@ -316,7 +328,8 @@ Parser.prototype.parseTX = function parseTX(p) {
|
||||
version: readU32(p, 0),
|
||||
inputs: txIn,
|
||||
outputs: txOut,
|
||||
lock: readU32(p, off)
|
||||
lock: readU32(p, off),
|
||||
_off: off + 4
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user