Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement istanbul hard fork - EIP 1679 #415

Merged
merged 12 commits into from
Nov 12, 2019
3 changes: 2 additions & 1 deletion nimbus/p2p/executor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
10 changes: 5 additions & 5 deletions nimbus/transaction.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
141 changes: 141 additions & 0 deletions nimbus/vm/blake2b_f.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import nimcrypto/utils

# Blake2 `F` compression function
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this change be contributed to NimCrypto? cc @cheatfate

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initially I have the same idea: cheatfate/nimcrypto#32. but I doubt there will be another use case.

# 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
17 changes: 15 additions & 2 deletions nimbus/vm/interpreter/gas_costs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -603,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
result[GasTXDataNonZero]= 16
const
HomesteadGasFees = BaseGasFees.homesteadGasFees
TangerineGasFees = HomesteadGasFees.tangerineGasFees
SpuriousGasFees = TangerineGasFees.spuriousGasFees
IstanbulGasFees = SpuriousGasFees.istanbulGasFees

gasFees*: array[Fork, GasFeeSchedule] = [
FkFrontier: BaseGasFees,
Expand All @@ -616,7 +624,8 @@ const
FkTangerine: TangerineGasFees,
FkSpurious: SpuriousGasFees,
FkByzantium: SpuriousGasFees,
FkConstantinople: SpuriousGasFees
FkConstantinople: SpuriousGasFees,
FkIstanbul: IstanbulGasFees
]


Expand Down Expand Up @@ -645,10 +654,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
3 changes: 3 additions & 0 deletions nimbus/vm/interpreter/opcode_values.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions nimbus/vm/interpreter/opcodes_impl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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: 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

Expand Down
11 changes: 7 additions & 4 deletions nimbus/vm/interpreter/vm_forks.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ type
FkTangerine,
FkSpurious,
FkByzantium,
FkConstantinople
FkConstantinople,
FkIstanbul

const
forkBlocks*: array[Fork, BlockNumber] = [
Expand All @@ -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 =
Expand All @@ -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
Expand All @@ -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"
19 changes: 17 additions & 2 deletions nimbus/vm/interpreter_dispatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ 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"

let IstanbulOpDispatch {.compileTime.}: array[Op, NimNode] = genIstanbulJumpTable(ConstantinopleOpDispatch)

proc opTableToCaseStmt(opTable: array[Op, NimNode], computation: NimNode): NimNode =

let instr = quote do: `computation`.instr
Expand Down Expand Up @@ -288,6 +295,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)

Expand All @@ -306,6 +316,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
Expand All @@ -317,10 +330,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
Expand Down
Loading