From d4571c01dd6ffa2f2eb4756f370c9ea5d065e82f Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 27 Jul 2018 18:02:02 +0100 Subject: [PATCH] Add JSON RPC hex strings support --- nimbus/rpc/hexstrings.nim | 103 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 nimbus/rpc/hexstrings.nim diff --git a/nimbus/rpc/hexstrings.nim b/nimbus/rpc/hexstrings.nim new file mode 100644 index 000000000..0ad4f78ab --- /dev/null +++ b/nimbus/rpc/hexstrings.nim @@ -0,0 +1,103 @@ +# Nimbus +# Copyright (c) 2018 Status Research & Development GmbH +# Licensed under either of +# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +# * MIT license ([LICENSE-MIT](LICENSE-MIT)) +# at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +## This module implements the Ethereum hexadecimal string formats for JSON +## See: https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding + +type + HexQuantityStr* = distinct string + HexDataStr* = distinct string + +# Hex validation + +template stripLeadingZeros(value: string): string = + var cidx = 0 + # ignore the last character so we retain '0' on zero value + while cidx < value.len - 1 and value[cidx] == '0': + cidx.inc + value[cidx .. ^1] + +proc encodeQuantity*(value: SomeUnsignedInt): string = + var hValue = value.toHex.stripLeadingZeros + result = "0x" & hValue + +template hasHexHeader*(value: string): bool = + if value != "" and value[0] == '0' and value[1] in {'x', 'X'} and value.len > 2: true + else: false + +template isHexChar*(c: char): bool = + if c notin {'0'..'9'} and + c notin {'a'..'f'} and + c notin {'A'..'F'}: false + else: true + +proc validateHexQuantity*(value: string): bool = + if not value.hasHexHeader: + return false + # No leading zeros + if value[2] == '0': return false + for i in 2 ..< value.len: + let c = value[i] + if not c.isHexChar: + return false + return true + +proc validateHexData*(value: string): bool = + if not value.hasHexHeader: + return false + # Must be even number of digits + if value.len mod 2 != 0: return false + # Leading zeros are allowed + for i in 2 ..< value.len: + let c = value[i] + if not c.isHexChar: + return false + return true + +# Initialisation + +proc hexQuantityStr*(value: string): HexQuantityStr = + if not value.validateHexQuantity: + raise newException(ValueError, "Invalid hex quantity format for Ethereum: " & value) + else: + result = value.HexQuantityStr + +proc hexDataStr*(value: string): HexDataStr = + if not value.validateHexData: + raise newException(ValueError, "Invalid hex data format for Ethereum: " & value) + else: + result = value.HexDataStr + +# Converters for use in RPC + +import json +from json_rpc/rpcserver import expect + +proc `%`*(value: HexQuantityStr): JsonNode = + result = %(value.string) + +proc `%`*(value: HexDataStr): JsonNode = + result = %(value.string) + +proc fromJson*(n: JsonNode, argName: string, result: var HexQuantityStr) = + # Note that '0x' is stripped after validation + n.kind.expect(JString, argName) + let hexStr = n.getStr() + if not hexStr.validateHexQuantity: + raise newException(ValueError, "Parameter \"" & argName & "\" value is not valid as an Ethereum hex quantity \"" & hexStr & "\"") + result = hexStr[2..hexStr.high].hexQuantityStr + +proc fromJson*(n: JsonNode, argName: string, result: var HexDataStr) = + # Note that '0x' is stripped after validation + n.kind.expect(JString, argName) + let hexStr = n.getStr() + if not hexStr.validateHexData: + raise newException(ValueError, "Parameter \"" & argName & "\" value is not valid as a Ethereum data \"" & hexStr & "\"") + result = hexStr[2..hexStr.high].hexDataStr +