mirror of
https://github.com/logos-storage/nim-nitro.git
synced 2026-01-04 22:53:06 +00:00
Accept incoming channel
This commit is contained in:
parent
48e27a9bd1
commit
2737732e6d
20
nitro/channelupdate.nim
Normal file
20
nitro/channelupdate.nim
Normal file
@ -0,0 +1,20 @@
|
||||
import ./basics
|
||||
import ./protocol
|
||||
|
||||
include questionable/errorban
|
||||
|
||||
type
|
||||
ChannelUpdate* = object
|
||||
state*: State
|
||||
signatures*: seq[(EthAddress, Signature)]
|
||||
|
||||
proc participants*(update: ChannelUpdate): seq[EthAddress] =
|
||||
update.state.channel.participants
|
||||
|
||||
proc verifySignatures*(update: ChannelUpdate): bool =
|
||||
for (participant, signature) in update.signatures:
|
||||
if not update.participants.contains(participant):
|
||||
return false
|
||||
if not signature.verify(update.state, participant):
|
||||
return false
|
||||
true
|
||||
20
nitro/ledger.nim
Normal file
20
nitro/ledger.nim
Normal file
@ -0,0 +1,20 @@
|
||||
import ./basics
|
||||
import ./channelupdate
|
||||
import ./protocol
|
||||
|
||||
proc startLedger*(me: EthAddress,
|
||||
hub: EthAddress,
|
||||
chainId: UInt256,
|
||||
nonce: UInt48,
|
||||
asset: EthAddress,
|
||||
amount: UInt256): ChannelUpdate =
|
||||
ChannelUpdate(
|
||||
state: State(
|
||||
channel: ChannelDefinition(
|
||||
chainId: chainId,
|
||||
participants: @[me, hub],
|
||||
nonce: nonce
|
||||
),
|
||||
outcome: Outcome.init(asset, {me.toDestination: amount})
|
||||
)
|
||||
)
|
||||
@ -1,21 +1,21 @@
|
||||
import ./basics
|
||||
import ./keys
|
||||
import ./protocol
|
||||
import ./channelupdate
|
||||
import ./ledger
|
||||
|
||||
include questionable/errorban
|
||||
|
||||
export basics
|
||||
export keys
|
||||
export channelupdate
|
||||
|
||||
type
|
||||
Wallet* = object
|
||||
key: PrivateKey
|
||||
channels*: seq[Channel]
|
||||
Channel* = object
|
||||
latest*, upcoming*: ?ChannelUpdate
|
||||
ChannelUpdate* = object
|
||||
state*: State
|
||||
signatures*: seq[(EthAddress, Signature)]
|
||||
latest*: ChannelUpdate
|
||||
|
||||
proc init*(_: type Wallet, key: PrivateKey): Wallet =
|
||||
result.key = key
|
||||
@ -23,25 +23,31 @@ proc init*(_: type Wallet, key: PrivateKey): Wallet =
|
||||
proc address*(wallet: Wallet): EthAddress =
|
||||
wallet.key.toPublicKey.toAddress
|
||||
|
||||
proc sign(wallet: Wallet, update: ChannelUpdate): ChannelUpdate =
|
||||
var signed = update
|
||||
signed.signatures &= @{wallet.address: wallet.key.sign(update.state)}
|
||||
signed
|
||||
|
||||
proc createChannel(wallet: var Wallet, update: ChannelUpdate): Channel =
|
||||
let signed = wallet.sign(update)
|
||||
let channel = Channel(latest: signed)
|
||||
wallet.channels.add(channel)
|
||||
channel
|
||||
|
||||
proc openLedgerChannel*(wallet: var Wallet,
|
||||
hub: EthAddress,
|
||||
chainId: UInt256,
|
||||
nonce: UInt48,
|
||||
asset: EthAddress,
|
||||
amount: UInt256): Channel =
|
||||
let state = State(
|
||||
channel: ChannelDefinition(
|
||||
chainId: chainId,
|
||||
participants: @[wallet.address, hub],
|
||||
nonce: nonce
|
||||
),
|
||||
outcome: Outcome.init(asset, {wallet.address.toDestination: amount})
|
||||
)
|
||||
let channel = Channel(
|
||||
upcoming: ChannelUpdate(
|
||||
state: state,
|
||||
signatures: @{wallet.address: wallet.key.sign(state)}
|
||||
).some
|
||||
)
|
||||
wallet.channels.add(channel)
|
||||
channel
|
||||
let update = startLedger(wallet.address, hub, chainId, nonce, asset, amount)
|
||||
wallet.createChannel(update)
|
||||
|
||||
proc acceptChannel*(wallet: var Wallet, update: ChannelUpdate): ?!Channel =
|
||||
if not update.participants.contains(wallet.address):
|
||||
return Channel.failure "wallet owner is not a participant"
|
||||
|
||||
if not verifySignatures(update):
|
||||
return Channel.failure "incorrect signatures"
|
||||
|
||||
wallet.createChannel(update).success
|
||||
|
||||
@ -23,30 +23,62 @@ suite "wallet: opening ledger channel":
|
||||
wallet = Wallet.init(key)
|
||||
channel = wallet.openLedgerChannel(hub, chainId, nonce, asset, amount)
|
||||
|
||||
test "creates a new upcoming state":
|
||||
check channel.latest.isNone
|
||||
check channel.upcoming.isSome
|
||||
|
||||
test "sets correct channel definition":
|
||||
let definition = channel.upcoming?.state?.channel
|
||||
check definition?.chainId == chainId.some
|
||||
check definition?.nonce == nonce.some
|
||||
check definition?.participants == @[wallet.address, hub].some
|
||||
let definition = channel.latest.state.channel
|
||||
check definition.chainId == chainId
|
||||
check definition.nonce == nonce
|
||||
check definition.participants == @[wallet.address, hub]
|
||||
|
||||
test "provides correct outcome":
|
||||
let outcome = channel.upcoming?.state?.outcome
|
||||
let outcome = channel.latest.state.outcome
|
||||
let destination = wallet.address.toDestination
|
||||
check outcome == Outcome.init(asset, {destination: amount}).some
|
||||
check outcome == Outcome.init(asset, {destination: amount})
|
||||
|
||||
test "signs the upcoming state":
|
||||
let state = channel.upcoming?.state
|
||||
let signatures = channel.upcoming?.signatures
|
||||
check signatures == @{wallet.address: key.sign(state.get)}.some
|
||||
let state = channel.latest.state
|
||||
let signatures = channel.latest.signatures
|
||||
check signatures == @{wallet.address: key.sign(state)}
|
||||
|
||||
test "sets app definition and app data to zero":
|
||||
check channel.upcoming?.state?.appDefinition == EthAddress.zero.some
|
||||
check channel.upcoming?.state?.appData?.len == 0.some
|
||||
check channel.latest.state.appDefinition == EthAddress.zero
|
||||
check channel.latest.state.appData.len == 0
|
||||
|
||||
test "updates the list of channels":
|
||||
check wallet.channels == @[channel]
|
||||
|
||||
suite "wallet: accepting incoming channel":
|
||||
|
||||
let key = PrivateKey.random()
|
||||
var wallet: Wallet
|
||||
var update: ChannelUpdate
|
||||
|
||||
setup:
|
||||
wallet = Wallet.init(key)
|
||||
update = ChannelUpdate(state: State.example)
|
||||
update.state.channel.participants &= @[wallet.address]
|
||||
|
||||
test "returns the new channel instance":
|
||||
let channel = wallet.acceptChannel(update).get
|
||||
check channel.latest.state == update.state
|
||||
|
||||
test "updates the list of channels":
|
||||
let channel = wallet.acceptChannel(update).get
|
||||
check wallet.channels == @[channel]
|
||||
|
||||
test "signs the channel state":
|
||||
let channel = wallet.acceptChannel(update).get
|
||||
let expectedSignatures = @{wallet.address: key.sign(update.state)}
|
||||
check channel.latest.signatures == expectedSignatures
|
||||
|
||||
test "fails when wallet address is not a participant":
|
||||
let wrongParticipants = seq[EthAddress].example
|
||||
update.state.channel.participants = wrongParticipants
|
||||
check wallet.acceptChannel(update).isErr
|
||||
|
||||
test "fails when signatures are incorrect":
|
||||
let otherKey = PrivateKey.random()
|
||||
let otherWallet = Wallet.init(otherKey)
|
||||
let wrongAddress = EthAddress.example
|
||||
update.state.channel.participants &= @[otherWallet.address]
|
||||
update.signatures = @{wrongAddress: otherKey.sign(update.state)}
|
||||
check wallet.acceptChannel(update).isErr
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user