Merge branch 'master' of github.com:status-im/nimbus

This commit is contained in:
Ștefan Talpalaru 2019-11-12 19:08:45 +01:00
commit 8ad1eac41d
No known key found for this signature in database
GPG Key ID: CBF7934204F1B6F9
11 changed files with 407 additions and 39 deletions

View File

@ -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.}

View File

@ -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 =

View File

@ -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
View 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

View File

@ -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

View File

@ -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 chains 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.

View File

@ -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 chains 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)

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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()