commit
7a721c58af
|
@ -89,6 +89,7 @@ const
|
||||||
eth5, # FkSpurious
|
eth5, # FkSpurious
|
||||||
eth3, # FkByzantium
|
eth3, # FkByzantium
|
||||||
eth2, # FkConstantinople
|
eth2, # FkConstantinople
|
||||||
|
eth2, # FkPetersburg
|
||||||
eth2, # FkIstanbul
|
eth2, # FkIstanbul
|
||||||
eth2 # FkGlacierMuir
|
eth2 # FkGlacierMuir
|
||||||
]
|
]
|
||||||
|
|
|
@ -160,7 +160,7 @@ func calcDifficulty*(timeStamp: EthTime, parent: BlockHeader, fork: Fork): Diffi
|
||||||
case fork
|
case fork
|
||||||
of FkGlacierMuir:
|
of FkGlacierMuir:
|
||||||
result = calcDifficultyGlacierMuir(timeStamp, parent)
|
result = calcDifficultyGlacierMuir(timeStamp, parent)
|
||||||
of FkConstantinople:
|
of FkConstantinople..FkPetersburg:
|
||||||
result = calcDifficultyConstantinople(timeStamp, parent)
|
result = calcDifficultyConstantinople(timeStamp, parent)
|
||||||
of FkByzantium:
|
of FkByzantium:
|
||||||
result = calcDifficultyByzantium(timeStamp, parent)
|
result = calcDifficultyByzantium(timeStamp, parent)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import
|
||||||
../constants, ../errors, ../vm_state, ../vm_types,
|
../constants, ../errors, ../vm_state, ../vm_types,
|
||||||
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
|
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
|
||||||
./code_stream, ./memory, ./message, ./stack, ../db/[state_db, db_chain],
|
./code_stream, ./memory, ./message, ./stack, ../db/[state_db, db_chain],
|
||||||
../utils/header, stew/[byteutils, ranges], precompiles,
|
../utils/header, stew/[byteutils, ranges, ranges/ptr_arith], precompiles,
|
||||||
transaction_tracer, ../utils
|
transaction_tracer, ../utils
|
||||||
|
|
||||||
when defined(evmc_enabled):
|
when defined(evmc_enabled):
|
||||||
|
|
|
@ -116,7 +116,7 @@ proc hostEmitLogImpl(ctx: Computation, address: EthAddress,
|
||||||
for i in 0 ..< topicsCount:
|
for i in 0 ..< topicsCount:
|
||||||
log.topics[i] = topics[i].bytes
|
log.topics[i] = topics[i].bytes
|
||||||
|
|
||||||
log.data = @makeOpenArray(data, dataSize)
|
log.data = @(makeOpenArray(data, dataSize))
|
||||||
log.address = address
|
log.address = address
|
||||||
ctx.addLogEntry(log)
|
ctx.addLogEntry(log)
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ template createImpl(c: Computation, m: nimbus_message, res: nimbus_result) =
|
||||||
gas: m.gas,
|
gas: m.gas,
|
||||||
sender: m.sender,
|
sender: m.sender,
|
||||||
value: Uint256.fromEvmc(m.value),
|
value: Uint256.fromEvmc(m.value),
|
||||||
data: @makeOpenArray(m.inputData, m.inputSize.int)
|
data: @(makeOpenArray(m.inputData, m.inputSize.int))
|
||||||
)
|
)
|
||||||
|
|
||||||
let child = newComputation(c.vmState, childMsg, Uint256.fromEvmc(m.create2_salt))
|
let child = newComputation(c.vmState, childMsg, Uint256.fromEvmc(m.create2_salt))
|
||||||
|
@ -159,7 +159,7 @@ template callImpl(c: Computation, m: nimbus_message, res: nimbus_result) =
|
||||||
codeAddress: m.destination,
|
codeAddress: m.destination,
|
||||||
contractAddress: if m.kind == EVMC_CALL: m.destination else: c.msg.contractAddress,
|
contractAddress: if m.kind == EVMC_CALL: m.destination else: c.msg.contractAddress,
|
||||||
value: Uint256.fromEvmc(m.value),
|
value: Uint256.fromEvmc(m.value),
|
||||||
data: @makeOpenArray(m.inputData, m.inputSize.int)
|
data: @(makeOpenArray(m.inputData, m.inputSize.int)),
|
||||||
flags: MsgFlags(m.flags)
|
flags: MsgFlags(m.flags)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -222,7 +222,8 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
||||||
sstoreLoad = FeeSchedule[GasSload]
|
sstoreLoad = FeeSchedule[GasSload]
|
||||||
sstoreSet = FeeSchedule[GasSset]
|
sstoreSet = FeeSchedule[GasSset]
|
||||||
sstoreReset= FeeSchedule[GasSreset]
|
sstoreReset= FeeSchedule[GasSreset]
|
||||||
sstoreDirty= when fork >= FkIstanbul: sstoreLoad else: sstoreReset
|
sstoreDirty= when fork < FkConstantinople or fork == FkPetersburg: sstoreReset
|
||||||
|
else: sstoreLoad
|
||||||
InitRefundEIP2200 = FeeSchedule[GasSset] - FeeSchedule[GasSload]
|
InitRefundEIP2200 = FeeSchedule[GasSset] - FeeSchedule[GasSload]
|
||||||
CleanRefundEIP2200 = FeeSchedule[GasSreset] - FeeSchedule[GasSload]
|
CleanRefundEIP2200 = FeeSchedule[GasSreset] - FeeSchedule[GasSload]
|
||||||
ClearRefundEIP2200 = FeeSchedule[RefundsClear]
|
ClearRefundEIP2200 = FeeSchedule[RefundsClear]
|
||||||
|
@ -248,7 +249,7 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
||||||
else:
|
else:
|
||||||
result.gasRefund += CleanRefundEIP2200
|
result.gasRefund += CleanRefundEIP2200
|
||||||
else:
|
else:
|
||||||
when fork < FkIstanbul:
|
when fork < FkConstantinople or fork == FkPetersburg:
|
||||||
# workaround for static evaluation not working for if expression
|
# workaround for static evaluation not working for if expression
|
||||||
const
|
const
|
||||||
gSet = FeeSchedule[GasSset]
|
gSet = FeeSchedule[GasSset]
|
||||||
|
@ -721,6 +722,7 @@ const
|
||||||
FkSpurious: SpuriousGasFees,
|
FkSpurious: SpuriousGasFees,
|
||||||
FkByzantium: SpuriousGasFees,
|
FkByzantium: SpuriousGasFees,
|
||||||
FkConstantinople: SpuriousGasFees,
|
FkConstantinople: SpuriousGasFees,
|
||||||
|
FkPetersburg: SpuriousGasFees,
|
||||||
FkIstanbul: IstanbulGasFees,
|
FkIstanbul: IstanbulGasFees,
|
||||||
FkGlacierMuir: IstanbulGasFees
|
FkGlacierMuir: IstanbulGasFees
|
||||||
]
|
]
|
||||||
|
@ -730,6 +732,7 @@ gasCosts(FkFrontier, base, BaseGasCosts)
|
||||||
gasCosts(FkHomestead, homestead, HomesteadGasCosts)
|
gasCosts(FkHomestead, homestead, HomesteadGasCosts)
|
||||||
gasCosts(FkTangerine, tangerine, TangerineGasCosts)
|
gasCosts(FkTangerine, tangerine, TangerineGasCosts)
|
||||||
gasCosts(FkSpurious, spurious, SpuriousGasCosts)
|
gasCosts(FkSpurious, spurious, SpuriousGasCosts)
|
||||||
|
gasCosts(FkConstantinople, constantinople, ConstantinopleGasCosts)
|
||||||
gasCosts(FkIstanbul, istanbul, IstanbulGasCosts)
|
gasCosts(FkIstanbul, istanbul, IstanbulGasCosts)
|
||||||
|
|
||||||
proc forkToSchedule*(fork: Fork): GasCosts =
|
proc forkToSchedule*(fork: Fork): GasCosts =
|
||||||
|
@ -739,6 +742,8 @@ proc forkToSchedule*(fork: Fork): GasCosts =
|
||||||
HomesteadGasCosts
|
HomesteadGasCosts
|
||||||
elif fork < FkSpurious:
|
elif fork < FkSpurious:
|
||||||
TangerineGasCosts
|
TangerineGasCosts
|
||||||
|
elif fork == FkConstantinople:
|
||||||
|
ConstantinopleGasCosts # with EIP-1283
|
||||||
elif fork < FkIstanbul:
|
elif fork < FkIstanbul:
|
||||||
SpuriousGasCosts
|
SpuriousGasCosts
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -462,7 +462,7 @@ op sstore, inline = false, slot, newValue:
|
||||||
else:
|
else:
|
||||||
sstoreImpl(c, slot, newValue)
|
sstoreImpl(c, slot, newValue)
|
||||||
|
|
||||||
template sstoreEIP2200Impl(c: Computation, slot, newValue: Uint256) =
|
template sstoreNetGasMeteringImpl(c: Computation, slot, newValue: Uint256) =
|
||||||
let stateDB = c.vmState.readOnlyStateDB
|
let stateDB = c.vmState.readOnlyStateDB
|
||||||
let currentValue {.inject.} = c.getStorage(slot)
|
let currentValue {.inject.} = c.getStorage(slot)
|
||||||
|
|
||||||
|
@ -492,7 +492,15 @@ op sstoreEIP2200, inline = false, slot, newValue:
|
||||||
when evmc_enabled:
|
when evmc_enabled:
|
||||||
sstoreEvmc(c, slot, newValue)
|
sstoreEvmc(c, slot, newValue)
|
||||||
else:
|
else:
|
||||||
sstoreEIP2200Impl(c, slot, newValue)
|
sstoreNetGasMeteringImpl(c, slot, newValue)
|
||||||
|
|
||||||
|
op sstoreEIP1283, inline = false, slot, newValue:
|
||||||
|
checkInStaticContext(c)
|
||||||
|
|
||||||
|
when evmc_enabled:
|
||||||
|
sstoreEvmc(c, slot, newValue)
|
||||||
|
else:
|
||||||
|
sstoreNetGasMeteringImpl(c, slot, newValue)
|
||||||
|
|
||||||
proc jumpImpl(c: Computation, jumpTarget: UInt256) =
|
proc jumpImpl(c: Computation, jumpTarget: UInt256) =
|
||||||
if jumpTarget >= c.code.len.u256:
|
if jumpTarget >= c.code.len.u256:
|
||||||
|
@ -606,7 +614,7 @@ template genCreate(callName: untyped, opCode: Op): untyped =
|
||||||
)
|
)
|
||||||
|
|
||||||
var res = c.host.call(msg)
|
var res = c.host.call(msg)
|
||||||
c.returnData = @makeOpenArray(res.outputData, res.outputSize.int)
|
c.returnData = @(makeOpenArray(res.outputData, res.outputSize.int))
|
||||||
c.gasMeter.returnGas(res.gas_left)
|
c.gasMeter.returnGas(res.gas_left)
|
||||||
|
|
||||||
if res.status_code == EVMC_SUCCESS:
|
if res.status_code == EVMC_SUCCESS:
|
||||||
|
@ -772,7 +780,7 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
||||||
)
|
)
|
||||||
|
|
||||||
var res = c.host.call(msg)
|
var res = c.host.call(msg)
|
||||||
c.returnData = @makeOpenArray(res.outputData, res.outputSize.int)
|
c.returnData = @(makeOpenArray(res.outputData, res.outputSize.int))
|
||||||
|
|
||||||
let actualOutputSize = min(memOutLen, c.returnData.len)
|
let actualOutputSize = min(memOutLen, c.returnData.len)
|
||||||
if actualOutputSize > 0:
|
if actualOutputSize > 0:
|
||||||
|
|
|
@ -17,6 +17,7 @@ type
|
||||||
FkSpurious,
|
FkSpurious,
|
||||||
FkByzantium,
|
FkByzantium,
|
||||||
FkConstantinople,
|
FkConstantinople,
|
||||||
|
FkPetersburg,
|
||||||
FkIstanbul,
|
FkIstanbul,
|
||||||
FkGlacierMuir
|
FkGlacierMuir
|
||||||
|
|
||||||
|
@ -29,7 +30,8 @@ const
|
||||||
FkTangerine: 2_463_000.toBlockNumber, # 18/10/2016 17:19:31
|
FkTangerine: 2_463_000.toBlockNumber, # 18/10/2016 17:19:31
|
||||||
FkSpurious: 2_675_000.toBlockNumber, # 22/11/2016 18:15:44
|
FkSpurious: 2_675_000.toBlockNumber, # 22/11/2016 18:15:44
|
||||||
FkByzantium: 4_370_000.toBlockNumber, # 16/10/2017 09:22:11
|
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, # Never Occured in MainNet
|
||||||
|
FkPetersburg: 7_280_000.toBlockNumber, # 28/02/2019 07:52:04
|
||||||
FkIstanbul: 9_069_000.toBlockNumber, # 08/12/2019 12:25:09
|
FkIstanbul: 9_069_000.toBlockNumber, # 08/12/2019 12:25:09
|
||||||
FkGlacierMuir: 9_200_000.toBlockNumber # 02/01/2020 08:30:49
|
FkGlacierMuir: 9_200_000.toBlockNumber # 02/01/2020 08:30:49
|
||||||
]
|
]
|
||||||
|
@ -51,7 +53,7 @@ proc toFork*(blockNumber: BlockNumber): Fork =
|
||||||
elif blockNumber < forkBlocks[FkSpurious]: FkTangerine
|
elif blockNumber < forkBlocks[FkSpurious]: FkTangerine
|
||||||
elif blockNumber < forkBlocks[FkByzantium]: FkSpurious
|
elif blockNumber < forkBlocks[FkByzantium]: FkSpurious
|
||||||
elif blockNumber < forkBlocks[FkConstantinople]: FkByzantium
|
elif blockNumber < forkBlocks[FkConstantinople]: FkByzantium
|
||||||
elif blockNumber < forkBlocks[FkIstanbul]: FkConstantinople
|
elif blockNumber < forkBlocks[FkIstanbul]: FkPetersburg
|
||||||
elif blockNumber < forkBlocks[FkGlacierMuir]: FkIstanbul
|
elif blockNumber < forkBlocks[FkGlacierMuir]: FkIstanbul
|
||||||
else: FkGlacierMuir
|
else: FkGlacierMuir
|
||||||
|
|
||||||
|
@ -65,5 +67,6 @@ proc `$`*(fork: Fork): string =
|
||||||
of FkSpurious: result = "Spurious Dragon"
|
of FkSpurious: result = "Spurious Dragon"
|
||||||
of FkByzantium: result = "Byzantium"
|
of FkByzantium: result = "Byzantium"
|
||||||
of FkConstantinople: result = "Constantinople"
|
of FkConstantinople: result = "Constantinople"
|
||||||
|
of FkPetersburg: result = "Petersburg"
|
||||||
of FkIstanbul: result = "Istanbul"
|
of FkIstanbul: result = "Istanbul"
|
||||||
of FkGlacierMuir: result = "Glacier Muir"
|
of FkGlacierMuir: result = "Glacier Muir"
|
||||||
|
|
|
@ -212,16 +212,23 @@ proc genConstantinopleJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.c
|
||||||
result[Sar] = newIdentNode "sarOp"
|
result[Sar] = newIdentNode "sarOp"
|
||||||
result[ExtCodeHash] = newIdentNode "extCodeHash"
|
result[ExtCodeHash] = newIdentNode "extCodeHash"
|
||||||
result[Create2] = newIdentNode "create2"
|
result[Create2] = newIdentNode "create2"
|
||||||
|
result[SStore] = newIdentNode "sstoreEIP1283"
|
||||||
|
|
||||||
let ConstantinopleOpDispatch {.compileTime.}: array[Op, NimNode] = genConstantinopleJumpTable(ByzantiumOpDispatch)
|
let ConstantinopleOpDispatch {.compileTime.}: array[Op, NimNode] = genConstantinopleJumpTable(ByzantiumOpDispatch)
|
||||||
|
|
||||||
|
proc genPetersburgJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} =
|
||||||
|
result = ops
|
||||||
|
result[SStore] = newIdentNode "sstore" # disable EIP-1283
|
||||||
|
|
||||||
|
let PetersburgOpDispatch {.compileTime.}: array[Op, NimNode] = genPetersburgJumpTable(ConstantinopleOpDispatch)
|
||||||
|
|
||||||
proc genIstanbulJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} =
|
proc genIstanbulJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} =
|
||||||
result = ops
|
result = ops
|
||||||
result[ChainId] = newIdentNode "chainId"
|
result[ChainId] = newIdentNode "chainId"
|
||||||
result[SelfBalance] = newIdentNode "selfBalance"
|
result[SelfBalance] = newIdentNode "selfBalance"
|
||||||
result[SStore] = newIdentNode "sstoreEIP2200"
|
result[SStore] = newIdentNode "sstoreEIP2200"
|
||||||
|
|
||||||
let IstanbulOpDispatch {.compileTime.}: array[Op, NimNode] = genIstanbulJumpTable(ConstantinopleOpDispatch)
|
let IstanbulOpDispatch {.compileTime.}: array[Op, NimNode] = genIstanbulJumpTable(PetersburgOpDispatch)
|
||||||
|
|
||||||
proc opTableToCaseStmt(opTable: array[Op, NimNode], c: NimNode): NimNode =
|
proc opTableToCaseStmt(opTable: array[Op, NimNode], c: NimNode): NimNode =
|
||||||
|
|
||||||
|
@ -296,6 +303,9 @@ macro genByzantiumDispatch(c: Computation): untyped =
|
||||||
macro genConstantinopleDispatch(c: Computation): untyped =
|
macro genConstantinopleDispatch(c: Computation): untyped =
|
||||||
result = opTableToCaseStmt(ConstantinopleOpDispatch, c)
|
result = opTableToCaseStmt(ConstantinopleOpDispatch, c)
|
||||||
|
|
||||||
|
macro genPetersburgDispatch(c: Computation): untyped =
|
||||||
|
result = opTableToCaseStmt(PetersburgOpDispatch, c)
|
||||||
|
|
||||||
macro genIstanbulDispatch(c: Computation): untyped =
|
macro genIstanbulDispatch(c: Computation): untyped =
|
||||||
result = opTableToCaseStmt(IstanbulOpDispatch, c)
|
result = opTableToCaseStmt(IstanbulOpDispatch, c)
|
||||||
|
|
||||||
|
@ -317,6 +327,9 @@ proc byzantiumVM(c: Computation) {.gcsafe.} =
|
||||||
proc constantinopleVM(c: Computation) {.gcsafe.} =
|
proc constantinopleVM(c: Computation) {.gcsafe.} =
|
||||||
genConstantinopleDispatch(c)
|
genConstantinopleDispatch(c)
|
||||||
|
|
||||||
|
proc petersburgVM(c: Computation) {.gcsafe.} =
|
||||||
|
genPetersburgDispatch(c)
|
||||||
|
|
||||||
proc istanbulVM(c: Computation) {.gcsafe.} =
|
proc istanbulVM(c: Computation) {.gcsafe.} =
|
||||||
genIstanbulDispatch(c)
|
genIstanbulDispatch(c)
|
||||||
|
|
||||||
|
@ -335,6 +348,8 @@ proc selectVM(c: Computation, fork: Fork) {.gcsafe.} =
|
||||||
c.byzantiumVM()
|
c.byzantiumVM()
|
||||||
of FkConstantinople:
|
of FkConstantinople:
|
||||||
c.constantinopleVM()
|
c.constantinopleVM()
|
||||||
|
of FkPetersburg:
|
||||||
|
c.petersburgVM()
|
||||||
else:
|
else:
|
||||||
c.istanbulVM()
|
c.istanbulVM()
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ proc parseBlocks(blocks: JsonNode, testStatusIMPL: var TestStatus): seq[TesterBl
|
||||||
func vmConfiguration(network: string): VMConfig =
|
func vmConfiguration(network: string): VMConfig =
|
||||||
case network
|
case network
|
||||||
of "EIP150": result = [(0, FkTangerine), (0, FkTangerine)]
|
of "EIP150": result = [(0, FkTangerine), (0, FkTangerine)]
|
||||||
of "ConstantinopleFix": result = [(0, FkConstantinople), (0, FkConstantinople)]
|
of "ConstantinopleFix": result = [(0, FkPetersburg), (0, FkPetersburg)]
|
||||||
of "Homestead": result = [(0, FkHomestead), (0, FkHomestead)]
|
of "Homestead": result = [(0, FkHomestead), (0, FkHomestead)]
|
||||||
of "Frontier": result = [(0, FkFrontier), (0, FkFrontier)]
|
of "Frontier": result = [(0, FkFrontier), (0, FkFrontier)]
|
||||||
of "Byzantium": result = [(0, FkByzantium), (0, FkByzantium)]
|
of "Byzantium": result = [(0, FkByzantium), (0, FkByzantium)]
|
||||||
|
@ -200,7 +200,7 @@ func vmConfiguration(network: string): VMConfig =
|
||||||
of "Constantinople": result = [(0, FkConstantinople), (0, FkConstantinople)]
|
of "Constantinople": result = [(0, FkConstantinople), (0, FkConstantinople)]
|
||||||
of "HomesteadToEIP150At5": result = [(0, FkHomestead), (5, FkTangerine)]
|
of "HomesteadToEIP150At5": result = [(0, FkHomestead), (5, FkTangerine)]
|
||||||
of "FrontierToHomesteadAt5": result = [(0, FkFrontier), (5, FkHomestead)]
|
of "FrontierToHomesteadAt5": result = [(0, FkFrontier), (5, FkHomestead)]
|
||||||
of "ByzantiumToConstantinopleFixAt5": result = [(0, FkByzantium), (5, FkConstantinople)]
|
of "ByzantiumToConstantinopleFixAt5": result = [(0, FkByzantium), (5, FkPetersburg)]
|
||||||
of "Istanbul": result = [(0, FkIstanbul), (0, FkIstanbul)]
|
of "Istanbul": result = [(0, FkIstanbul), (0, FkIstanbul)]
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "unsupported network")
|
raise newException(ValueError, "unsupported network")
|
||||||
|
@ -232,7 +232,7 @@ proc parseTester(fixture: JsonNode, testStatusIMPL: var TestStatus): Tester =
|
||||||
result.good = false
|
result.good = false
|
||||||
|
|
||||||
# TODO: implement missing VM
|
# TODO: implement missing VM
|
||||||
if network in ["Constantinople", "HomesteadToDaoAt5"]:
|
if network in ["HomesteadToDaoAt5"]:
|
||||||
result.good = false
|
result.good = false
|
||||||
|
|
||||||
proc assignBlockRewards(minedBlock: PlainBlock, vmState: BaseVMState, fork: Fork, chainDB: BaseChainDB) =
|
proc assignBlockRewards(minedBlock: PlainBlock, vmState: BaseVMState, fork: Fork, chainDB: BaseChainDB) =
|
||||||
|
|
|
@ -26,7 +26,8 @@ const
|
||||||
FkTangerine: "EIP150",
|
FkTangerine: "EIP150",
|
||||||
FkSpurious: "EIP158",
|
FkSpurious: "EIP158",
|
||||||
FkByzantium: "Byzantium",
|
FkByzantium: "Byzantium",
|
||||||
FkConstantinople: "ConstantinopleFix",
|
FkConstantinople: "Constantinople",
|
||||||
|
FkPetersburg: "ConstantinopleFix",
|
||||||
FkIstanbul: "Istanbul"
|
FkIstanbul: "Istanbul"
|
||||||
}.toTable
|
}.toTable
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ const
|
||||||
FkSpurious,
|
FkSpurious,
|
||||||
FkByzantium,
|
FkByzantium,
|
||||||
FkConstantinople,
|
FkConstantinople,
|
||||||
|
FkPetersburg,
|
||||||
FkIstanbul}
|
FkIstanbul}
|
||||||
|
|
||||||
nameToFork* = revmap(forkNames)
|
nameToFork* = revmap(forkNames)
|
||||||
|
|
Loading…
Reference in New Issue