Skip to content

Commit 19ebf7b

Browse files
authored
Merge pull request #37 from ainblockchain/feature/lia/streamlet-consensus
Feature/lia/streamlet consensus
2 parents f9e20c1 + b678ac9 commit 19ebf7b

33 files changed

+1920
-482
lines changed

README.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ GET http://<ip_address>:5000/peer_nodes
7676

7777
## Node
7878

79-
Operates a single peer node instance of the AIN blockchain. A single blockchain node instance processes incoming transaction requests and maintains a local copy of the entire blockchain. The blockchain node first queries the tracker-server for ip addresses of other peers, and then syncs its local blockchain to the network consensus blockchain. If the blockchain specifies a "STAKE" argument on startup, it will then begin to take part in the forging/validating process for new blocks.
79+
Operates a single peer node instance of the AIN blockchain. A single blockchain node instance processes incoming transaction requests and maintains a local copy of the entire blockchain. The blockchain node first queries the tracker-server for ip addresses of other peers, and then syncs its local blockchain to the network consensus blockchain. If a node is included in the whitelist and has staked appropriate amount of AIN, it will then take part in the consensus protocol.
8080

8181
### Running without Docker
8282

@@ -90,16 +90,25 @@ yarn install
9090
```
9191
- Run blockchain nodes
9292
```
93-
STAKE=250 ACCOUNT_INDEX=0 HOSTING_ENV=local DEBUG=false node client/index.js
94-
STAKE=250 ACCOUNT_INDEX=1 HOSTING_ENV=local DEBUG=false node client/index.js
95-
STAKE=250 ACCOUNT_INDEX=2 HOSTING_ENV=local DEBUG=false node client/index.js
96-
STAKE=250 ACCOUNT_INDEX=3 HOSTING_ENV=local DEBUG=false node client/index.js
97-
STAKE=250 ACCOUNT_INDEX=4 HOSTING_ENV=local DEBUG=false node client/index.js
93+
ACCOUNT_INDEX=0 HOSTING_ENV=local DEBUG=false node client/index.js
94+
ACCOUNT_INDEX=1 HOSTING_ENV=local DEBUG=false node client/index.js
95+
ACCOUNT_INDEX=2 HOSTING_ENV=local DEBUG=false node client/index.js
96+
ACCOUNT_INDEX=3 HOSTING_ENV=local DEBUG=false node client/index.js
97+
ACCOUNT_INDEX=4 HOSTING_ENV=local DEBUG=false node client/index.js
9898
```
9999
Before starting node jobs, remove existing blockchain files and logs if necessary:
100100
```
101101
rm -rf blockchain/blockchains logger/logs
102102
```
103+
The default size of the validator whitelist is 5. Set NUM_VALIDATORS environment variable when running the first node if you'd like to run different number of validator nodes than 5.
104+
105+
### How to run tests
106+
107+
```
108+
npm run test_unit
109+
npm run test_smoke
110+
npm run test_integration
111+
```
103112

104113
#### On Google Coud Platform (GCP)
105114

@@ -134,7 +143,7 @@ docker pull ainblockchain/blockchain-database
134143
```
135144
- Run with Docker image
136145
```
137-
docker run -e STAKE=250 -e ACCOUNT_INDEX=0 -e HOSTING_ENV="gcp" -e TRACKER_WS_ADDR="ws://<ip_address_of_tracker_server>:5000" --network="host" -d ainblockchain/ain-blockchain:latest
146+
docker run -e ACCOUNT_INDEX=0 -e HOSTING_ENV="gcp" -e TRACKER_WS_ADDR="ws://<ip_address_of_tracker_server>:5000" --network="host" -d ainblockchain/ain-blockchain:latest
138147
```
139148
140149
#### Enter Docker container and inspect blockchain files

blockchain/block.js

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,25 @@ const ChainUtil = require('../chain-util');
66
const Transaction = require('../tx-pool/transaction');
77
const {
88
GENESIS_OWNERS, ADDITIONAL_OWNERS, GENESIS_RULES, ADDITIONAL_RULES, GENESIS_FUNCTIONS,
9-
ADDITIONAL_FUNCTIONS, PredefinedDbPaths, GenesisToken, GenesisAccounts
9+
ADDITIONAL_FUNCTIONS, PredefinedDbPaths, GenesisToken, GenesisAccounts, GenesisWhitelist
1010
} = require('../constants');
11+
const { ConsensusDbPaths, ConsensusConsts } = require('../consensus/constants');
1112
const BlockFilePatterns = require('./block-file-patterns');
1213
const zipper = require('zip-local');
1314
const sizeof = require('object-sizeof');
1415

1516
const LOG_PREFIX = 'BLOCK';
1617

1718
class Block {
18-
constructor(lastHash, lastVotes, transactions, number, timestamp, proposer, validators) {
19+
constructor(lastHash, lastVotes, transactions, number, epoch, timestamp, proposer, validators) {
1920
this.last_votes = lastVotes;
2021
this.transactions = transactions;
2122
// Block's header
2223
this.last_hash = lastHash;
2324
this.last_votes_hash = ChainUtil.hashString(stringify(lastVotes));
2425
this.transactions_hash = ChainUtil.hashString(stringify(transactions));
2526
this.number = number;
27+
this.epoch = epoch;
2628
this.timestamp = timestamp;
2729
this.proposer = proposer;
2830
this.validators = validators;
@@ -37,6 +39,7 @@ class Block {
3739
last_votes_hash: this.last_votes_hash,
3840
transactions_hash: this.transactions_hash,
3941
number: this.number,
42+
epoch: this.epoch,
4043
timestamp: this.timestamp,
4144
proposer: this.proposer,
4245
validators: this.validators,
@@ -51,6 +54,7 @@ class Block {
5154
last_votes_hash: ${ChainUtil.shortenHash(this.last_votes_hash)}
5255
transactions_hash: ${ChainUtil.shortenHash(this.transactions_hash)}
5356
number: ${this.number}
57+
epoch: ${this.epoch}
5458
timestamp: ${this.timestamp}
5559
proposer: ${this.proposer}
5660
validators: ${this.validators}
@@ -66,8 +70,8 @@ class Block {
6670
return ChainUtil.hashString(stringify(block.header));
6771
}
6872

69-
static createBlock(lastHash, lastVotes, transactions, number, proposer, validators) {
70-
return new Block(lastHash, lastVotes, transactions, number, Date.now(), proposer, validators);
73+
static createBlock(lastHash, lastVotes, transactions, number, epoch, proposer, validators) {
74+
return new Block(lastHash, lastVotes, transactions, number, epoch, Date.now(), proposer, validators);
7175
}
7276

7377
static getFileName(block) {
@@ -84,15 +88,15 @@ class Block {
8488
if (!Block.hasRequiredFields(blockInfo)) return null;
8589
if (blockInfo instanceof Block) return blockInfo;
8690
return new Block(blockInfo['last_hash'], blockInfo['last_votes'],
87-
blockInfo['transactions'], blockInfo['number'], blockInfo['timestamp'],
88-
blockInfo['proposer'], blockInfo['validators']);
91+
blockInfo['transactions'], blockInfo['number'], blockInfo['epoch'],
92+
blockInfo['timestamp'], blockInfo['proposer'], blockInfo['validators']);
8993
}
9094

9195
static hasRequiredFields(block) {
9296
return (block.last_hash !== undefined && block.last_votes !== undefined &&
9397
block.transactions !== undefined && block.number !== undefined &&
94-
block.timestamp !== undefined && block.proposer !== undefined &&
95-
block.validators !== undefined);
98+
block.epoch !== undefined && block.timestamp !== undefined &&
99+
block.proposer !== undefined && block.validators !== undefined);
96100
}
97101

98102
static validateHashes(block) {
@@ -112,18 +116,8 @@ class Block {
112116
return true;
113117
}
114118

115-
static validateProposedBlock(block, blockchain) {
116-
if (!Block.validateHashes(block)) {
117-
return false;
118-
}
119-
if (block.number !== (blockchain.lastBlockNumber() + 1)) {
120-
logger.error(`[${LOG_PREFIX}] Number is not correct for block ${block.hash} ` +
121-
`Expected: ${(blockchain.lastBlockNumber() + 1)} ` +
122-
`Actual: ${block.number}`);
123-
return false;
124-
}
125-
// TODO (lia): check the contents of block.last_votes if they indeed voted for
126-
// the previous block.
119+
static validateProposedBlock(block) {
120+
if (!Block.validateHashes(block)) return false;
127121
const nonceTracker = {};
128122
let transaction;
129123
for (let i=0; i<block.transactions.length; i++) {
@@ -166,6 +160,14 @@ class Block {
166160
throw Error('Missing genesis rules file: ' + GENESIS_RULES);
167161
}
168162

163+
// Consensus (whitelisting) operation
164+
// TODO(lia): increase this list to 10
165+
const whitelistValOp = {
166+
type: 'SET_VALUE',
167+
ref: `/${ConsensusDbPaths.CONSENSUS}/${ConsensusDbPaths.WHITELIST}`,
168+
value: GenesisWhitelist
169+
};
170+
169171
// Function configs operation
170172
const functionConfigs = JSON.parse(fs.readFileSync(GENESIS_FUNCTIONS));
171173
if (ADDITIONAL_FUNCTIONS) {
@@ -216,14 +218,37 @@ class Block {
216218
ref: '/',
217219
value: ownerConfigs
218220
};
221+
const whitelistRuleOp = {
222+
type: 'SET_RULE',
223+
ref: `/${ConsensusDbPaths.CONSENSUS}/${ConsensusDbPaths.WHITELIST}`,
224+
value: `auth === '${ownerAccount.address}'`
225+
}
226+
const whitelistOwnerOp = {
227+
type: 'SET_OWNER',
228+
ref: `/${ConsensusDbPaths.CONSENSUS}/${ConsensusDbPaths.WHITELIST}`,
229+
value: {
230+
[ownerAccount.address]: {
231+
"branch_owner": true,
232+
"write_function": true,
233+
"write_owner": true,
234+
"write_rule": true
235+
},
236+
"*": {
237+
"branch_owner": false,
238+
"write_function": false,
239+
"write_owner": false,
240+
"write_rule": false
241+
}
242+
}
243+
}
219244

220245
// Transaction
221246
const firstTxData = {
222247
nonce: -1,
223248
timestamp,
224249
operation: {
225250
type: 'SET',
226-
op_list: [ tokenOp, balanceOp, functionsOp, rulesOp, ownersOp ]
251+
op_list: [ tokenOp, balanceOp, whitelistValOp, functionsOp, rulesOp, whitelistRuleOp, ownersOp, whitelistOwnerOp ]
227252
}
228253
};
229254
const firstSig = ainUtil.ecSignTransaction(firstTxData, keyBuffer);
@@ -286,9 +311,13 @@ class Block {
286311
const lastVotes = [];
287312
const transactions = Block.getGenesisBlockData();
288313
const number = 0;
314+
const epoch = 0;
289315
const proposer = ownerAccount.address;
290-
const validators = [];
291-
return new this(lastHash, lastVotes, transactions, number, timestamp,
316+
const validators = {};
317+
for (let i = 0; i < ConsensusConsts.INITIAL_NUM_VALIDATORS; i++) {
318+
validators[GenesisAccounts.others[i].address] = ConsensusConsts.INITIAL_STAKE;
319+
}
320+
return new this(lastHash, lastVotes, transactions, number, epoch, timestamp,
292321
proposer, validators);
293322
}
294323
}

blockchain/genesis_owners.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,19 @@
2222
}
2323
}
2424
}
25+
},
26+
"consensus": {
27+
"whitelist": {
28+
".owner": {
29+
"owners": {
30+
"*": {
31+
"branch_owner": true,
32+
"write_function": true,
33+
"write_owner": true,
34+
"write_rule": true
35+
}
36+
}
37+
}
38+
}
2539
}
2640
}

blockchain/genesis_rules.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
"$number": {
1717
".write": "newData === null && !!getValue('/consensus/number/' + (Number($number) + 1000))",
1818
"propose": {
19-
".write": "newData !== null && util.isDict(newData) && newData.proposer === auth && Number($number) === newData.number && (newData.number === 1 || getValue('/consensus/number/' + (newData.number - 1) + '/propose/validators/' + auth) > 0)"
19+
".write": "newData !== null && util.isDict(newData) && newData.proposer === auth && Number($number) === newData.number && getValue('/consensus/whitelist/' + auth) > 0 && (lastBlockNumber < 1 || getValue('/deposit_accounts/consensus/' + auth + '/value') > 0)"
2020
},
21-
"register": {
21+
"vote": {
2222
"$user_addr": {
23-
".write": "auth === $user_addr && util.isDict(newData) && util.isString(newData.block_hash) && util.isNumber(newData.stake) && newData.stake > 0 && getValue('/deposit_accounts/consensus/' + $user_addr + '/value') >= newData.stake && !getValue('/consensus/number/' + (Number($number) + 1) + '/propose')"
23+
".write": "auth === $user_addr && util.isDict(newData) && util.isString(newData.block_hash) && util.isNumber(newData.stake) && newData.stake > 0 && getValue('/consensus/whitelist/' + auth) >= newData.stake && (lastBlockNumber < 1 || getValue('/deposit_accounts/consensus/' + auth + '/value') > 0)"
2424
}
2525
}
2626
}

0 commit comments

Comments
 (0)