#!/usr/bin/env node 'use strict'; process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var should = require('chai'); var assert = require('assert'), async = require('async'), HistoricSync = require('../../lib/HistoricSync'); var s; var b = [ '00000000c4cbd75af741f3a2b2ff72d9ed4d83a048462c1efe331be31ccf006b', //0 B#16 '00000000fe198cce4c8abf9dca0fee1182cb130df966cc428ad2a230df8da743', //1 '000000008d55c3e978639f70af1d2bf1fe6f09cb3143e104405a599215c89a48', //2 '000000009b3bca4909f38313f2746120129cce4a699a1f552390955da470c5a9', //3 '00000000ede57f31cc598dc241d129ccb4d8168ef112afbdc870dc60a85f5dd3', //4 B#20 '000000001a29aff8154de6230a4909a3a971c52ae590006e00fddff33f9e3773', //5 B#21 ]; var t = [ 'd08582d3711f75d085c618874fb0d049ae09d5ec95ec6f5abd289f4b54712c54', // TX from B#16 '1729001087e0cebea8d14de1653d5cf59628d9746bc1ae65f776f1cbaff7ebad', //1 'cf53d7ccd83a099acfbc319ee10c1e3b10e3d42ba675b569fdd6b69cb8d2db4e', //2 '73a4988adf462b6540cfa59097804174b298cfa439f73c1a072c2c6fbdbe57c7', //3 'd45f9da73619799e9d7bd03cc290e70875ea4cbad56b8bffa15135fbbb3df9ea', //4 Tx from B20 ]; var initialTest = function(cb) { async.each([2,3,4], function(i,c) { s.sync.bDb.getPrev(b[i], function(err, p) { assert.equal(p,b[i-1]); return c(); }); }, function() { async.each([0,1,2,3,4], function(i,c) { s.sync.bDb.has(b[i], function(err, p) { assert(p); return c(); }); }, function() { async.each([0,1,2,3], function(i,c) { s.sync.bDb.getNext(b[i], function(err, p) { assert.equal(p,b[i+1]); return c(); }); }, function() { async.each([0,1,2,3], function(i,c) { s.sync.bDb.getHeight(b[i], function(err, h) { assert.equal(h,16+i); return c(); }); }, function() { async.each([0,1,2,3], function(i,c) { s.sync.bDb.getDepth(b[i], function(err, d) { assert.equal(d,4-i); return c(); }); }, cb); }); }); }); }); }; /* * TEST CASES * * b0 height = 16; * * Blocks: 0-1-2-3-4 * case 1) * 0-1-2-3-4 * \ * C1* (height should be 19) * * case 2) * 0-1-2---3-4 * \ \ * C1 C2* * * case 2b) * 0-1-2---3-4 * \ \ * C1 C2-C2b(TX=C1.TX)* * case 2c) * 0-1-2---3-4 * \ \ * C1 C2-C2b(TX=C1.TX) * \ * C2c(TX=C2.TX)* * */ var checkTxs = function(txs,heights){ var i=0; txs.forEach(function(tx){ var v=heights[i++]; it('tx height:'+tx+' #'+v,function(done){ s.sync.bDb.getBlockForTx(tx, function(err,hash,height) { assert(!err,err); if (v>=0) { assert(hash); height.should.equal(v); } else { assert(!hash); assert(!height); } return done(); }); }); }); }; //heights is optional var checkBlocks = function(hashes,heights){ var i=0; hashes.forEach(function(hash){ var v = heights[i++]; it('block height:'+hash+' #'+v,function(done){ s.sync.bDb.getHeight(hash, function(err,height) { assert(!err,err); height.should.equal(v); return done(); }); }); }); }; describe('Sync Reorgs', function(){ var opts = { forceRPC: true, stopAt: b[5], }; before(function(done) { s = new HistoricSync(); s.sync.destroy(done); }); it('simple RPC forward syncing', function(done) { s.start(opts, function(err) { if (err) return done(err); initialTest(done); }); }); var case1 = { hash: '0000000042d3db6c529dd8f1b085367cb6d907b534f5b1a5dfdd3a34a3459886', tx: [ '2dc27c8d4b98f5e2ed009cfc164ac4990f18e03c4d5b71e780f9c8b7b2c5f151' ], time: 1296690099, previousblockhash: b[2], }; describe('reorg, case 1', function() { checkTxs([t[0], t[1], t[2], t[3], t[4]],[16,17,18,19,20]); checkBlocks([b[0], b[1], b[2], b[3], b[4]],[16,17,18,19,20]); it('trigger reorg case 1', function(done1){ s.sync.storeTipBlock(case1, function(err) { assert(!err, 'shouldnt return error' + err); done1(); }); }); checkTxs([t[0], t[1], t[2], t[3], t[4],case1.tx],[16,17,18,-1,-1,19]); checkBlocks([b[0], b[1], b[2],b[3],b[4],case1.hash],[16,17,18,-1,-1,19]); it('reorg, case 1 (repeat)', function(done) { s.sync.storeTipBlock(case1, function(err) { assert(!err, 'shouldnt return error' + err); return done(); }); }); }); var case2 = { hash: '00000000c262f9428bb84407780bb0bd008b74941d651111ab2500cf649fa45d', tx: [ '3fa6fce216e91c9dc9a6267168e9d8bfb4ae57aec0d26590442cfec6e8233682' ], time: 1296690099, previousblockhash: b[3], }; // * case 2) // * 0-1-2---3-4 // * \ \ // * C1 C2* describe('reorg, case 2', function() { checkTxs([t[0], t[1], t[2], t[3], t[4],case1.tx[0]],[16,17,18,-1,-1,19]); checkBlocks([b[0], b[1], b[2],b[3],b[4],case1.hash],[16,17,18,-1,-1,19]); it('trigger reorg case 2', function(done1){ s.sync.storeTipBlock(case2, function(err) { assert(!err, 'shouldnt return error' + err); return done1(); }); }); checkBlocks([b[0], b[1], b[2],b[3],b[4],case2.hash],[16,17,18,19,-1,20]); checkTxs([t[0], t[1], t[2],t[3],t[4],case2.tx[0]],[16,17,18,19,-1,20]); it('next from block 2', function(done1){ s.sync.bDb.getNext(b[2], function(err, val) { assert(!err); assert.equal(val,b[3]); return done1(); }); }); }); // * case 2b) // * 0-1-2---3-4 // * \ \ // * C1 C2-C2b(TX=C1.TX)* var case2b = { hash: '0000000022bb34bc09f8d8d0ae26b86c87f8483e54b9c3addfe6f30b12bc656a', tx: case1.tx, time: 1296690099, previousblockhash: case2.hash, }; describe('reorg, case 2', function() { it('reorg case 2b', function(done1){ s.sync.storeTipBlock(case2b, function(err) { assert(!err, 'shouldnt return error' + err); return done1(); }); }); checkBlocks([b[0], b[1], b[2],b[3],b[4],case2.hash, case2b.hash, case1.hash],[16,17,18,19,-1,20, 21, -1]); checkTxs([t[0], t[1], t[2],t[3],t[4],case2.tx[0], case1.tx],[16,17,18,19,-1,20, 21]); }); var case2c = { hash: '0000000000000000000000000000000000000000000000000000000000000004', tx: case2.tx, time: 1296690099, previousblockhash: case1.hash, }; // * case 2c) // * 0-1-2---3-4 // * \ \ // * C1 C2-C2b(TX=C1.TX) // * \ // * C2c(TX=C2.TX)* describe('reorg, case 2c', function() { it('trigger reorg case 2c', function(done1){ s.sync.storeTipBlock(case2c, function(err) { assert(!err, 'shouldnt return error' + err); return done1(); }); }); checkBlocks([b[0], b[1], b[2], b[3],b[4],case2.hash, case2b.hash, case1.hash, case2c.hash], [16,17,18, -1,-1,-1, -1, 19, 20 ]); checkTxs( [t[0], t[1], t[2], t[3],t[4], case2.tx[0], case1.tx, case2c.tx[0]], [16,17,18, -1,-1, 20, 19, 20]); }); // * case 3) // * 0-1-2---3-4 // * \ \ // * C1 C2-C2b(TX=C1.TX) // * \ // * C2c(TX=C2.TX) // // (orphan)-C3* // -> returns error var case3 = { hash: '0000000000000000000000000000000000000000000000000000000000000005', tx: case2.tx, time: 1296690099, previousblockhash: '666', }; describe('reorg, case 3)', function() { it('case 3). Should return an error', function(done1){ s.sync.storeTipBlock(case3, function(err) { assert(err, 'should return error' + err); return done1(); }); }); //shoudnt change anything checkBlocks([b[0], b[1], b[2], b[3],b[4],case2.hash, case2b.hash, case1.hash, case2c.hash], [16,17,18, -1,-1,-1, -1, 19, 20 ]); checkTxs( [t[0], t[1], t[2], t[3],t[4], case2.tx[0], case1.tx, case2c.tx[0]], [16,17,18, -1,-1, 20, 19, 20]); }); var p2p = { hash: '0000000000000000000000000000000000000000000000000000000000000006', tx: ['f6c2901f39fd07f2f2e503183d76f73ecc1aee9ac9216fde58e867bc29ce674e'], time: 1296690099, previousblockhash: '111', }; describe('p2p sync. no reorgs', function() { it('Should return an error', function(done1){ s.sync.storeTipBlock(p2p, false, function(err) { assert(!err, 'shouldnt return error' + err); return done1(); }); it('Block should be stored', function(done1){ s.sync.bDb.has(p2p.hash, function(err,is) { assert(!err); assert(is); return done1(); }); }); //shoudnt change anything checkBlocks([b[0], b[1], b[2], b[3],b[4],case2.hash, case2b.hash, case1.hash, case2c.hash, p2p.hash], [16,17,18, -1,-1,-1, -1, 19, 20, -1 ]); }); it('next Block should be stored', function(done1){ s.sync.bDb.getNext(p2p.hash, function(err,v) { assert(!err); assert.equal(v,p2p.nextblockhash); return done1(); }); }); it('next Block should be stored', function(done1){ s.sync.bDb.getNext(p2p.previousblockhash, function(err,v) { assert(!err); assert.equal(v,p2p.hash); return done1(); }); }); }); });