Skip to content

Commit

Permalink
Add pulling algorithm test: multiple forks
Browse files Browse the repository at this point in the history
  • Loading branch information
c-geek committed Apr 17, 2016
1 parent fad8630 commit e7d7b05
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 6 deletions.
2 changes: 1 addition & 1 deletion app/lib/pulling.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = (conf, dao) => co(function *() {

let forks = [];
let localCurrent = yield dao.localCurrent();
let peers = yield dao.remoteBlockchains();
let peers = yield dao.remotePeers();
// Try to get new legit blocks for local blockchain
for (let i = 0, len = peers.length; i < len; i++) {
let peer = peers[i];
Expand Down
113 changes: 108 additions & 5 deletions test/fast/block_pulling.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,66 @@ describe('Pulling blocks', () => {
],
expectHash: 'B5'
}));

it('sync with multiple forks', pullinTest({
blockchain: [
newBlock(0, 'A'),
newBlock(1, 'A'),
newBlock(2, 'A'),
newBlock(3, 'A')
],
sidechains: [
[
newBlock(0, 'A'),
newBlock(1, 'A'),
newBlock(2, 'B'),
newBlock(3, 'B'),
newBlock(4, 'B'),
newBlock(5, 'B')
],
// This fork should not be followed because we switch only one time per pulling, and B5 is already OK
[
newBlock(0, 'A'),
newBlock(1, 'A'),
newBlock(2, 'B'),
newBlock(3, 'B'),
newBlock(4, 'B'),
newBlock(5, 'B'),
newBlock(6, 'B')
]
],
expectHash: 'B5'
}));

it('sync with multiple forks, with one invalid', pullinTest({
blockchain: [
newBlock(0, 'A'),
newBlock(1, 'A'),
newBlock(2, 'A'),
newBlock(3, 'A')
],
sidechains: [
[
newBlock(0, 'A'),
newBlock(1, 'A'),
newBlock(2, 'C'),
newBlock(3, 'C'),
newBlock(4, 'C'),
newBlock(5, 'C')
],
// This fork should be followed because C will be marked as wrong
[
newBlock(0, 'A'),
newBlock(1, 'A'),
newBlock(2, 'B'),
newBlock(3, 'B'),
newBlock(4, 'B'),
newBlock(5, 'B'),
newBlock(6, 'B')
]
],
expectHash: 'B6'
}));
});

function newBlock(number, branch, rootBranch) {
Expand All @@ -101,11 +161,21 @@ function newBlock(number, branch, rootBranch) {

function pullinTest(testConfiguration) {
return () => co(function *() {

// The blockchains we are testing against
let blockchain = testConfiguration.blockchain;
let sidechains = testConfiguration.sidechains;

// The data access object simulating network access
let dao = mockDao(blockchain, sidechains);

// The very last block of a blockchain should have the good number
(yield dao.localCurrent()).should.have.property('number').equal(blockchain[blockchain.length - 1].number);

// And after we make a pulling...
yield pulling(commonConf, dao);

// We test the new local blockchain current block (it should have changed in case of successful pull)
let localCurrent = yield dao.localCurrent();
if (testConfiguration.expectHash !== undefined && testConfiguration.expectHash !== null) {
localCurrent.should.have.property('hash').equal(testConfiguration.expectHash);
Expand All @@ -116,20 +186,53 @@ function pullinTest(testConfiguration) {
});
}

/**
* Network mocker
* @param blockchain
* @param sideChains
* @returns {{localCurrent: (function(): (*|Q.Promise<*>|Q.Promise<T>)), remoteCurrent: (function(): (*|Q.Promise<*>|Q.Promise<T>)), remotePeers: (function(): (*|Q.Promise<*>|Q.Promise<T>)), getRemoteBlock: (function(): (*|Q.Promise<*|null>|Q.Promise<T>)), applyMainBranch: (function(): (*|Q.Promise<Number|*|_Chain<*>>|Q.Promise<T>)), removeForks: (function(): (*|Q.Promise<T>)), isMemberPeer: (function(): (*|Q.Promise<boolean>|Q.Promise<T>)), findCommonRoot: (function(): (*|Promise)), downloadBlocks: (function(): (*|Q.Promise<Buffer|ArrayBuffer|Array.<any>|string|*|_Chain<any>>|Q.Promise<T>)), applyBranch: (function())}}
*/
function mockDao(blockchain, sideChains) {
return {
// This simulates a real network access

// Get the local blockchain current block
localCurrent: () => Q(blockchain[blockchain.length - 1]),

// Get the remote blockchain (bc) current block
remoteCurrent: (bc) => Q(bc[bc.length - 1]),
remoteBlockchains: () => Q(sideChains),

// Get the remote peers to be pulled
remotePeers: () => Q(sideChains.map((sc, index) => {
sc.pubkey = 'PUBK' + index;
return sc;
})),

// Get block of given peer with given block number
getRemoteBlock: (bc, number) => Q(bc[number] || null),

// Simulate the adding of a single new block on local blockchain
applyMainBranch: (block) => Q(blockchain.push(block)),

// Clean the eventual fork blocks already registered in DB (since real fork mechanism uses them, so we want
// every old fork block to be removed)
removeForks: () => Q(),
isMemberPeer: () => Q(true),
// Not required in this test

// Tells wether given peer is a member peer
isMemberPeer: (peer) => Q(true),

// TODO: make a real algorithm like binary tree search
findCommonRoot: (fork, forksize) => Q(blockchain[1]),
findCommonRoot: (fork, forksize) => co(function *() {
// No common root for sidechain 'Cx'
if (fork.current.hash.match(/^C/)) {
return null;
}
return Q(blockchain[1]);
}),

// Simulates the downloading of blocks from a peer
downloadBlocks: (bc, fromNumber, count) => Q(bc.slice(fromNumber, fromNumber + count)),

// Simulate the adding of new blocks on local blockchain
applyBranch: (blocks) => {
blockchain = blockchain.concat(blocks);
return Q(true);
Expand Down

0 comments on commit e7d7b05

Please sign in to comment.