Skip to content

Commit 7848ebe

Browse files
Merge pull request #91 from nguyenphuminh/big-fix-6
Big fix 6
2 parents 5bb4828 + 2b49ecb commit 7848ebe

File tree

9 files changed

+159
-164
lines changed

9 files changed

+159
-164
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jechain",
3-
"version": "0.30.3",
3+
"version": "0.30.4",
44
"description": "Node for JeChain - an experimental smart contract blockchain network",
55
"main": "./index.js",
66
"scripts": {

src/consensus/consensus.js

-3
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ async function verifyBlock(newBlock, chainInfo, stateDB, codeDB, enableLogging =
4646
// Check block number
4747
newBlock.blockNumber - 1 === chainInfo.latestBlock.blockNumber &&
4848

49-
// Check gas limit
50-
Block.hasValidGasLimit(newBlock) &&
51-
5249
// Check transactions and transit state right after
5350
await Block.verifyTxAndTransit(newBlock, stateDB, codeDB, enableLogging)
5451
)

src/core/block.js

+52-54
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
const { Level } = require('level');
44
const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex");
5-
const EC = require("elliptic").ec, ec = new EC("secp256k1");
65
const Transaction = require("./transaction");
76
const Merkle = require("./merkle");
87
const { BLOCK_REWARD, BLOCK_GAS_LIMIT, EMPTY_HASH } = require("../config.json");
@@ -130,39 +129,52 @@ class Block {
130129
for (const tx of block.transactions) {
131130
if (!(await Transaction.isValid(tx, stateDB))) return false;
132131
}
133-
134-
// Get all existing addresses
135-
const addressesInBlock = block.transactions.map(tx => SHA256(Transaction.getPubKey(tx)));
136-
const existedAddresses = await stateDB.keys().all();
137-
138-
// If senders' address doesn't exist, return false
139-
if (!addressesInBlock.every(address => existedAddresses.includes(address))) return false;
140132

141133
// Start state replay to check if transactions are legit
142-
let states = {}, code = {}, storage = {};
134+
const states = {}, code = {}, storage = {};
135+
136+
let totalTxGas = 0n;
143137

138+
// Execute transactions and add them to the block sequentially
144139
for (const tx of block.transactions) {
140+
// If packed transactions exceed block gas limit, stop
141+
if (totalTxGas + BigInt(tx.additionalData.contractGas || 0) >= BigInt(BLOCK_GAS_LIMIT)) return false;
142+
145143
const txSenderPubkey = Transaction.getPubKey(tx);
146144
const txSenderAddress = SHA256(txSenderPubkey);
147145

148146
const totalAmountToPay = BigInt(tx.amount) + BigInt(tx.gas) + BigInt(tx.additionalData.contractGas || 0);
149-
147+
148+
// Cache the state of sender
150149
if (!states[txSenderAddress]) {
151150
const senderState = deserializeState(await stateDB.get(txSenderAddress));
152151

153152
states[txSenderAddress] = senderState;
154-
155153
code[senderState.codeHash] = await codeDB.get(senderState.codeHash);
154+
}
155+
156+
// If sender does not have enough money or is now a contract, skip
157+
if (states[txSenderAddress].codeHash !== EMPTY_HASH || BigInt(states[txSenderAddress].balance) < totalAmountToPay) return false;
156158

157-
if (senderState.codeHash !== EMPTY_HASH || BigInt(senderState.balance) < totalAmountToPay) return false;
158-
159-
states[txSenderAddress].balance = (BigInt(senderState.balance) - totalAmountToPay).toString();
160-
} else {
161-
if (states[txSenderAddress].codeHash !== EMPTY_HASH || BigInt(states[txSenderAddress].balance) < totalAmountToPay) return false;
159+
// Update balance of sender
160+
states[txSenderAddress].balance = (BigInt(states[txSenderAddress].balance) - BigInt(tx.amount) - BigInt(tx.gas) - BigInt(tx.additionalData.contractGas || 0)).toString();
162161

163-
states[txSenderAddress].balance = (BigInt(states[txSenderAddress].balance) - totalAmountToPay).toString();
162+
// Cache the state of recipient
163+
if (!states[tx.recipient]) {
164+
try { // If account exists but is not cached
165+
states[tx.recipient] = deserializeState(await stateDB.get(tx.recipient));
166+
code[states[tx.recipient].codeHash] = await codeDB.get(states[tx.recipient].codeHash);
167+
} catch (e) { // If account does not exist and is not cached
168+
states[tx.recipient] = { balance: "0", codeHash: EMPTY_HASH, nonce: 0, storageRoot: EMPTY_HASH }
169+
code[EMPTY_HASH] = "";
170+
}
164171
}
165172

173+
// Update balance of recipient
174+
states[tx.recipient].balance = (BigInt(states[tx.recipient].balance) + BigInt(tx.amount)).toString();
175+
176+
// console.log(tx.recipient, states[tx.recipient].balance, block);
177+
166178
// Contract deployment
167179
if (
168180
states[txSenderAddress].codeHash === EMPTY_HASH &&
@@ -175,44 +187,41 @@ class Block {
175187
// Update nonce
176188
states[txSenderAddress].nonce += 1;
177189

178-
if (BigInt(states[txSenderAddress].balance) < 0n) return false;
179-
180-
if (!existedAddresses.includes(tx.recipient) && !states[tx.recipient]) {
181-
states[tx.recipient] = { balance: "0", codeHash: EMPTY_HASH, nonce: 0, storageRoot: EMPTY_HASH }
182-
code[EMPTY_HASH] = "";
183-
}
184-
185-
if (existedAddresses.includes(tx.recipient) && !states[tx.recipient]) {
186-
states[tx.recipient] = deserializeState(await stateDB.get(tx.recipient));
187-
code[states[tx.recipient].codeHash] = await codeDB.get(states[tx.recipient].codeHash);
188-
}
189-
190-
states[tx.recipient].balance = (BigInt(states[tx.recipient].balance) + BigInt(tx.amount)).toString();
191-
192190
// Contract execution
193191
if (states[tx.recipient].codeHash !== EMPTY_HASH) {
194192
const contractInfo = { address: tx.recipient };
195193

196-
const [ newState, newStorage ] = await jelscript(code[states[tx.recipient].codeHash], states, BigInt(tx.additionalData.contractGas || 0), stateDB, block, tx, contractInfo, enableLogging);
197-
194+
const [ newState, newStorage ] = await jelscript(
195+
code[states[tx.recipient].codeHash],
196+
states,
197+
storage[tx.recipient] || {},
198+
BigInt(tx.additionalData.contractGas || 0),
199+
stateDB,
200+
block,
201+
tx,
202+
contractInfo,
203+
enableLogging
204+
);
205+
198206
for (const account of Object.keys(newState)) {
199207
states[account] = newState[account];
200208
}
201209

202210
storage[tx.recipient] = newStorage;
203211
}
204-
}
205212

206-
// Reward
207-
208-
if (!existedAddresses.includes(block.coinbase) && !states[block.coinbase]) {
209-
states[block.coinbase] = { balance: "0", codeHash: EMPTY_HASH, nonce: 0, storageRoot: EMPTY_HASH }
210-
code[EMPTY_HASH] = "";
213+
// console.log(tx.recipient, states[tx.recipient].balance, block);
211214
}
212-
213-
if (existedAddresses.includes(block.coinbase) && !states[block.coinbase]) {
214-
states[block.coinbase] = deserializeState(await stateDB.get(block.coinbase));
215-
code[states[block.coinbase].codeHash] = await codeDB.get(states[block.coinbase].codeHash);
215+
216+
// Send reward to coinbase's address
217+
if (!states[block.coinbase]) {
218+
try { // If account exists but is not cached
219+
states[block.coinbase] = deserializeState(await stateDB.get(block.coinbase));
220+
code[states[block.coinbase].codeHash] = await codeDB.get(states[block.coinbase].codeHash);
221+
} catch (e) { // If account does not exist and is not cached
222+
states[block.coinbase] = { balance: "0", codeHash: EMPTY_HASH, nonce: 0, storageRoot: EMPTY_HASH }
223+
code[EMPTY_HASH] = "";
224+
}
216225
}
217226

218227
let gas = 0n;
@@ -222,7 +231,6 @@ class Block {
222231
states[block.coinbase].balance = (BigInt(states[block.coinbase].balance) + BigInt(BLOCK_REWARD) + gas).toString();
223232

224233
// Finalize state and contract storage into DB
225-
226234
for (const address in storage) {
227235
const storageDB = new Level("./log/accountStore/" + address);
228236
const keys = Object.keys(storage[address]);
@@ -275,16 +283,6 @@ class Block {
275283

276284
return true;
277285
}
278-
279-
static hasValidGasLimit(block) {
280-
let totalGas = 0n;
281-
282-
for (const tx of block.transactions) {
283-
totalGas += BigInt(tx.additionalData.contractGas || 0);
284-
}
285-
286-
return totalGas <= BigInt(BLOCK_GAS_LIMIT);
287-
}
288286
}
289287

290288
module.exports = Block;

src/core/runtime.js

+33-30
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const { EMPTY_HASH } = require("../config.json");
99

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

12-
async function jelscript(input, originalState = {}, gas, stateDB, block, txInfo, contractInfo, enableLogging = false) {
12+
async function jelscript(input, originalState = {}, originalStorage = {}, gas, stateDB, block, txInfo, contractInfo, enableLogging = false) {
1313
// Prepare code, memory, state, storage placeholder
1414
const instructions = input.trim().replace(/\t/g, "").split("\n").map(ins => ins.trim()).filter(ins => ins !== "");
1515

@@ -20,15 +20,22 @@ async function jelscript(input, originalState = {}, gas, stateDB, block, txInfo,
2020
let ptr = 0;
2121

2222

23-
// Get contract state and storage
24-
const storageDB = new Level("./log/accountStore/" + contractInfo.address);
23+
// Get contract storage
24+
if (Object.keys(originalStorage).length === 0) {
25+
const storageDB = new Level("./log/accountStore/" + contractInfo.address);
2526

26-
for (const key of (await storageDB.keys().all())) {
27-
storage[key] = await storageDB.get(key);
27+
for (const key of (await storageDB.keys().all())) {
28+
storage[key] = await storageDB.get(key);
29+
}
30+
31+
await storageDB.close();
2832
}
2933

30-
const contractState = deserializeState(await stateDB.get(contractInfo.address));
31-
state[contractInfo.address] = contractState;
34+
// Get contract state
35+
if (!state[contractInfo.address]) {
36+
const contractState = deserializeState(await stateDB.get(contractInfo.address));
37+
state[contractInfo.address] = contractState;
38+
}
3239

3340

3441
while (
@@ -241,18 +248,14 @@ async function jelscript(input, originalState = {}, gas, stateDB, block, txInfo,
241248
case "balance": // Get balance from address
242249
const address = getValue(args[1]).slice(2); // Get address
243250

244-
const existedAddresses = await stateDB.keys().all();
245-
246-
if (!existedAddresses.includes(address) && !state[address]) {
247-
setMem(c, "0x0");
248-
}
249-
250-
if (existedAddresses.includes(address) && !state[address]) {
251-
setMem(c, "0x" + BigInt(deserializeState((await stateDB.get(address))).balance).toString(16));
252-
}
253-
254-
if (!existedAddresses.includes(address) && state[address]) {
251+
if (state[address]) {
255252
setMem(c, "0x" + BigInt(state[address].balance).toString(16));
253+
} else {
254+
try {
255+
setMem(c, "0x" + BigInt(deserializeState((await stateDB.get(address))).balance).toString(16));
256+
} catch (e) {
257+
setMem(c, "0x0");
258+
}
256259
}
257260

258261
break;
@@ -263,18 +266,20 @@ async function jelscript(input, originalState = {}, gas, stateDB, block, txInfo,
263266
const balance = state[contractInfo.address].balance;
264267

265268
if (BigInt(balance) >= amount) {
266-
if (!(await stateDB.keys().all()).includes(target) && !state[target]) {
267-
state[target] = {
268-
balance: amount.toString(),
269-
codeHash: EMPTY_HASH,
270-
nonce: 0,
271-
storageRoot: EMPTY_HASH
272-
}
273-
} else {
274-
if (!state[target]) {
269+
if (!state[target]) {
270+
try {
275271
state[target] = deserializeState(await stateDB.get(target));
276-
}
277272

273+
state[target].balance = BigInt(state[target].balance) + amount;
274+
} catch (e) {
275+
state[target] = {
276+
balance: amount.toString(),
277+
codeHash: EMPTY_HASH,
278+
nonce: 0,
279+
storageRoot: EMPTY_HASH
280+
}
281+
}
282+
} else {
278283
state[target].balance = BigInt(state[target].balance) + amount;
279284
}
280285

@@ -349,8 +354,6 @@ async function jelscript(input, originalState = {}, gas, stateDB, block, txInfo,
349354
return storage[key] ? storage[key] : "0x0";
350355
}
351356

352-
await storageDB.close();
353-
354357
return [state, storage];
355358
}
356359

src/core/state.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ async function changeState(newBlock, stateDB, codeDB, enableLogging = false) { /
6464
if (dataFromRecipient.codeHash !== EMPTY_HASH) {
6565
const contractInfo = { address: tx.recipient };
6666

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

6969
const storageDB = new Level("./log/accountStore/" + tx.recipient);
7070
const keys = Object.keys(newStorage);
@@ -87,7 +87,6 @@ async function changeState(newBlock, stateDB, codeDB, enableLogging = false) { /
8787
}
8888

8989
// Reward
90-
9190
let gas = 0n;
9291

9392
for (const tx of newBlock.transactions) { gas += BigInt(tx.gas) + BigInt(tx.additionalData.contractGas || 0) }

src/core/transaction.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,15 @@ class Transaction {
182182
const txSenderAddress = SHA256(txSenderPubkey);
183183

184184
// If sender does not exist return false
185-
if (!(await stateDB.keys().all()).includes(txSenderAddress)) return false;
186-
187-
// Fetch sender's state object
188-
const dataFromSender = deserializeState(await stateDB.get(txSenderAddress));
185+
try {
186+
// Fetch sender's state object
187+
const dataFromSender = deserializeState(await stateDB.get(txSenderAddress));
189188

190-
// If sender is a contract address, then it's not supposed to be used to send money, so return false if it is.
191-
if (dataFromSender.codeHash !== EMPTY_HASH) return false;
189+
// If sender is a contract address, then it's not supposed to be used to send money, so return false if it is.
190+
if (dataFromSender.codeHash !== EMPTY_HASH) return false;
191+
} catch (e) {
192+
return false;
193+
}
192194

193195
return true;
194196

src/core/txPool.js

-6
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,6 @@ async function addTransaction(transaction, chainInfo, stateDB) {
3232
const txSenderPubkey = Transaction.getPubKey(transaction);
3333
const txSenderAddress = SHA256(txSenderPubkey);
3434

35-
if (!(await stateDB.keys().all()).includes(txSenderAddress)) {
36-
console.log(`\x1b[31mERROR\x1b[0m [${(new Date()).toISOString()}] Failed to add one transaction to pool: Sender does not exist.`);
37-
38-
return;
39-
}
40-
4135
// Check nonce
4236
let maxNonce = deserializeState(await stateDB.get(txSenderAddress)).nonce;
4337

0 commit comments

Comments
 (0)