Compare commits

...

22 Commits
0.4.0 ... main

Author SHA1 Message Date
Jacek Sieka
5ccdeb46e0
Merge pull request #3 from logos-storage/stew-bump
chore: bump stew, fix import conflict
2025-12-11 09:43:43 +01:00
Jacek Sieka
fdcea5a2aa
try without maxtagged 2025-12-11 09:30:27 +01:00
Jacek Sieka
86d5601b4b
readd windows 2025-12-11 09:25:26 +01:00
Jacek Sieka
a15660f70f
chore: fix import conflict
There's a new byteutils in newer versions of stew - also, remove
upraises and disable windows testing which requires SSL library install
2025-12-10 22:06:29 +01:00
Jacek Sieka
309431a482
Merge pull request #2 from logos-storage/update-to-nim-2-x
Update to nim 2 x
2025-12-10 20:48:38 +01:00
Arnaud
1e7358c7c4
Fix tests 2025-02-14 11:01:51 +01:00
Arnaud
f924a32458
Fix typo 2025-02-14 10:59:08 +01:00
Arnaud
f3879ed666
Update dependencies and remove support to Nim 1.6 due to dependencies issue 2025-02-14 10:55:38 +01:00
Arnaud
9cbecdde20
Update to Nim 2.0.14 2025-01-07 11:37:03 +01:00
Arnaud
e3719433d5
Update versions 2024-12-18 15:28:59 +01:00
Arnaud
89052f638d
Update dependencies for Nim 2.x 2024-12-18 15:27:16 +01:00
zah
c13aba9942
Merge pull request #1 from yyoncho/main
Add setup files
2022-07-12 23:21:49 +03:00
Ivan Yonchovski
991d56f7d1 Add setup files 2022-07-12 15:48:13 +03:00
Mark Spanbroek
7047da19b1 Eliminate small chance of test failing
Test would sometimes fail because the example
seq would be empty in both cases.
2022-05-09 15:31:22 +02:00
Mark Spanbroek
6b4c455bf4 version 0.5.1 2022-05-09 10:46:45 +02:00
Mark Spanbroek
9611c36c78 Update to latest contractabi 2022-01-19 09:37:42 +01:00
Mark Spanbroek
ad95604c18 Add license 2022-01-10 11:22:17 +01:00
Mark Spanbroek
5977ea47e3 Update to latest version of nim-contract-abi
Encoding is rewritten to make use of the new tuple api.
2021-12-06 15:39:27 +01:00
Mark Spanbroek
39ef902177 Remove tests that are now in the contractabi module 2021-12-06 15:26:35 +01:00
Mark Spanbroek
213551c512 version 0.5.0 2021-11-25 10:07:47 +01:00
Mark Spanbroek
a8bd625e3f Use contractabi module
Which was extracted from this project.
2021-11-25 10:04:10 +01:00
Mark Spanbroek
9396fcb7d0 Remove workaround for CI issue 2021-05-10 09:24:03 +02:00
29 changed files with 234 additions and 467 deletions

View File

@ -8,22 +8,15 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macOS-latest, windows-latest] os: [ubuntu-latest, macOS-latest, windows-latest]
nim: [stable, 1.2.6] nim: [stable, 2.0.14]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v6
- uses: iffy/install-nim@v3
with: with:
version: ${{ matrix.nim }} submodules: recursive
- uses: jiro4989/setup-nim-action@v2
# workaround for https://github.com/iffy/install-nim/issues/11 with:
- name: Workaround SSL error with choosenim nim-version: ${{matrix.nim}}
run: | repo-token: ${{ secrets.GITHUB_TOKEN }}
curl -fO https://curl.se/ca/cacert.pem
install cacert.pem ~/.nimble/bin
ls ~/.nimble/bin
shell: bash
if: runner.os == 'Windows'
- name: Build - name: Build
run: nimble install -y run: nimble install -y
- name: Test - name: Test

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
* *
!*/ !*/
!*.* !*.*
nimble.develop
nimble.paths
nimbledeps

View File

@ -1 +1 @@
nim 1.4.6 nim 1.6.2

5
License.md Normal file
View File

@ -0,0 +1,5 @@
Licensed and distributed under either of
[MIT license](http://opensource.org/licenses/MIT) or
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
at your option. These files may not be copied, modified, or distributed except
according to those terms.

View File

@ -10,3 +10,11 @@ References
- [statechannels.org](https://statechannels.org/) - [statechannels.org](https://statechannels.org/)
- [Nitro paper](https://magmo.com/nitro-protocol.pdf) - [Nitro paper](https://magmo.com/nitro-protocol.pdf)
- [Nitro smart contracts & Javascript code](https://github.com/statechannels/statechannels/tree/master/packages/nitro-protocol) - [Nitro smart contracts & Javascript code](https://github.com/statechannels/statechannels/tree/master/packages/nitro-protocol)
# Installation
To avoid conflicts with previous versions of `contractabi`, use the following command to install dependencies:
```bash
nimble install --maximumtaggedversions=2
```

5
config.nims Normal file
View File

@ -0,0 +1,5 @@
# begin Nimble config (version 2)
--noNimblePath
when withDir(thisDir(), system.fileExists("nimble.paths")):
include "nimble.paths"
# end Nimble config

101
nimble.lock Normal file
View File

@ -0,0 +1,101 @@
{
"version": 2,
"packages": {
"results": {
"version": "0.5.1",
"vcsRevision": "df8113dda4c2d74d460a8fa98252b0b771bf1f27",
"url": "https://github.com/arnetheduck/nim-results",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "a9c011f74bc9ed5c91103917b9f382b12e82a9e7"
}
},
"unittest2": {
"version": "0.2.5",
"vcsRevision": "26f2ef3ae0ec72a2a75bfe557e02e88f6a31c189",
"url": "https://github.com/status-im/nim-unittest2",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "02bb3751ba9ddc3c17bfd89f2e41cb6bfb8fc0c9"
}
},
"stew": {
"version": "0.4.2",
"vcsRevision": "b66168735d6f3841c5239c3169d3fe5fe98b1257",
"url": "https://github.com/status-im/nim-stew",
"downloadMethod": "git",
"dependencies": [
"results",
"unittest2"
],
"checksums": {
"sha1": "928e82cb8d2f554e8f10feb2349ee9c32fee3a8c"
}
},
"stint": {
"version": "0.8.2",
"vcsRevision": "470b7892561b5179ab20bd389a69217d6213fe58",
"url": "https://github.com/status-im/nim-stint",
"downloadMethod": "git",
"dependencies": [
"stew",
"unittest2"
],
"checksums": {
"sha1": "d8f871fd617e7857192d4609fe003b48942a8ae5"
}
},
"nimcrypto": {
"version": "0.6.4",
"vcsRevision": "721fb99ee099b632eb86dfad1f0d96ee87583774",
"url": "https://github.com/cheatfate/nimcrypto",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "f9ab24fa940ed03d0fb09729a7303feb50b7eaec"
}
},
"questionable": {
"version": "0.10.15",
"vcsRevision": "82d90b67bcfb7f2e918b61dace2ff1a4ced60935",
"url": "https://github.com/codex-storage/questionable",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "3238ff637c7b44d2fa8fcb839a8ded968e389de3"
}
},
"contractabi": {
"version": "0.7.3",
"vcsRevision": "0a7b4cecce725bcb11ad8648035a92704a8854d3",
"url": "https://github.com/status-im/nim-contract-abi",
"downloadMethod": "git",
"dependencies": [
"stint",
"stew",
"nimcrypto",
"questionable"
],
"checksums": {
"sha1": "1bb9af15f02a77b44af02ec94b0e392b1ec88438"
}
},
"secp256k1": {
"version": "0.6.0.3.2",
"vcsRevision": "b526c4b436809aa1cfe650026d796cf7b8328b91",
"url": "https://github.com/status-im/nim-secp256k1",
"downloadMethod": "git",
"dependencies": [
"stew",
"results",
"nimcrypto"
],
"checksums": {
"sha1": "e6e50bd4a29cb473b070eb5359d87d8946d96075"
}
}
},
"tasks": {}
}

View File

@ -1,12 +1,12 @@
version = "0.4.0" version = "0.6.1"
author = "Nim Nitro developers" author = "Nim Nitro developers"
license = "MIT" license = "MIT"
description = "Nitro state channels" description = "Nitro state channels"
requires "nim >= 1.2.6 & < 2.0.0" requires "nim >= 2.0.14 & < 3.0.0"
requires "nimcrypto >= 0.5.4 & < 0.6.0" requires "nimcrypto >= 0.6.0 & < 0.7.0"
requires "questionable >= 0.9.1 & < 0.10.0" requires "questionable >= 0.10.10 & < 0.11.0"
requires "upraises >= 0.1.0 & < 0.2.0" requires "contractabi >= 0.7.1 & < 0.8.0"
requires "secp256k1" requires "secp256k1 >= 0.6.0 & < 0.7.0"
requires "stint" requires "stint >= 0.8.0 & < 0.9.0"
requires "stew" requires "stew >= 0.2.0"

View File

@ -1,6 +1,5 @@
import pkg/questionable import pkg/questionable
import pkg/questionable/results import pkg/questionable/results
import pkg/upraises
import pkg/stint import pkg/stint
import ./basics/uint48 import ./basics/uint48
import ./basics/ethaddress import ./basics/ethaddress
@ -8,7 +7,6 @@ import ./basics/destination
export questionable export questionable
export results export results
export upraises
export stint export stint
export uint48 export uint48
export ethaddress export ethaddress

View File

@ -1,11 +1,10 @@
import std/hashes import std/hashes
import pkg/questionable import pkg/questionable
import pkg/questionable/results import pkg/questionable/results
import pkg/upraises
import pkg/stew/byteutils import pkg/stew/byteutils
import ./ethaddress import ./ethaddress
push: {.upraises:[].} {.push raises: [].}
type Destination* = distinct array[32, byte] type Destination* = distinct array[32, byte]

View File

@ -1,23 +1,12 @@
import std/hashes import std/hashes
import pkg/questionable import pkg/contractabi/address
import pkg/questionable/results
import pkg/stew/byteutils
export questionable export address
type EthAddress* = distinct array[20, byte] type EthAddress* = Address
func zero*(_: type EthAddress): EthAddress = func zero*(_: type EthAddress): EthAddress =
EthAddress.default EthAddress.default
func toArray*(address: EthAddress): array[20, byte] = proc `hash`*(a: EthAddress): Hash =
array[20, byte](address) hash(a.toArray)
func `$`*(a: EthAddress): string =
a.toArray().toHex()
func parse*(_: type EthAddress, hex: string): ?EthAddress =
EthAddress(array[20, byte].fromHex(hex)).catch.option
proc `==`*(a, b: EthAddress): bool {.borrow.}
proc `hash`*(a: EthAddress): Hash {.borrow.}

View File

@ -8,7 +8,7 @@ import ./wallet/signedstate
export signedstate export signedstate
push: {.upraises:[].} {.push raises: [].}
func `%`(value: Outcome | Allocation): JsonNode = func `%`(value: Outcome | Allocation): JsonNode =
type Base = distinctBase(typeof value) type Base = distinctBase(typeof value)
@ -37,7 +37,7 @@ func toJson*(payment: SignedState): string =
{.pop.} {.pop.}
push: {.upraises: [ValueError].} {.push raises: [ValueError].}
func expectKind(node: JsonNode, kind: JsonNodeKind) = func expectKind(node: JsonNode, kind: JsonNodeKind) =
if node.kind != kind: if node.kind != kind:
@ -52,7 +52,7 @@ func initFromJson*(bytes: var seq[byte], node: JsonNode, _: var string) =
func initFromJson*(address: var EthAddress, node: JsonNode, _: var string) = func initFromJson*(address: var EthAddress, node: JsonNode, _: var string) =
node.expectKind(JString) node.expectKind(JString)
without parsed =? EthAddress.parse(node.getStr): without parsed =? EthAddress.init(node.getStr):
raise newException(ValueError, "invalid ethereum address") raise newException(ValueError, "invalid ethereum address")
address = parsed address = parsed
@ -74,7 +74,7 @@ func initFromJson*(signature: var Signature, node: JsonNode, _: var string) =
{.pop.} {.pop.}
push: {.upraises: [].} {.push raises: [].}
proc fromJson*(_: type SignedState, json: string): ?SignedState = proc fromJson*(_: type SignedState, json: string): ?SignedState =
try: try:

View File

@ -5,7 +5,7 @@ import ./basics
export basics export basics
export toPublicKey export toPublicKey
push: {.upraises:[].} {.push raises: [].}
type type
EthPrivateKey* = SkSecretKey EthPrivateKey* = SkSecretKey

View File

@ -1,132 +1,10 @@
import pkg/stew/endians2 import pkg/contractabi
import ../basics import ../basics
push: {.upraises:[].} {.push raises: [].}
export basics export basics
export contractabi
type func encode*(encoder: var AbiEncoder, destination: Destination) =
AbiEncoder* = object encoder.write(destination.toArray)
stack: seq[Tuple]
Tuple = object
bytes: seq[byte]
postponed: seq[Split]
dynamic: bool
Split = object
head: Slice[int]
tail: seq[byte]
func write*[T](encoder: var AbiEncoder, value: T)
func encode*[T](_: type AbiEncoder, value: T): seq[byte]
func init*(_: type AbiEncoder): AbiEncoder =
AbiEncoder(stack: @[Tuple()])
func append(tupl: var Tuple, bytes: openArray[byte]) =
tupl.bytes.add(bytes)
func 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)
func 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
func append(encoder: var AbiEncoder, bytes: openArray[byte]) =
encoder.stack[^1].append(bytes)
func postpone(encoder: var AbiEncoder, bytes: seq[byte]) =
if encoder.stack.len > 1:
encoder.stack[^1].postpone(bytes)
else:
encoder.stack[0].append(bytes)
func setDynamic(encoder: var AbiEncoder) =
encoder.stack[^1].dynamic = true
func startTuple*(encoder: var AbiEncoder) =
encoder.stack.add(Tuple())
func encode(encoder: var AbiEncoder, tupl: Tuple) =
if tupl.dynamic:
encoder.postpone(tupl.finish())
encoder.setDynamic()
else:
encoder.append(tupl.finish())
func finishTuple*(encoder: var AbiEncoder) =
encoder.encode(encoder.stack.pop())
func pad(encoder: var AbiEncoder, len: int) =
let padlen = (32 - len mod 32) mod 32
for _ in 0..<padlen:
encoder.append([0'u8])
func padleft(encoder: var AbiEncoder, bytes: openArray[byte]) =
encoder.pad(bytes.len)
encoder.append(bytes)
func padright(encoder: var AbiEncoder, bytes: openArray[byte]) =
encoder.append(bytes)
encoder.pad(bytes.len)
func encode(encoder: var AbiEncoder, value: SomeUnsignedInt | StUint) =
encoder.padleft(value.toBytesBE)
func encode(encoder: var AbiEncoder, value: bool) =
encoder.encode(cast[uint8](value))
func encode(encoder: var AbiEncoder, value: enum) =
encoder.encode(uint64(ord(value)))
func encode[I](encoder: var AbiEncoder, bytes: array[I, byte]) =
encoder.padright(bytes)
func encode(encoder: var AbiEncoder, bytes: seq[byte]) =
encoder.encode(bytes.len.uint64)
encoder.padright(bytes)
encoder.setDynamic()
func encode(encoder: var AbiEncoder, address: EthAddress) =
encoder.padleft(address.toArray)
func encode(encoder: var AbiEncoder, destination: Destination) =
encoder.encode(destination.toArray)
func encode[I, T](encoder: var AbiEncoder, value: array[I, T]) =
encoder.startTuple()
for element in value:
encoder.write(element)
encoder.finishTuple()
func encode[T](encoder: var AbiEncoder, value: seq[T]) =
encoder.encode(value.len.uint64)
encoder.startTuple()
for element in value:
encoder.write(element)
encoder.finishTuple()
encoder.setDynamic()
func write*[T](encoder: var AbiEncoder, value: T) =
var writer = AbiEncoder.init()
writer.encode(value)
encoder.encode(writer.stack[0])
func finish*(encoder: var AbiEncoder): seq[byte] =
doAssert encoder.stack.len == 1, "not all tuples were finished"
doAssert encoder.stack[0].bytes.len mod 32 == 0, "encoding invariant broken"
encoder.stack[0].bytes
func encode*[T](_: type AbiEncoder, value: T): seq[byte] =
var encoder = AbiEncoder.init()
encoder.write(value)
encoder.finish()

View File

@ -2,7 +2,7 @@ import pkg/nimcrypto
import ../basics import ../basics
import ./abi import ./abi
push: {.upraises:[].} {.push raises: [].}
export basics export basics
@ -13,10 +13,6 @@ type
chainId*: UInt256 chainId*: UInt256
func getChannelId*(channel: ChannelDefinition): Destination = func getChannelId*(channel: ChannelDefinition): Destination =
var encoder= AbiEncoder.init() let encoding = AbiEncoder.encode:
encoder.startTuple() (channel.chainId, channel.participants, channel.nonce)
encoder.write(channel.chainId) Destination(keccak256.digest(encoding).data)
encoder.write(channel.participants)
encoder.write(channel.nonce)
encoder.finishTuple()
Destination(keccak256.digest(encoder.finish()).data)

View File

@ -2,7 +2,7 @@ import pkg/nimcrypto
import ../basics import ../basics
import ./abi import ./abi
push: {.upraises:[].} {.push raises: [].}
export basics export basics
@ -52,46 +52,25 @@ func `==`*(a, b: AssetOutcome): bool =
proc `==`*(a, b: Outcome): bool {.borrow.} proc `==`*(a, b: Outcome): bool {.borrow.}
func encode*(encoder: var AbiEncoder, guarantee: Guarantee) = func encode*(encoder: var AbiEncoder, guarantee: Guarantee) =
encoder.startTuple() encoder.write:
encoder.startTuple() ( (guarantee.targetChannelId, guarantee.destinations), )
encoder.write(guarantee.targetChannelId)
encoder.write(guarantee.destinations)
encoder.finishTuple()
encoder.finishTuple()
func encode*(encoder: var AbiEncoder, item: AllocationItem) =
encoder.startTuple()
encoder.write(item.destination)
encoder.write(item.amount)
encoder.finishTuple()
func encode*(encoder: var AbiEncoder, allocation: Allocation) = func encode*(encoder: var AbiEncoder, allocation: Allocation) =
encoder.startTuple() encoder.write: (seq[AllocationItem](allocation),)
encoder.write(seq[AllocationItem](allocation))
encoder.finishTuple()
func encode*(encoder: var AbiEncoder, assetOutcome: AssetOutcome) = func encode*(encoder: var AbiEncoder, assetOutcome: AssetOutcome) =
var content= AbiEncoder.init() var content: seq[byte]
content.startTuple()
content.startTuple()
content.write(assetOutcome.kind)
case assetOutcome.kind: case assetOutcome.kind:
of allocationType: of allocationType:
content.write(AbiEncoder.encode(assetOutcome.allocation)) content = AbiEncoder.encode:
( (assetOutcome.kind, ABiEncoder.encode(assetOutcome.allocation)), )
of guaranteeType: of guaranteeType:
content.write(AbiEncoder.encode(assetOutcome.guarantee)) content = AbiEncoder.encode:
content.finishTuple() ( (assetOutcome.kind, AbiEncoder.encode(assetOutcome.guarantee)), )
content.finishTuple() encoder.write( (assetOutcome.assetHolder, content) )
encoder.startTuple()
encoder.write(assetOutcome.assetHolder)
encoder.write(content.finish())
encoder.finishTuple()
func encode*(encoder: var AbiEncoder, outcome: Outcome) = func encode*(encoder: var AbiEncoder, outcome: Outcome) =
encoder.startTuple() encoder.write: (seq[AssetOutcome](outcome),)
encoder.write(seq[AssetOutcome](outcome))
encoder.finishTuple()
func hashOutcome*(outcome: Outcome): array[32, byte] = func hashOutcome*(outcome: Outcome): array[32, byte] =
keccak256.digest(AbiEncoder.encode(outcome)).data keccak256.digest(AbiEncoder.encode(outcome)).data

View File

@ -1,11 +1,11 @@
import pkg/secp256k1 import pkg/secp256k1
import pkg/nimcrypto import pkg/nimcrypto/keccak
import pkg/stew/byteutils import pkg/stew/byteutils
import ../basics import ../basics
import ../keys import ../keys
import ./state import ./state
push: {.upraises:[].} {.push raises: [].}
export basics export basics
export keys export keys

View File

@ -4,7 +4,7 @@ import ./channel
import ./outcome import ./outcome
import ./abi import ./abi
push: {.upraises:[].} {.push raises: [].}
export basics export basics
export channel export channel
@ -45,21 +45,17 @@ func variablePart*(state: State): VariablePart =
) )
func hashAppPart*(state: State): array[32, byte] = func hashAppPart*(state: State): array[32, byte] =
var encoder= AbiEncoder.init() let encoding = AbiEncoder.encode:
encoder.startTuple() (state.challengeDuration, state.appDefinition, state.appData)
encoder.write(state.challengeDuration) keccak256.digest(encoding).data
encoder.write(state.appDefinition)
encoder.write(state.appData)
encoder.finishTuple()
keccak256.digest(encoder.finish).data
func hashState*(state: State): array[32, byte] = func hashState*(state: State): array[32, byte] =
var encoder= AbiEncoder.init() let encoding = AbiEncoder.encode:
encoder.startTuple() (
encoder.write(state.turnNum) state.turnNum,
encoder.write(state.isFinal) state.isFinal,
encoder.write(getChannelId(state.channel)) getChannelId(state.channel),
encoder.write(hashAppPart(state)) hashAppPart(state),
encoder.write(hashOutcome(state.outcome)) hashOutcome(state.outcome)
encoder.finishTuple() )
keccak256.digest(encoder.finish).data keccak256.digest(encoding).data

View File

@ -3,7 +3,7 @@ import std/sequtils
import ../basics import ../basics
import ../protocol import ../protocol
push: {.upraises:[].} {.push raises: [].}
export tables export tables

View File

@ -3,7 +3,7 @@ import std/sets
import std/hashes import std/hashes
import ../basics import ../basics
push: {.upraises: [].} {.push raises: [].}
type type
Nonces* = object Nonces* = object

View File

@ -1,7 +1,7 @@
import ../basics import ../basics
import ../protocol import ../protocol
push: {.upraises:[].} {.push raises: [].}
type type
SignedState* = object SignedState* = object

View File

@ -8,7 +8,7 @@ import ./balances
import ./nonces import ./nonces
import ./deref import ./deref
push: {.upraises:[].} {.push raises: [].}
export basics export basics
export keys export keys

View File

@ -19,10 +19,10 @@ proc example*[T](_: type seq[T], len = 0..5): seq[T] =
newSeqWith(chosenlen, T.example) newSeqWith(chosenlen, T.example)
proc example*(_: type UInt256): UInt256 = proc example*(_: type UInt256): UInt256 =
UInt256.fromBytes(array[32, byte].example) UInt256.fromBytesBE(array[32, byte].example)
proc example*(_: type UInt128): UInt128 = proc example*(_: type UInt128): UInt128 =
UInt128.fromBytes(array[16, byte].example) UInt128.fromBytesBE(array[16, byte].example)
proc example*(_: type EthAddress): EthAddress = proc example*(_: type EthAddress): EthAddress =
EthAddress(array[20, byte].example) EthAddress(array[20, byte].example)

View File

@ -6,154 +6,11 @@ suite "ABI encoding":
proc zeroes(amount: int): seq[byte] = proc zeroes(amount: int): seq[byte] =
newSeq[byte](amount) newSeq[byte](amount)
test "encodes uint8":
check AbiEncoder.encode(42'u8) == 31.zeroes & 42'u8
test "encodes booleans":
check AbiEncoder.encode(false) == 31.zeroes & 0'u8
check AbiEncoder.encode(true) == 31.zeroes & 1'u8
test "encodes uint16, 32, 64":
check AbiEncoder.encode(0xABCD'u16) ==
30.zeroes & 0xAB'u8 & 0xCD'u8
check AbiEncoder.encode(0x11223344'u32) ==
28.zeroes & 0x11'u8 & 0x22'u8 & 0x33'u8 & 0x44'u8
check AbiEncoder.encode(0x1122334455667788'u64) ==
24.zeroes &
0x11'u8 & 0x22'u8 & 0x33'u8 & 0x44'u8 &
0x55'u8 & 0x66'u8 & 0x77'u8 & 0x88'u8
test "encodes ranges":
type SomeRange = range[0x0000'u16..0xAAAA'u16]
check AbiEncoder.encode(SomeRange(0x1122)) == 30.zeroes & 0x11'u8 & 0x22'u8
test "encodes enums":
type SomeEnum = enum
one = 1
two = 2
check AbiEncoder.encode(one) == 31.zeroes & 1'u8
check AbiEncoder.encode(two) == 31.zeroes & 2'u8
test "encodes stints":
let uint256 = UInt256.example
check AbiEncoder.encode(uint256) == @(uint256.toBytesBE)
let uint128 = UInt128.example
check AbiEncoder.encode(uint128) == 16.zeroes & @(uint128.toBytesBE)
test "encodes byte arrays":
let bytes3 = [1'u8, 2'u8, 3'u8]
check AbiEncoder.encode(bytes3) == @bytes3 & 29.zeroes
let bytes32 = array[32, byte].example
check AbiEncoder.encode(bytes32) == @bytes32
let bytes33 = array[33, byte].example
check AbiEncoder.encode(bytes33) == @bytes33 & 31.zeroes
test "encodes byte sequences":
let bytes3 = @[1'u8, 2'u8, 3'u8]
let bytes3len = AbiEncoder.encode(bytes3.len.uint64)
check AbiEncoder.encode(bytes3) == bytes3len & bytes3 & 29.zeroes
let bytes32 = @(array[32, byte].example)
let bytes32len = AbiEncoder.encode(bytes32.len.uint64)
check AbiEncoder.encode(bytes32) == bytes32len & bytes32
let bytes33 = @(array[33, byte].example)
let bytes33len = AbiEncoder.encode(bytes33.len.uint64)
check AbiEncoder.encode(bytes33) == bytes33len & bytes33 & 31.zeroes
test "encodes ethereum addresses": test "encodes ethereum addresses":
let address = EthAddress.example let address = EthAddress.example
check AbiEncoder.encode(address) == 12.zeroes & @(address.toArray) check AbiEncoder.encode(address) == 12.zeroes & @(address.toArray)
test "encodes tuples": test "encodes nitro destinations":
let a = true let destination = Destination.example
let b = @[1'u8, 2'u8, 3'u8] check:
let c = 0xAABBCCDD'u32 AbiEncoder.encode(destination) == AbiEncoder.encode(destination.toArray)
let d = @[4'u8, 5'u8, 6'u8]
var encoder= AbiEncoder.init()
encoder.startTuple()
encoder.write(a)
encoder.write(b)
encoder.write(c)
encoder.write(d)
encoder.finishTuple()
check encoder.finish() ==
AbiEncoder.encode(a) &
AbiEncoder.encode(4 * 32'u8) & # offset in tuple
AbiEncoder.encode(c) &
AbiEncoder.encode(6 * 32'u8) & # offset in tuple
AbiEncoder.encode(b) &
AbiEncoder.encode(d)
test "encodes 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]
var encoder= AbiEncoder.init()
encoder.startTuple()
encoder.write(a)
encoder.write(b)
encoder.startTuple()
encoder.write(c)
encoder.write(d)
encoder.finishTuple()
encoder.finishTuple()
check encoder.finish() ==
AbiEncoder.encode(a) &
AbiEncoder.encode(3 * 32'u8) & # offset of b in outer tuple
AbiEncoder.encode(5 * 32'u8) & # offset of inner tuple in outer tuple
AbiEncoder.encode(b) &
AbiEncoder.encode(c) &
AbiEncoder.encode(2 * 32'u8) & # offset of d in inner tuple
AbiEncoder.encode(d)
test "encodes arrays":
let element1 = seq[byte].example
let element2 = seq[byte].example
var expected= AbiEncoder.init()
expected.startTuple()
expected.write(element1)
expected.write(element2)
expected.finishTuple()
check AbiEncoder.encode([element1, element2]) == expected.finish()
test "encodes sequences":
let element1 = seq[byte].example
let element2 = seq[byte].example
var expected= AbiEncoder.init()
expected.write(2'u8)
expected.startTuple()
expected.write(element1)
expected.write(element2)
expected.finishTuple()
check AbiEncoder.encode(@[element1, element2]) == expected.finish()
test "encodes sequence as dynamic element":
let s = @[42.u256, 43.u256]
var encoder= AbiEncoder.init()
encoder.startTuple()
encoder.write(s)
encoder.finishTuple()
check encoder.finish() ==
AbiEncoder.encode(32'u8) & # offset in tuple
AbiEncoder.encode(s)
test "encodes array of static elements as static element":
let a = [[42'u8], [43'u8]]
var encoder= AbiEncoder.init()
encoder.startTuple()
encoder.write(a)
encoder.finishTuple()
check encoder.finish() == AbiEncoder.encode(a)
test "encodes array of dynamic elements as dynamic element":
let a = [@[42'u8], @[43'u8]]
var encoder= AbiEncoder.init()
encoder.startTuple()
encoder.write(a)
encoder.finishTuple()
check encoder.finish() ==
AbiEncoder.encode(32'u8) & # offset in tuple
AbiEncoder.encode(a)
# https://medium.com/b2expand/abi-encoding-explanation-4f470927092d
# https://docs.soliditylang.org/en/v0.8.1/abi-spec.html#formal-specification-of-the-encoding

View File

@ -7,13 +7,8 @@ suite "channel definition":
let channel = ChannelDefinition.example let channel = ChannelDefinition.example
test "calculates channel id": test "calculates channel id":
var encoder= AbiEncoder.init() let encoded = AbiEncoder.encode:
encoder.startTuple() (channel.chainId, channel.participants, channel.nonce)
encoder.write(channel.chainId)
encoder.write(channel.participants)
encoder.write(channel.nonce)
encoder.finishTuple()
let encoded = encoder.finish()
let hashed = keccak256.digest(encoded).data let hashed = keccak256.digest(encoded).data
check getChannelId(channel) == Destination(hashed) check getChannelId(channel) == Destination(hashed)
@ -22,7 +17,7 @@ suite "channel definition":
chainId: 9001.u256, chainId: 9001.u256,
nonce: 1, nonce: 1,
participants: @[ participants: @[
!EthAddress.parse("24b905Dcc8A11C0FE57C2592f3D25f0447402C10") !EthAddress.init("24b905Dcc8A11C0FE57C2592f3D25f0447402C10")
] ]
) )
let expected = !Destination.parse( let expected = !Destination.parse(

View File

@ -6,31 +6,20 @@ suite "outcome":
test "encodes guarantees": test "encodes guarantees":
let guarantee = Guarantee.example let guarantee = Guarantee.example
var encoder= AbiEncoder.init() let expected = AbiEncoder.encode:
encoder.startTuple() ((guarantee.targetChannelId, guarantee.destinations),)
encoder.startTuple() check AbiEncoder.encode(guarantee) == expected
encoder.write(guarantee.targetChannelId)
encoder.write(guarantee.destinations)
encoder.finishTuple()
encoder.finishTuple()
check AbiEncoder.encode(guarantee) == encoder.finish()
test "encodes allocation items": test "encodes allocation items":
let item = AllocationItem.example let item = AllocationItem.example
var encoder= AbiEncoder.init() let expected = AbiEncoder.encode: (item.destination, item.amount)
encoder.startTuple() check AbiEncoder.encode(item) == expected
encoder.write(item.destination)
encoder.write(item.amount)
encoder.finishTuple()
check AbiEncoder.encode(item) == encoder.finish()
test "encodes allocation": test "encodes allocation":
let allocation = Allocation.example let allocation = Allocation.example
var encoder= AbiEncoder.init() let expected = AbiEncoder.encode:
encoder.startTuple() (seq[AllocationItem](allocation),)
encoder.write(seq[AllocationItem](allocation)) check AbiEncoder.encode(allocation) == expected
encoder.finishTuple()
check AbiEncoder.encode(allocation) == encoder.finish()
test "encodes allocation outcome": test "encodes allocation outcome":
let assetOutcome = AssetOutcome( let assetOutcome = AssetOutcome(
@ -38,19 +27,11 @@ suite "outcome":
assetHolder: EthAddress.example, assetHolder: EthAddress.example,
allocation: Allocation.example allocation: Allocation.example
) )
var content= AbiEncoder.init() let content = AbiEncoder.encode:
content.startTuple() ((allocationType, AbiEncoder.encode(assetOutcome.allocation)),)
content.startTuple() let expected = AbiEncoder.encode:
content.write(allocationType) (assetOutcome.assetHolder, content)
content.write(AbiEncoder.encode(assetOutcome.allocation)) check AbiEncoder.encode(assetOutcome) == expected
content.finishTuple()
content.finishTuple()
var encoder= AbiEncoder.init()
encoder.startTuple()
encoder.write(assetOutcome.assetHolder)
encoder.write(content.finish())
encoder.finishTuple()
check AbiEncoder.encode(assetOutcome) == encoder.finish()
test "encodes guarantee outcome": test "encodes guarantee outcome":
let assetOutcome = AssetOutcome( let assetOutcome = AssetOutcome(
@ -58,27 +39,17 @@ suite "outcome":
assetHolder: EthAddress.example, assetHolder: EthAddress.example,
guarantee: Guarantee.example guarantee: Guarantee.example
) )
var content= AbiEncoder.init() let content = AbiEncoder.encode:
content.startTuple() ((guaranteeType, AbiEncoder.encode(assetOutcome.guarantee)),)
content.startTuple() let expected = AbiEncoder.encode:
content.write(guaranteeType) (assetOutcome.assetHolder, content)
content.write(AbiEncoder.encode(assetOutcome.guarantee)) check AbiEncoder.encode(assetOutcome) == expected
content.finishTuple()
content.finishTuple()
var encoder= AbiEncoder.init()
encoder.startTuple()
encoder.write(assetOutcome.assetHolder)
encoder.write(content.finish())
encoder.finishTuple()
check AbiEncoder.encode(assetOutcome) == encoder.finish()
test "encodes outcomes": test "encodes outcomes":
let outcome = Outcome.example() let outcome = Outcome.example()
var encoder= AbiEncoder.init() let expected = AbiEncoder.encode:
encoder.startTuple() (seq[AssetOutcome](outcome),)
encoder.write(seq[AssetOutcome](outcome)) check AbiEncoder.encode(outcome) == expected
encoder.finishTuple()
check AbiEncoder.encode(outcome) == encoder.finish()
test "hashes outcomes": test "hashes outcomes":
let outcome = Outcome.example let outcome = Outcome.example
@ -90,7 +61,7 @@ suite "outcome":
let outcome = Outcome(@[ let outcome = Outcome(@[
AssetOutcome( AssetOutcome(
kind: allocationType, kind: allocationType,
assetHolder: !EthAddress.parse( assetHolder: !EthAddress.init(
"1E90B49563da16D2537CA1Ddd9b1285279103D93" "1E90B49563da16D2537CA1Ddd9b1285279103D93"
), ),
allocation: Allocation(@[ allocation: Allocation(@[
@ -104,7 +75,7 @@ suite "outcome":
), ),
AssetOutcome( AssetOutcome(
kind: guaranteeType, kind: guaranteeType,
assetHolder: !EthAddress.parse( assetHolder: !EthAddress.init(
"1E90B49563da16D2537CA1Ddd9b1285279103D93" "1E90B49563da16D2537CA1Ddd9b1285279103D93"
), ),
guarantee: Guarantee( guarantee: Guarantee(

View File

@ -40,7 +40,7 @@ suite "signature":
chainId: 0x1.u256, chainId: 0x1.u256,
nonce: 1, nonce: 1,
participants: @[ participants: @[
!EthAddress.parse("0x8a64E10FF40Bc9C90EA5750313dB5e036495c10E") !EthAddress.init("0x8a64E10FF40Bc9C90EA5750313dB5e036495c10E")
] ]
), ),
outcome: Outcome(@[]), outcome: Outcome(@[]),

View File

@ -23,26 +23,20 @@ suite "state":
) )
test "hashes app part of state": test "hashes app part of state":
var encoder= AbiEncoder.init() let encoded = AbiEncoder.encode:
encoder.startTuple() (state.challengeDuration, state.appDefinition, state.appData)
encoder.write(state.challengeDuration)
encoder.write(state.appDefinition)
encoder.write(state.appData)
encoder.finishTuple()
let encoded = encoder.finish()
let hashed = keccak256.digest(encoded).data let hashed = keccak256.digest(encoded).data
check hashAppPart(state) == hashed check hashAppPart(state) == hashed
test "hashes state": test "hashes state":
var encoder= AbiEncoder.init() let encoded = AbiEncoder.encode:
encoder.startTuple() (
encoder.write(state.turnNum) state.turnNum,
encoder.write(state.isFinal) state.isFinal,
encoder.write(getChannelId(state.channel)) getChannelId(state.channel),
encoder.write(hashAppPart(state)) hashAppPart(state),
encoder.write(hashOutcome(state.outcome)) hashOutcome(state.outcome)
encoder.finishTuple() )
let encoded = encoder.finish()
let hashed = keccak256.digest(encoded).data let hashed = keccak256.digest(encoded).data
check hashState(state) == hashed check hashState(state) == hashed
@ -52,7 +46,7 @@ suite "state":
chainId: 0x1.u256, chainId: 0x1.u256,
nonce: 1, nonce: 1,
participants: @[ participants: @[
!EthAddress.parse("DBE821484648c73C1996Da25f2355342B9803eBD") !EthAddress.init("DBE821484648c73C1996Da25f2355342B9803eBD")
] ]
), ),
outcome: Outcome(@[]), outcome: Outcome(@[]),

View File

@ -4,7 +4,7 @@ import pkg/nitro/wallet/nonces
suite "nonces": suite "nonces":
let chainId = UInt256.example let chainId = UInt256.example
let participants = seq[EthAddress].example let participants = seq[EthAddress].example(1..5)
var nonces: Nonces var nonces: Nonces
@ -27,7 +27,7 @@ suite "nonces":
check nonces.getNonce(chainId, participants) == 102 check nonces.getNonce(chainId, participants) == 102
test "nonces are different when participants differ": test "nonces are different when participants differ":
let otherParticipants = seq[EthAddress].example let otherParticipants = seq[EthAddress].example(1..5)
nonces.incNonce(0, chainId, participants) nonces.incNonce(0, chainId, participants)
check nonces.getNonce(chainId, otherParticipants) == 0 check nonces.getNonce(chainId, otherParticipants) == 0