From 3dbf41aac8a3f78b7bde1746f42fa3ecaa111ecb Mon Sep 17 00:00:00 2001 From: andri lim Date: Mon, 11 Nov 2019 11:20:16 +0700 Subject: [PATCH 01/12] adding FkIstanbul to vm_forks gas_costs --- nimbus/vm/interpreter/gas_costs.nim | 3 ++- nimbus/vm/interpreter/vm_forks.nim | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index 7b8b09b89..f4870bc71 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -616,7 +616,8 @@ const FkTangerine: TangerineGasFees, FkSpurious: SpuriousGasFees, FkByzantium: SpuriousGasFees, - FkConstantinople: SpuriousGasFees + FkConstantinople: SpuriousGasFees, + FkIstanbul: SpuriousGasFees ] diff --git a/nimbus/vm/interpreter/vm_forks.nim b/nimbus/vm/interpreter/vm_forks.nim index 38303ed51..5b40ebb7d 100644 --- a/nimbus/vm/interpreter/vm_forks.nim +++ b/nimbus/vm/interpreter/vm_forks.nim @@ -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" From b3cbf620d6b6586098c284c9340e1a9c6d332b75 Mon Sep 17 00:00:00 2001 From: andri lim Date: Mon, 11 Nov 2019 11:21:16 +0700 Subject: [PATCH 02/12] implement EIP-152 --- nimbus/vm/blake2b_f.nim | 141 +++++++++++++++++++++++++++++++++++++ nimbus/vm/precompiles.nim | 30 ++++++-- tests/test_precompiles.nim | 68 +++++++++++++++++- 3 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 nimbus/vm/blake2b_f.nim diff --git a/nimbus/vm/blake2b_f.nim b/nimbus/vm/blake2b_f.nim new file mode 100644 index 000000000..a4c956922 --- /dev/null +++ b/nimbus/vm/blake2b_f.nim @@ -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.. Date: Mon, 11 Nov 2019 11:33:56 +0700 Subject: [PATCH 03/12] implement EIP 1108 --- nimbus/vm/interpreter/gas_costs.nim | 6 +++++- nimbus/vm/precompiles.nim | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index f4870bc71..afc4d9d38 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -646,10 +646,14 @@ 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 - GasQuadDivisor* = 20 + GasQuadDivisor* = 100 diff --git a/nimbus/vm/precompiles.nim b/nimbus/vm/precompiles.nim index 636324527..cb7285281 100644 --- a/nimbus/vm/precompiles.nim +++ b/nimbus/vm/precompiles.nim @@ -237,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] @@ -257,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] @@ -279,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] @@ -349,9 +354,9 @@ 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() From 0bb6c73bdbe8335af8161d8c5d9dcad5e5332912 Mon Sep 17 00:00:00 2001 From: andri lim Date: Mon, 11 Nov 2019 11:58:58 +0700 Subject: [PATCH 04/12] implement EIP 1344 stub --- nimbus/vm/interpreter/gas_costs.nim | 1 + nimbus/vm/interpreter/opcode_values.nim | 2 ++ nimbus/vm/interpreter/opcodes_impl.nim | 5 +++++ nimbus/vm/interpreter_dispatch.nim | 18 ++++++++++++++++-- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index afc4d9d38..8193b1e46 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -436,6 +436,7 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) = Number: fixed GasBase, Difficulty: fixed GasBase, GasLimit: fixed GasBase, + ChainID: fixed GasBase, # 50s: Stack, Memory, Storage and Flow Operations Pop: fixed GasBase, diff --git a/nimbus/vm/interpreter/opcode_values.nim b/nimbus/vm/interpreter/opcode_values.nim index 916b155e1..3bc7b8ebe 100644 --- a/nimbus/vm/interpreter/opcode_values.nim +++ b/nimbus/vm/interpreter/opcode_values.nim @@ -79,6 +79,8 @@ 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. + # 50s: Stack, Memory, Storage and Flow Operations Pop = 0x50, # Remove item from stack. Mload = 0x51, # Load word from memory. diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index 73432a761..8ed57fd1e 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -372,6 +372,11 @@ 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: 0 + # ########################################## # 50s: Stack, Memory, Storage and Flow Operations diff --git a/nimbus/vm/interpreter_dispatch.nim b/nimbus/vm/interpreter_dispatch.nim index 857e400f6..82fcd2622 100644 --- a/nimbus/vm/interpreter_dispatch.nim +++ b/nimbus/vm/interpreter_dispatch.nim @@ -215,6 +215,12 @@ 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" + +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 +294,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 +315,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 +329,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 From 078375061b4171e802b506af5f193c7fc5e448f6 Mon Sep 17 00:00:00 2001 From: andri lim Date: Mon, 11 Nov 2019 12:12:58 +0700 Subject: [PATCH 05/12] implement EIP 1884 stub --- nimbus/vm/interpreter/gas_costs.nim | 9 ++++++++- nimbus/vm/interpreter/opcode_values.nim | 1 + nimbus/vm/interpreter/opcodes_impl.nim | 5 +++++ nimbus/vm/interpreter_dispatch.nim | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index 8193b1e46..d84820ae0 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -604,10 +604,17 @@ 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 + const HomesteadGasFees = BaseGasFees.homesteadGasFees TangerineGasFees = HomesteadGasFees.tangerineGasFees SpuriousGasFees = TangerineGasFees.spuriousGasFees + IstanbulGasFees = SpuriousGasFees.istanbulGasFees gasFees*: array[Fork, GasFeeSchedule] = [ FkFrontier: BaseGasFees, @@ -618,7 +625,7 @@ const FkSpurious: SpuriousGasFees, FkByzantium: SpuriousGasFees, FkConstantinople: SpuriousGasFees, - FkIstanbul: SpuriousGasFees + FkIstanbul: IstanbulGasFees ] diff --git a/nimbus/vm/interpreter/opcode_values.nim b/nimbus/vm/interpreter/opcode_values.nim index 3bc7b8ebe..0de3b2bc4 100644 --- a/nimbus/vm/interpreter/opcode_values.nim +++ b/nimbus/vm/interpreter/opcode_values.nim @@ -80,6 +80,7 @@ fill_enum_holes: 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. diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index 8ed57fd1e..a7dfddb12 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -377,6 +377,11 @@ op chainID, inline = true: # TODO: this is a stub push: 0 +op selfBalance, inline = true: + ## 0x47, Get current contract's balance. + # TODO: this is a stub + push: 0 + # ########################################## # 50s: Stack, Memory, Storage and Flow Operations diff --git a/nimbus/vm/interpreter_dispatch.nim b/nimbus/vm/interpreter_dispatch.nim index 82fcd2622..c3832b5e0 100644 --- a/nimbus/vm/interpreter_dispatch.nim +++ b/nimbus/vm/interpreter_dispatch.nim @@ -218,6 +218,7 @@ let ConstantinopleOpDispatch {.compileTime.}: array[Op, NimNode] = genConstantin proc genIstanbulJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} = result = ops result[ChainID] = newIdentNode "chainID" + result[SelfBalance] = newIdentNode "selfBalance" let IstanbulOpDispatch {.compileTime.}: array[Op, NimNode] = genIstanbulJumpTable(ConstantinopleOpDispatch) From 4cf3931f349c1e49f83868c4d1e512cc13bb5dca Mon Sep 17 00:00:00 2001 From: andri lim Date: Mon, 11 Nov 2019 12:20:21 +0700 Subject: [PATCH 06/12] add istanbul block reward --- nimbus/p2p/executor.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nimbus/p2p/executor.nim b/nimbus/p2p/executor.nim index 9ce778d2f..2bd729e70 100644 --- a/nimbus/p2p/executor.nim +++ b/nimbus/p2p/executor.nim @@ -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 = From 1937fd700dbdba54a2da0b6c32ed0b6cc9b5effd Mon Sep 17 00:00:00 2001 From: andri lim Date: Mon, 11 Nov 2019 12:20:46 +0700 Subject: [PATCH 07/12] implement EIP-2028 --- nimbus/transaction.nim | 10 +++++----- nimbus/vm/interpreter/gas_costs.nim | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nimbus/transaction.nim b/nimbus/transaction.nim index 5c06d9f7c..b8a9549ce 100644 --- a/nimbus/transaction.nim +++ b/nimbus/transaction.nim @@ -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] diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index d84820ae0..77070770c 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -609,7 +609,7 @@ func istanbulGasFees(previous_fees: GasFeeSchedule): GasFeeSchedule = result[GasSload] = 800 result[GasExtCodeHash] = 700 result[GasBalance] = 700 - + result[GasTXDataNonZero]= 16 const HomesteadGasFees = BaseGasFees.homesteadGasFees TangerineGasFees = HomesteadGasFees.tangerineGasFees From 4fbc9727d8194e04400829707201baa6bc9aba65 Mon Sep 17 00:00:00 2001 From: andri lim Date: Tue, 12 Nov 2019 13:20:29 +0700 Subject: [PATCH 08/12] revert accidentally changed GasQuadDivisor --- nimbus/vm/interpreter/gas_costs.nim | 2 +- nimbus/vm/precompiles.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index 77070770c..01783b5a7 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -664,4 +664,4 @@ const # 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 - GasQuadDivisor* = 100 + GasQuadDivisor* = 20 diff --git a/nimbus/vm/precompiles.nim b/nimbus/vm/precompiles.nim index cb7285281..818c75e76 100644 --- a/nimbus/vm/precompiles.nim +++ b/nimbus/vm/precompiles.nim @@ -14,7 +14,7 @@ type paModExp, paEcAdd, paEcMul, - paPairing + paPairing, # Istanbul paBlake2bf = 9 From 1e4f290cc60fa0d0ed5f7d290d2f711301658380 Mon Sep 17 00:00:00 2001 From: andri lim Date: Tue, 12 Nov 2019 19:21:33 +0700 Subject: [PATCH 09/12] implement 'selfBalance' opcode and gas cost --- nimbus/vm/interpreter/gas_costs.nim | 1 + nimbus/vm/interpreter/opcodes_impl.nim | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index 01783b5a7..b101d6dcf 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -437,6 +437,7 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) = Difficulty: fixed GasBase, GasLimit: fixed GasBase, ChainID: fixed GasBase, + SelfBalance: fixed GasLow, # 50s: Stack, Memory, Storage and Flow Operations Pop: fixed GasBase, diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index a7dfddb12..cbd05a95c 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -379,8 +379,8 @@ op chainID, inline = true: op selfBalance, inline = true: ## 0x47, Get current contract's balance. - # TODO: this is a stub - push: 0 + let stateDb = computation.vmState.readOnlyStateDb + push: stateDb.getBalance(computation.msg.storageAddress) # ########################################## # 50s: Stack, Memory, Storage and Flow Operations From b5e8a8d61b17c16f32c46a711403741a975fc737 Mon Sep 17 00:00:00 2001 From: andri lim Date: Tue, 12 Nov 2019 19:41:19 +0700 Subject: [PATCH 10/12] implement 'chainId' opcode --- nimbus/vm/interpreter/opcode_values.nim | 2 +- nimbus/vm/interpreter/opcodes_impl.nim | 4 ++-- nimbus/vm/interpreter_dispatch.nim | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nimbus/vm/interpreter/opcode_values.nim b/nimbus/vm/interpreter/opcode_values.nim index 0de3b2bc4..f85a354ca 100644 --- a/nimbus/vm/interpreter/opcode_values.nim +++ b/nimbus/vm/interpreter/opcode_values.nim @@ -79,7 +79,7 @@ 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. + ChainId = 0x46, # Get current chain’s EIP-155 unique identifier. SelfBalance = 0x47, # Get current contract's balance. # 50s: Stack, Memory, Storage and Flow Operations diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index cbd05a95c..257551270 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -372,10 +372,10 @@ op gasLimit, inline = true: ## 0x45, Get the block's gas limit push: computation.vmState.gasLimit -op chainID, inline = true: +op chainId, inline = true: ## 0x46, Get current chain’s EIP-155 unique identifier. # TODO: this is a stub - push: 0 + push: computation.vmState.chaindb.config.chainId op selfBalance, inline = true: ## 0x47, Get current contract's balance. diff --git a/nimbus/vm/interpreter_dispatch.nim b/nimbus/vm/interpreter_dispatch.nim index c3832b5e0..b59278349 100644 --- a/nimbus/vm/interpreter_dispatch.nim +++ b/nimbus/vm/interpreter_dispatch.nim @@ -217,7 +217,7 @@ let ConstantinopleOpDispatch {.compileTime.}: array[Op, NimNode] = genConstantin proc genIstanbulJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} = result = ops - result[ChainID] = newIdentNode "chainID" + result[ChainId] = newIdentNode "chainId" result[SelfBalance] = newIdentNode "selfBalance" let IstanbulOpDispatch {.compileTime.}: array[Op, NimNode] = genIstanbulJumpTable(ConstantinopleOpDispatch) From c0c62b94b8d572a62775d7c0f3fb171e86420422 Mon Sep 17 00:00:00 2001 From: andri lim Date: Tue, 12 Nov 2019 19:49:46 +0700 Subject: [PATCH 11/12] implement EIP-2200 stub --- nimbus/vm/interpreter/gas_costs.nim | 50 +++++++++++++++++++------- nimbus/vm/interpreter/opcodes_impl.nim | 4 +++ nimbus/vm/interpreter_dispatch.nim | 1 + 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index b101d6dcf..670689d4f 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -208,20 +208,45 @@ 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 func `prefix gasLog0`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} = result = `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength) @@ -611,6 +636,7 @@ func istanbulGasFees(previous_fees: GasFeeSchedule): GasFeeSchedule = result[GasExtCodeHash] = 700 result[GasBalance] = 700 result[GasTXDataNonZero]= 16 + const HomesteadGasFees = BaseGasFees.homesteadGasFees TangerineGasFees = HomesteadGasFees.tangerineGasFees diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index 257551270..c3ad82616 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -930,3 +930,7 @@ op extCodeHash, inline = true: push: 0 else: push: computation.vmState.readOnlyStateDB.getCodeHash(address) + +op sstoreEIP2200, inline = false, slot, value: + checkInStaticContext(computation) + # TODO: stub diff --git a/nimbus/vm/interpreter_dispatch.nim b/nimbus/vm/interpreter_dispatch.nim index b59278349..3d3d2bece 100644 --- a/nimbus/vm/interpreter_dispatch.nim +++ b/nimbus/vm/interpreter_dispatch.nim @@ -219,6 +219,7 @@ proc genIstanbulJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compile result = ops result[ChainId] = newIdentNode "chainId" result[SelfBalance] = newIdentNode "selfBalance" + result[SStore] = newIdentNode "sstoreEIP2200" let IstanbulOpDispatch {.compileTime.}: array[Op, NimNode] = genIstanbulJumpTable(ConstantinopleOpDispatch) From 61f340ae873362f7a5b5c2328dd63217f2a4b1f9 Mon Sep 17 00:00:00 2001 From: andri lim Date: Tue, 12 Nov 2019 20:24:52 +0700 Subject: [PATCH 12/12] implement EIP2200's SSTORE and gas cost --- nimbus/db/state_db.nim | 5 +++++ nimbus/vm/interpreter/gas_costs.nim | 30 ++++++++++++++++++++++++++ nimbus/vm/interpreter/opcodes_impl.nim | 24 ++++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/nimbus/db/state_db.nim b/nimbus/db/state_db.nim index 1f16d021d..146a86882 100644 --- a/nimbus/db/state_db.nim +++ b/nimbus/db/state_db.nim @@ -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.} diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index 670689d4f..407c9cd73 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -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 @@ -247,6 +249,34 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) = 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) diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index c3ad82616..21f592c38 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -933,4 +933,26 @@ op extCodeHash, inline = true: op sstoreEIP2200, inline = false, slot, value: checkInStaticContext(computation) - # TODO: stub + 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)