Prototype an example fork subclass
This commit is contained in:
parent
84ccfc5966
commit
637b56fa4c
|
@ -8,3 +8,4 @@ type
|
|||
Block* = ref object of RootObj
|
||||
header*: Header
|
||||
uncles*: CountableList[Header]
|
||||
blockNumber*: Int256
|
||||
|
|
|
@ -173,14 +173,14 @@ const
|
|||
GASLIMITADJUSTMENTFACTOR = 1024
|
||||
GASLIMITMINIMUM = 5000
|
||||
# GASLIMITMAXIMUM = 2 ^ 63 - 1
|
||||
GASLIMITUSAGEADJUSTMENTNUMERATOR = 3
|
||||
GASLIMITUSAGEADJUSTMENTDENOMINATOR = 2
|
||||
DIFFICULTYADJUSTMENTDENOMINATOR = 2048
|
||||
DIFFICULTYMINIMUM = 131072
|
||||
BOMBEXPONENTIALPERIOD = 100000
|
||||
# BOMBEXPONENTIALFREEPERIODS = 2
|
||||
# BLOCKREWARD = 5 * denoms.ether
|
||||
UNCLEDEPTHPENALTYFACTOR = 8
|
||||
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3.Int256
|
||||
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2.Int256
|
||||
DIFFICULTY_ADJUSTMENT_DENOMINATOR* = 2_048.Int256
|
||||
DIFFICULTY_MINIMUM* = 131_072.Int256
|
||||
BOMB_EXPONENTIAL_PERIOD* = 100_000.Int256
|
||||
BOMB_EXPONENTIAL_FREE_PERIODS* = 2.Int256
|
||||
BLOCK_REWARD* = 5.Int256 * 2.Int256 # denoms.ether
|
||||
UNCLE_DEPTH_PENALTY_FACTOR* = 8.Int256
|
||||
MAXUNCLEDEPTH = 6
|
||||
MAXUNCLES = 2
|
||||
# SECPK1P = 2 ^ 256 - 2 ^ 32 - 977
|
||||
|
@ -192,8 +192,8 @@ const
|
|||
SECPK1G = (SECPK1Gx, SECPK1Gy)
|
||||
EMPTYUNCLEHASH = cstring"\x1d\xccM\xe8\xde\xc7]z\xab\x85\xb5g\xb6\xcc\xd4\x1a\xd3\x12E\x1b\x94\x8at\x13\xf0\xa1B\xfd@\xd4\x93G"
|
||||
GENESIS_BLOCK_NUMBER* = 0.Int256
|
||||
GENESIS_DIFFICULTY* = 131072.Int256
|
||||
GENESIS_GAS_LIMIT* = 3141592.Int256
|
||||
GENESIS_DIFFICULTY* = 131_072.Int256
|
||||
GENESIS_GAS_LIMIT* = 3_141_592.Int256
|
||||
GENESIS_PARENT_HASH* = ZERO_HASH32
|
||||
GENESIS_COINBASE* = ZERO_ADDRESS
|
||||
GENESIS_NONCE* = cstring"\x00\x00\x00\x00\x00\x00\x00B"
|
||||
|
|
|
@ -30,3 +30,7 @@ proc validate*(t: BaseTransaction) =
|
|||
if t.intrinsic_gas() > t.gas:
|
||||
raise newException(ValidationError, "Insufficient gas")
|
||||
# self.check_signature_validity()
|
||||
|
||||
proc sender*(t: BaseTransaction): cstring =
|
||||
# TODO
|
||||
cstring""
|
|
@ -1,5 +1,10 @@
|
|||
import ../constants
|
||||
|
||||
type
|
||||
Header* = ref object
|
||||
timestamp*: int
|
||||
difficulty*: Int256
|
||||
blockNumber*: Int256
|
||||
# TODO
|
||||
|
||||
proc generateHeaderFromParentHeader*(
|
||||
|
@ -35,3 +40,15 @@ proc generateHeaderFromParentHeader*(
|
|||
# )
|
||||
|
||||
# return header
|
||||
|
||||
proc computeGasLimit*(header: Header, gasLimitFloor: Int256): Int256 =
|
||||
# TODO
|
||||
gasLimitFloor
|
||||
|
||||
proc gasUsed*(header: Header): Int256 =
|
||||
# TODO
|
||||
0.Int256
|
||||
|
||||
proc gasLimit*(header: Header): Int256 =
|
||||
# TODO
|
||||
0.Int256
|
||||
|
|
|
@ -13,6 +13,16 @@ proc validateCanonicalAddress*(value: cstring, title: string = "Value") =
|
|||
|
||||
|
||||
proc validateGte*(value: Int256, minimum: int, title: string = "Value") =
|
||||
if value <= minimum.Int256:
|
||||
raise newException(ValidationError,
|
||||
fmt"{title} {value} is not greater than or equal to {minimum}")
|
||||
|
||||
proc validateGt*(value: Int256, minimum: int, title: string = "Value") =
|
||||
if value < minimum.Int256:
|
||||
raise newException(ValidationError,
|
||||
fmt"{title} {value} is not greater than or equal to {minimum}")
|
||||
|
||||
proc validateGt*(value: int, minimum: int, title: string = "Value") =
|
||||
if value < minimum:
|
||||
raise newException(ValidationError,
|
||||
fmt"{title} {value} is not greater than or equal to {minimum}")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import
|
||||
../logging, ../constants, ../errors, ../transaction, ../computation, "../block", ../vm_state, ../db/chain, ../utils/db, ../utils/header
|
||||
../logging, ../constants, ../errors, ../transaction, ../computation, "../block", ../vm_state, ../vm_state_transactions, ../db/chain, ../utils/header
|
||||
|
||||
type
|
||||
VM* = ref object of RootObj
|
||||
|
@ -11,38 +11,26 @@ type
|
|||
chainDB*: BaseChainDB
|
||||
isStateless*: bool
|
||||
state*: BaseVMState
|
||||
`block`*: Block
|
||||
|
||||
proc newVM*(header: Header, chainDB: BaseChainDB): VM =
|
||||
new(result)
|
||||
result.chainDB = chainDB
|
||||
|
||||
method name*(vm: VM): string =
|
||||
"VM"
|
||||
|
||||
method addTransaction*(vm: var VM, transaction: BaseTransaction, computation: BaseComputation): Block =
|
||||
# Add a transaction to the given block and save the block data into chaindb
|
||||
var receipt = self.state.makeReceipt(transaction, computation)
|
||||
var transactionIdx = len(vm.`block`.transactions)
|
||||
# var receipt = vm.state.makeReceipt(transaction, computation)
|
||||
# var transactionIdx = len(vm.`block`.transactions)
|
||||
# TODO
|
||||
return Block()
|
||||
# var indexKey = rlp.encode(transaction_idx, sedes=rlp.sedes.big_endian_int)
|
||||
|
||||
# self.block.transactions.append(transaction)
|
||||
|
||||
# tx_root_hash = self.chaindb.add_transaction(self.block.header, index_key, transaction)
|
||||
# receipt_root_hash = self.chaindb.add_receipt(self.block.header, index_key, receipt)
|
||||
|
||||
# self.block.bloom_filter |= receipt.bloom
|
||||
|
||||
# self.block.header.transaction_root = tx_root_hash
|
||||
# self.block.header.receipt_root = receipt_root_hash
|
||||
# self.block.header.bloom = int(self.block.bloom_filter)
|
||||
# self.block.header.gas_used = receipt.gas_used
|
||||
|
||||
# return self.block
|
||||
|
||||
method applyTransaction*(vm: var VM, transaction: BaseTransaction): (BaseComputation, Block) =
|
||||
# Apply the transaction to the vm in the current block
|
||||
if vm.isStateless:
|
||||
var (computation, b, trieData) = vm.state.applyTransaction(
|
||||
vm.state,
|
||||
transaction,
|
||||
vm.`block`,
|
||||
isStateless=true)
|
||||
|
@ -51,11 +39,10 @@ method applyTransaction*(vm: var VM, transaction: BaseTransaction): (BaseComputa
|
|||
# Persist changed transaction and receipt key-values to self.chaindb.
|
||||
else:
|
||||
var (computation, _, _) = vm.state.applyTransaction(
|
||||
vm.state,
|
||||
transaction,
|
||||
vm.`block`,
|
||||
isStateless=false)
|
||||
vm.addTransaction(transaction, computation)
|
||||
discard vm.addTransaction(transaction, computation)
|
||||
|
||||
result = (computation, vm.`block`)
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import
|
||||
logging, constants, errors, transaction, "block", utils/header
|
||||
|
||||
type
|
||||
FrontierBlock* = object of Block
|
||||
# bloomFilter*: BloomFilter
|
||||
# header*: BlockHeader
|
||||
transactions*: seq[BaseTransaction]
|
||||
# transactionClass*: Any
|
||||
stateRoot*: cstring
|
||||
# fields*: seq[(string, Function)]
|
||||
# cachedRlp: cstring
|
||||
# uncles*: void
|
||||
|
||||
# import
|
||||
# rlp, rlp.sedes, eth_bloom, evm.constants, evm.rlp.receipts, evm.rlp.blocks,
|
||||
# evm.rlp.headers, evm.utils.keccak, transactions
|
||||
|
||||
# method makeFrontierBlock*(header: auto; transactions: auto; uncles: void): auto =
|
||||
# if transactions is None:
|
||||
# transactions = @[]
|
||||
# if uncles is None:
|
||||
# uncles = @[]
|
||||
# result.bloomFilter = BloomFilter(header.bloom)
|
||||
# super(FrontierBlock, result).__init__()
|
||||
|
||||
# method number*(self: FrontierBlock): int =
|
||||
# return self.header.blockNumber
|
||||
|
||||
# method hash*(self: FrontierBlock): cstring =
|
||||
# return self.header.hash
|
||||
|
||||
# method getTransactionClass*(cls: typedesc): typedesc =
|
||||
# return cls.transactionClass
|
||||
|
||||
# method getReceipts*(self: FrontierBlock; chaindb: BaseChainDB): seq[Receipt] =
|
||||
# return chaindb.getReceipts(self.header, Receipt)
|
||||
|
||||
# method fromHeader*(cls: typedesc; header: BlockHeader; chaindb: BaseChainDB): FrontierBlock =
|
||||
# ## Returns the block denoted by the given block header.
|
||||
# if header.unclesHash == EMPTYUNCLEHASH:
|
||||
# var uncles = @[]
|
||||
# else:
|
||||
# uncles = chaindb.getBlockUncles(header.unclesHash)
|
||||
# var transactions = chaindb.getBlockTransactions(header, cls.getTransactionClass())
|
||||
# return cls()
|
||||
|
||||
# proc makeFrontierBlock*(): FrontierBlock =
|
||||
# result.transactionClass = FrontierTransaction
|
||||
# result.fields = @[("header", BlockHeader),
|
||||
# ("transactions", CountableList(transactionClass)),
|
||||
# ("uncles", CountableList(BlockHeader))]
|
||||
# result.bloomFilter = nil
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import
|
||||
logging, constants, errors, validation, utils/header, vm / forks / frontier / vm
|
||||
|
||||
method computeDifficulty*(parentHeader: Header, timestamp: int): Int256 =
|
||||
validateGt(timestamp, parentHeader.timestamp, title="Header timestamp")
|
||||
let offset = parentHeader.difficulty div DIFFICULTY_ADJUSTMENT_DENOMINATOR
|
||||
# We set the minimum to the lowest of the protocol minimum and the parent
|
||||
# minimum to allow for the initial frontier *warming* period during which
|
||||
# the difficulty begins lower than the protocol minimum
|
||||
let difficultyMinimum = min(parentHeader.difficulty, DIFFICULTY_MINIMUM)
|
||||
# let test = (timestamp - parentHeader.timestamp).Int256 < FRONTIER_DIFFICULTY_ADJUSTMENT_CUTOFF
|
||||
# let baseDifficulty = max(parent.Header.difficulty + (if test: offset else: -offset), difficultyMinimum)
|
||||
# # Adjust for difficulty bomb
|
||||
# let numBombPeriods = ((parentHeader.blockNumber + 1) div BOMB_EXPONENTIAL_PERIOD) - BOMB_EXPONENTIAL_FREE_PERIODS
|
||||
# result = if numBombPeriods >= 0: max(baseDifficulty + 2.Int256 ^ numBombPeriods, DIFFICULTY_MINIMUM) else: baseDifficulty
|
||||
result = 0.Int256
|
||||
|
||||
method createHeaderFromParent*(parentHeader: Header): Header =
|
||||
# TODO
|
||||
result = Header()
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import
|
||||
strformat,
|
||||
constants, errors, vm_state, transaction, utils/header
|
||||
|
||||
proc validateFrontierTransaction*(vmState: BaseVmState, transaction: BaseTransaction) =
|
||||
let gasCost = transaction.gas * transaction.gasPrice
|
||||
var senderBalance: Int256
|
||||
# inDB(vmState.stateDB(readOnly=true):
|
||||
# senderBalance = db.getBalance(transaction.sender)
|
||||
senderBalance = gasCost # TODO
|
||||
if senderBalance < gasCost:
|
||||
raise newException(ValidationError, %"Sender account balance cannot afford txn gas: {transaction.sender}")
|
||||
|
||||
let totalCost = transaction.value + gasCost
|
||||
|
||||
if senderBalance < totalCost:
|
||||
raise newException(ValidationError, "Sender account balance cannot afford txn")
|
||||
|
||||
if vmState.blockHeader.gasUsed + transaction.gas > vmState.blockHeader.gasLimit:
|
||||
raise newException(ValidationError, "Transaction exceeds gas limit")
|
||||
|
||||
# inDB(vmState.stateDb(readOnly=true):
|
||||
# if db.getNonce(transaction.sender) != transaction.nonce:
|
||||
# raise newException(ValidationError, "Invalid transaction nonce")
|
|
@ -0,0 +1,155 @@
|
|||
import
|
||||
logging, constants, errors, vm_state, utils/header, db/chain
|
||||
|
||||
type
|
||||
FrontierVMState* = object of BaseVMState
|
||||
# receipts*:
|
||||
# computationClass*: Any
|
||||
# accessLogs*: AccessLogs
|
||||
|
||||
# import
|
||||
# py2nim_helpers, __future__, rlp, evm, evm.constants, evm.exceptions, evm.rlp.logs,
|
||||
# evm.rlp.receipts, evm.vm.message, evm.vm_state, evm.utils.address,
|
||||
# evm.utils.hexadecimal, evm.utils.keccak, evm.validation, computation, constants,
|
||||
# validation
|
||||
|
||||
# type
|
||||
# FrontierVMState* = object of Function
|
||||
# prevHeaders*: seq[BlockHeader]
|
||||
# receipts*: void
|
||||
# computationClass*: Any
|
||||
# _chaindb*: BaseChainDB
|
||||
# accessLogs*: AccessLogs
|
||||
# blockHeader*: BlockHeader
|
||||
|
||||
# proc _executeFrontierTransaction*(vmState: FrontierVMState;
|
||||
# transaction: FrontierTransaction): FrontierComputation =
|
||||
# transaction.validate()
|
||||
# validateFrontierTransaction(vmState, transaction)
|
||||
# var gasFee = transaction.gas * transaction.gasPrice
|
||||
# with vmState.stateDb(),
|
||||
# stateDb.deltaBalance(transaction.sender, -1 * gasFee)
|
||||
# stateDb.incrementNonce(transaction.sender)
|
||||
# var messageGas = transaction.gas - transaction.intrinsicGas
|
||||
# if transaction.to == constants.CREATECONTRACTADDRESS:
|
||||
# var
|
||||
# contractAddress = generateContractAddress(transaction.sender,
|
||||
# stateDb.getNonce(transaction.sender) - 1)
|
||||
# data = cstring""
|
||||
# code = transaction.data
|
||||
# else:
|
||||
# contractAddress = None
|
||||
# data = transaction.data
|
||||
# code = stateDb.getCode(transaction.to)
|
||||
# vmState.logger.info("TRANSACTION: sender: %s | to: %s | value: %s | gas: %s | gas-price: %s | s: %s | r: %s | v: %s | data-hash: %s",
|
||||
# encodeHex(transaction.sender), encodeHex(transaction.to),
|
||||
# transaction.value, transaction.gas, transaction.gasPrice,
|
||||
# transaction.s, transaction.r, transaction.v,
|
||||
# encodeHex(keccak(transaction.data)))
|
||||
# var message = Message()
|
||||
# if message.isCreate:
|
||||
# with vmState.stateDb(),
|
||||
# var isCollision = stateDb.accountHasCodeOrNonce(contractAddress)
|
||||
# if isCollision:
|
||||
# var computation = vmState.getComputation(message)
|
||||
# computation._error = ContractCreationCollision("Address collision while creating contract: {0}".format(
|
||||
# encodeHex(contractAddress)))
|
||||
# vmState.logger.debug("Address collision while creating contract: %s",
|
||||
# encodeHex(contractAddress))
|
||||
# else:
|
||||
# computation = vmState.getComputation(message).applyCreateMessage()
|
||||
# else:
|
||||
# computation = vmState.getComputation(message).applyMessage()
|
||||
# var numDeletions = len(computation.getAccountsForDeletion())
|
||||
# if numDeletions:
|
||||
# computation.gasMeter.refundGas(REFUNDSELFDESTRUCT * numDeletions)
|
||||
# var
|
||||
# gasRemaining = computation.getGasRemaining()
|
||||
# gasRefunded = computation.getGasRefund()
|
||||
# gasUsed = transaction.gas - gasRemaining
|
||||
# gasRefund = min(gasRefunded, gasUsed div 2)
|
||||
# gasRefundAmount = gasRefund + gasRemaining * transaction.gasPrice
|
||||
# if gasRefundAmount:
|
||||
# vmState.logger.debug("TRANSACTION REFUND: %s -> %s", gasRefundAmount,
|
||||
# encodeHex(message.sender))
|
||||
# with vmState.stateDb(),
|
||||
# stateDb.deltaBalance(message.sender, gasRefundAmount)
|
||||
# var transactionFee = transaction.gas - gasRemaining - gasRefund *
|
||||
# transaction.gasPrice
|
||||
# vmState.logger.debug("TRANSACTION FEE: %s -> %s", transactionFee,
|
||||
# encodeHex(vmState.coinbase))
|
||||
# with vmState.stateDb(),
|
||||
# stateDb.deltaBalance(vmState.coinbase, transactionFee)
|
||||
# with vmState.stateDb(),
|
||||
# for account, beneficiary in computation.getAccountsForDeletion():
|
||||
# vmState.logger.debug("DELETING ACCOUNT: %s", encodeHex(account))
|
||||
# stateDb.setBalance(account, 0)
|
||||
# stateDb.deleteAccount(account)
|
||||
# return computation
|
||||
|
||||
# proc _makeFrontierReceipt*(vmState: FrontierVMState;
|
||||
# transaction: FrontierTransaction;
|
||||
# computation: FrontierComputation): Receipt =
|
||||
# var
|
||||
# logs = ## py2nim can't generate code for
|
||||
# ## Log(address, topics, data)
|
||||
# gasRemaining = computation.getGasRemaining()
|
||||
# gasRefund = computation.getGasRefund()
|
||||
# txGasUsed = transaction.gas - gasRemaining -
|
||||
# min(gasRefund, transaction.gas - gasRemaining div 2)
|
||||
# gasUsed = vmState.blockHeader.gasUsed + txGasUsed
|
||||
# receipt = Receipt()
|
||||
# return receipt
|
||||
|
||||
# method executeTransaction*(self: FrontierVMState; transaction: FrontierTransaction): (
|
||||
# , ) =
|
||||
# var computation = _executeFrontierTransaction(self, transaction)
|
||||
# return (computation, self.blockHeader)
|
||||
|
||||
# method makeReceipt*(self: FrontierVMState; transaction: FrontierTransaction;
|
||||
# computation: FrontierComputation): Receipt =
|
||||
# var receipt = _makeFrontierReceipt(self, transaction, computation)
|
||||
# return receipt
|
||||
|
||||
# method validateBlock*(self: FrontierVMState; block: FrontierBlock): void =
|
||||
# if notblock.isGenesis:
|
||||
# var parentHeader = self.parentHeader
|
||||
# self._validateGasLimit(block)
|
||||
# validateLengthLte(block.header.extraData, 32)
|
||||
# if block.header.timestamp < parentHeader.timestamp:
|
||||
# raise newException(ValidationError, "`timestamp` is before the parent block\'s timestamp.\\n- block : {0}\\n- parent : {1}. ".format(
|
||||
# block.header.timestamp, parentHeader.timestamp))
|
||||
# elif block.header.timestamp == parentHeader.timestamp:
|
||||
# raise ValidationError("`timestamp` is equal to the parent block\'s timestamp\\n- block : {0}\\n- parent: {1}. ".format(
|
||||
# block.header.timestamp, parentHeader.timestamp))
|
||||
# if len(block.uncles) > MAXUNCLES:
|
||||
# raise newException(ValidationError, "Blocks may have a maximum of {0} uncles. Found {1}.".format(
|
||||
# MAXUNCLES, len(block.uncles)))
|
||||
# for uncle in block.uncles:
|
||||
# self.validateUncle(block, uncle)
|
||||
# if notself.isKeyExists(block.header.stateRoot):
|
||||
# raise newException(ValidationError, "`state_root` was not found in the db.\\n- state_root: {0}".format(
|
||||
# block.header.stateRoot))
|
||||
# var localUncleHash = keccak(rlp.encode(block.uncles))
|
||||
# if localUncleHash != block.header.unclesHash:
|
||||
# raise newException(ValidationError, "`uncles_hash` and block `uncles` do not match.\\n - num_uncles : {0}\\n - block uncle_hash : {1}\\n - header uncle_hash: {2}".format(
|
||||
# len(block.uncles), localUncleHash, block.header.uncleHash))
|
||||
|
||||
# method _validateGasLimit*(self: FrontierVMState; block: FrontierBlock): void =
|
||||
# var gasLimit = block.header.gasLimit
|
||||
# if gasLimit < GASLIMITMINIMUM:
|
||||
# raise newException(ValidationError, "Gas limit {0} is below minimum {1}".format(
|
||||
# gasLimit, GASLIMITMINIMUM))
|
||||
# if gasLimit > GASLIMITMAXIMUM:
|
||||
# raise newException(ValidationError, "Gas limit {0} is above maximum {1}".format(
|
||||
# gasLimit, GASLIMITMAXIMUM))
|
||||
# var
|
||||
# parentGasLimit = self.parentHeader.gasLimit
|
||||
# diff = gasLimit - parentGasLimit
|
||||
# if diff > parentGasLimit // GASLIMITADJUSTMENTFACTOR:
|
||||
# raise newException(ValidationError, "Gas limit {0} difference to parent {1} is too big {2}".format(
|
||||
# gasLimit, parentGasLimit, diff))
|
||||
|
||||
# proc makeFrontierVMState*(): FrontierVMState =
|
||||
# result.computationClass = FrontierComputation
|
||||
|
|
@ -0,0 +1 @@
|
|||
p:"../../../"
|
|
@ -0,0 +1,25 @@
|
|||
import
|
||||
logging, constants, errors, "block", vm / [base, stack], db / chain, utils / header,
|
||||
frontier_block, frontier_vm_state, frontier_validation
|
||||
|
||||
|
||||
type
|
||||
FrontierVM* = ref object of VM
|
||||
|
||||
method name*(vm: FrontierVM): string =
|
||||
"FrontierVM"
|
||||
|
||||
method getBlockReward(vm: FrontierVM): Int256 =
|
||||
BLOCK_REWARD
|
||||
|
||||
method getetUncleReward(vm: FrontierVM, blockNumber: Int256, uncle: Block): Int256 =
|
||||
BLOCK_REWARD * (UNCLE_DEPTH_PENALTY_FACTOR + uncle.blockNumber - blockNumber) div UNCLE_DEPTH_PENALTY_FACTOR
|
||||
|
||||
|
||||
method getNephewReward(vm: FrontierVM): Int256 =
|
||||
vm.getBlockReward() div 32
|
||||
|
||||
proc newFrontierVM*(header: Header, chainDB: BaseChainDB): FrontierVM =
|
||||
new(result)
|
||||
result.chainDB = chainDB
|
||||
result.isStateless = true
|
Loading…
Reference in New Issue