Allow wallet to be used as a reference type

This commit is contained in:
Mark Spanbroek 2021-04-19 16:14:27 +02:00
parent 50d59ce48a
commit 2a8e4e5bf4
3 changed files with 90 additions and 17 deletions

46
nitro/wallet/deref.nim Normal file
View File

@ -0,0 +1,46 @@
import std/macros
func identName(identDefs: NimNode): NimNode =
identDefs.expectKind(nnkIdentDefs)
identDefs[0]
func identType(identDefs: NimNode): NimNode =
identDefs.expectKind(nnkIdentDefs)
identDefs[^2]
func `identType=`(identDefs: NimNode, identType: NimNode) =
identDefs.expectKind(nnkIdentDefs)
identDefs[^2] = identType
func insertRef(function: NimNode) =
function.expectKind(nnkFuncDef)
var paramType = function.params[1].identType
if paramType.kind == nnkVarTy:
paramType = paramType[0]
function.params[1].identType = newNimNode(nnkRefTy, paramType).add(paramType)
func paramNames(function: NimNode): seq[NimNode] =
function.expectKind(nnkFuncDef)
for i in 1..<function.params.len:
result.add(function.params[i].identName)
func insertDeref(params: var seq[NimNode]) =
params[0] = newNimNode(nnkBracketExpr, params[0]).add(params[0])
func derefOverload(function: NimNode): NimNode =
function.expectKind(nnkFuncDef)
var arguments = function.paramNames
arguments.insertDeref()
result = function.copyNimTree()
result.insertRef()
result.body = newCall(function.name, arguments)
macro deref*(function: untyped{nkFuncDef}): untyped =
## Creates an overload that dereferences the first argument of the function
## call. Roughly equivalent to the `implicitDeref` experimental feature of
## Nim.
let overload = derefOverload(function)
quote do:
`function`
`overload`

View File

@ -6,6 +6,7 @@ import ./signedstate
import ./ledger
import ./balances
import ./nonces
import ./deref
push: {.upraises:[].}
@ -19,21 +20,25 @@ type
key: EthPrivateKey
channels: Table[ChannelId, SignedState]
nonces: Nonces
WalletRef* = ref Wallet
ChannelId* = Destination
Payment* = tuple
destination: Destination
amount: UInt256
func init*(_: type Wallet, key: EthPrivateKey): Wallet =
result.key = key
Wallet(key: key)
func publicKey*(wallet: Wallet): EthPublicKey =
func new*(_: type WalletRef, key: EthPrivateKey): WalletRef =
WalletRef(key: key)
func publicKey*(wallet: Wallet): EthPublicKey {.deref.} =
wallet.key.toPublicKey
func address*(wallet: Wallet): EthAddress =
func address*(wallet: Wallet): EthAddress {.deref.} =
wallet.publicKey.toAddress
func destination*(wallet: Wallet): Destination =
func destination*(wallet: Wallet): Destination {.deref.}=
wallet.address.toDestination
func sign(wallet: Wallet, state: SignedState): SignedState =
@ -63,7 +68,7 @@ func openLedgerChannel*(wallet: var Wallet,
chainId: UInt256,
nonce: UInt48,
asset: EthAddress,
amount: UInt256): ?!ChannelId =
amount: UInt256): ?!ChannelId {.deref.} =
let state = startLedger(wallet.address, hub, chainId, nonce, asset, amount)
wallet.createChannel(state)
@ -71,11 +76,12 @@ func openLedgerChannel*(wallet: var Wallet,
hub: EthAddress,
chainId: UInt256,
asset: EthAddress,
amount: UInt256): ?!ChannelId =
amount: UInt256): ?!ChannelId {.deref.} =
let nonce = wallet.nonces.getNonce(chainId, wallet.address, hub)
openLedgerChannel(wallet, hub, chainId, nonce, asset, amount)
func acceptChannel*(wallet: var Wallet, signed: SignedState): ?!ChannelId =
func acceptChannel*(wallet: var Wallet,
signed: SignedState): ?!ChannelId {.deref.} =
if not signed.hasParticipant(wallet.address):
return failure "wallet owner is not a participant"
@ -84,18 +90,21 @@ func acceptChannel*(wallet: var Wallet, signed: SignedState): ?!ChannelId =
wallet.createChannel(signed)
func latestSignedState*(wallet: Wallet, channel: ChannelId): ?SignedState =
func latestSignedState*(wallet: Wallet,
channel: ChannelId): ?SignedState {.deref.} =
wallet.channels.?[channel]
func state*(wallet: Wallet, channel: ChannelId): ?State =
func state*(wallet: Wallet,
channel: ChannelId): ?State {.deref.} =
wallet.latestSignedState(channel).?state
func signatures*(wallet: Wallet, channel: ChannelId): ?seq[Signature] =
func signatures*(wallet: Wallet,
channel: ChannelId): ?seq[Signature] {.deref.} =
wallet.latestSignedState(channel).?signatures
func signature*(wallet: Wallet,
channel: ChannelId,
address: EthAddress): ?Signature =
address: EthAddress): ?Signature {.deref.} =
if signed =? wallet.latestSignedState(channel):
for signature in signed.signatures:
if signer =? signature.recover(signed.state):
@ -117,7 +126,7 @@ func balance(state: State,
func balance*(wallet: Wallet,
channel: ChannelId,
asset: EthAddress,
destination: Destination): UInt256 =
destination: Destination): UInt256 {.deref.} =
if state =? wallet.state(channel):
state.balance(asset, destination)
else:
@ -126,10 +135,12 @@ func balance*(wallet: Wallet,
func balance*(wallet: Wallet,
channel: ChannelId,
asset: EthAddress,
address: EthAddress): UInt256 =
address: EthAddress): UInt256 {.deref.} =
wallet.balance(channel, asset, address.toDestination)
func balance*(wallet: Wallet, channel: ChannelId, asset: EthAddress): UInt256 =
func balance*(wallet: Wallet,
channel: ChannelId,
asset: EthAddress): UInt256 {.deref.} =
wallet.balance(channel, asset, wallet.address)
func total(state: State, asset: EthAddress): UInt256 =
@ -149,7 +160,7 @@ func pay*(wallet: var Wallet,
channel: ChannelId,
asset: EthAddress,
receiver: Destination,
amount: UInt256): ?!SignedState =
amount: UInt256): ?!SignedState {.deref.} =
without var state =? wallet.state(channel):
return failure "channel not found"
@ -165,14 +176,14 @@ func pay*(wallet: var Wallet,
channel: ChannelId,
asset: EthAddress,
receiver: EthAddress,
amount: UInt256): ?!SignedState =
amount: UInt256): ?!SignedState {.deref.} =
wallet.pay(channel, asset, receiver.toDestination, amount)
func acceptPayment*(wallet: var Wallet,
channel: ChannelId,
asset: EthAddress,
sender: EthAddress,
payment: SignedState): ?!void =
payment: SignedState): ?!void {.deref.} =
if not wallet.channels.contains(channel):
return failure "unknown channel"

View File

@ -213,3 +213,19 @@ suite "wallet: accepting payments":
payment.state.appDefinition = EthAddress.example
payment.signatures = @[payerKey.sign(payment.state)]
check receiver.acceptPayment(channel, asset, payer.address, payment).isFailure
suite "wallet reference type":
let asset = EthAddress.example
let amount = 42.u256
let chainId = UInt256.example
test "wallet can also be used as a reference type":
let wallet1 = WalletRef.new(EthPrivateKey.random())
let wallet2 = WalletRef.new(EthPrivateKey.random())
let address1 = wallet1.address
let address2 = wallet2.address
let channel = !wallet1.openLedgerChannel(address2, chainId, asset, amount)
check !wallet2.acceptChannel(!wallet1.latestSignedState(channel)) == channel
let payment = !wallet1.pay(channel, asset, address2, amount)
check wallet2.acceptPayment(channel, asset, address1, payment).isSuccess