mirror of
https://github.com/status-im/nim-web3.git
synced 2026-08-27 10:31:06 +00:00
* Add nimbledeps to gitignore * Add abi encoder * Add encoding tests * Add AbiEncoder * Add encoding tests * Fix async signature * Add encoding tests to all tests * Do not bubble the exception * Add proper error handling * Use faststream to simplify internal logic * Partial implementation of the decoding * Fix tuple encoding and try to provide better docs / tests * Update abi util to to take tuple in consideration * Add deprecation notice * Refactor the dynamic array / seq decoding * Add decoding * Add ABI encoder / decoder to the readme * Fix indent * Export decoding * Restore previous encoding functions and mark them deprecated * Remove compiler hints and warnings * Use inputStream signature and use readAll * Add custom types * Cleanup * Add support for serialization * Add support for dontSerialize pragma * Add readValue compatibility * Add readme for serialization * Use enumInstanceSerializedFields to get the serialized fields instead of checking manually the pragma * Use Abi serialization as main interface * Update README * Update deprecation message and export abi_serialization * Update deprecation message and export abi_serialization * Add tests for string * Test encode empty string * Encode object as tuple * Remove ref object * Ensure that object are encoded as tuple * Decode object as tuple * Remove compilation hint * Remove unused import * Replace ABI encoding and decoding by the new encoding method * Improve serialization test * Add test more 3 slots * Remove unused variables * Use existing type T to get the arity * Use advance function to point on the right position instead of copying the whole buffer * Use local encoder instead of calling Abi.encode * Use SomeRange type in testing * Add overflow detection when decoding int and provide clearer comments * Re organize comments * Fix indentation * Add raises pragma * Remove int / uint support * Use toOpenArray method and replace StUint usage by uint. * Use used pragma * Reject range with int / uint value * Apply toOpenArray function * Remove unused function * Check hardhat version on ci * Update ci with hardhat 3 * Remove the intermediate data seq * Remove comment * Remove comment * Use explicit return
248 lines
6.4 KiB
Nim
248 lines
6.4 KiB
Nim
import
|
|
std/[unittest, random],
|
|
stew/[endians2],
|
|
stint,
|
|
serialization,
|
|
../web3/eth_api_types,
|
|
../web3/decoding,
|
|
../web3/encoding,
|
|
../web3/abi_serialization,
|
|
./helpers/primitives_utils
|
|
|
|
randomize()
|
|
|
|
type SomeDistinctType = distinct uint16
|
|
|
|
func `==`*(a, b: SomeDistinctType): bool =
|
|
uint16(a) == uint16(b)
|
|
|
|
suite "ABI decoding":
|
|
proc checkDecode[T](value: T) =
|
|
let encoded = Abi.encode(value)
|
|
check Abi.decode(encoded, T) == value
|
|
|
|
proc randomBytes[N: static int](): array[N, byte] =
|
|
var a: array[N, byte]
|
|
for b in a.mitems:
|
|
b = rand(byte)
|
|
return a
|
|
|
|
proc checkDecode(T: type) =
|
|
checkDecode(T.default)
|
|
checkDecode(T.low)
|
|
checkDecode(T.high)
|
|
|
|
test "decodes uint8, uint16, 32, 64":
|
|
checkDecode(uint8)
|
|
checkDecode(uint16)
|
|
checkDecode(uint32)
|
|
checkDecode(uint64)
|
|
|
|
test "decodes int8, int16, int32, int64":
|
|
checkDecode(int8)
|
|
checkDecode(int16)
|
|
checkDecode(int32)
|
|
checkDecode(int64)
|
|
|
|
test "fails when trying to decode overfow data":
|
|
try:
|
|
let encoded = Abi.encode(int16.high)
|
|
discard Abi.decode(encoded, int8)
|
|
fail()
|
|
except SerializationError:
|
|
discard
|
|
|
|
test "fails to decode when reading past end":
|
|
var encoded = Abi.encode(uint8.fromBytes(randomBytes[8](), bigEndian))
|
|
encoded.delete(encoded.len-1)
|
|
|
|
try:
|
|
discard Abi.decode(encoded, uint8)
|
|
fail()
|
|
except SerializationError as decoded:
|
|
check decoded.msg == "reading past end of bytes"
|
|
|
|
test "fails to decode when trailing bytes remain":
|
|
var encoded = Abi.encode(uint8.fromBytes(randomBytes[8](), bigEndian))
|
|
encoded.add(uint8.fromBytes(randomBytes[8](), bigEndian))
|
|
|
|
try:
|
|
discard Abi.decode(encoded, uint8)
|
|
fail()
|
|
except SerializationError as decoded:
|
|
check decoded.msg == "unread trailing bytes found"
|
|
|
|
test "fails to decode when padding does not consist of zeroes with unsigned value":
|
|
var encoded = Abi.encode(uint8.fromBytes(randomBytes[8](), bigEndian))
|
|
encoded[3] = 42'u8
|
|
|
|
try:
|
|
discard Abi.decode(encoded, uint8)
|
|
fail()
|
|
except SerializationError as decoded:
|
|
check decoded.msg == "invalid padding found"
|
|
|
|
test "fails to decode when padding does not consist of zeroes":
|
|
var encoded = Abi.encode(8.int8)
|
|
encoded[3] = 42'u8
|
|
|
|
try:
|
|
discard Abi.decode(encoded, int8)
|
|
fail()
|
|
except SerializationError as decoded:
|
|
check decoded.msg == "invalid padding found"
|
|
|
|
test "decodes booleans":
|
|
checkDecode(false)
|
|
checkDecode(true)
|
|
|
|
test "fails to decode boolean when value is not 0 or 1":
|
|
let encoded = Abi.encode(2'u8)
|
|
|
|
try:
|
|
discard Abi.decode(encoded, bool)
|
|
fail()
|
|
except SerializationError as decoded:
|
|
check decoded.msg == "invalid boolean value"
|
|
|
|
test "decodes ranges":
|
|
type SomeRange = range[0x0000'u16..0xAAAA'u16]
|
|
checkDecode(SomeRange)
|
|
|
|
test "fails to decode when value not in range":
|
|
type SomeRange = range[0x0000'u16..0xAAAA'u16]
|
|
let encoded = Abi.encode(0xFFFF'u16)
|
|
|
|
try:
|
|
discard Abi.decode(encoded, SomeRange)
|
|
fail()
|
|
except SerializationError as decoded:
|
|
check decoded.msg == "value not in range"
|
|
|
|
test "decodes enums":
|
|
type SomeEnum = enum
|
|
one = 1
|
|
two = 2
|
|
checkDecode(one)
|
|
checkDecode(two)
|
|
|
|
test "fails to decode enum when encountering invalid value":
|
|
type SomeEnum = enum
|
|
one = 1
|
|
two = 2
|
|
let encoded = Abi.encode(3'u8)
|
|
|
|
try:
|
|
discard Abi.decode(encoded, SomeEnum)
|
|
fail()
|
|
except SerializationError as decoded:
|
|
check decoded.msg == "invalid enum value"
|
|
|
|
test "decodes stints":
|
|
checkDecode(UInt128)
|
|
checkDecode(UInt256)
|
|
checkDecode(Int128)
|
|
checkDecode(Int256)
|
|
|
|
test "decodes addresses":
|
|
checkDecode(address(3))
|
|
|
|
test "decodes byte arrays":
|
|
checkDecode([1'u8, 2'u8, 3'u8])
|
|
checkDecode(randomBytes[32]())
|
|
checkDecode(randomBytes[33]())
|
|
checkDecode(randomBytes[65]())
|
|
|
|
test "fails to decode array when padding does not consist of zeroes":
|
|
var arr = randomBytes[33]()
|
|
var encoded = Abi.encode(arr)
|
|
encoded[62] = 42'u8
|
|
|
|
try:
|
|
discard Abi.decode(encoded, type(arr))
|
|
fail()
|
|
except SerializationError as decoded:
|
|
check decoded.msg == "invalid padding found"
|
|
|
|
test "decodes byte sequences":
|
|
checkDecode(@[1'u8, 2'u8, 3'u8])
|
|
checkDecode(@(randomBytes[32]()))
|
|
checkDecode(@(randomBytes[33]()))
|
|
|
|
test "fails to decode seq when padding does not consist of zeroes":
|
|
var value = @(randomBytes[64]())
|
|
value[62] = 42'u8
|
|
|
|
try:
|
|
discard Abi.decode(value, seq[byte])
|
|
fail()
|
|
except SerializationError as decoded:
|
|
check decoded.msg == "invalid padding found"
|
|
|
|
test "decodes sequences":
|
|
let seq1 = @(randomBytes[33]())
|
|
let seq2 = @(randomBytes[32]())
|
|
let value = @[seq1, seq2]
|
|
checkDecode(value)
|
|
|
|
test "decodes arrays with static elements":
|
|
checkDecode([randomBytes[32](), randomBytes[32]()])
|
|
|
|
test "decodes arrays with dynamic elements":
|
|
let seq1 = @(randomBytes[32]())
|
|
let seq2 = @(randomBytes[32]())
|
|
checkDecode([seq1, seq2])
|
|
|
|
test "decodes arrays with string":
|
|
checkDecode(["hello", "world"])
|
|
|
|
test "decodes strings":
|
|
checkDecode("hello!☺")
|
|
|
|
test "decodes distinct types as their base type":
|
|
checkDecode(SomeDistinctType(0xAABB'u16))
|
|
|
|
test "decodes tuples":
|
|
let a = true
|
|
let b = @[1'u8, 2'u8, 3'u8]
|
|
let c = 0xAABBCCDD'u32
|
|
let d = @[4'u8, 5'u8, 6'u8]
|
|
checkDecode( (a, b, c, d) )
|
|
|
|
test "decodes nested tuples":
|
|
let a = true
|
|
let b = @[1'u8, 2'u8, 3'u8]
|
|
let c = 0xAABBCCDD'u32
|
|
let d = @[4'u8, 5'u8, 6'u8]
|
|
checkDecode( (a, b, (c, d)) )
|
|
|
|
test "reads elements after dynamic tuple":
|
|
let a = @[1'u8, 2'u8, 3'u8]
|
|
let b = 0xAABBCCDD'u32
|
|
checkDecode( ((a,), b) )
|
|
|
|
test "reads elements after static tuple":
|
|
let a = 0x123'u16
|
|
let b = 0xAABBCCDD'u32
|
|
checkDecode( ((a,), b) )
|
|
|
|
test "reads static tuple inside dynamic tuple":
|
|
let a = @[1'u8, 2'u8, 3'u8]
|
|
let b = 0xAABBCCDD'u32
|
|
checkDecode( (a, (b,)) )
|
|
|
|
test "reads empty tuples":
|
|
checkDecode( ((),) )
|
|
|
|
test "reads empty tuple":
|
|
checkDecode( (), )
|
|
|
|
test "encodes strings":
|
|
let encoded = Abi.encode("hello")
|
|
check Abi.decode(encoded, string) == "hello"
|
|
|
|
test "encodes empty strings":
|
|
let encoded = Abi.encode("")
|
|
let decoded = Abi.decode(encoded, string)
|
|
check decoded == ""
|
|
check len(decoded) == 0 |