This commit is contained in:
mratsim 2018-05-08 13:50:05 +02:00
commit c296e14dde
5 changed files with 161 additions and 23 deletions

View File

@ -117,7 +117,7 @@ func unsafeConv[bits: static[int]](n: range[0..16], T: typedesc[Stint[bits]|Stui
else:
r_ptr[r_ptr[].len - 1] = n.byte
func parse*[bits: static[int]](T: typedesc[Stuint[bits]], input: string, base: static[int]): T =
func parse*[bits: static[int]](input: string, T: typedesc[Stuint[bits]], base: static[int]): T =
## Parse a string and store the result in a Stint[bits] or Stuint[bits].
static: assert (base >= 2) and base <= 16, "Only base from 2..16 are supported"
@ -138,7 +138,7 @@ func parse*[bits: static[int]](T: typedesc[Stuint[bits]], input: string, base: s
result = result * radix + input[curr].readHexChar.unsafeConv(T)
nextNonBlank(curr, input)
func parse*[bits: static[int]](T: typedesc[Stint[bits]], input: string, base: static[int]): T =
func parse*[bits: static[int]](input: string, T: typedesc[Stint[bits]], base: static[int]): T =
## Parse a string and store the result in a Stint[bits] or Stuint[bits].
static: assert (base >= 2) and base <= 16, "Only base from 2..16 are supported"
@ -176,12 +176,16 @@ func parse*[bits: static[int]](T: typedesc[Stint[bits]], input: string, base: st
else:
result = cast[Stint[bits]](no_overflow)
func parse*[bits: static[int]](T: typedesc[Stint[bits]|Stuint[bits]], input: string): T {.inline.}=
func parse*[bits: static[int]](input: string, T: typedesc[Stint[bits]|Stuint[bits]]): T {.inline.}=
## Parse a string and store the result in a Stint[bits] or Stuint[bits].
## Input is considered a decimal string.
# TODO: Have a default static argument in the previous proc. Currently we get
# "Cannot evaluate at compile-time" in several places (2018-04-26).
parse(T, input, 10)
parse(input, T, 10)
func hexToUint*[bits: static[int]](hexString: string): Stuint[bits] {.inline.} =
## Convert an hex string to the corresponding unsigned integer
parse(hexString, type result, base = 16)
func toString*[bits: static[int]](num: StUint[bits], base: static[int]): string =
## Convert a Stint or Stuint to string.
@ -262,7 +266,7 @@ func dumpHex*(x: Stint or StUint, order: static[Endianness]): string =
hexChars = "0123456789abcdef"
size = getSize(x.data) div 8
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let bytes {.restrict.}= cast[ptr array[size, byte]](x.unsafeaddr)
result = newString(2*size)
@ -281,3 +285,40 @@ func dumpHex*(x: Stint or StUint): string {.inline.}=
dumpHex(x, bigEndian)
# TODO: Have a default static argument in the previous proc. Currently we get
# "Cannot evaluate at compile-time".
func readUintBE*[bits: static[int]](ba: openarray[byte]): Stuint[bits] =
## Convert a big-endian array of (bits div 8) Bytes to an UInt[bits] (in native host endianness)
## Input:
## - a big-endian openarray of size (bits div 8) at least
## Returns:
## - A unsigned integer of the same size with `bits` bits
##
## ⚠ If the openarray length is bigger than bits div 8, part converted is undefined behaviour.
const N = bits div 8
assert(ba.len == N)
let r_ptr = cast[ptr array[N, byte]](result.addr)
for i, val in ba:
when system.cpuEndian == bigEndian:
# TODO: due to https://github.com/status-im/nim-stint/issues/38
# We can't cast a stack byte array to stuint with a convenient proc signature.
r_ptr[i] = val
else:
r_ptr[N-1 - i] = val
func toByteArrayBE*[bits: static[int]](n: StUint[bits]): array[bits div 8, byte] =
## Convert a uint[bits] to to a big-endian array of bits div 8 bytes
## Input:
## - an unsigned integer
## Returns:
## - a big-endian array of the same size
const N = bits div 8
when system.cpuEndian == bigEndian:
result = cast[type result](n)
else:
let n_ptr = cast[ptr array[N, byte]](n.unsafeAddr)
for i in 0 ..< N:
result[N-1 - i] = n_ptr[i]

View File

@ -133,7 +133,7 @@ macro asSignedWordsZip*[T](
# if we have multiple iterations to do
if system.cpuEndian == bigEndian:
result = quote do:
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let
`next_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
`next_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
@ -143,7 +143,7 @@ macro asSignedWordsZip*[T](
else:
# Little-Endian, iteration in reverse
result = quote do:
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let
`next_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
`next_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)

View File

@ -159,7 +159,7 @@ macro asWordsZip*(x, y: UintImpl or IntImpl, ignoreEndianness: static[bool], loo
else:
if ignoreEndianness or system.cpuEndian == bigEndian:
result = quote do:
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
`inner_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
@ -168,7 +168,7 @@ macro asWordsZip*(x, y: UintImpl or IntImpl, ignoreEndianness: static[bool], loo
else:
# Little-Endian, iteration in reverse
result = quote do:
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
`inner_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
@ -224,7 +224,7 @@ macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x: T,
else:
if ignoreEndianness or system.cpuEndian == bigEndian:
result = quote do:
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let
`inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr)
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
@ -233,7 +233,7 @@ macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x: T,
else:
# Little-Endian, iteration in reverse
result = quote do:
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let
`inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr)
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
@ -298,7 +298,7 @@ macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x, y: T,
else:
if ignoreEndianness or system.cpuEndian == bigEndian:
result = quote do:
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let
`inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr)
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
@ -308,7 +308,7 @@ macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x, y: T,
else:
# Little-Endian, iteration in reverse
result = quote do:
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let
`inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr)
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)

View File

@ -20,7 +20,7 @@ template make_conv(conv_name: untyped, size: int): untyped =
func `convname`*(n: SomeInteger): StUint[size] {.inline.}=
n.stuint(size)
func `convname`*(input: string): StUint[size] {.inline.}=
parse(Stuint[size], input)
input.parse(Stuint[size])
make_conv(u128, 128)
make_conv(u256, 256)
@ -29,7 +29,7 @@ template make_conv(conv_name: untyped, size: int): untyped =
func `convname`*(n: SomeInteger): Stint[size] {.inline.}=
n.stint(size)
func `convname`*(input: string): Stint[size] {.inline.}=
parse(Stint[size], input)
input.parse(Stint[size])
make_conv(i128, 128)
make_conv(i256, 256)

View File

@ -12,21 +12,21 @@ import ../src/stint, unittest, strutils
suite "Testing input and output procedures":
test "Creation from decimal strings":
block:
let a = parse(Stint[64], "123456789")
let a = "123456789".parse(Stint[64])
let b = 123456789.stint(64)
check: a == b
check: 123456789'i64 == cast[int64](a)
block:
let a = parse(Stuint[64], "123456789")
let a = "123456789".parse(Stuint[64])
let b = 123456789.stuint(64)
check: a == b
check: 123456789'u64 == cast[uint64](a)
block:
let a = parse(Stint[64], "-123456789")
let a = "-123456789".parse(Stint[64])
let b = (-123456789).stint(64)
check: a == b
@ -34,21 +34,24 @@ suite "Testing input and output procedures":
test "Creation from hex strings":
block:
let a = parse(Stint[64], "0xFF", 16)
let a = "0xFF".parse(Stint[64], base = 16)
let b = 255.stint(64)
check: a == b
check: 255'i64 == cast[int64](a)
block:
let a = parse(Stuint[64], "0xFF", 16)
let a = "0xFF".parse(Stuint[64], base = 16)
let b = 255.stuint(64)
check: a == b
check: 255'u64 == cast[uint64](a)
let a2 = hexToUint[64]("0xFF")
check: a == a2
block:
let a = parse(Stint[16], "0xFFFF", 16)
let a = "0xFFFF".parse(Stint[16], 16)
let b = (-1'i16).stint(16)
check: a == b
@ -90,10 +93,104 @@ suite "Testing input and output procedures":
test "Back and forth bigint conversion consistency":
block:
let s = "1234567890123456789012345678901234567890123456789"
let a = parse(StInt[512], s)
let a = parse(s, StInt[512])
check: a.toString == s
block:
let s = "1234567890123456789012345678901234567890123456789"
let a = parse(StUInt[512], s)
let a = parse(s, StUInt[512])
check: a.toString == s
suite "Testing conversion functions: Hex, Bytes, Endianness using secp256k1 curve":
let
SECPK1_N_HEX = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141".toLowerAscii
SECPK1_N = "115792089237316195423570985008687907852837564279074904382605163141518161494337".u256
SECPK1_N_BYTES = [byte(255), 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 186, 174, 220, 230, 175, 72, 160, 59, 191, 210, 94, 140, 208, 54, 65, 65]
test "hex -> uint256":
check: SECPK1_N_HEX.parse(Stuint[256], base = 16) == SECPK1_N
test "uint256 -> hex":
check: SECPK1_N.dumpHex == SECPK1_N_HEX
test "hex -> big-endian array -> uint256":
check: readUintBE[256](SECPK1_N_BYTES) == SECPK1_N
test "uint256 -> big-endian array -> hex":
check: SECPK1_N.toByteArrayBE == SECPK1_N_BYTES
# This is a sample of signatures generated with a known-good implementation of the ECDSA
# algorithm, which we use to test our ECC backends. If necessary, it can be generated from scratch
# with the following code:
#
# """python
# from devp2p import crypto
# from eth_utils import encode_hex
# msg = b'message'
# msghash = crypto.sha3(b'message')
# for secret in ['alice', 'bob', 'eve']:
# print("'{}': dict(".format(secret))
# privkey = crypto.mk_privkey(secret)
# pubkey = crypto.privtopub(privkey)
# print(" privkey='{}',".format(encode_hex(privkey)))
# print(" pubkey='{}',".format(encode_hex(crypto.privtopub(privkey))))
# ecc = crypto.ECCx(raw_privkey=privkey)
# sig = ecc.sign(msghash)
# print(" sig='{}',".format(encode_hex(sig)))
# print(" raw_sig='{}')".format(crypto._decode_sig(sig)))
# assert crypto.ecdsa_recover(msghash, sig) == pubkey
# """
type
testKeySig = object
privkey*: string
pubkey*: string
raw_sig*: tuple[v: int, r, s: string]
serialized_sig*: string
let
alice = testKeySig(
privkey: "9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501",
pubkey: "5eed5fa3a67696c334762bb4823e585e2ee579aba3558d9955296d6c04541b426078dbd48d74af1fd0c72aa1a05147cf17be6b60bdbed6ba19b08ec28445b0ca",
raw_sig: (
v: 1,
r: "B20E2EA5D3CBAA83C1E0372F110CF12535648613B479B64C1A8C1A20C5021F38", # Decimal "80536744857756143861726945576089915884233437828013729338039544043241440681784",
s: "0434D07EC5795E3F789794351658E80B7FAF47A46328F41E019D7B853745CDFD" # Decimal "1902566422691403459035240420865094128779958320521066670269403689808757640701"
),
serialized_sig: "b20e2ea5d3cbaa83c1e0372f110cf12535648613b479b64c1a8c1a20c5021f380434d07ec5795e3f789794351658e80b7faf47a46328f41e019d7b853745cdfd01"
)
bob = testKeySig(
privkey: "38e47a7b719dce63662aeaf43440326f551b8a7ee198cee35cb5d517f2d296a2",
pubkey: "347746ccb908e583927285fa4bd202f08e2f82f09c920233d89c47c79e48f937d049130e3d1c14cf7b21afefc057f71da73dec8e8ff74ff47dc6a574ccd5d570",
raw_sig: (
v: 1,
r: "5C48EA4F0F2257FA23BD25E6FCB0B75BBE2FF9BBDA0167118DAB2BB6E31BA76E", # Decimal "41741612198399299636429810387160790514780876799439767175315078161978521003886",
s: "691DBDAF2A231FC9958CD8EDD99507121F8184042E075CF10F98BA88ABFF1F36" # Decimal "47545396818609319588074484786899049290652725314938191835667190243225814114102"
),
serialized_sig: "5c48ea4f0f2257fa23bd25e6fcb0b75bbe2ff9bbda0167118dab2bb6e31ba76e691dbdaf2a231fc9958cd8edd99507121f8184042e075cf10f98ba88abff1f3601"
)
eve = testKeySig(
privkey: "876be0999ed9b7fc26f1b270903ef7b0c35291f89407903270fea611c85f515c",
pubkey: "c06641f0d04f64dba13eac9e52999f2d10a1ff0ca68975716b6583dee0318d91e7c2aed363ed22edeba2215b03f6237184833fd7d4ad65f75c2c1d5ea0abecc0",
raw_sig: (
v: 0,
r: "BABEEFC5082D3CA2E0BC80532AB38F9CFB196FB9977401B2F6A98061F15ED603", # Decimal "84467545608142925331782333363288012579669270632210954476013542647119929595395",
s: "603D0AF084BF906B2CDF6CDDE8B2E1C3E51A41AF5E9ADEC7F3643B3F1AA2AADF" # Decimal "43529886636775750164425297556346136250671451061152161143648812009114516499167"
),
serialized_sig: "babeefc5082d3ca2e0bc80532ab38f9cfb196fb9977401b2f6a98061f15ed603603d0af084bf906b2cdf6cdde8b2e1c3e51a41af5e9adec7f3643b3f1aa2aadf00"
)
test "Alice signature":
check: alice.raw_sig.r.parse(Stuint[256], 16) == "80536744857756143861726945576089915884233437828013729338039544043241440681784".u256
check: alice.raw_sig.s.parse(Stuint[256], 16) == "1902566422691403459035240420865094128779958320521066670269403689808757640701".u256
test "Bob signature":
check: bob.raw_sig.r.parse(Stuint[256], 16) == "41741612198399299636429810387160790514780876799439767175315078161978521003886".u256
check: bob.raw_sig.s.parse(Stuint[256], 16) == "47545396818609319588074484786899049290652725314938191835667190243225814114102".u256
test "Eve signature":
check: eve.raw_sig.r.parse(Stuint[256], 16) == "84467545608142925331782333363288012579669270632210954476013542647119929595395".u256
check: eve.raw_sig.s.parse(Stuint[256], 16) == "43529886636775750164425297556346136250671451061152161143648812009114516499167".u256