Skip to content

Commit 07bec8b

Browse files
authored
Merge pull request #99 from ainblockchain/develop
Release v0.3.0
2 parents 6cf5b79 + b31b7aa commit 07bec8b

27 files changed

+561
-117
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ load1.txt
77
load2.txt
88
blocks1.txt
99
blocks2.txt
10+
blockchain/shard_*
1011

1112
# XXX: .dockerignore
1213
.git

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ You can override default port numbering system by setting `PORT` and `P2P_PORT`
2929
#### On Google Coud Platform (GCP)
3030

3131
- Deploy code (in common with Node server)
32+
Set <NUMBER_OF_SHARDS> to 0 if you only want to run a parent chain, or set it to the specific number of shard chains you want to run in addition to the parent chain.
3233
```
3334
gcloud init
34-
sh deploy_gcp.sh {dev|spring|summer} <YOUR_GCP_USER_NAME>
35+
sh deploy_gcp.sh {dev|spring|summer} <YOUR_GCP_USER_NAME> <NUMBER_OF_SHARDS>
3536
```
3637
- Set up Ubuntu machine (if it's on a new VM)
3738
```
@@ -121,9 +122,10 @@ npm run test_integration
121122
#### On Google Coud Platform (GCP)
122123

123124
- Deploy code (in common with Tracker server)
125+
Set <NUMBER_OF_SHARDS> to 0 if you only want to run a parent chain, or set it to the specific number of shard chains you want to run in addition to the parent chain.
124126
```
125127
gcloud init
126-
sh deploy_gcp.sh {dev|spring|summer} <YOUR_GCP_USER_NAME>
128+
sh deploy_gcp.sh {dev|spring|summer} <YOUR_GCP_USER_NAME> <NUMBER_OF_SHARDS>
127129
```
128130
- Set up Ubuntu machine (if it's on a new VM)
129131
```
@@ -133,9 +135,9 @@ sh setup_ubuntu.sh
133135
```
134136
source setup_node_gcp.sh
135137
```
136-
- Start Node server job
138+
- Start Node server job (set shard index to 0 if you're running a root chain node)
137139
```
138-
sh start_node_gcp.sh {dev|spring|summer} <SERVER_INDEX>
140+
sh start_node_gcp.sh {dev|spring|summer} <SHARD_INDEX> <SERVER_INDEX>
139141
```
140142

141143
<!--

blockchain/block.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class Block {
3232
this.number = number;
3333
this.epoch = epoch;
3434
this.timestamp = timestamp;
35+
// TODO(lia): change this to snake case
3536
this.stateProofHash = stateProofHash;
3637
this.proposer = proposer;
3738
this.validators = validators;

client/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const semver = require('semver');
66
const express = require('express');
77
const jayson = require('jayson');
88
const logger = require('../logger');
9-
const Node = require('../node');
9+
const BlockchainNode = require('../node');
1010
const P2pServer = require('../server');
1111
const ChainUtil = require('../chain-util');
1212
const { PORT, PROTOCOL_VERSIONS, WriteDbOperations, TransactionStatus } = require('../constants');
@@ -41,7 +41,7 @@ const maxProtocolVersion = VERSION_LIST[CURRENT_PROTOCOL_VERSION].max;
4141
const app = express();
4242
app.use(express.json()); // support json encoded bodies
4343

44-
const node = new Node();
44+
const node = new BlockchainNode();
4545
const p2pServer = new P2pServer(node, minProtocolVersion, maxProtocolVersion);
4646

4747
const jsonRpcMethods = require('../json_rpc')(

consensus/constants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const ConsensusConsts = {
22
MAJORITY: 2 / 3,
33
DAY_MS: 86400000,
4-
EPOCH_MS: 1000,
4+
EPOCH_MS: 3000,
55
MAX_CONSENSUS_STATE_DB: 1000,
66
INITIAL_NUM_VALIDATORS: process.env.NUM_VALIDATORS ? Number(process.env.NUM_VALIDATORS) : 5,
77
INITIAL_STAKE: 250,

consensus/index.js

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@ const {
2020
ShardingProtocols,
2121
ProofProperties,
2222
MAX_TX_BYTES,
23-
MAX_SHARD_REPORT
23+
MAX_SHARD_REPORT,
24+
GenesisWhitelist,
25+
LIGHTWEIGHT,
2426
} = require('../constants');
2527
const { ConsensusMessageTypes, ConsensusConsts, ConsensusStatus, ConsensusDbPaths }
2628
= require('./constants');
27-
const { sendTxAndWaitForConfirmation, sendGetRequest } = require('../server/util');
29+
const { signAndSendTx, sendGetRequest } = require('../server/util');
30+
const { sleep } = require('sleep');
2831
const LOG_PREFIX = 'CONSENSUS';
2932
const parentChainEndpoint = GenesisSharding[ShardingProperties.PARENT_CHAIN_POC] + '/json-rpc';
3033
const shardingPath = GenesisSharding[ShardingProperties.SHARDING_PATH];
@@ -45,11 +48,15 @@ class Consensus {
4548
this.timeAdjustment = 0;
4649
this.isShardReporter = false;
4750
this.isReporting = false;
51+
this.isInEpochTransition = false;
4852
this.state = {
4953
// epoch increases by 1 every EPOCH_MS, and at each epoch a new proposer is pseudo-randomly selected.
5054
epoch: 1,
5155
proposer: null
5256
}
57+
// This feature is only used when LIGHTWEIGHT=true.
58+
this.cache = {};
59+
this.validatorList = Object.keys(GenesisWhitelist).sort();
5360
}
5461

5562
init(lastBlockWithoutProposal, isFirstNode = false) {
@@ -117,6 +124,10 @@ class Consensus {
117124
clearInterval(this.epochInterval);
118125
}
119126
this.epochInterval = setInterval(async () => {
127+
if (this.isInEpochTransition) {
128+
return;
129+
}
130+
this.isInEpochTransition = true;
120131
this.tryFinalize();
121132
let currentTime = Date.now();
122133
if (this.state.epoch % 100 === 0) {
@@ -143,6 +154,7 @@ class Consensus {
143154
this.updateProposer();
144155
this.tryPropose();
145156
}
157+
this.isInEpochTransition = false;
146158
}, ConsensusConsts.EPOCH_MS);
147159
}
148160

@@ -241,6 +253,12 @@ class Consensus {
241253
const lastBlock = longestNotarizedChain && longestNotarizedChain.length ?
242254
longestNotarizedChain[longestNotarizedChain.length - 1] : this.node.bc.lastBlock();
243255
const blockNumber = lastBlock.number + 1;
256+
257+
if (blockNumber > 1 && LIGHTWEIGHT && this.cache[blockNumber]) {
258+
logger.error(`Already proposed ${blockNumber} / ${this.cache[blockNumber]}`);
259+
return null;
260+
}
261+
244262
const transactions = this.node.tp.getValidTransactions(longestNotarizedChain);
245263
const validTransactions = [];
246264
const prevState = lastBlock.number === this.node.bc.lastBlockNumber() ?
@@ -259,28 +277,30 @@ class Consensus {
259277
}
260278
lastVotes.forEach(voteTx => {
261279
if (!ChainUtil.transactionFailed(tempState.executeTransaction(voteTx))) {
262-
logger.info(`[${LOG_PREFIX}:${LOG_SUFFIX}] vote tx result: success!`);
280+
logger.debug(`[${LOG_PREFIX}:${LOG_SUFFIX}] vote tx result: success!`);
263281
} else {
264-
logger.info(`[${LOG_PREFIX}:${LOG_SUFFIX}] vote tx result: failed..`);
282+
logger.error(`[${LOG_PREFIX}:${LOG_SUFFIX}] vote tx result: failed..`);
265283
}
266284
})
267285

268286
transactions.forEach(tx => {
269287
logger.debug(`[${LOG_PREFIX}:${LOG_SUFFIX}] Checking tx ${JSON.stringify(tx, null, 2)}`);
270288
if (!ChainUtil.transactionFailed(tempState.executeTransaction(tx))) {
271-
logger.info(`[${LOG_PREFIX}:${LOG_SUFFIX}] tx result: success!`);
289+
logger.debug(`[${LOG_PREFIX}:${LOG_SUFFIX}] tx result: success!`);
272290
validTransactions.push(tx);
273291
} else {
274-
logger.info(`[${LOG_PREFIX}:${LOG_SUFFIX}] tx result: failed..`);
292+
logger.error(`[${LOG_PREFIX}:${LOG_SUFFIX}] tx result: failed..`);
275293
}
276294
})
295+
277296
const myAddr = this.node.account.address;
278297
// Need the block#1 to be finalized to have the deposits reflected in the state
279298
const validators = this.node.bc.lastBlockNumber() < 1 ? lastBlock.validators : this.getWhitelist();
280299
if (!validators || !(Object.keys(validators).length)) throw Error('No whitelisted validators')
281300
const totalAtStake = Object.values(validators).reduce(function(a, b) { return a + b; }, 0);
301+
const stateProofHash = LIGHTWEIGHT ? '' : tempState.getProof('/')[ProofProperties.PROOF_HASH];
282302
const proposalBlock = Block.createBlock(lastBlock.hash, lastVotes, validTransactions,
283-
blockNumber, this.state.epoch, tempState.getProof('/')[ProofProperties.PROOF_HASH], myAddr,
303+
blockNumber, this.state.epoch, stateProofHash, myAddr,
284304
validators);
285305

286306
let proposalTx;
@@ -325,6 +345,9 @@ class Consensus {
325345
}
326346
}, false);
327347
}
348+
if (LIGHTWEIGHT) {
349+
this.cache[blockNumber] = proposalBlock.hash;
350+
}
328351
return { proposalBlock, proposalTx };
329352
}
330353

@@ -343,9 +366,11 @@ class Consensus {
343366
logger.error(`[${LOG_PREFIX}:${LOG_SUFFIX}] The block_hash value in proposalTx (${block_hash}) and the actual proposalBlock's hash (${proposalBlock.hash}) don't match`);
344367
return false;
345368
}
346-
if (!Block.validateProposedBlock(proposalBlock)) {
347-
logger.error(`[${LOG_PREFIX}:${LOG_SUFFIX}] Proposed block didn't pass the basic checks`);
348-
return false;
369+
if (!LIGHTWEIGHT) {
370+
if (!Block.validateProposedBlock(proposalBlock)) {
371+
logger.error(`[${LOG_PREFIX}:${LOG_SUFFIX}] Proposed block didn't pass the basic checks`);
372+
return false;
373+
}
349374
}
350375
const { proposer, number, epoch, last_hash } = proposalBlock;
351376
if (number <= this.node.bc.lastBlockNumber()) {
@@ -374,6 +399,7 @@ class Consensus {
374399
const depositSum = depositTxs.reduce((a, b) => { return a + b.operation.value; }, 0);
375400
if (depositSum < majority) {
376401
logger.info(`[${LOG_PREFIX}:${LOG_SUFFIX}] We don't have enough deposits yet`)
402+
this.blockPool.addSeenBlock(proposalBlock, proposalTx);
377403
return false;
378404
}
379405
// TODO(lia): make sure each validator staked only once at this point
@@ -416,7 +442,7 @@ class Consensus {
416442
tempState.setDbToSnapshot(prevState);
417443
proposalBlock.last_votes.forEach(voteTx => {
418444
if (voteTx.hash === prevBlockProposal.hash) return;
419-
if (!Consensus.isValidConsensusTx(voteTx) ||
445+
if (!Consensus.isValidConsensusTx(voteTx) ||
420446
ChainUtil.transactionFailed(tempState.executeTransaction(voteTx))) {
421447
logger.info(`[${LOG_PREFIX}:${LOG_SUFFIX}] voting tx execution for prev block failed`);
422448
// return;
@@ -467,10 +493,12 @@ class Consensus {
467493
logger.error(`[${LOG_PREFIX}:${LOG_SUFFIX}] Failed to execute transactions`);
468494
return false;
469495
}
470-
newState.lastBlockNumber += 1;
471-
if (newState.getProof('/')[ProofProperties.PROOF_HASH] !== proposalBlock.stateProofHash) {
472-
logger.error(`[${LOG_PREFIX}:${LOG_SUFFIX}] State proof hashes don't match: ${newState.getProof('/')[ProofProperties.PROOF_HASH]} / ${proposalBlock.stateProofHash}`);
473-
return false;
496+
newState.blockNumberSnapshot += 1;
497+
if (!LIGHTWEIGHT) {
498+
if (newState.getProof('/')[ProofProperties.PROOF_HASH] !== proposalBlock.stateProofHash) {
499+
logger.error(`[${LOG_PREFIX}:${LOG_SUFFIX}] State proof hashes don't match: ${newState.getProof('/')[ProofProperties.PROOF_HASH]} / ${proposalBlock.stateProofHash}`);
500+
return false;
501+
}
474502
}
475503
this.blockPool.hashToState.set(proposalBlock.hash, newState);
476504
if (!this.blockPool.addSeenBlock(proposalBlock, proposalTx)) {
@@ -518,7 +546,10 @@ class Consensus {
518546
if (ainUtil.areSameAddresses(this.state.proposer, this.node.account.address)) {
519547
logger.info(`[${LOG_PREFIX}:${LOG_SUFFIX}] I'm the proposer`);
520548
try {
521-
this.handleConsensusMessage({ value: this.createProposal(), type: ConsensusMessageTypes.PROPOSE });
549+
const proposal = this.createProposal();
550+
if (proposal !== null) {
551+
this.handleConsensusMessage({ value: proposal, type: ConsensusMessageTypes.PROPOSE });
552+
}
522553
} catch (e) {
523554
logger.error(`Error while creating a proposal: ${e}`);
524555
}
@@ -565,7 +596,7 @@ class Consensus {
565596
}
566597
}
567598
}, false);
568-
599+
569600
this.handleConsensusMessage({ value: voteTx, type: ConsensusMessageTypes.VOTE });
570601
}
571602

@@ -709,10 +740,10 @@ class Consensus {
709740
}
710741

711742
getWhitelist() {
712-
return this.node.db.getValue(ChainUtil.formatPath([
713-
ConsensusDbPaths.CONSENSUS,
714-
ConsensusDbPaths.WHITELIST
715-
])) || {};
743+
return LIGHTWEIGHT ? GenesisWhitelist : this.node.db.getValue(ChainUtil.formatPath([
744+
ConsensusDbPaths.CONSENSUS,
745+
ConsensusDbPaths.WHITELIST
746+
])) || {};
716747
}
717748

718749
getValidConsensusDeposit(address) {
@@ -815,7 +846,7 @@ class Consensus {
815846
nonce: -1
816847
};
817848
// TODO(lia): save the blockNumber - txHash mapping at /sharding/reports of the child state
818-
await sendTxAndWaitForConfirmation(
849+
await signAndSendTx(
819850
parentChainEndpoint,
820851
tx,
821852
Buffer.from(this.node.account.private_key, 'hex')
@@ -839,7 +870,7 @@ class Consensus {
839870
return _.get(response, 'data.result.result');
840871
} catch (e) {
841872
logger.error(`Failed to get the latest reported block number: ${e}`);
842-
}
873+
}
843874
}
844875

845876
isRunning() {
@@ -862,7 +893,7 @@ class Consensus {
862893
* proposer
863894
* },
864895
* block_pool: {
865-
* hashToBlockInfo,
896+
* hashToBlockInfo,
866897
* hashToState,
867898
* hashToNextBlockSet,
868899
* epochToBlock,
@@ -959,4 +990,4 @@ class Consensus {
959990
}
960991
}
961992

962-
module.exports = Consensus;
993+
module.exports = Consensus;

constants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const PORT = process.env.PORT || getPortNumber(8080, 8081);
2727
const P2P_PORT = process.env.P2P_PORT || getPortNumber(5000, 5001);
2828
const HASH_DELIMITER = '#';
2929
const MAX_SHARD_REPORT = 100;
30+
const LIGHTWEIGHT = process.env.LIGHTWEIGHT || false;
3031

3132
function getPortNumber(defaultValue, baseValue) {
3233
if (HOSTING_ENV == 'local') {
@@ -431,6 +432,7 @@ module.exports = {
431432
P2P_PORT,
432433
TRACKER_WS_ADDR,
433434
MAX_SHARD_REPORT,
435+
LIGHTWEIGHT,
434436
MessageTypes,
435437
PredefinedDbPaths,
436438
TokenProperties,

db/index.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
ShardingProperties,
1111
GenesisSharding,
1212
buildOwnerPermissions,
13+
LIGHTWEIGHT,
1314
} = require('../constants');
1415
const ChainUtil = require('../chain-util');
1516
const Transaction = require('../tx-pool/transaction');
@@ -147,10 +148,12 @@ class DB {
147148
}
148149
if (DB.isEmptyNode(stateTree)) {
149150
this.removeEmptyNodes(fullPath);
150-
} else {
151+
} else if (!LIGHTWEIGHT) {
151152
setProofHashForStateTree(stateTree);
152153
}
153-
updateProofHashForPath(pathToParent, this.stateTree);
154+
if (!LIGHTWEIGHT) {
155+
updateProofHashForPath(pathToParent, this.stateTree);
156+
}
154157
}
155158

156159
static isEmptyNode(dbNode) {

0 commit comments

Comments
 (0)