From 76cc0a9bccce7c59f5d90f3637f3adecbdee4946 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Wed, 14 Apr 2021 12:57:14 +0200 Subject: [PATCH] Wallet keeps track of nonces --- nitro/basics/ethaddress.nim | 2 ++ nitro/wallet/nonces.nim | 34 ++++++++++++++++++++++++++++ nitro/wallet/wallet.nim | 15 +++++++++++++ tests/nitro/testWallet.nim | 6 +++++ tests/nitro/wallet/testNonces.nim | 37 +++++++++++++++++++++++++++++++ tests/testAll.nim | 1 + 6 files changed, 95 insertions(+) create mode 100644 nitro/wallet/nonces.nim create mode 100644 tests/nitro/wallet/testNonces.nim diff --git a/nitro/basics/ethaddress.nim b/nitro/basics/ethaddress.nim index 2e44b49..8202547 100644 --- a/nitro/basics/ethaddress.nim +++ b/nitro/basics/ethaddress.nim @@ -1,3 +1,4 @@ +import std/hashes import pkg/questionable import pkg/questionable/results import pkg/stew/byteutils @@ -19,3 +20,4 @@ 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.} diff --git a/nitro/wallet/nonces.nim b/nitro/wallet/nonces.nim new file mode 100644 index 0000000..ea1d029 --- /dev/null +++ b/nitro/wallet/nonces.nim @@ -0,0 +1,34 @@ +import std/tables +import std/sets +import std/hashes +import ../basics + +push: {.upraises: [].} + +type + Nonces* = object + next: Table[NonceKey, UInt48] + NonceKey = object + chainId: UInt256 + participants: HashSet[EthAddress] + +func hash(key: NonceKey): Hash = + var h: Hash + h = h !& key.chainId.hash + h = h !& key.participants.hash + !$h + +func key(chainId: UInt256, participants: openArray[EthAddress]): NonceKey = + NonceKey(chainId: chainId, participants: participants.toHashSet) + +func getNonce*(nonces: var Nonces, + chainId: UInt256, + participants: varargs[EthAddress]): UInt48 = + nonces.next.?[key(chainId, participants)] |? 0 + +func incNonce*(nonces: var Nonces, + oldNonce: UInt48, + chainId: UInt256, + participants: varargs[EthAddress]) = + let next = max(oldNonce, nonces.getNonce(chainId, participants)) + 1 + nonces.next[key(chainId, participants)] = next diff --git a/nitro/wallet/wallet.nim b/nitro/wallet/wallet.nim index f5a9f68..404ec22 100644 --- a/nitro/wallet/wallet.nim +++ b/nitro/wallet/wallet.nim @@ -5,6 +5,7 @@ import ../protocol import ./signedstate import ./ledger import ./balances +import ./nonces push: {.upraises:[].} @@ -17,6 +18,7 @@ type Wallet* = object key: EthPrivateKey channels: Table[ChannelId, SignedState] + nonces: Nonces ChannelId* = Destination Payment* = tuple destination: Destination @@ -39,11 +41,16 @@ func sign(wallet: Wallet, state: SignedState): SignedState = signed.signatures &= wallet.key.sign(state.state) signed +func incNonce(wallet: var Wallet, state: SignedState) = + let channel = state.state.channel + wallet.nonces.incNonce(channel.nonce, channel.chainId, channel.participants) + func createChannel(wallet: var Wallet, state: SignedState): ?!ChannelId = let id = getChannelId(state.state.channel) if wallet.channels.contains(id): return ChannelId.failure("channel with id " & $id & " already exists") wallet.channels[id] = wallet.sign(state) + wallet.incNonce(state) ok id func updateChannel(wallet: var Wallet, state: SignedState) = @@ -60,6 +67,14 @@ func openLedgerChannel*(wallet: var Wallet, let state = startLedger(wallet.address, hub, chainId, nonce, asset, amount) wallet.createChannel(state) +func openLedgerChannel*(wallet: var Wallet, + hub: EthAddress, + chainId: UInt256, + asset: EthAddress, + amount: UInt256): ?!ChannelId = + let nonce = wallet.nonces.getNonce(chainId, wallet.address, hub) + openLedgerChannel(wallet, hub, chainId, nonce, asset, amount) + func acceptChannel*(wallet: var Wallet, signed: SignedState): ?!ChannelId = if not signed.hasParticipant(wallet.address): return ChannelId.failure "wallet owner is not a participant" diff --git a/tests/nitro/testWallet.nim b/tests/nitro/testWallet.nim index 9939516..44a2288 100644 --- a/tests/nitro/testWallet.nim +++ b/tests/nitro/testWallet.nim @@ -31,6 +31,12 @@ suite "wallet: opening ledger channel": check definition.nonce == nonce check definition.participants == @[wallet.address, hub] + test "uses consecutive nonces when none is provided": + channel = wallet.openLedgerChannel(hub, chainId, asset, amount).get + check wallet.state(channel).get.channel.nonce == nonce + 1 + channel = wallet.openLedgerChannel(hub, chainId, asset, amount).get + check wallet.state(channel).get.channel.nonce == nonce + 2 + test "provides correct outcome": let outcome = wallet.state(channel).get.outcome check outcome == Outcome.init(asset, {wallet.destination: amount}) diff --git a/tests/nitro/wallet/testNonces.nim b/tests/nitro/wallet/testNonces.nim new file mode 100644 index 0000000..fbc93b7 --- /dev/null +++ b/tests/nitro/wallet/testNonces.nim @@ -0,0 +1,37 @@ +import ../basics +import pkg/nitro/wallet/nonces + +suite "nonces": + + let chainId = UInt256.example + let participants = seq[EthAddress].example + + var nonces: Nonces + + setup: + nonces = Nonces() + + test "nonces start at 0": + check nonces.getNonce(chainId, participants) == 0 + + test "nonces increase by 1": + nonces.incNonce(0, chainId, participants) + check nonces.getNonce(chainId, participants) == 1 + nonces.incNonce(1, chainId, participants) + check nonces.getNonce(chainId, participants) == 2 + + test "nonces do not decrease": + nonces.incNonce(100, chainId, participants) + check nonces.getNonce(chainId, participants) == 101 + nonces.incNonce(0, chainId, participants) + check nonces.getNonce(chainId, participants) == 102 + + test "nonces are different when participants differ": + let otherParticipants = seq[EthAddress].example + nonces.incNonce(0, chainId, participants) + check nonces.getNonce(chainId, otherParticipants) == 0 + + test "nonces are different when chain ids differ": + let otherChainId = UInt256.example + nonces.incNonce(0, chainId, participants) + check nonces.getNonce(otherChainId, participants) == 0 diff --git a/tests/testAll.nim b/tests/testAll.nim index c747a70..650548e 100644 --- a/tests/testAll.nim +++ b/tests/testAll.nim @@ -3,6 +3,7 @@ import ./nitro/protocol/testChannel import ./nitro/protocol/testOutcome import ./nitro/protocol/testState import ./nitro/protocol/testSignature +import ./nitro/wallet/testNonces import ./nitro/testWallet import ./nitro/testJson