From 93b04bc2149d5887e1ccbe496c0cdd499e3695d3 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 29 Jun 2020 20:30:19 +0300 Subject: [PATCH] Add an option for graffiti customization --- beacon_chain/beacon_node.nim | 13 +++--- beacon_chain/beacon_node_common.nim | 1 + beacon_chain/conf.nim | 21 ++++++++- beacon_chain/eth2_json_rpc_serialization.nim | 10 +++++ beacon_chain/spec/datatypes.nim | 43 +++++++++++++++++-- .../spec/eth2_apis/validator_callsigs.nim | 2 +- beacon_chain/spec/state_transition.nim | 2 +- beacon_chain/ssz/bytes_reader.nim | 5 +++ beacon_chain/ssz/spec_types.nim | 2 +- beacon_chain/validator_api.nim | 3 +- beacon_chain/validator_client.nim | 12 +++--- beacon_chain/validator_duties.nim | 9 ++-- beacon_chain/version.nim | 1 + research/block_sim.nim | 2 +- tests/test_attestation_pool.nim | 2 +- tests/testblockutil.nim | 4 +- 16 files changed, 98 insertions(+), 34 deletions(-) diff --git a/beacon_chain/beacon_node.nim b/beacon_chain/beacon_node.nim index 6ad1184b1..f88d88081 100644 --- a/beacon_chain/beacon_node.nim +++ b/beacon_chain/beacon_node.nim @@ -134,9 +134,9 @@ func enrForkIdFromState(state: BeaconState): ENRForkID = next_fork_version: forkVer, next_fork_epoch: FAR_FUTURE_EPOCH) -proc init*( - T: type BeaconNode, rng: ref BrHmacDrbgContext, - conf: BeaconNodeConf): Future[BeaconNode] {.async.} = +proc init*(T: type BeaconNode, + rng: ref BrHmacDrbgContext, + conf: BeaconNodeConf): Future[BeaconNode] {.async.} = let netKeys = getPersistentNetKeys(rng[], conf) nickname = if conf.nodeName == "auto": shortForm(netKeys) @@ -237,6 +237,8 @@ proc init*( var res = BeaconNode( nickname: nickname, + graffitiBytes: if conf.graffiti.isSome: conf.graffiti.get.GraffitiBytes + else: defaultGraffitiBytes(), network: network, netKeys: netKeys, db: db, @@ -635,11 +637,6 @@ proc currentSlot(node: BeaconNode): Slot = proc connectedPeersCount(node: BeaconNode): int = nbc_peers.value.int -func fromJson(n: JsonNode; argName: string; result: var Slot) = - var i: int - fromJson(n, argName, i) - result = Slot(i) - proc installBeaconApiHandlers(rpcServer: RpcServer, node: BeaconNode) = rpcServer.rpc("getBeaconHead") do () -> Slot: return node.blockPool.head.blck.slot diff --git a/beacon_chain/beacon_node_common.nim b/beacon_chain/beacon_node_common.nim index 8fd0f2a06..4097bed20 100644 --- a/beacon_chain/beacon_node_common.nim +++ b/beacon_chain/beacon_node_common.nim @@ -32,6 +32,7 @@ type BeaconNode* = ref object nickname*: string + graffitiBytes*: GraffitiBytes network*: Eth2Node netKeys*: KeyPair requestManager*: RequestManager diff --git a/beacon_chain/conf.nim b/beacon_chain/conf.nim index d6d1fc3b1..e90e33416 100644 --- a/beacon_chain/conf.nim +++ b/beacon_chain/conf.nim @@ -4,8 +4,8 @@ import os, options, chronicles, chronicles/options as chroniclesOptions, confutils, confutils/defs, confutils/std/net, - json_serialization, web3/ethtypes, - network_metadata, spec/[crypto, keystore, digest] + stew/byteutils, json_serialization, web3/ethtypes, + network_metadata, spec/[crypto, keystore, digest, datatypes] export defs, enabledLogLevel, parseCmdArg, completeCmdArg, @@ -142,6 +142,11 @@ type "If you set this to 'auto', a persistent automatically generated ID will be selected for each --data-dir folder" name: "node-name" }: string + graffiti* {. + desc: "The graffiti value that will appear in proposed blocks. " & + "You can use a 0x-prefixed hex encoded string to specify raw bytes." + name: "graffiti" }: Option[GraffitiBytes] + verifyFinalization* {. defaultValue: false desc: "Specify whether to verify finalization occurs on schedule, for testing" @@ -361,6 +366,11 @@ type defaultValue: VCNoCommand }: VCStartUpCmd of VCNoCommand: + graffiti* {. + desc: "The graffiti value that will appear in proposed blocks. " & + "You can use a 0x-prefixed hex encoded string to specify raw bytes." + name: "graffiti" }: Option[GraffitiBytes] + rpcPort* {. defaultValue: defaultEth2RpcPort desc: "HTTP port of the server to connect to for RPC" @@ -431,6 +441,13 @@ func parseCmdArg*(T: type Eth1BlockHash, input: TaintedString): T func completeCmdArg*(T: type Eth1BlockHash, input: TaintedString): seq[string] = return @[] +func parseCmdArg*(T: type GraffitiBytes, input: TaintedString): T + {.raises: [ValueError, Defect].} = + GraffitiBytes.init(string input) + +func completeCmdArg*(T: type GraffitiBytes, input: TaintedString): seq[string] = + return @[] + func parseCmdArg*(T: type WalletName, input: TaintedString): T {.raises: [ValueError, Defect].} = if input.len == 0: diff --git a/beacon_chain/eth2_json_rpc_serialization.nim b/beacon_chain/eth2_json_rpc_serialization.nim index ad23a1ec4..63212a7ee 100644 --- a/beacon_chain/eth2_json_rpc_serialization.nim +++ b/beacon_chain/eth2_json_rpc_serialization.nim @@ -10,6 +10,7 @@ import spec/[datatypes, crypto] proc fromJson*(n: JsonNode, argName: string, result: var ValidatorPubKey) = + n.kind.expect(JString, argName) result = ValidatorPubKey.fromHex(n.getStr()).tryGet() proc `%`*(pubkey: ValidatorPubKey): JsonNode = @@ -26,12 +27,14 @@ proc fromJson*(n: JsonNode, argName: string, result: var BitList) = proc `%`*(bitlist: BitList): JsonNode = %(seq[byte](BitSeq(bitlist))) proc fromJson*(n: JsonNode, argName: string, result: var ValidatorSig) = + n.kind.expect(JString, argName) result = ValidatorSig.fromHex(n.getStr()).tryGet() proc `%`*(value: ValidatorSig): JsonNode = result = newJString($value) proc fromJson*(n: JsonNode, argName: string, result: var Version) = + n.kind.expect(JString, argName) hexToByteArray(n.getStr(), array[4, byte](result)) proc `%`*(value: Version): JsonNode = @@ -46,5 +49,12 @@ genFromJsonForIntType(Epoch) genFromJsonForIntType(Slot) genFromJsonForIntType(CommitteeIndex) +template `%`*(value: GraffitiBytes): JsonNode = + %($value) + +proc fromJson*(n: JsonNode, argName: string, value: var GraffitiBytes) = + n.kind.expect(JString, argName) + value = GraffitiBytes.init n.getStr() + proc `%`*(value: CommitteeIndex): JsonNode = result = newJInt(value.int) diff --git a/beacon_chain/spec/datatypes.nim b/beacon_chain/spec/datatypes.nim index 7ebb56fb1..737ab126f 100644 --- a/beacon_chain/spec/datatypes.nim +++ b/beacon_chain/spec/datatypes.nim @@ -25,7 +25,7 @@ import macros, hashes, json, strutils, tables, typetraits, stew/[byteutils], chronicles, json_serialization/types as jsonTypes, - ../ssz/types as sszTypes, ./crypto, ./digest, ./presets + ../version, ../ssz/types as sszTypes, ./crypto, ./digest, ./presets export sszTypes, presets @@ -257,11 +257,13 @@ type object_root*: Eth2Digest domain*: Domain + GraffitiBytes* = distinct array[32, byte] + # https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#beaconblockbody BeaconBlockBody* = object randao_reveal*: ValidatorSig eth1_data*: Eth1Data - graffiti*: Eth2Digest # TODO make that raw bytes + graffiti*: GraffitiBytes # Operations proposer_slashings*: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] @@ -273,7 +275,7 @@ type TrustedBeaconBlockBody* = object randao_reveal*: TrustedSig eth1_data*: Eth1Data - graffiti*: Eth2Digest # TODO make that raw bytes + graffiti*: GraffitiBytes # Operations proposer_slashings*: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] @@ -676,6 +678,41 @@ import json_serialization export json_serialization export writeValue, readValue +func `$`*(value: GraffitiBytes): string = + strip(string.fromBytes(distinctBase value), chars = Whitespace + {'\0'}) + +func init*(T: type GraffitiBytes, input: string): GraffitiBytes + {.raises: [ValueError, Defect].} = + if input.len > 2 and input[0] == '0' and input[1] == 'x': + if input.len > sizeof(GraffitiBytes) * 2 + 2: + raise newException(ValueError, "The graffiti bytes should be less than 32") + elif input.len mod 2 != 0: + raise newException(ValueError, "The graffiti hex string should have an even length") + + hexToByteArray(string input, distinctBase(result)) + else: + if input.len > 32: + raise newException(ValueError, "The graffiti value should be 32 characters or less") + distinctBase(result)[0 ..< input.len] = toBytes(input) + +func defaultGraffitiBytes*(): GraffitiBytes = + let graffityBytes = toBytes("Nimbus " & fullVersionStr) + distinctBase(result)[0 ..< graffityBytes.len] = graffityBytes + +proc writeValue*(w: var JsonWriter, value: GraffitiBytes) + {.raises: [IOError, Defect].} = + w.writeValue $value + +template `==`*(lhs, rhs: GraffitiBytes): bool = + distinctBase(lhs) == distinctBase(rhs) + +proc readValue*(r: var JsonReader, T: type GraffitiBytes): T + {.raises: [IOError, SerializationError, Defect].} = + try: + init(GraffitiBytes, r.readValue(string)) + except ValueError as err: + r.raiseUnexpectedValue err.msg + static: # Sanity checks - these types should be trivial enough to copy with memcpy doAssert supportsCopyMem(Validator) diff --git a/beacon_chain/spec/eth2_apis/validator_callsigs.nim b/beacon_chain/spec/eth2_apis/validator_callsigs.nim index 7fcfc2a4b..2fdf11032 100644 --- a/beacon_chain/spec/eth2_apis/validator_callsigs.nim +++ b/beacon_chain/spec/eth2_apis/validator_callsigs.nim @@ -10,7 +10,7 @@ import # calls that return a bool are actually without a return type in the main REST API # spec but nim-json-rpc requires that all RPC calls have a return type. -proc get_v1_validator_block(slot: Slot, graffiti: Eth2Digest, randao_reveal: ValidatorSig): BeaconBlock +proc get_v1_validator_block(slot: Slot, graffiti: GraffitiBytes, randao_reveal: ValidatorSig): BeaconBlock proc post_v1_validator_block(body: SignedBeaconBlock): bool diff --git a/beacon_chain/spec/state_transition.nim b/beacon_chain/spec/state_transition.nim index e177afd69..80d2d23b6 100644 --- a/beacon_chain/spec/state_transition.nim +++ b/beacon_chain/spec/state_transition.nim @@ -273,7 +273,7 @@ proc makeBeaconBlock*( parent_root: Eth2Digest, randao_reveal: ValidatorSig, eth1_data: Eth1Data, - graffiti: Eth2Digest, + graffiti: GraffitiBytes, attestations: seq[Attestation], deposits: seq[Deposit], rollback: RollbackHashedProc, diff --git a/beacon_chain/ssz/bytes_reader.nim b/beacon_chain/ssz/bytes_reader.nim index 58602f76c..1b2318b86 100644 --- a/beacon_chain/ssz/bytes_reader.nim +++ b/beacon_chain/ssz/bytes_reader.nim @@ -41,6 +41,11 @@ func fromSszBytes*(T: type Eth2Digest, data: openarray[byte]): T {.raisesssz.} = raiseIncorrectSize T copyMem(result.data.addr, unsafeAddr data[0], sizeof(result.data)) +func fromSszBytes*(T: type GraffitiBytes, data: openarray[byte]): T {.raisesssz.} = + if data.len != sizeof(result): + raiseIncorrectSize T + copyMem(result.addr, unsafeAddr data[0], sizeof(result)) + template fromSszBytes*(T: type Slot, bytes: openarray[byte]): Slot = Slot fromSszBytes(uint64, bytes) diff --git a/beacon_chain/ssz/spec_types.nim b/beacon_chain/ssz/spec_types.nim index accb01c27..d1e9feb1b 100644 --- a/beacon_chain/ssz/spec_types.nim +++ b/beacon_chain/ssz/spec_types.nim @@ -15,5 +15,5 @@ template toSszType*(x: auto): auto = when x is Slot|Epoch|ValidatorIndex: uint64(x) elif x is Eth2Digest: x.data elif x is BlsCurveType: toRaw(x) - elif x is ForkDigest|Version: distinctBase(x) + elif x is ForkDigest|Version|GraffitiBytes: distinctBase(x) else: x diff --git a/beacon_chain/validator_api.nim b/beacon_chain/validator_api.nim index 52bfda20b..de8df238c 100644 --- a/beacon_chain/validator_api.nim +++ b/beacon_chain/validator_api.nim @@ -300,10 +300,9 @@ proc installValidatorApiHandlers*(rpcServer: RpcServer, node: BeaconNode) = return state rpcServer.rpc("get_v1_validator_block") do ( - slot: Slot, graffiti: Eth2Digest, randao_reveal: ValidatorSig) -> BeaconBlock: + slot: Slot, graffiti: GraffitiBytes, randao_reveal: ValidatorSig) -> BeaconBlock: debug "get_v1_validator_block", slot = slot let head = node.doChecksAndGetCurrentHead(slot) - let proposer = node.blockPool.getProposer(head, slot) if proposer.isNone(): raise newException(CatchableError, "could not retrieve block for slot: " & $slot) diff --git a/beacon_chain/validator_client.nim b/beacon_chain/validator_client.nim index a3b9eeb68..2a79783a2 100644 --- a/beacon_chain/validator_client.nim +++ b/beacon_chain/validator_client.nim @@ -17,7 +17,7 @@ import # Local modules spec/[datatypes, digest, crypto, helpers, network], - conf, time, + conf, time, version, eth2_network, eth2_discovery, validator_pool, beacon_node_types, nimbus_binary_common, version, ssz/merkleization, @@ -36,6 +36,7 @@ createRpcSigs(RpcClient, sourceDir / "spec" / "eth2_apis" / "beacon_callsigs.nim type ValidatorClient = ref object config: ValidatorClientConf + graffitiBytes: GraffitiBytes client: RpcHttpClient beaconClock: BeaconClock attachedValidators: ValidatorPool @@ -136,10 +137,8 @@ proc onSlotStart(vc: ValidatorClient, lastSlot, scheduledSlot: Slot) {.gcsafe, a let randao_reveal = validator.genRandaoReveal( vc.fork, vc.beaconGenesis.genesis_validators_root, slot) - var graffiti: Eth2Digest - graffiti.data[0..<5] = toBytes("quack") var newBlock = SignedBeaconBlock( - message: await vc.client.get_v1_validator_block(slot, graffiti, randao_reveal) + message: await vc.client.get_v1_validator_block(slot, vc.graffitiBytes, randao_reveal) ) let blockRoot = hash_tree_root(newBlock.message) @@ -212,8 +211,9 @@ programMain: var vc = ValidatorClient( config: config, - client: newRpcHttpClient() - ) + client: newRpcHttpClient(), + graffitiBytes: if config.graffiti.isSome: config.graffiti.get.GraffitiBytes + else: defaultGraffitiBytes()) # load all the validators from the data dir into memory for curr in vc.config.validatorKeys: diff --git a/beacon_chain/validator_duties.nim b/beacon_chain/validator_duties.nim index 4990a2a14..006098a7f 100644 --- a/beacon_chain/validator_duties.nim +++ b/beacon_chain/validator_duties.nim @@ -10,7 +10,7 @@ import os, tables, strutils, # Nimble packages - stew/[byteutils, objects], stew/shims/macros, + stew/[objects], stew/shims/macros, chronos, metrics, json_rpc/[rpcserver, jsonmarshal], chronicles, json_serialization/std/[options, sets, net], serialization/errors, @@ -160,7 +160,7 @@ type proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode, val_info: ValidatorInfoForMakeBeaconBlock, validator_index: ValidatorIndex, - graffiti: Eth2Digest, + graffiti: GraffitiBytes, head: BlockRef, slot: Slot): tuple[message: Option[BeaconBlock], fork: Fork, genesis_validators_root: Eth2Digest] = @@ -271,11 +271,8 @@ proc proposeBlock(node: BeaconNode, cat = "fastforward" return head - var graffiti: Eth2Digest - graffiti.data[0..<5] = toBytes("quack") let valInfo = ValidatorInfoForMakeBeaconBlock(kind: viValidator, validator: validator) - let beaconBlockTuple = makeBeaconBlockForHeadAndSlot(node, valInfo, validator_index, graffiti, head, slot) - + let beaconBlockTuple = makeBeaconBlockForHeadAndSlot(node, valInfo, validator_index, node.graffitiBytes, head, slot) if not beaconBlockTuple.message.isSome(): return head # already logged elsewhere! var diff --git a/beacon_chain/version.nim b/beacon_chain/version.nim index 3ca3b5218..2707a4426 100644 --- a/beacon_chain/version.nim +++ b/beacon_chain/version.nim @@ -37,3 +37,4 @@ func shortNimBanner*(): string = tmp[0] & " (" & gitHash & ")" else: tmp[0] + diff --git a/research/block_sim.nim b/research/block_sim.nim index c70e65c41..60064edd7 100644 --- a/research/block_sim.nim +++ b/research/block_sim.nim @@ -114,7 +114,7 @@ cli do(slots = SLOTS_PER_EPOCH * 6, head.root, privKey.genRandaoReveal(state.fork, state.genesis_validators_root, slot), eth1data, - Eth2Digest(), + default(GraffitiBytes), attPool.getAttestationsForBlock(state), @[], noRollback, diff --git a/tests/test_attestation_pool.nim b/tests/test_attestation_pool.nim index e007b5551..632e57361 100644 --- a/tests/test_attestation_pool.nim +++ b/tests/test_attestation_pool.nim @@ -204,7 +204,7 @@ suiteReport "Attestation pool processing" & preset(): let b11 = makeTestBlock(state.data, blockPool[].tail.root, cache, - graffiti = Eth2Digest(data: [1'u8, 0, 0, 0 ,0 ,0 ,0 ,0 ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + graffiti = GraffitiBytes [1'u8, 0, 0, 0 ,0 ,0 ,0 ,0 ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ) b11Root = hash_tree_root(b11.message) b11Add = blockpool[].addRawBlock(b11Root, b11) do (validBlock: BlockRef): diff --git a/tests/testblockutil.nim b/tests/testblockutil.nim index 76dd8d8f3..89e05e114 100644 --- a/tests/testblockutil.nim +++ b/tests/testblockutil.nim @@ -91,7 +91,7 @@ proc addTestBlock*( eth1_data = Eth1Data(), attestations = newSeq[Attestation](), deposits = newSeq[Deposit](), - graffiti = Eth2Digest(), + graffiti = default(GraffitiBytes), flags: set[UpdateFlag] = {}): SignedBeaconBlock = # Create and add a block to state - state will advance by one slot! advance_slot(state, flags, cache) @@ -140,7 +140,7 @@ proc makeTestBlock*( eth1_data = Eth1Data(), attestations = newSeq[Attestation](), deposits = newSeq[Deposit](), - graffiti = Eth2Digest(), + graffiti = default(GraffitiBytes), flags: set[UpdateFlag] = {}): SignedBeaconBlock = # Create a block for `state.slot + 1` - like a block proposer would do! # It's a bit awkward - in order to produce a block for N+1, we need to