2
2
3
3
const { Level } = require ( 'level' ) ;
4
4
const crypto = require ( "crypto" ) , SHA256 = message => crypto . createHash ( "sha256" ) . update ( message ) . digest ( "hex" ) ;
5
- const EC = require ( "elliptic" ) . ec , ec = new EC ( "secp256k1" ) ;
6
5
const Transaction = require ( "./transaction" ) ;
7
6
const Merkle = require ( "./merkle" ) ;
8
7
const { BLOCK_REWARD , BLOCK_GAS_LIMIT , EMPTY_HASH } = require ( "../config.json" ) ;
@@ -130,39 +129,52 @@ class Block {
130
129
for ( const tx of block . transactions ) {
131
130
if ( ! ( await Transaction . isValid ( tx , stateDB ) ) ) return false ;
132
131
}
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 ;
140
132
141
133
// Start state replay to check if transactions are legit
142
- let states = { } , code = { } , storage = { } ;
134
+ const states = { } , code = { } , storage = { } ;
135
+
136
+ let totalTxGas = 0n ;
143
137
138
+ // Execute transactions and add them to the block sequentially
144
139
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
+
145
143
const txSenderPubkey = Transaction . getPubKey ( tx ) ;
146
144
const txSenderAddress = SHA256 ( txSenderPubkey ) ;
147
145
148
146
const totalAmountToPay = BigInt ( tx . amount ) + BigInt ( tx . gas ) + BigInt ( tx . additionalData . contractGas || 0 ) ;
149
-
147
+
148
+ // Cache the state of sender
150
149
if ( ! states [ txSenderAddress ] ) {
151
150
const senderState = deserializeState ( await stateDB . get ( txSenderAddress ) ) ;
152
151
153
152
states [ txSenderAddress ] = senderState ;
154
-
155
153
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 ;
156
158
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 ( ) ;
162
161
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
+ }
164
171
}
165
172
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
+
166
178
// Contract deployment
167
179
if (
168
180
states [ txSenderAddress ] . codeHash === EMPTY_HASH &&
@@ -175,44 +187,41 @@ class Block {
175
187
// Update nonce
176
188
states [ txSenderAddress ] . nonce += 1 ;
177
189
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
-
192
190
// Contract execution
193
191
if ( states [ tx . recipient ] . codeHash !== EMPTY_HASH ) {
194
192
const contractInfo = { address : tx . recipient } ;
195
193
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
+
198
206
for ( const account of Object . keys ( newState ) ) {
199
207
states [ account ] = newState [ account ] ;
200
208
}
201
209
202
210
storage [ tx . recipient ] = newStorage ;
203
211
}
204
- }
205
212
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);
211
214
}
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
+ }
216
225
}
217
226
218
227
let gas = 0n ;
@@ -222,7 +231,6 @@ class Block {
222
231
states [ block . coinbase ] . balance = ( BigInt ( states [ block . coinbase ] . balance ) + BigInt ( BLOCK_REWARD ) + gas ) . toString ( ) ;
223
232
224
233
// Finalize state and contract storage into DB
225
-
226
234
for ( const address in storage ) {
227
235
const storageDB = new Level ( "./log/accountStore/" + address ) ;
228
236
const keys = Object . keys ( storage [ address ] ) ;
@@ -275,16 +283,6 @@ class Block {
275
283
276
284
return true ;
277
285
}
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
- }
288
286
}
289
287
290
288
module . exports = Block ;
0 commit comments