mirror of
https://github.com/codex-storage/nim-json-rpc.git
synced 2025-02-23 00:18:17 +00:00
Add validated hex string type
This commit is contained in:
parent
a5467063b7
commit
81d52cb3cb
151
tests/ethhexstrings.nim
Normal file
151
tests/ethhexstrings.nim
Normal file
@ -0,0 +1,151 @@
|
||||
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 | HexDataStr | HexQuantityStr): bool =
|
||||
template strVal: untyped = value.string
|
||||
if strVal[0] == '0' and strVal[1] in {'x', 'X'} and strVal.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 validate*(value: HexQuantityStr): bool =
|
||||
template strVal: untyped = value.string
|
||||
if not value.hasHexHeader:
|
||||
return false
|
||||
# No leading zeros
|
||||
if strVal[2] == '0': return false
|
||||
for i in 2..<strVal.len:
|
||||
let c = strVal[i]
|
||||
if not c.isHexChar:
|
||||
return false
|
||||
return true
|
||||
|
||||
proc validate*(value: HexDataStr): bool =
|
||||
template strVal: untyped = value.string
|
||||
if not value.hasHexHeader:
|
||||
return false
|
||||
# Leading zeros are allowed
|
||||
for i in 2..<strVal.len:
|
||||
let c = strVal[i]
|
||||
if not c.isHexChar:
|
||||
return false
|
||||
# Must be even number of digits
|
||||
if strVal.len mod 2 != 0: return false
|
||||
return true
|
||||
|
||||
# Initialisation
|
||||
|
||||
template hexDataStr*(value: string): HexDataStr = value.HexDataStr
|
||||
template hexQuantityStr*(value: string): HexQuantityStr = value.HexQuantityStr
|
||||
|
||||
# Converters
|
||||
|
||||
import json
|
||||
from ../rpcserver import expect
|
||||
|
||||
proc `%`*(value: HexDataStr): JsonNode =
|
||||
if not value.validate:
|
||||
raise newException(ValueError, "HexDataStr: Invalid hex for Ethereum: " & value.string)
|
||||
else:
|
||||
result = %(value.string)
|
||||
|
||||
proc `%`*(value: HexQuantityStr): JsonNode =
|
||||
if not value.validate:
|
||||
raise newException(ValueError, "HexQuantityStr: Invalid hex for Ethereum: " & value.string)
|
||||
else:
|
||||
result = %(value.string)
|
||||
|
||||
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.hexDataStr.validate:
|
||||
raise newException(ValueError, "Parameter \"" & argName & "\" value is not valid as a Ethereum data \"" & hexStr & "\"")
|
||||
result = hexStr[2..hexStr.high].hexDataStr
|
||||
|
||||
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.hexQuantityStr.validate:
|
||||
raise newException(ValueError, "Parameter \"" & argName & "\" value is not valid as an Ethereum hex quantity \"" & hexStr & "\"")
|
||||
result = hexStr[2..hexStr.high].hexQuantityStr
|
||||
|
||||
# testing
|
||||
|
||||
when isMainModule:
|
||||
import unittest
|
||||
suite "Hex quantity":
|
||||
test "Even length":
|
||||
let
|
||||
source = "0x123"
|
||||
x = hexQuantityStr source
|
||||
check %x == %source
|
||||
test "Odd length":
|
||||
let
|
||||
source = "0x123"
|
||||
x = hexQuantityStr"0x123"
|
||||
check %x == %source
|
||||
test "Missing header":
|
||||
expect ValueError:
|
||||
let
|
||||
source = "1234"
|
||||
x = hexQuantityStr source
|
||||
check %x != %source
|
||||
expect ValueError:
|
||||
let
|
||||
source = "01234"
|
||||
x = hexQuantityStr source
|
||||
check %x != %source
|
||||
expect ValueError:
|
||||
let
|
||||
source = "x1234"
|
||||
x = hexQuantityStr source
|
||||
check %x != %source
|
||||
|
||||
suite "Hex data":
|
||||
test "Even length":
|
||||
let
|
||||
source = "0x1234"
|
||||
x = hexDataStr source
|
||||
check %x == %source
|
||||
test "Odd length":
|
||||
expect ValueError:
|
||||
let
|
||||
source = "0x123"
|
||||
x = hexDataStr source
|
||||
check %x != %source
|
||||
test "Missing header":
|
||||
expect ValueError:
|
||||
let
|
||||
source = "1234"
|
||||
x = hexDataStr source
|
||||
check %x != %source
|
||||
expect ValueError:
|
||||
let
|
||||
source = "01234"
|
||||
x = hexDataStr source
|
||||
check %x != %source
|
||||
expect ValueError:
|
||||
let
|
||||
source = "x1234"
|
||||
x = hexDataStr source
|
||||
check %x != %source
|
@ -1,4 +1,4 @@
|
||||
import ../rpcserver, nimcrypto, json, stint, strutils, ethtypes, stintjson, ethutils
|
||||
import ../rpcserver, nimcrypto, json, stint, strutils, ethtypes, stintjson, ethhexstrings
|
||||
|
||||
#[
|
||||
For details on available RPC calls, see: https://github.com/ethereum/wiki/wiki/JSON-RPC
|
||||
@ -31,19 +31,16 @@ proc addEthRpcs*(server: RpcServer) =
|
||||
## Returns the current client version.
|
||||
result = "Nimbus-RPC-Test"
|
||||
|
||||
server.rpc("web3_sha3") do(data: string) -> string:
|
||||
server.rpc("web3_sha3") do(data: HexDataStr) -> HexDataStr:
|
||||
## Returns Keccak-256 (not the standardized SHA3-256) of the given data.
|
||||
##
|
||||
## data: the data to convert into a SHA3 hash.
|
||||
## Returns the SHA3 result of the given string.
|
||||
# TODO: Capture error on malformed input
|
||||
var rawData: seq[byte]
|
||||
if data.validateHexData:
|
||||
rawData = data[2..data.high].fromHex
|
||||
else:
|
||||
raise newException(ValueError, "Invalid hex format")
|
||||
rawData = data.string.fromHex
|
||||
# data will have 0x prefix
|
||||
result = "0x" & $keccak_256.digest(rawData)
|
||||
result = hexDataStr "0x" & $keccak_256.digest(rawData)
|
||||
|
||||
server.rpc("net_version") do() -> string:
|
||||
## Returns string of the current network id:
|
||||
|
@ -1,43 +0,0 @@
|
||||
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[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
|
||||
# Leading zeros are allowed
|
||||
for i in 2..<value.len:
|
||||
let c = value[i]
|
||||
if not c.isHexChar:
|
||||
return false
|
||||
# Must be even number of digits
|
||||
if value.len mod 2 != 0: return false
|
||||
return true
|
@ -1,6 +1,6 @@
|
||||
import unittest, json, tables
|
||||
import ../rpcclient, ../rpcstreamservers
|
||||
import stint, ethtypes, ethprocs, stintjson, nimcrypto
|
||||
import stint, ethtypes, ethprocs, stintjson, nimcrypto, ethhexstrings
|
||||
|
||||
from os import getCurrentDir, DirSep
|
||||
from strutils import rsplit
|
||||
|
Loading…
x
Reference in New Issue
Block a user