Skip to content

Commit

Permalink
Merge pull request #79 from nguyenphuminh/big-fix-5
Browse files Browse the repository at this point in the history
Big fix 5
  • Loading branch information
nguyenphuminh authored Oct 17, 2023
2 parents 37ff69e + b03021e commit c9c9e46
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 60 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jechain",
"version": "0.27.0",
"version": "0.28.0",
"description": "Node for JeChain - an experimental smart contract blockchain network",
"main": "./index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/core/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ class Block {
// Finalize state and contract storage into DB

for (const address in storage) {
const storageDB = new Level(__dirname + "/../log/accountStore/" + address);
const storageDB = new Level(__dirname + "/../../log/accountStore/" + address);
const keys = Object.keys(storage[address]);

states[address].storageRoot = buildMerkleTree(keys.map(key => key + " " + storage[address][key])).val;
Expand Down
2 changes: 2 additions & 0 deletions src/core/genesis.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use strict";

const EC = require("elliptic").ec, ec = new EC("secp256k1");
const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex");

Expand Down
2 changes: 2 additions & 0 deletions src/core/merkle.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use strict";

const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex");

function Node(val, left = null, right = null) {
Expand Down
4 changes: 3 additions & 1 deletion src/core/runtime.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use strict";

const { Level } = require('level');

const { bigIntable, isHex, deserializeState, serializeState } = require("../utils/utils");
Expand All @@ -8,7 +10,7 @@ const { EMPTY_HASH } = require("../config.json");
const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex");

async function jelscript(input, originalState = {}, gas, stateDB, block, txInfo, contractInfo, enableLogging = false) {
const storageDB = new Level(__dirname + "/../log/accountStore/" + contractInfo.address);
const storageDB = new Level(__dirname + "/../../log/accountStore/" + contractInfo.address);

const instructions = input.trim().replace(/\t/g, "").split("\n").map(ins => ins.trim()).filter(ins => ins !== "");

Expand Down
2 changes: 1 addition & 1 deletion src/core/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ async function changeState(newBlock, stateDB, codeDB, enableLogging = false) { /

const [ newState, newStorage ] = await jelscript(await codeDB.get(dataFromRecipient.codeHash), {}, BigInt(tx.additionalData.contractGas || 0), stateDB, newBlock, tx, contractInfo, enableLogging);

const storageDB = new Level(__dirname + "/../log/accountStore/" + tx.recipient);
const storageDB = new Level(__dirname + "/../../log/accountStore/" + tx.recipient);
const keys = Object.keys(newStorage);

newState[tx.recipient].storageRoot = buildMerkleTree(keys.map(key => key + " " + newStorage[key])).val;
Expand Down
7 changes: 7 additions & 0 deletions src/core/txPool.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use strict";

const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex");
const EC = require("elliptic").ec, ec = new EC("secp256k1");

Expand All @@ -11,6 +13,8 @@ async function addTransaction(transaction, chainInfo, stateDB) {
try {
transaction = Transaction.deserialize(transaction);
} catch (e) {
console.log(`\x1b[31mERROR\x1b[0m [${(new Date()).toISOString()}] Failed to add one transaction to pool.`);

// If transaction can not be deserialized, it's faulty
return;
}
Expand All @@ -21,6 +25,7 @@ async function addTransaction(transaction, chainInfo, stateDB) {
BigInt(transaction.additionalData.contractGas || 0) > BigInt(BLOCK_GAS_LIMIT)
) {
console.log(`\x1b[31mERROR\x1b[0m [${(new Date()).toISOString()}] Failed to add one transaction to pool.`);

return;
}

Expand All @@ -32,6 +37,7 @@ async function addTransaction(transaction, chainInfo, stateDB) {

if (!(await stateDB.keys().all()).includes(txSenderAddress)) {
console.log(`\x1b[31mERROR\x1b[0m [${(new Date()).toISOString()}] Failed to add one transaction to pool.`);

return;
}

Expand All @@ -49,6 +55,7 @@ async function addTransaction(transaction, chainInfo, stateDB) {

if (maxNonce + 1 !== transaction.nonce) {
console.log(`\x1b[31mERROR\x1b[0m [${(new Date()).toISOString()}] Failed to add one transaction to pool.`);

return;
}

Expand Down
33 changes: 33 additions & 0 deletions src/node/queue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"use strict";

class SyncQueue {
constructor (chainInfo) {
this.queue = [];
this.chainInfo = chainInfo;
}

async add(block, verificationHandler) {
this.queue.push(block);

if (!this.chainInfo.syncing) {
this.chainInfo.syncing = true;
await this.sync(verificationHandler);
}
}

async sync(verificationHandler) {
while (this.queue.length !== 0) {
const block = this.queue.shift();

if (await verificationHandler(block)) break;
}

this.chainInfo.syncing = false;
}

wipe() {
this.queue = [];
}
}

module.exports = { SyncQueue };
118 changes: 64 additions & 54 deletions src/node/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const { verifyBlock, updateDifficulty } = require("../consensus/consensus");
const { parseJSON, indexTxns, numToBuffer, serializeState, deserializeState } = require("../utils/utils");
const jelscript = require("../core/runtime");
const { buildMerkleTree } = require("../core/merkle");
const { SyncQueue } = require("./queue");

const opened = []; // Addresses and sockets from connected nodes.
const connected = []; // Addresses from connected nodes.
Expand All @@ -33,15 +34,16 @@ const chainInfo = {
transactionPool: [],
latestBlock: generateGenesisBlock(),
latestSyncBlock: null,
syncQueue: new SyncQueue(this),
syncing: false,
checkedBlock: {},
tempStates: {},
difficulty: 1
};

const stateDB = new Level(__dirname + "/../log/stateStore", { valueEncoding: "buffer" });
const blockDB = new Level(__dirname + "/../log/blockStore", { valueEncoding: "buffer" });
const bhashDB = new Level(__dirname + "/../log/bhashStore", { valueEncoding: "buffer" });
const codeDB = new Level(__dirname + "/../log/codeStore");
const stateDB = new Level(__dirname + "/../../log/stateStore", { valueEncoding: "buffer" });
const blockDB = new Level(__dirname + "/../../log/blockStore", { valueEncoding: "buffer" });
const bhashDB = new Level(__dirname + "/../../log/bhashStore", { valueEncoding: "buffer" });
const codeDB = new Level(__dirname + "/../../log/codeStore");

async function startServer(options) {
const PORT = options.PORT || 3000; // Node's PORT
Expand Down Expand Up @@ -189,22 +191,23 @@ async function startServer(options) {
break;

case TYPE.REQUEST_BLOCK:
if (!ENABLE_CHAIN_REQUEST) { // Unsynced nodes should not be able to send blocks
const { blockNumber, requestAddress } = _message.data;
const { blockNumber, requestAddress } = _message.data;

const socket = opened.find(node => node.address === requestAddress).socket; // Get socket from address
let requestedBlock;

try {
requestedBlock = [ ...await blockDB.get( blockNumber.toString() ) ]; // Get block
} catch (e) {
// If block does not exist, break
break;
}

const currentBlockNumber = Math.max(...(await blockDB.keys().all()).map(key => parseInt(key))); // Get latest block number
const socket = opened.find(node => node.address === requestAddress).socket; // Get socket from address

if (blockNumber > 0 && blockNumber <= currentBlockNumber) { // Check if block number is valid
const block = [ ...await blockDB.get( blockNumber.toString() ) ]; // Get block
socket.send(produceMessage(TYPE.SEND_BLOCK, requestedBlock)); // Send block

console.log(`\x1b[32mLOG\x1b[0m [${(new Date()).toISOString()}] Sent block at position ${blockNumber} to ${requestAddress}.`);

socket.send(produceMessage(TYPE.SEND_BLOCK, block)); // Send block

console.log(`\x1b[32mLOG\x1b[0m [${(new Date()).toISOString()}] Sent block at position ${blockNumber} to ${requestAddress}.`);
}
}

break;

case TYPE.SEND_BLOCK:
Expand All @@ -214,45 +217,54 @@ async function startServer(options) {
block = Block.deserialize(_message.data);
} catch (e) {
// If block fails to be deserialized, it's faulty

return;
}

if (ENABLE_CHAIN_REQUEST && currentSyncBlock === block.blockNumber) {
if (
chainInfo.latestSyncBlock === null // If latest synced block is null then we immediately add the block into the chain without verification.
|| // This happens due to the fact that the genesis block can discard every possible set rule ¯\_(ツ)_/¯
await verifyBlock(block, chainInfo, stateDB, codeDB, ENABLE_LOGGING)
) {
currentSyncBlock += 1;

await blockDB.put(block.blockNumber.toString(), Buffer.from(_message.data)); // Add block to chain.
await bhashDB.put(block.hash, numToBuffer(block.blockNumber)); // Assign block number to the matching block hash

if (!chainInfo.latestSyncBlock) {
chainInfo.latestSyncBlock = block; // Update latest synced block.

await changeState(block, stateDB, codeDB, ENABLE_LOGGING); // Transit state
}

chainInfo.latestBlock = block; // Update latest block cache

await updateDifficulty(block, chainInfo, blockDB); // Update difficulty.

console.log(`\x1b[32mLOG\x1b[0m [${(new Date()).toISOString()}] Synced block at position ${block.blockNumber}.`);

// Continue requesting the next block
for (const node of opened) {
node.socket.send(
produceMessage(
TYPE.REQUEST_BLOCK,
{ blockNumber: currentSyncBlock, requestAddress: MY_ADDRESS }
)
);

await new Promise(r => setTimeout(r, 5000)); // Delay for block verification
if (ENABLE_CHAIN_REQUEST && block.blockNumber === currentSyncBlock) {
const verificationHandler = async function(block) {
if (
chainInfo.latestSyncBlock === null // If latest synced block is null, we immediately add the block into the chain without verification.
|| // This happens due to the fact that the genesis block can discard every possible set rule ¯\_(ツ)_/¯
await verifyBlock(block, chainInfo, stateDB, codeDB, ENABLE_LOGGING)
) {
await blockDB.put(block.blockNumber.toString(), Buffer.from(_message.data)); // Add block to chain
await bhashDB.put(block.hash, numToBuffer(block.blockNumber)); // Assign block number to the matching block hash

if (!chainInfo.latestSyncBlock) {
chainInfo.latestSyncBlock = block; // Update latest synced block.

await changeState(block, stateDB, codeDB, ENABLE_LOGGING); // Force transit state
}

chainInfo.latestBlock = block; // Update latest block cache

await updateDifficulty(block, chainInfo, blockDB); // Update difficulty

console.log(`\x1b[32mLOG\x1b[0m [${(new Date()).toISOString()}] Synced block at position ${block.blockNumber}.`);

chainInfo.syncing = false;
// Wipe sync queue
chainInfo.syncQueue.wipe();

currentSyncBlock++;

// Continue requesting the next block
for (const node of opened) {
node.socket.send(
produceMessage(
TYPE.REQUEST_BLOCK,
{ blockNumber: currentSyncBlock, requestAddress: MY_ADDRESS }
)
);
}

return true;
}

return false;
}

chainInfo.syncQueue.add(block, verificationHandler);
}

break;
Expand Down Expand Up @@ -315,8 +327,6 @@ async function startServer(options) {
{ blockNumber: currentSyncBlock, requestAddress: MY_ADDRESS }
)
);

await new Promise(r => setTimeout(r, 5000)); // Delay for block verification
}
}, 5000);
}
Expand Down Expand Up @@ -514,7 +524,7 @@ async function mine(publicKey, ENABLE_LOGGING) {

// Transit state
for (const address in storage) {
const storageDB = new Level(__dirname + "/../log/accountStore/" + address);
const storageDB = new Level(__dirname + "/../../log/accountStore/" + address);
const keys = Object.keys(storage[address]);

states[address].storageRoot = buildMerkleTree(keys.map(key => key + " " + storage[address][key])).val;
Expand Down
4 changes: 2 additions & 2 deletions src/rpc/rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ function rpc(PORT, client, transactionHandler, keyPair, stateDB, blockDB, bhashD
) {
throwError("Invalid request.", 400);
} else {
const storageDB = new Level(__dirname + "/../log/accountStore/" + contractInfo.address);
const storageDB = new Level(__dirname + "/../../log/accountStore/" + contractInfo.address);

respond({ storage: await storageDB.get(req.body.params.key) });

Expand All @@ -240,7 +240,7 @@ function rpc(PORT, client, transactionHandler, keyPair, stateDB, blockDB, bhashD
) {
throwError("Invalid request.", 400);
} else {
const storageDB = new Level(__dirname + "/../log/accountStore/" + contractInfo.address);
const storageDB = new Level(__dirname + "/../../log/accountStore/" + contractInfo.address);

respond({ storage: await storageDB.keys().all() });
}
Expand Down

0 comments on commit c9c9e46

Please sign in to comment.