From 34072f4749e1722fee2583bb45dd8084ca0c776a Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Thu, 25 Feb 2021 09:11:46 +0100 Subject: [PATCH] Fix ABI encoding of Nitro state to match javascript implementation - rewrite ABI encoding to properly handle dynamic tuples - surround Nitro types with extra tuples to match javascript - test with examples of encoding extracted from javascript --- nitro/abi.nim | 176 ++++++++++++++++++++---------------- nitro/channel.nim | 14 +-- nitro/outcome.nim | 60 ++++++------ nitro/state.nim | 34 +++---- nitro/types.nim | 4 + tests/nitro/examples.nim | 3 + tests/nitro/testAbi.nim | 149 +++++++++++++++--------------- tests/nitro/testChannel.nim | 27 ++++-- tests/nitro/testOutcome.nim | 121 ++++++++++++++++++------- tests/nitro/testState.nim | 55 +++++++---- 10 files changed, 382 insertions(+), 261 deletions(-) diff --git a/nitro/abi.nim b/nitro/abi.nim index 32eef1f..ef2dea3 100644 --- a/nitro/abi.nim +++ b/nitro/abi.nim @@ -3,104 +3,124 @@ import pkg/stint import ./types type - Abi* = object - AbiWriter* = object - bytes: seq[byte] - tuples: seq[Tuple] + AbiEncoder* = object + stack: seq[Tuple] Tuple = object - start: int + bytes: seq[byte] postponed: seq[Split] + dynamic: bool Split = object head: Slice[int] tail: seq[byte] -proc isStatic*(_: type Abi, t: type SomeUnsignedInt): bool = true -proc isStatic*(_: type Abi, t: type StUint): bool = true -proc isStatic*(_: type Abi, t: type bool): bool = true -proc isStatic*(_: type Abi, t: type enum): bool = true -proc isStatic*[T](_: type Abi, t: type seq[T]): bool = false -proc isStatic*[I, T](_: type Abi, t: type array[I, T]): bool = Abi.isStatic(T) +proc write*[T](encoder: var AbiEncoder, value: T) +proc encode*[T](_: type AbiEncoder, value: T): seq[byte] -proc encode*[T](_: type Abi, value: T): seq[byte] +proc init*(_: type AbiEncoder): AbiEncoder = + AbiEncoder(stack: @[Tuple()]) -proc pad(writer: var AbiWriter, len: int) = +proc append(tupl: var Tuple, bytes: openArray[byte]) = + tupl.bytes.add(bytes) + +proc postpone(tupl: var Tuple, bytes: seq[byte]) = + var split: Split + split.head.a = tupl.bytes.len + tupl.append(AbiEncoder.encode(0'u64)) + split.head.b = tupl.bytes.high + split.tail = bytes + tupl.postponed.add(split) + +proc finish(tupl: Tuple): seq[byte] = + var bytes = tupl.bytes + for split in tupl.postponed: + let offset = bytes.len + bytes[split.head] = AbiEncoder.encode(offset.uint64) + bytes.add(split.tail) + bytes + +proc append(encoder: var AbiEncoder, bytes: openArray[byte]) = + encoder.stack[^1].append(bytes) + +proc postpone(encoder: var AbiEncoder, bytes: seq[byte]) = + if encoder.stack.len > 1: + encoder.stack[^1].postpone(bytes) + else: + encoder.stack[0].append(bytes) + +proc setDynamic(encoder: var AbiEncoder) = + encoder.stack[^1].dynamic = true + +proc startTuple*(encoder: var AbiEncoder) = + encoder.stack.add(Tuple()) + +proc encode(encoder: var AbiEncoder, tupl: Tuple) = + if tupl.dynamic: + encoder.postpone(tupl.finish()) + encoder.setDynamic() + else: + encoder.append(tupl.finish()) + +proc finishTuple*(encoder: var AbiEncoder) = + encoder.encode(encoder.stack.pop()) + +proc pad(encoder: var AbiEncoder, len: int) = let padlen = (32 - len mod 32) mod 32 for _ in 0..