mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-26 20:19:31 +00:00
Merge branch 'master' of github.com:status-im/nimbus
This commit is contained in:
commit
8ad1eac41d
@ -208,6 +208,10 @@ proc isDeadAccount*(db: AccountStateDB, address: EthAddress): bool =
|
||||
else:
|
||||
result = true
|
||||
|
||||
proc getCommittedStorage*(db: AccountStateDB, address: EthAddress, slot: UInt256): UInt256 =
|
||||
discard
|
||||
# TODO: stub
|
||||
|
||||
proc rootHash*(db: ReadOnlyStateDB): KeccakHash {.borrow.}
|
||||
proc getAccount*(db: ReadOnlyStateDB, address: EthAddress): Account {.borrow.}
|
||||
proc getCodeHash*(db: ReadOnlyStateDB, address: EthAddress): Hash256 {.borrow.}
|
||||
@ -220,3 +224,4 @@ proc hasCodeOrNonce*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
|
||||
proc accountExists*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
|
||||
proc isDeadAccount*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
|
||||
proc isEmptyAccount*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
|
||||
proc getCommittedStorage*(db: ReadOnlyStateDB, address: EthAddress, slot: UInt256): UInt256 {.borrow.}
|
||||
|
@ -113,7 +113,8 @@ const
|
||||
eth5, # FkTangerine
|
||||
eth5, # FkSpurious
|
||||
eth3, # FkByzantium
|
||||
eth2 # FkConstantinople
|
||||
eth2, # FkConstantinople
|
||||
eth2 # FkIstanbul
|
||||
]
|
||||
|
||||
proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, vmState: BaseVMState): ValidationResult =
|
||||
|
@ -12,19 +12,19 @@ import
|
||||
import eth/common/transaction as common_transaction
|
||||
export common_transaction
|
||||
|
||||
func intrinsicGas*(data: openarray[byte]): GasInt =
|
||||
result = 21_000 # GasTransaction
|
||||
func intrinsicGas*(data: openarray[byte], fork: Fork): GasInt =
|
||||
result = gasFees[fork][GasTransaction]
|
||||
for i in data:
|
||||
if i == 0:
|
||||
result += 4 # GasTXDataZero
|
||||
result += gasFees[fork][GasTXDataZero]
|
||||
else:
|
||||
result += 68 # GasTXDataNonZero
|
||||
result += gasFees[fork][GasTXDataNonZero]
|
||||
|
||||
proc intrinsicGas*(tx: Transaction, fork: Fork): GasInt =
|
||||
# Compute the baseline gas cost for this transaction. This is the amount
|
||||
# of gas needed to send this transaction (but that is not actually used
|
||||
# for computation)
|
||||
result = tx.payload.intrinsicGas
|
||||
result = tx.payload.intrinsicGas(fork)
|
||||
|
||||
if tx.isContractCreation:
|
||||
result = result + gasFees[fork][GasTXCreate]
|
||||
|
141
nimbus/vm/blake2b_f.nim
Normal file
141
nimbus/vm/blake2b_f.nim
Normal file
@ -0,0 +1,141 @@
|
||||
import nimcrypto/utils
|
||||
|
||||
# Blake2 `F` compression function
|
||||
# taken from nimcrypto with modification
|
||||
|
||||
# in nimcrypto, blake2 compression function `F`
|
||||
# is hardcoded for blake2b and blake2s
|
||||
# we need a generic `F` function with
|
||||
# `rounds` parameter
|
||||
|
||||
type
|
||||
Blake2bContext = object
|
||||
h: array[8, uint64]
|
||||
t: array[2, uint64]
|
||||
|
||||
const Sigma = [
|
||||
[0'u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
[14'u8, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
|
||||
[11'u8, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
|
||||
[7'u8, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
|
||||
[9'u8, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
|
||||
[2'u8, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
|
||||
[12'u8, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
|
||||
[13'u8, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
|
||||
[6'u8, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
|
||||
[10'u8, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
|
||||
[0'u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
[14'u8, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]
|
||||
]
|
||||
|
||||
const B2BIV = [
|
||||
0x6A09E667F3BCC908'u64, 0xBB67AE8584CAA73B'u64,
|
||||
0x3C6EF372FE94F82B'u64, 0xA54FF53A5F1D36F1'u64,
|
||||
0x510E527FADE682D1'u64, 0x9B05688C2B3E6C1F'u64,
|
||||
0x1F83D9ABFB41BD6B'u64, 0x5BE0CD19137E2179'u64
|
||||
]
|
||||
|
||||
template B2B_G(v, a, b, c, d, x, y: untyped) =
|
||||
v[a] = v[a] + v[b] + x
|
||||
v[d] = ROR(v[d] xor v[a], 32)
|
||||
v[c] = v[c] + v[d]
|
||||
v[b] = ROR(v[b] xor v[c], 24)
|
||||
v[a] = v[a] + v[b] + y
|
||||
v[d] = ROR(v[d] xor v[a], 16)
|
||||
v[c] = v[c] + v[d]
|
||||
v[b] = ROR(v[b] xor v[c], 63)
|
||||
|
||||
template B2BROUND(v, m, n: untyped) =
|
||||
B2B_G(v, 0, 4, 8, 12, m[Sigma[n][ 0]], m[Sigma[n][ 1]])
|
||||
B2B_G(v, 1, 5, 9, 13, m[Sigma[n][ 2]], m[Sigma[n][ 3]])
|
||||
B2B_G(v, 2, 6, 10, 14, m[Sigma[n][ 4]], m[Sigma[n][ 5]])
|
||||
B2B_G(v, 3, 7, 11, 15, m[Sigma[n][ 6]], m[Sigma[n][ 7]])
|
||||
B2B_G(v, 0, 5, 10, 15, m[Sigma[n][ 8]], m[Sigma[n][ 9]])
|
||||
B2B_G(v, 1, 6, 11, 12, m[Sigma[n][10]], m[Sigma[n][11]])
|
||||
B2B_G(v, 2, 7, 8, 13, m[Sigma[n][12]], m[Sigma[n][13]])
|
||||
B2B_G(v, 3, 4, 9, 14, m[Sigma[n][14]], m[Sigma[n][15]])
|
||||
|
||||
proc blake2Transform(ctx: var Blake2bContext, input: openArray[byte], last: bool, rounds: uint32) {.inline.} =
|
||||
var v: array[16, uint64]
|
||||
var m: array[16, uint64]
|
||||
|
||||
v[0] = ctx.h[0]; v[1] = ctx.h[1]
|
||||
v[2] = ctx.h[2]; v[3] = ctx.h[3]
|
||||
v[4] = ctx.h[4]; v[5] = ctx.h[5]
|
||||
v[6] = ctx.h[6]; v[7] = ctx.h[7]
|
||||
v[8] = B2BIV[0]; v[9] = B2BIV[1]
|
||||
v[10] = B2BIV[2]; v[11] = B2BIV[3]
|
||||
v[12] = B2BIV[4]; v[13] = B2BIV[5]
|
||||
v[14] = B2BIV[6]; v[15] = B2BIV[7]
|
||||
|
||||
v[12] = v[12] xor ctx.t[0]
|
||||
v[13] = v[13] xor ctx.t[1]
|
||||
if last:
|
||||
v[14] = not(v[14])
|
||||
|
||||
m[0] = leLoad64(input, 0); m[1] = leLoad64(input, 8)
|
||||
m[2] = leLoad64(input, 16); m[3] = leLoad64(input, 24)
|
||||
m[4] = leLoad64(input, 32); m[5] = leLoad64(input, 40)
|
||||
m[6] = leLoad64(input, 48); m[7] = leLoad64(input, 56)
|
||||
m[8] = leLoad64(input, 64); m[9] = leLoad64(input, 72)
|
||||
m[10] = leLoad64(input, 80); m[11] = leLoad64(input, 88)
|
||||
m[12] = leLoad64(input, 96); m[13] = leLoad64(input, 104)
|
||||
m[14] = leLoad64(input, 112); m[15] = leLoad64(input, 120)
|
||||
|
||||
for i in 0..<rounds:
|
||||
B2BROUND(v, m, i mod 10)
|
||||
|
||||
ctx.h[0] = ctx.h[0] xor (v[0] xor v[0 + 8])
|
||||
ctx.h[1] = ctx.h[1] xor (v[1] xor v[1 + 8])
|
||||
ctx.h[2] = ctx.h[2] xor (v[2] xor v[2 + 8])
|
||||
ctx.h[3] = ctx.h[3] xor (v[3] xor v[3 + 8])
|
||||
ctx.h[4] = ctx.h[4] xor (v[4] xor v[4 + 8])
|
||||
ctx.h[5] = ctx.h[5] xor (v[5] xor v[5 + 8])
|
||||
ctx.h[6] = ctx.h[6] xor (v[6] xor v[6 + 8])
|
||||
ctx.h[7] = ctx.h[7] xor (v[7] xor v[7 + 8])
|
||||
|
||||
const
|
||||
blake2FInputLength* = 213
|
||||
blake2FFinalBlockBytes = byte(1)
|
||||
blake2FNonFinalBlockBytes = byte(0)
|
||||
|
||||
# input should exactly 213 bytes
|
||||
# output needs to accomodate 64 bytes
|
||||
proc blake2b_F*(input: openArray[byte], output: var openArray[byte]): bool =
|
||||
# Make sure the input is valid (correct length and final flag)
|
||||
if input.len != blake2FInputLength:
|
||||
return false
|
||||
|
||||
if input[212] notin {blake2FNonFinalBlockBytes, blake2FFinalBlockBytes}:
|
||||
return false
|
||||
|
||||
# Parse the input into the Blake2b call parameters
|
||||
var
|
||||
rounds = beLoad32(input, 0)
|
||||
final = (input[212] == blake2FFinalBlockBytes)
|
||||
ctx: Blake2bContext
|
||||
|
||||
ctx.h[0] = leLoad64(input, 4+0)
|
||||
ctx.h[1] = leLoad64(input, 4+8)
|
||||
ctx.h[2] = leLoad64(input, 4+16)
|
||||
ctx.h[3] = leLoad64(input, 4+24)
|
||||
ctx.h[4] = leLoad64(input, 4+32)
|
||||
ctx.h[5] = leLoad64(input, 4+40)
|
||||
ctx.h[6] = leLoad64(input, 4+48)
|
||||
ctx.h[7] = leLoad64(input, 4+56)
|
||||
|
||||
ctx.t[0] = leLoad64(input, 196)
|
||||
ctx.t[1] = leLoad64(input, 204)
|
||||
|
||||
# Execute the compression function, extract and return the result
|
||||
blake2Transform(ctx, input.toOpenArray(68, 195), final, rounds)
|
||||
|
||||
leStore64(output, 0, ctx.h[0])
|
||||
leStore64(output, 8, ctx.h[1])
|
||||
leStore64(output, 16, ctx.h[2])
|
||||
leStore64(output, 24, ctx.h[3])
|
||||
leStore64(output, 32, ctx.h[4])
|
||||
leStore64(output, 40, ctx.h[5])
|
||||
leStore64(output, 48, ctx.h[6])
|
||||
leStore64(output, 56, ctx.h[7])
|
||||
result = true
|
@ -65,6 +65,8 @@ type
|
||||
case kind*: Op
|
||||
of Sstore:
|
||||
s_isStorageEmpty*: bool
|
||||
s_currentValue*: Uint256
|
||||
s_originalValue*: Uint256
|
||||
of Call, CallCode, DelegateCall, StaticCall:
|
||||
c_isNewAccount*: bool
|
||||
c_gasBalance*: GasInt
|
||||
@ -208,20 +210,73 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
||||
func `prefix gasSstore`(value: Uint256, gasParams: Gasparams): GasResult {.nimcall.} =
|
||||
## Value is word to save
|
||||
|
||||
# workaround for static evaluation not working for if expression
|
||||
const
|
||||
gSet = FeeSchedule[GasSset]
|
||||
gSreset = FeeSchedule[GasSreset]
|
||||
when fork < FkIstanbul:
|
||||
# workaround for static evaluation not working for if expression
|
||||
const
|
||||
gSet = FeeSchedule[GasSset]
|
||||
gSreset = FeeSchedule[GasSreset]
|
||||
|
||||
# Gas cost - literal translation of Yellow Paper
|
||||
result.gasCost = if value.isZero.not and gasParams.s_isStorageEmpty:
|
||||
gSet
|
||||
else:
|
||||
gSreset
|
||||
# Gas cost - literal translation of Yellow Paper
|
||||
result.gasCost = if value.isZero.not and gasParams.s_isStorageEmpty:
|
||||
gSet
|
||||
else:
|
||||
gSreset
|
||||
|
||||
# Refund
|
||||
if value.isZero and not gasParams.s_isStorageEmpty:
|
||||
result.gasRefund = static(FeeSchedule[RefundSclear])
|
||||
# Refund
|
||||
if value.isZero and not gasParams.s_isStorageEmpty:
|
||||
result.gasRefund = static(FeeSchedule[RefundSclear])
|
||||
else:
|
||||
# 0. If *gasleft* is less than or equal to 2300, fail the current call.
|
||||
# 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted.
|
||||
# 2. If current value does not equal new value:
|
||||
# 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
|
||||
# 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted.
|
||||
# 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter.
|
||||
# 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses:
|
||||
# 2.2.1. If original value is not 0:
|
||||
# 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0.
|
||||
# 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter.
|
||||
# 2.2.2. If original value equals new value (this storage slot is reset):
|
||||
# 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter.
|
||||
# 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter.
|
||||
const
|
||||
NoopGasEIP2200 = FeeSchedule[GasSload] # if the value doesn't change.
|
||||
DirtyGasEIP2200 = FeeSchedule[GasSload] # if a dirty value is changed.
|
||||
InitGasEIP2200 = FeeSchedule[GasSset] # from clean zero to non-zero
|
||||
InitRefundEIP2200 = FeeSchedule[GasSset] - FeeSchedule[GasSload] # resetting to the original zero value
|
||||
CleanGasEIP2200 = FeeSchedule[GasSreset]# from clean non-zero to something else
|
||||
CleanRefundEIP2200 = FeeSchedule[GasSreset] - FeeSchedule[GasSload] # resetting to the original non-zero value
|
||||
ClearRefundEIP2200 = FeeSchedule[RefundSclear]# clearing an originally existing storage slot
|
||||
|
||||
# Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
if gasParams.s_currentValue == value: # noop (1)
|
||||
result.gasCost = NoopGasEIP2200
|
||||
return
|
||||
|
||||
if gasParams.s_originalValue == gasParams.s_currentValue:
|
||||
if gasParams.s_originalValue.isZero: # create slot (2.1.1)
|
||||
result.gasCost = InitGasEIP2200
|
||||
return
|
||||
|
||||
if value.isZero: # delete slot (2.1.2b)
|
||||
result.gasRefund = ClearRefundEIP2200
|
||||
|
||||
result.gasCost = CleanGasEIP2200 # write existing slot (2.1.2)
|
||||
return
|
||||
|
||||
if not gasParams.s_originalValue.isZero:
|
||||
if gasParams.s_currentValue.isZero: # recreate slot (2.2.1.1)
|
||||
result.gasRefund -= ClearRefundEIP2200
|
||||
if value.isZero: # delete slot (2.2.1.2)
|
||||
result.gasRefund += ClearRefundEIP2200
|
||||
|
||||
if gasParams.s_originalValue == value:
|
||||
if gasParams.s_originalValue.isZero: # reset to original inexistent slot (2.2.2.1)
|
||||
result.gasRefund = InitRefundEIP2200
|
||||
else: # reset to original existing slot (2.2.2.2)
|
||||
result.gasRefund = CleanRefundEIP2200
|
||||
|
||||
result.gasCost = DirtyGasEIP2200 # dirty update (2.2)
|
||||
|
||||
func `prefix gasLog0`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} =
|
||||
result = `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength)
|
||||
@ -436,6 +491,8 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
||||
Number: fixed GasBase,
|
||||
Difficulty: fixed GasBase,
|
||||
GasLimit: fixed GasBase,
|
||||
ChainID: fixed GasBase,
|
||||
SelfBalance: fixed GasLow,
|
||||
|
||||
# 50s: Stack, Memory, Storage and Flow Operations
|
||||
Pop: fixed GasBase,
|
||||
@ -603,10 +660,18 @@ func spuriousGasFees(previous_fees: GasFeeSchedule): GasFeeSchedule =
|
||||
result = previous_fees
|
||||
result[GasExpByte] = 50
|
||||
|
||||
func istanbulGasFees(previous_fees: GasFeeSchedule): GasFeeSchedule =
|
||||
# https://eips.ethereum.org/EIPS/eip-1884
|
||||
result[GasSload] = 800
|
||||
result[GasExtCodeHash] = 700
|
||||
result[GasBalance] = 700
|
||||
result[GasTXDataNonZero]= 16
|
||||
|
||||
const
|
||||
HomesteadGasFees = BaseGasFees.homesteadGasFees
|
||||
TangerineGasFees = HomesteadGasFees.tangerineGasFees
|
||||
SpuriousGasFees = TangerineGasFees.spuriousGasFees
|
||||
IstanbulGasFees = SpuriousGasFees.istanbulGasFees
|
||||
|
||||
gasFees*: array[Fork, GasFeeSchedule] = [
|
||||
FkFrontier: BaseGasFees,
|
||||
@ -616,7 +681,8 @@ const
|
||||
FkTangerine: TangerineGasFees,
|
||||
FkSpurious: SpuriousGasFees,
|
||||
FkByzantium: SpuriousGasFees,
|
||||
FkConstantinople: SpuriousGasFees
|
||||
FkConstantinople: SpuriousGasFees,
|
||||
FkIstanbul: IstanbulGasFees
|
||||
]
|
||||
|
||||
|
||||
@ -645,9 +711,13 @@ const
|
||||
GasIdentityWord* = 3
|
||||
GasECRecover* = 3000
|
||||
GasECAdd* = 500
|
||||
GasECAddIstanbul* = 150
|
||||
GasECMul* = 40000
|
||||
GasECMulIstanbul* = 6000
|
||||
GasECPairingBase* = 100000
|
||||
GasECPairingBaseIstanbul* = 45000
|
||||
GasECPairingPerPoint* = 80000
|
||||
GasECPairingPerPointIstanbul* = 34000
|
||||
# The Yellow Paper is special casing the GasQuadDivisor.
|
||||
# It is defined in Appendix G with the other GasFeeKind constants
|
||||
# instead of Appendix E for precompiled contracts
|
||||
|
@ -79,6 +79,9 @@ fill_enum_holes:
|
||||
Difficulty = 0x44, # Get the block's difficulty.
|
||||
GasLimit = 0x45, # Get the block's gas limit.
|
||||
|
||||
ChainId = 0x46, # Get current chain’s EIP-155 unique identifier.
|
||||
SelfBalance = 0x47, # Get current contract's balance.
|
||||
|
||||
# 50s: Stack, Memory, Storage and Flow Operations
|
||||
Pop = 0x50, # Remove item from stack.
|
||||
Mload = 0x51, # Load word from memory.
|
||||
|
@ -372,6 +372,16 @@ op gasLimit, inline = true:
|
||||
## 0x45, Get the block's gas limit
|
||||
push: computation.vmState.gasLimit
|
||||
|
||||
op chainId, inline = true:
|
||||
## 0x46, Get current chain’s EIP-155 unique identifier.
|
||||
# TODO: this is a stub
|
||||
push: computation.vmState.chaindb.config.chainId
|
||||
|
||||
op selfBalance, inline = true:
|
||||
## 0x47, Get current contract's balance.
|
||||
let stateDb = computation.vmState.readOnlyStateDb
|
||||
push: stateDb.getBalance(computation.msg.storageAddress)
|
||||
|
||||
# ##########################################
|
||||
# 50s: Stack, Memory, Storage and Flow Operations
|
||||
|
||||
@ -920,3 +930,29 @@ op extCodeHash, inline = true:
|
||||
push: 0
|
||||
else:
|
||||
push: computation.vmState.readOnlyStateDB.getCodeHash(address)
|
||||
|
||||
op sstoreEIP2200, inline = false, slot, value:
|
||||
checkInStaticContext(computation)
|
||||
const SentryGasEIP2200 = 2300 # Minimum gas required to be present for an SSTORE call, not consumed
|
||||
|
||||
if computation.gasMeter.gasRemaining < SentryGasEIP2200:
|
||||
raise newException(OutOfGas, "Gas not enough to perform EIP2200 SSTORE")
|
||||
|
||||
let stateDB = computation.vmState.readOnlyStateDB
|
||||
let (currentValue, existing) = stateDB.getStorage(computation.msg.storageAddress, slot)
|
||||
|
||||
let
|
||||
gasParam = GasParams(kind: Op.Sstore,
|
||||
s_isStorageEmpty: currentValue.isZero,
|
||||
s_currentValue: currentValue,
|
||||
s_originalValue: stateDB.getCommittedStorage(computation.msg.storageAddress, slot)
|
||||
)
|
||||
(gasCost, gasRefund) = computation.gasCosts[Sstore].c_handler(value, gasParam)
|
||||
|
||||
computation.gasMeter.consumeGas(gasCost, &"SSTORE EIP2200: {computation.msg.storageAddress}[{slot}] -> {value} ({currentValue})")
|
||||
|
||||
if gasRefund > 0:
|
||||
computation.gasMeter.refundGas(gasRefund)
|
||||
|
||||
computation.vmState.mutateStateDB:
|
||||
db.setStorage(computation.msg.storageAddress, slot, value)
|
||||
|
@ -16,7 +16,8 @@ type
|
||||
FkTangerine,
|
||||
FkSpurious,
|
||||
FkByzantium,
|
||||
FkConstantinople
|
||||
FkConstantinople,
|
||||
FkIstanbul
|
||||
|
||||
const
|
||||
forkBlocks*: array[Fork, BlockNumber] = [
|
||||
@ -27,7 +28,8 @@ const
|
||||
FkTangerine: 2_463_000.toBlockNumber, # 18/10/2016 17:19:31
|
||||
FkSpurious: 2_675_000.toBlockNumber, # 22/11/2016 18:15:44
|
||||
FkByzantium: 4_370_000.toBlockNumber, # 16/10/2017 09:22:11
|
||||
FkConstantinople: 7_280_000.toBlockNumber # 28/02/2019 07:52:04
|
||||
FkConstantinople: 7_280_000.toBlockNumber, # 28/02/2019 07:52:04
|
||||
FkIstanbul: 9_069_000.toBlockNumber
|
||||
]
|
||||
|
||||
proc toFork*(blockNumber: BlockNumber): Fork =
|
||||
@ -47,7 +49,8 @@ proc toFork*(blockNumber: BlockNumber): Fork =
|
||||
elif blockNumber < forkBlocks[FkSpurious]: FkTangerine
|
||||
elif blockNumber < forkBlocks[FkByzantium]: FkSpurious
|
||||
elif blockNumber < forkBlocks[FkConstantinople]: FkByzantium
|
||||
else: FkConstantinople
|
||||
elif blockNumber < forkBlocks[FkIstanbul]: FkConstantinople
|
||||
else: FkIstanbul
|
||||
|
||||
proc `$`*(fork: Fork): string =
|
||||
case fork
|
||||
@ -59,4 +62,4 @@ proc `$`*(fork: Fork): string =
|
||||
of FkSpurious: result = "Spurious Dragon"
|
||||
of FkByzantium: result = "Byzantium"
|
||||
of FkConstantinople: result = "Constantinople"
|
||||
|
||||
of FkIstanbul: result = "Istanbul"
|
||||
|
@ -215,6 +215,14 @@ proc genConstantinopleJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.c
|
||||
|
||||
let ConstantinopleOpDispatch {.compileTime.}: array[Op, NimNode] = genConstantinopleJumpTable(ByzantiumOpDispatch)
|
||||
|
||||
proc genIstanbulJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} =
|
||||
result = ops
|
||||
result[ChainId] = newIdentNode "chainId"
|
||||
result[SelfBalance] = newIdentNode "selfBalance"
|
||||
result[SStore] = newIdentNode "sstoreEIP2200"
|
||||
|
||||
let IstanbulOpDispatch {.compileTime.}: array[Op, NimNode] = genIstanbulJumpTable(ConstantinopleOpDispatch)
|
||||
|
||||
proc opTableToCaseStmt(opTable: array[Op, NimNode], computation: NimNode): NimNode =
|
||||
|
||||
let instr = quote do: `computation`.instr
|
||||
@ -288,6 +296,9 @@ macro genByzantiumDispatch(computation: BaseComputation): untyped =
|
||||
macro genConstantinopleDispatch(computation: BaseComputation): untyped =
|
||||
result = opTableToCaseStmt(ConstantinopleOpDispatch, computation)
|
||||
|
||||
macro genIstanbulDispatch(computation: BaseComputation): untyped =
|
||||
result = opTableToCaseStmt(IstanbulOpDispatch, computation)
|
||||
|
||||
proc frontierVM(computation: BaseComputation) =
|
||||
genFrontierDispatch(computation)
|
||||
|
||||
@ -306,6 +317,9 @@ proc byzantiumVM(computation: BaseComputation) {.gcsafe.} =
|
||||
proc constantinopleVM(computation: BaseComputation) {.gcsafe.} =
|
||||
genConstantinopleDispatch(computation)
|
||||
|
||||
proc istanbulVM(computation: BaseComputation) {.gcsafe.} =
|
||||
genIstanbulDispatch(computation)
|
||||
|
||||
proc selectVM(computation: BaseComputation, fork: Fork) {.gcsafe.} =
|
||||
# TODO: Optimise getting fork and updating opCodeExec only when necessary
|
||||
case fork
|
||||
@ -317,10 +331,12 @@ proc selectVM(computation: BaseComputation, fork: Fork) {.gcsafe.} =
|
||||
computation.tangerineVM()
|
||||
of FkSpurious:
|
||||
computation.spuriousVM()
|
||||
of FKByzantium:
|
||||
of FkByzantium:
|
||||
computation.byzantiumVM()
|
||||
else:
|
||||
of FkConstantinople:
|
||||
computation.constantinopleVM()
|
||||
else:
|
||||
computation.istanbulVM()
|
||||
|
||||
proc executeOpcodes(computation: BaseComputation) =
|
||||
let fork = computation.getFork
|
||||
|
@ -1,7 +1,7 @@
|
||||
import
|
||||
../vm_types, interpreter/[gas_meter, gas_costs, utils/utils_numeric, vm_forks],
|
||||
../errors, stint, eth/[keys, common], chronicles, tables, macros,
|
||||
message, math, nimcrypto, bncurve/[fields, groups]
|
||||
message, math, nimcrypto, bncurve/[fields, groups], blake2b_f
|
||||
|
||||
type
|
||||
PrecompileAddresses* = enum
|
||||
@ -10,11 +10,13 @@ type
|
||||
paSha256,
|
||||
paRipeMd160,
|
||||
paIdentity,
|
||||
# Byzantium onward
|
||||
# Byzantium and Constantinople
|
||||
paModExp,
|
||||
paEcAdd,
|
||||
paEcMul,
|
||||
paPairing = 8
|
||||
paPairing,
|
||||
# Istanbul
|
||||
paBlake2bf = 9
|
||||
|
||||
proc getSignature(computation: BaseComputation): (array[32, byte], Signature) =
|
||||
# input is Hash, V, R, S
|
||||
@ -235,8 +237,9 @@ proc modExp*(computation: BaseComputation) =
|
||||
else:
|
||||
raise newException(EVMError, "The Nimbus VM doesn't support modular exponentiation with numbers larger than uint8192")
|
||||
|
||||
proc bn256ecAdd*(computation: BaseComputation) =
|
||||
computation.gasMeter.consumeGas(GasECAdd, reason = "ecAdd Precompile")
|
||||
proc bn256ecAdd*(computation: BaseComputation, fork: Fork = FkByzantium) =
|
||||
let gasFee = if fork < FkIstanbul: GasECAdd else: GasECAddIstanbul
|
||||
computation.gasMeter.consumeGas(gasFee, reason = "ecAdd Precompile")
|
||||
|
||||
var
|
||||
input: array[128, byte]
|
||||
@ -255,8 +258,9 @@ proc bn256ecAdd*(computation: BaseComputation) =
|
||||
|
||||
computation.rawOutput = @output
|
||||
|
||||
proc bn256ecMul*(computation: BaseComputation) =
|
||||
computation.gasMeter.consumeGas(GasECMul, reason="ecMul Precompile")
|
||||
proc bn256ecMul*(computation: BaseComputation, fork: Fork = FkByzantium) =
|
||||
let gasFee = if fork < FkIstanbul: GasECMul else: GasECMulIstanbul
|
||||
computation.gasMeter.consumeGas(gasFee, reason="ecMul Precompile")
|
||||
|
||||
var
|
||||
input: array[96, byte]
|
||||
@ -277,13 +281,16 @@ proc bn256ecMul*(computation: BaseComputation) =
|
||||
|
||||
computation.rawOutput = @output
|
||||
|
||||
proc bn256ecPairing*(computation: BaseComputation) =
|
||||
proc bn256ecPairing*(computation: BaseComputation, fork: Fork = FkByzantium) =
|
||||
let msglen = len(computation.msg.data)
|
||||
if msglen mod 192 != 0:
|
||||
raise newException(ValidationError, "Invalid input length")
|
||||
|
||||
let numPoints = msglen div 192
|
||||
let gasFee = GasECPairingBase + numPoints * GasECPairingPerPoint
|
||||
let gasFee = if fork < FkIstanbul:
|
||||
GasECPairingBase + numPoints * GasECPairingPerPoint
|
||||
else:
|
||||
GasECPairingBaseIstanbul + numPoints * GasECPairingPerPointIstanbul
|
||||
computation.gasMeter.consumeGas(gasFee, reason="ecPairing Precompile")
|
||||
|
||||
var output: array[32, byte]
|
||||
@ -311,12 +318,31 @@ proc bn256ecPairing*(computation: BaseComputation) =
|
||||
|
||||
computation.rawOutput = @output
|
||||
|
||||
proc blake2bf*(computation: BaseComputation) =
|
||||
template input(): untyped =
|
||||
computation.msg.data
|
||||
|
||||
if len(input) == blake2FInputLength:
|
||||
let gasFee = GasInt(beLoad32(input, 0))
|
||||
computation.gasMeter.consumeGas(gasFee, reason="ecPairing Precompile")
|
||||
|
||||
var output: array[64, byte]
|
||||
if not blake2b_F(input, output):
|
||||
raise newException(ValidationError, "Blake2b F function invalid input")
|
||||
else:
|
||||
computation.rawOutput = @output
|
||||
|
||||
proc getMaxPrecompileAddr(fork: Fork): PrecompileAddresses =
|
||||
if fork < FkByzantium: paIdentity
|
||||
elif fork < FkIstanbul: paPairing
|
||||
else: PrecompileAddresses.high
|
||||
|
||||
proc execPrecompiles*(computation: BaseComputation, fork: Fork): bool {.inline.} =
|
||||
for i in 0..18:
|
||||
if computation.msg.codeAddress[i] != 0: return
|
||||
|
||||
let lb = computation.msg.codeAddress[19]
|
||||
let maxPrecompileAddr = if fork < FkByzantium: paIdentity else: PrecompileAddresses.high
|
||||
let maxPrecompileAddr = getMaxPrecompileAddr(fork)
|
||||
if lb in PrecompileAddresses.low.byte .. maxPrecompileAddr.byte:
|
||||
result = true
|
||||
let precompile = PrecompileAddresses(lb)
|
||||
@ -328,9 +354,10 @@ proc execPrecompiles*(computation: BaseComputation, fork: Fork): bool {.inline.}
|
||||
of paRipeMd160: ripeMd160(computation)
|
||||
of paIdentity: identity(computation)
|
||||
of paModExp: modExp(computation)
|
||||
of paEcAdd: bn256ecAdd(computation)
|
||||
of paEcMul: bn256ecMul(computation)
|
||||
of paPairing: bn256ecPairing(computation)
|
||||
of paEcAdd: bn256ecAdd(computation, fork)
|
||||
of paEcMul: bn256ecMul(computation, fork)
|
||||
of paPairing: bn256ecPairing(computation, fork)
|
||||
of paBlake2bf: blake2bf(computation)
|
||||
except OutOfGas:
|
||||
let msg = getCurrentExceptionMsg()
|
||||
# cannot use setError here, cyclic dependency
|
||||
|
@ -8,7 +8,8 @@
|
||||
import
|
||||
unittest2, ../nimbus/vm/precompiles, json, stew/byteutils, test_helpers, ospaths, tables,
|
||||
strformat, strutils, eth/trie/db, eth/common, ../nimbus/db/[db_chain, state_db],
|
||||
../nimbus/[constants, vm_types, vm_state], ../nimbus/vm/[computation, message], macros
|
||||
../nimbus/[constants, vm_types, vm_state], ../nimbus/vm/[computation, message], macros,
|
||||
../nimbus/vm/blake2b_f
|
||||
|
||||
proc initAddress(i: byte): EthAddress = result[19] = i
|
||||
|
||||
@ -51,6 +52,71 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
|
||||
#raise newException(ValueError, "Unknown test vector '" & $label & "'")
|
||||
echo "Unknown test vector '" & $label & "'"
|
||||
|
||||
const blake2InputTests = [
|
||||
(
|
||||
input: "",
|
||||
expected: "error",
|
||||
name: "vector 0: empty input",
|
||||
),
|
||||
(
|
||||
input: "00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
|
||||
expected: "error",
|
||||
name: "vector 1: less than 213 bytes input",
|
||||
),
|
||||
(
|
||||
input: "000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
|
||||
expected: "error",
|
||||
name: "vector 2: more than 213 bytes input",
|
||||
),
|
||||
(
|
||||
input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002",
|
||||
expected: "error",
|
||||
name: "vector 3: malformed final block indicator flag",
|
||||
),
|
||||
(
|
||||
input: "0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
|
||||
expected: "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b",
|
||||
name: "vector 4",
|
||||
),
|
||||
(
|
||||
input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
|
||||
expected: "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
|
||||
name: "vector 5",
|
||||
),
|
||||
(
|
||||
input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000",
|
||||
expected: "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735",
|
||||
name: "vector 6",
|
||||
),
|
||||
(
|
||||
input: "0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
|
||||
expected: "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421",
|
||||
name: "vector 7",
|
||||
),
|
||||
(
|
||||
input: "007A120048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
|
||||
expected: "6d2ce9e534d50e18ff866ae92d70cceba79bbcd14c63819fe48752c8aca87a4bb7dcc230d22a4047f0486cfcfb50a17b24b2899eb8fca370f22240adb5170189",
|
||||
name: "vector 8",
|
||||
),
|
||||
]
|
||||
|
||||
proc precompilesMain*() =
|
||||
suite "Precompiles":
|
||||
jsonTest("PrecompileTests", testFixture)
|
||||
|
||||
suite "blake2bf":
|
||||
var output: array[64, byte]
|
||||
var expectedOutput: array[64, byte]
|
||||
for x in blake2InputTests:
|
||||
test x.name:
|
||||
let z = if x.input.len == 0: @[] else: hexToSeqByte(x.input)
|
||||
let res = blake2b_F(z, output)
|
||||
if x.expected == "error":
|
||||
check res == false
|
||||
else:
|
||||
hexToByteArray(x.expected, expectedOutput)
|
||||
check res == true
|
||||
check expectedOutput == output
|
||||
|
||||
when isMainModule:
|
||||
precompilesMain()
|
||||
|
Loading…
x
Reference in New Issue
Block a user