mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-03 07:45:18 +00:00
More state restoring
This commit is contained in:
parent
766d1c4091
commit
3d64c66b7a
@ -64,6 +64,12 @@ proc setBalance*(db: var AccountStateDB, address: EthAddress, balance: UInt256)
|
|||||||
proc increaseBalance*(db: var AccountStateDB, address: EthAddress, delta: UInt256) =
|
proc increaseBalance*(db: var AccountStateDB, address: EthAddress, delta: UInt256) =
|
||||||
db.setBalance(address, db.getBalance(address) + delta)
|
db.setBalance(address, db.getBalance(address) + delta)
|
||||||
|
|
||||||
|
proc addBalance*(db: var AccountStateDB, address: EthAddress, delta: UInt256) =
|
||||||
|
db.setBalance(address, db.getBalance(address) + delta)
|
||||||
|
|
||||||
|
proc subBalance*(db: var AccountStateDB, address: EthAddress, delta: UInt256) =
|
||||||
|
db.setBalance(address, db.getBalance(address) - delta)
|
||||||
|
|
||||||
template createTrieKeyFromSlot(slot: UInt256): ByteRange =
|
template createTrieKeyFromSlot(slot: UInt256): ByteRange =
|
||||||
# XXX: This is too expensive. Similar to `createRangeFromAddress`
|
# XXX: This is too expensive. Similar to `createRangeFromAddress`
|
||||||
# Converts a number to hex big-endian representation including
|
# Converts a number to hex big-endian representation including
|
||||||
|
@ -63,8 +63,9 @@ proc defaultGenesisBlockForNetwork*(id: PublicNetwork): Genesis =
|
|||||||
Genesis()
|
Genesis()
|
||||||
result.config = publicChainConfig(id)
|
result.config = publicChainConfig(id)
|
||||||
|
|
||||||
proc toBlock*(g: Genesis): BlockHeader =
|
proc toBlock*(g: Genesis, db: BaseChainDB = nil): BlockHeader =
|
||||||
let tdb = trieDB(newMemDB())
|
let tdb = if db.isNil: trieDB(newMemDB())
|
||||||
|
else: db.db
|
||||||
var trie = initHexaryTrie(tdb)
|
var trie = initHexaryTrie(tdb)
|
||||||
var sdb = newAccountStateDB(tdb, trie.rootHash)
|
var sdb = newAccountStateDB(tdb, trie.rootHash)
|
||||||
|
|
||||||
@ -98,6 +99,6 @@ proc toBlock*(g: Genesis): BlockHeader =
|
|||||||
result.difficulty = GENESIS_DIFFICULTY
|
result.difficulty = GENESIS_DIFFICULTY
|
||||||
|
|
||||||
proc commit*(g: Genesis, db: BaseChainDB) =
|
proc commit*(g: Genesis, db: BaseChainDB) =
|
||||||
let b = g.toBlock()
|
let b = g.toBlock(db)
|
||||||
assert(b.blockNumber == 0, "can't commit genesis block with number > 0")
|
assert(b.blockNumber == 0, "can't commit genesis block with number > 0")
|
||||||
discard db.persistHeaderToDb(b)
|
discard db.persistHeaderToDb(b)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import ../db/[db_chain, state_db], eth_common, chronicles, ../vm_state, ../vm_types, ../transaction, ranges,
|
import ../db/[db_chain, state_db], eth_common, chronicles, ../vm_state, ../vm_types, ../transaction, ranges,
|
||||||
../vm/[computation, interpreter_dispatch, message], ../constants
|
../vm/[computation, interpreter_dispatch, message], ../constants, stint, nimcrypto, ../utils/addresses,
|
||||||
|
eth_trie/memdb, eth_trie, rlp
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -30,33 +31,136 @@ method getSuccessorHeader*(c: Chain, h: BlockHeader, output: var BlockHeader): b
|
|||||||
method getBlockBody*(c: Chain, blockHash: KeccakHash): BlockBodyRef =
|
method getBlockBody*(c: Chain, blockHash: KeccakHash): BlockBodyRef =
|
||||||
result = nil
|
result = nil
|
||||||
|
|
||||||
|
proc dataGas(data: openarray[byte]): GasInt =
|
||||||
|
for i in data:
|
||||||
|
if i == 0:
|
||||||
|
result += 4
|
||||||
|
else:
|
||||||
|
result += 68
|
||||||
|
|
||||||
|
proc processTransaction(db: var AccountStateDB, t: Transaction, sender: EthAddress): UInt256 =
|
||||||
|
echo "Sender: ", sender
|
||||||
|
echo "txHash: ", t.rlpHash
|
||||||
|
# Inct nonce:
|
||||||
|
db.setNonce(sender, db.getNonce(sender) + 1)
|
||||||
|
var transactionFailed = false
|
||||||
|
|
||||||
|
let upfrontGasCost = t.gasLimit.u256 * t.gasPrice.u256
|
||||||
|
let upfrontCost = upfrontGasCost + t.value
|
||||||
|
var balance = db.getBalance(sender)
|
||||||
|
if balance < upfrontCost:
|
||||||
|
if balance <= upfrontGasCost:
|
||||||
|
result = balance
|
||||||
|
balance = 0.u256
|
||||||
|
else:
|
||||||
|
result = upfrontGasCost
|
||||||
|
balance -= upfrontGasCost
|
||||||
|
transactionFailed = true
|
||||||
|
else:
|
||||||
|
balance -= upfrontCost
|
||||||
|
|
||||||
|
db.setBalance(sender, balance)
|
||||||
|
if transactionFailed:
|
||||||
|
return
|
||||||
|
|
||||||
|
var gasUsed = 21000.GasInt
|
||||||
|
if t.payload.len != 0:
|
||||||
|
gasUsed += dataGas(t.payload)
|
||||||
|
|
||||||
|
if t.isContractCreation:
|
||||||
|
gasUsed += 32000
|
||||||
|
echo "Contract creation"
|
||||||
|
|
||||||
|
if gasUsed > t.gasLimit:
|
||||||
|
echo "Transaction failed. Out of gas."
|
||||||
|
transactionFailed = true
|
||||||
|
else:
|
||||||
|
if t.isContractCreation:
|
||||||
|
db.setCode(generateAddress(sender, t.accountNonce), t.payload.toRange)
|
||||||
|
else:
|
||||||
|
let code = db.getCode(t.to)
|
||||||
|
if code.len == 0:
|
||||||
|
# Value transfer
|
||||||
|
echo "Transfer ", t.value, " from ", sender, " to ", t.to
|
||||||
|
|
||||||
|
db.addBalance(t.to, t.value)
|
||||||
|
else:
|
||||||
|
# Contract call
|
||||||
|
echo "Contract call"
|
||||||
|
|
||||||
|
debug "Transaction", sender, to = t.to, value = t.value, hasCode = code.len != 0
|
||||||
|
let msg = newMessage(t.gasLimit, t.gasPrice, t.to, sender, t.value, t.payload, code.toSeq)
|
||||||
|
|
||||||
|
if gasUsed > t.gasLimit:
|
||||||
|
gasUsed = t.gasLimit
|
||||||
|
|
||||||
|
var refund = (t.gasLimit - gasUsed).u256 * t.gasPrice.u256
|
||||||
|
if transactionFailed:
|
||||||
|
refund += t.value
|
||||||
|
|
||||||
|
db.addBalance(sender, refund)
|
||||||
|
|
||||||
|
return gasUsed.u256 * t.gasPrice.u256
|
||||||
|
|
||||||
|
proc calcTxRoot(transactions: openarray[Transaction]): Hash256 =
|
||||||
|
var tr = initHexaryTrie(trieDB(newMemDB()))
|
||||||
|
for i, t in transactions:
|
||||||
|
tr.put(rlp.encode(i), rlp.encode(t))
|
||||||
|
return tr.rootHash
|
||||||
|
|
||||||
method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarray[BlockBody]) =
|
method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarray[BlockBody]) =
|
||||||
# Run the VM here
|
# Run the VM here
|
||||||
assert(headers.len == bodies.len)
|
assert(headers.len == bodies.len)
|
||||||
|
|
||||||
|
let blockReward = 5.u256 * pow(10.u256, 18) # 5 ETH
|
||||||
|
|
||||||
|
echo "Persisting blocks: ", headers[0].blockNumber, " - ", headers[^1].blockNumber
|
||||||
for i in 0 ..< headers.len:
|
for i in 0 ..< headers.len:
|
||||||
echo "Persisting block: ", headers[i].blockNumber
|
let head = c.db.getCanonicalHead()
|
||||||
|
assert(head.blockNumber == headers[i].blockNumber - 1)
|
||||||
|
var stateDb = newAccountStateDB(c.db.db, head.stateRoot)
|
||||||
|
var gasReward = 0.u256
|
||||||
|
|
||||||
|
assert(bodies[i].transactions.calcTxRoot == headers[i].txRoot)
|
||||||
|
|
||||||
if headers[i].txRoot != BLANK_ROOT_HASH:
|
if headers[i].txRoot != BLANK_ROOT_HASH:
|
||||||
let head = c.db.getCanonicalHead()
|
|
||||||
# assert(head.blockNumber == headers[i].blockNumber - 1)
|
# assert(head.blockNumber == headers[i].blockNumber - 1)
|
||||||
let vmState = newBaseVMState(head, c.db)
|
let vmState = newBaseVMState(head, c.db)
|
||||||
let stateDb = newAccountStateDB(c.db.db, head.stateRoot)
|
assert(bodies[i].transactions.len != 0)
|
||||||
|
|
||||||
if bodies[i].transactions.len != 0:
|
if bodies[i].transactions.len != 0:
|
||||||
# echo "block: ", headers[i].blockNumber
|
echo "block: ", headers[i].blockNumber
|
||||||
|
echo "h: ", headers[i].blockHash
|
||||||
|
|
||||||
for t in bodies[i].transactions:
|
for t in bodies[i].transactions:
|
||||||
var sender: EthAddress
|
var sender: EthAddress
|
||||||
if t.getSender(sender):
|
if t.getSender(sender):
|
||||||
echo "Sender: ", sender
|
gasReward += processTransaction(stateDb, t, sender)
|
||||||
let code = stateDb.getCode(sender)
|
else:
|
||||||
debug "Transaction", sender, to = t.to, value = t.value, hasCode = code.len != 0
|
assert(false, "Could not get sender")
|
||||||
let msg = newMessage(t.gasLimit, t.gasPrice, t.to, sender, t.value, t.payload, code.toSeq)
|
|
||||||
assert(false, "Dont know how to persist transactions")
|
var mainReward = blockReward + gasReward
|
||||||
|
|
||||||
if headers[i].ommersHash != EMPTY_UNCLE_HASH:
|
if headers[i].ommersHash != EMPTY_UNCLE_HASH:
|
||||||
debug "Ignoring ommers", blockNumber = headers[i].blockNumber
|
let h = c.db.persistUncles(bodies[i].uncles)
|
||||||
|
assert(h == headers[i].ommersHash)
|
||||||
|
for u in 0 ..< bodies[i].uncles.len:
|
||||||
|
var uncleReward = bodies[i].uncles[u].blockNumber + 8.u256
|
||||||
|
uncleReward -= headers[i].blockNumber
|
||||||
|
uncleReward = uncleReward * blockReward
|
||||||
|
uncleReward = uncleReward div 8.u256
|
||||||
|
stateDb.addBalance(bodies[i].uncles[u].coinbase, uncleReward)
|
||||||
|
mainReward += blockReward div 32.u256
|
||||||
|
|
||||||
|
# Reward beneficiary
|
||||||
|
stateDb.addBalance(headers[i].coinbase, mainReward)
|
||||||
|
|
||||||
|
if headers[i].stateRoot != stateDb.rootHash:
|
||||||
|
echo "Wrong state root in block ", headers[i].blockNumber, ". Expected: ", headers[i].stateRoot, ", Actual: ", stateDb.rootHash, " arrived from ", c.db.getCanonicalHead().stateRoot
|
||||||
|
assert(headers[i].stateRoot == stateDb.rootHash)
|
||||||
|
|
||||||
|
|
||||||
discard c.db.persistHeaderToDb(headers[i])
|
discard c.db.persistHeaderToDb(headers[i])
|
||||||
assert(c.db.getCanonicalHead().blockHash == headers[i].blockHash)
|
assert(c.db.getCanonicalHead().blockHash == headers[i].blockHash)
|
||||||
|
|
||||||
|
|
||||||
discard
|
discard
|
||||||
|
@ -21,48 +21,77 @@ proc validate*(t: Transaction) =
|
|||||||
raise newException(ValidationError, "Insufficient gas")
|
raise newException(ValidationError, "Insufficient gas")
|
||||||
# self.check_signature_validity()
|
# self.check_signature_validity()
|
||||||
|
|
||||||
|
type
|
||||||
|
TransHashObj = object
|
||||||
|
accountNonce: AccountNonce
|
||||||
|
gasPrice: GasInt
|
||||||
|
gasLimit: GasInt
|
||||||
|
to {.rlpCustomSerialization.}: EthAddress
|
||||||
|
value: UInt256
|
||||||
|
payload: Blob
|
||||||
|
mIsContractCreation {.rlpIgnore.}: bool
|
||||||
|
|
||||||
|
proc read(rlp: var Rlp, t: var TransHashObj, _: type EthAddress): EthAddress {.inline.} =
|
||||||
|
if rlp.blobLen != 0:
|
||||||
|
result = rlp.read(EthAddress)
|
||||||
|
else:
|
||||||
|
t.mIsContractCreation = true
|
||||||
|
|
||||||
|
proc append(rlpWriter: var RlpWriter, t: TransHashObj, a: EthAddress) {.inline.} =
|
||||||
|
if t.mIsContractCreation:
|
||||||
|
rlpWriter.append("")
|
||||||
|
else:
|
||||||
|
rlpWriter.append(a)
|
||||||
|
|
||||||
func rlpEncode*(transaction: Transaction): auto =
|
func rlpEncode*(transaction: Transaction): auto =
|
||||||
# Encode transaction without signature
|
# Encode transaction without signature
|
||||||
type
|
|
||||||
TransHashObj = object
|
|
||||||
accountNonce: AccountNonce
|
|
||||||
gasPrice: GasInt
|
|
||||||
gasLimit: GasInt
|
|
||||||
to: EthAddress
|
|
||||||
value: UInt256
|
|
||||||
payload: Blob
|
|
||||||
return rlp.encode(TransHashObj(
|
return rlp.encode(TransHashObj(
|
||||||
accountNonce: transaction.accountNonce,
|
accountNonce: transaction.accountNonce,
|
||||||
gasPrice: transaction.gasPrice,
|
gasPrice: transaction.gasPrice,
|
||||||
gasLimit: transaction.gasLimit,
|
gasLimit: transaction.gasLimit,
|
||||||
to: transaction.to,
|
to: transaction.to,
|
||||||
value: transaction.value,
|
value: transaction.value,
|
||||||
payload: transaction.payload
|
payload: transaction.payload,
|
||||||
|
mIsContractCreation: transaction.isContractCreation
|
||||||
))
|
))
|
||||||
|
|
||||||
func hash*(transaction: Transaction): Hash256 =
|
func txHashNoSignature*(transaction: Transaction): Hash256 =
|
||||||
# Hash transaction without signature
|
# Hash transaction without signature
|
||||||
return keccak256.digest(transaction.rlpEncode.toOpenArray)
|
return keccak256.digest(transaction.rlpEncode.toOpenArray)
|
||||||
|
|
||||||
proc toSignature*(transaction: Transaction): Signature =
|
proc getSignature*(transaction: Transaction, output: var Signature): bool =
|
||||||
var bytes: array[65, byte]
|
var bytes: array[65, byte]
|
||||||
bytes[0..31] = transaction.R.toByteArrayBE()
|
bytes[0..31] = transaction.R.toByteArrayBE()
|
||||||
bytes[32..63] = transaction.S.toByteArrayBE()
|
bytes[32..63] = transaction.S.toByteArrayBE()
|
||||||
# TODO: V will become a byte or range soon.
|
# TODO: V will become a byte or range soon.
|
||||||
bytes[64] = transaction.V - 27 # TODO: 27 should come from somewhere
|
let v = transaction.V.int
|
||||||
initSignature(bytes)
|
var chainId: int
|
||||||
|
if v > 36:
|
||||||
|
chainId = (v - 35) div 2
|
||||||
|
elif v == 27 or v == 28:
|
||||||
|
chainId = -4
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
|
||||||
|
bytes[64] = byte(v - (chainId * 2 + 35))
|
||||||
|
result = recoverSignature(bytes, output) == EthKeysStatus.Success
|
||||||
|
|
||||||
|
proc toSignature*(transaction: Transaction): Signature =
|
||||||
|
if not getSignature(transaction, result):
|
||||||
|
raise newException(Exception, "Invalid signaure")
|
||||||
|
|
||||||
proc getSender*(transaction: Transaction, output: var EthAddress): bool =
|
proc getSender*(transaction: Transaction, output: var EthAddress): bool =
|
||||||
## Find the address the transaction was sent from.
|
## Find the address the transaction was sent from.
|
||||||
let
|
var sig: Signature
|
||||||
txHash = transaction.hash # hash without signature
|
if transaction.getSignature(sig):
|
||||||
sig = transaction.toSignature()
|
var pubKey: PublicKey
|
||||||
var pubKey: PublicKey
|
let txHash = transaction.txHashNoSignature
|
||||||
if recoverSignatureKey(sig, txHash.data, pubKey) == EthKeysStatus.Success:
|
if recoverSignatureKey(sig, txHash.data, pubKey) == EthKeysStatus.Success:
|
||||||
output = pubKey.toCanonicalAddress()
|
output = pubKey.toCanonicalAddress()
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc getSender*(transaction: Transaction): EthAddress =
|
proc getSender*(transaction: Transaction): EthAddress =
|
||||||
## Raises error on failure to recover public key
|
## Raises error on failure to recover public key
|
||||||
if not transaction.getSender(result):
|
if not transaction.getSender(result):
|
||||||
raise newException(ValidationError, "Could not derive sender address from transaction")
|
raise newException(ValidationError, "Could not derive sender address from transaction")
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user