mirror of https://github.com/waku-org/nwaku.git
chore: remove waku swap protocol
This commit is contained in:
parent
67fa736db3
commit
2b5fd2a21f
|
@ -95,11 +95,3 @@ when defined(rln):
|
||||||
./v2/waku_rln_relay/test_wakunode_rln_relay,
|
./v2/waku_rln_relay/test_wakunode_rln_relay,
|
||||||
./v2/waku_rln_relay/test_rln_group_manager_onchain,
|
./v2/waku_rln_relay/test_rln_group_manager_onchain,
|
||||||
./v2/waku_rln_relay/test_rln_group_manager_static
|
./v2/waku_rln_relay/test_rln_group_manager_static
|
||||||
|
|
||||||
# Waku swap test suite
|
|
||||||
import
|
|
||||||
./v2/test_waku_swap
|
|
||||||
|
|
||||||
# TODO: Only enable this once swap module is integrated more nicely as a dependency, i.e. as submodule with CI etc
|
|
||||||
# For PoC execute it manually and run separate module here: https://github.com/vacp2p/swap-contracts-module
|
|
||||||
# import ./v2/test_waku_swap_contracts
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import
|
||||||
../../waku/v2/protocol/waku_filter,
|
../../waku/v2/protocol/waku_filter,
|
||||||
../../waku/v2/protocol/waku_lightpush,
|
../../waku/v2/protocol/waku_lightpush,
|
||||||
../../waku/v2/protocol/waku_peer_exchange,
|
../../waku/v2/protocol/waku_peer_exchange,
|
||||||
../../waku/v2/protocol/waku_swap/waku_swap,
|
|
||||||
./testlib/common,
|
./testlib/common,
|
||||||
./testlib/testutils,
|
./testlib/testutils,
|
||||||
./testlib/waku2
|
./testlib/waku2
|
||||||
|
@ -97,9 +96,6 @@ procSuite "Peer Manager":
|
||||||
# Create filter peer
|
# Create filter peer
|
||||||
filterLoc = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet()
|
filterLoc = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet()
|
||||||
filterPeer = PeerInfo.new(generateEcdsaKey(), @[filterLoc])
|
filterPeer = PeerInfo.new(generateEcdsaKey(), @[filterLoc])
|
||||||
# Create swap peer
|
|
||||||
swapLoc = MultiAddress.init("/ip4/127.0.0.2/tcp/2").tryGet()
|
|
||||||
swapPeer = PeerInfo.new(generateEcdsaKey(), @[swapLoc])
|
|
||||||
# Create store peer
|
# Create store peer
|
||||||
storeLoc = MultiAddress.init("/ip4/127.0.0.3/tcp/4").tryGet()
|
storeLoc = MultiAddress.init("/ip4/127.0.0.3/tcp/4").tryGet()
|
||||||
storePeer = PeerInfo.new(generateEcdsaKey(), @[storeLoc])
|
storePeer = PeerInfo.new(generateEcdsaKey(), @[storeLoc])
|
||||||
|
@ -107,22 +103,17 @@ procSuite "Peer Manager":
|
||||||
await node.start()
|
await node.start()
|
||||||
|
|
||||||
await node.mountFilterClient()
|
await node.mountFilterClient()
|
||||||
await node.mountSwap()
|
|
||||||
node.mountStoreClient()
|
node.mountStoreClient()
|
||||||
|
|
||||||
node.peerManager.addServicePeer(swapPeer.toRemotePeerInfo(), WakuSwapCodec)
|
|
||||||
node.peerManager.addServicePeer(storePeer.toRemotePeerInfo(), WakuStoreCodec)
|
node.peerManager.addServicePeer(storePeer.toRemotePeerInfo(), WakuStoreCodec)
|
||||||
node.peerManager.addServicePeer(filterPeer.toRemotePeerInfo(), WakuFilterCodec)
|
node.peerManager.addServicePeer(filterPeer.toRemotePeerInfo(), WakuFilterCodec)
|
||||||
|
|
||||||
# Check peers were successfully added to peer manager
|
# Check peers were successfully added to peer manager
|
||||||
check:
|
check:
|
||||||
node.peerManager.peerStore.peers().len == 3
|
node.peerManager.peerStore.peers().len == 2
|
||||||
node.peerManager.peerStore.peers(WakuFilterCodec).allIt(it.peerId == filterPeer.peerId and
|
node.peerManager.peerStore.peers(WakuFilterCodec).allIt(it.peerId == filterPeer.peerId and
|
||||||
it.addrs.contains(filterLoc) and
|
it.addrs.contains(filterLoc) and
|
||||||
it.protocols.contains(WakuFilterCodec))
|
it.protocols.contains(WakuFilterCodec))
|
||||||
node.peerManager.peerStore.peers(WakuSwapCodec).allIt(it.peerId == swapPeer.peerId and
|
|
||||||
it.addrs.contains(swapLoc) and
|
|
||||||
it.protocols.contains(WakuSwapCodec))
|
|
||||||
node.peerManager.peerStore.peers(WakuStoreCodec).allIt(it.peerId == storePeer.peerId and
|
node.peerManager.peerStore.peers(WakuStoreCodec).allIt(it.peerId == storePeer.peerId and
|
||||||
it.addrs.contains(storeLoc) and
|
it.addrs.contains(storeLoc) and
|
||||||
it.protocols.contains(WakuStoreCodec))
|
it.protocols.contains(WakuStoreCodec))
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
{.used.}
|
|
||||||
|
|
||||||
import
|
|
||||||
stew/shims/net as stewNet,
|
|
||||||
testutils/unittests,
|
|
||||||
chronos,
|
|
||||||
chronicles,
|
|
||||||
libp2p/switch,
|
|
||||||
libp2p/protobuf/minprotobuf,
|
|
||||||
libp2p/stream/bufferstream,
|
|
||||||
libp2p/stream/connection,
|
|
||||||
libp2p/crypto/crypto,
|
|
||||||
libp2p/crypto/secp,
|
|
||||||
eth/keys
|
|
||||||
import
|
|
||||||
../../waku/v2/protocol/waku_swap/waku_swap
|
|
||||||
|
|
||||||
|
|
||||||
procSuite "Waku SWAP Accounting":
|
|
||||||
test "Handshake Encode/Decode":
|
|
||||||
let
|
|
||||||
beneficiary = @[byte 0, 1, 2]
|
|
||||||
handshake = Handshake(beneficiary: beneficiary)
|
|
||||||
pb = handshake.encode()
|
|
||||||
|
|
||||||
let decodedHandshake = Handshake.init(pb.buffer)
|
|
||||||
|
|
||||||
check:
|
|
||||||
decodedHandshake.isErr == false
|
|
||||||
decodedHandshake.get().beneficiary == beneficiary
|
|
||||||
|
|
||||||
test "Cheque Encode/Decode":
|
|
||||||
let
|
|
||||||
amount = 1'u32
|
|
||||||
date = 9000'u32
|
|
||||||
beneficiary = @[byte 0, 1, 2]
|
|
||||||
cheque = Cheque(beneficiary: beneficiary, amount: amount, date: date)
|
|
||||||
pb = cheque.encode()
|
|
||||||
|
|
||||||
let decodedCheque = Cheque.init(pb.buffer)
|
|
||||||
|
|
||||||
check:
|
|
||||||
decodedCheque.isErr == false
|
|
||||||
decodedCheque.get() == cheque
|
|
|
@ -1,78 +0,0 @@
|
||||||
# Tests of Swap contracts via external module
|
|
||||||
#
|
|
||||||
import
|
|
||||||
std/[options, osproc, strutils, json],
|
|
||||||
testutils/unittests,
|
|
||||||
chronicles
|
|
||||||
import
|
|
||||||
../../waku/v2/protocol/waku_swap/waku_swap_contracts
|
|
||||||
|
|
||||||
|
|
||||||
procSuite "Basic balance test":
|
|
||||||
var aliceSwapAddress = ""
|
|
||||||
var signature = ""
|
|
||||||
var erc20address = ""
|
|
||||||
|
|
||||||
test "Get pwd of swap module":
|
|
||||||
let (output, errC) = osproc.execCmdEx("(cd ../swap-contracts-module && pwd)")
|
|
||||||
debug "output", output
|
|
||||||
|
|
||||||
check:
|
|
||||||
contains(output, "swap-contracts-module")
|
|
||||||
|
|
||||||
test "Get balance from running node":
|
|
||||||
# NOTE: This corresponds to the first default account in Hardhat
|
|
||||||
let balRes = waku_swap_contracts.getBalance("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
|
|
||||||
var balance: float
|
|
||||||
if balRes.isOk():
|
|
||||||
let json = balRes[]
|
|
||||||
let balanceStr = json["balance"].getStr()
|
|
||||||
balance = parseFloat(balanceStr)
|
|
||||||
|
|
||||||
check:
|
|
||||||
balRes.isOk()
|
|
||||||
balance > 0
|
|
||||||
|
|
||||||
test "Setup Swap":
|
|
||||||
let res = waku_swap_contracts.setupSwap()
|
|
||||||
let json = res[]
|
|
||||||
|
|
||||||
var aliceAddress = json["aliceAddress"].getStr()
|
|
||||||
aliceSwapAddress = json["aliceSwapAddress"].getStr()
|
|
||||||
erc20address = json["erc20address"].getStr()
|
|
||||||
debug "erc20address", erc20address
|
|
||||||
debug "json", json
|
|
||||||
|
|
||||||
# Contains default Alice account
|
|
||||||
check:
|
|
||||||
contains(aliceAddress, "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
|
|
||||||
|
|
||||||
test "Sign Cheque":
|
|
||||||
var sigRes = waku_swap_contracts.signCheque(aliceSwapAddress)
|
|
||||||
if sigRes.isOk():
|
|
||||||
let json = sigRes[]
|
|
||||||
signature = json["signature"].getStr()
|
|
||||||
|
|
||||||
check:
|
|
||||||
sigRes.isOk()
|
|
||||||
contains(signature, "0x")
|
|
||||||
|
|
||||||
test "Get ERC20 Balances":
|
|
||||||
let res = waku_swap_contracts.getERC20Balances(erc20address)
|
|
||||||
|
|
||||||
check:
|
|
||||||
res.isOk()
|
|
||||||
res[]["bobBalance"].getInt() == 10000
|
|
||||||
|
|
||||||
test "Redeem cheque and check balance":
|
|
||||||
let redeemRes = waku_swap_contracts.redeemCheque(aliceSwapAddress, signature)
|
|
||||||
var resp = redeemRes[]["resp"].getStr()
|
|
||||||
debug "Redeem resp", resp
|
|
||||||
|
|
||||||
let balRes = getERC20Balances(erc20address)
|
|
||||||
|
|
||||||
# Balance for Bob has now increased
|
|
||||||
check:
|
|
||||||
redeemRes.isOk()
|
|
||||||
balRes.isOk()
|
|
||||||
balRes[]["bobBalance"].getInt() == 10500
|
|
|
@ -154,7 +154,6 @@ procSuite "Waku v2 JSON-RPC API - Admin":
|
||||||
|
|
||||||
await node.mountFilter()
|
await node.mountFilter()
|
||||||
await node.mountFilterClient()
|
await node.mountFilterClient()
|
||||||
await node.mountSwap()
|
|
||||||
let driver: ArchiveDriver = QueueDriver.new()
|
let driver: ArchiveDriver = QueueDriver.new()
|
||||||
node.mountArchive(some(driver), none(MessageValidator), none(RetentionPolicy))
|
node.mountArchive(some(driver), none(MessageValidator), none(RetentionPolicy))
|
||||||
await node.mountStore()
|
await node.mountStore()
|
||||||
|
|
|
@ -29,7 +29,6 @@ import
|
||||||
../protocol/waku_archive,
|
../protocol/waku_archive,
|
||||||
../protocol/waku_store,
|
../protocol/waku_store,
|
||||||
../protocol/waku_store/client as store_client,
|
../protocol/waku_store/client as store_client,
|
||||||
../protocol/waku_swap/waku_swap,
|
|
||||||
../protocol/waku_filter,
|
../protocol/waku_filter,
|
||||||
../protocol/waku_filter/client as filter_client,
|
../protocol/waku_filter/client as filter_client,
|
||||||
../protocol/waku_lightpush,
|
../protocol/waku_lightpush,
|
||||||
|
@ -90,7 +89,6 @@ type
|
||||||
wakuStoreClient*: WakuStoreClient
|
wakuStoreClient*: WakuStoreClient
|
||||||
wakuFilter*: WakuFilter
|
wakuFilter*: WakuFilter
|
||||||
wakuFilterClient*: WakuFilterClient
|
wakuFilterClient*: WakuFilterClient
|
||||||
wakuSwap*: WakuSwap
|
|
||||||
when defined(rln):
|
when defined(rln):
|
||||||
wakuRlnRelay*: WakuRLNRelay
|
wakuRlnRelay*: WakuRLNRelay
|
||||||
wakuLightPush*: WakuLightPush
|
wakuLightPush*: WakuLightPush
|
||||||
|
@ -651,21 +649,6 @@ proc unsubscribe*(node: WakuNode, pubsubTopic: PubsubTopic, contentTopics: Conte
|
||||||
await node.filterUnsubscribe(pubsubTopic, contentTopics, peer=peerOpt.get())
|
await node.filterUnsubscribe(pubsubTopic, contentTopics, peer=peerOpt.get())
|
||||||
|
|
||||||
|
|
||||||
## Waku swap
|
|
||||||
|
|
||||||
# NOTE: If using the swap protocol, it must be mounted before store. This is
|
|
||||||
# because store is using a reference to the swap protocol.
|
|
||||||
proc mountSwap*(node: WakuNode, swapConfig: SwapConfig = SwapConfig.init()) {.async, raises: [Defect, LPError].} =
|
|
||||||
info "mounting swap", mode = $swapConfig.mode
|
|
||||||
|
|
||||||
node.wakuSwap = WakuSwap.init(node.peerManager, node.rng, swapConfig)
|
|
||||||
if node.started:
|
|
||||||
# Node has started already. Let's start swap too.
|
|
||||||
await node.wakuSwap.start()
|
|
||||||
|
|
||||||
node.switch.mount(node.wakuSwap, protocolMatcher(WakuSwapCodec))
|
|
||||||
|
|
||||||
|
|
||||||
## Waku archive
|
## Waku archive
|
||||||
|
|
||||||
proc mountArchive*(node: WakuNode,
|
proc mountArchive*(node: WakuNode,
|
||||||
|
|
|
@ -1,310 +0,0 @@
|
||||||
## SWAP implements Accounting for Waku. See
|
|
||||||
## https://github.com/vacp2p/specs/issues/24 for more.
|
|
||||||
##
|
|
||||||
## This is based on the SWAP based approach researched by the Swarm team, and
|
|
||||||
## can be thought of as an economic extension to Bittorrent's tit-for-tat
|
|
||||||
## economics.
|
|
||||||
##
|
|
||||||
## It is quite suitable for accounting for imbalances between peers, and
|
|
||||||
## specifically for something like the Store protocol.
|
|
||||||
##
|
|
||||||
## It is structured as follows:
|
|
||||||
##
|
|
||||||
## 1) First a handshake is made, where terms are agreed upon
|
|
||||||
##
|
|
||||||
## 2) Then operation occurs as normal with HistoryRequest, HistoryResponse etc
|
|
||||||
## through store protocol (or otherwise)
|
|
||||||
##
|
|
||||||
## 3) When payment threshhold is met, a cheque is sent. This acts as promise to
|
|
||||||
## pay. Right now it is best thought of as karma points.
|
|
||||||
##
|
|
||||||
## Things like settlement is for future work.
|
|
||||||
##
|
|
||||||
|
|
||||||
when (NimMajor, NimMinor) < (1, 4):
|
|
||||||
{.push raises: [Defect].}
|
|
||||||
else:
|
|
||||||
{.push raises: [].}
|
|
||||||
|
|
||||||
# TODO Generally clean up errors here, a lot of Exceptions, Defects and KeyErros
|
|
||||||
#
|
|
||||||
# On KeyEror specifically:
|
|
||||||
# Accessing Table's items is prone to KeyError exception when the key does not belong to the table
|
|
||||||
# such exception can be avoided by calling hasKey() before accessing the key (which is the case in this module)
|
|
||||||
# but from the compiler point of view, the use of hasKey() does not make any difference in the potential exceptions
|
|
||||||
# - thus any key access should be wrapped inside try-except
|
|
||||||
# - or otherwise the exception should be thrown by the proc and handled by the higher level calls
|
|
||||||
|
|
||||||
import
|
|
||||||
std/[tables, options, json],
|
|
||||||
stew/results,
|
|
||||||
chronos,
|
|
||||||
chronicles,
|
|
||||||
metrics,
|
|
||||||
bearssl/rand,
|
|
||||||
libp2p/crypto/crypto,
|
|
||||||
libp2p/protocols/protocol,
|
|
||||||
libp2p/protobuf/minprotobuf,
|
|
||||||
libp2p/stream/connection
|
|
||||||
import
|
|
||||||
../../../common/protobuf,
|
|
||||||
../../node/peer_manager,
|
|
||||||
./waku_swap_contracts,
|
|
||||||
./waku_swap_types
|
|
||||||
|
|
||||||
export waku_swap_types
|
|
||||||
|
|
||||||
const swapAccountBalanceBuckets = [-Inf, -200.0, -150.0, -100.0, -50.0, 0.0, 50.0, 100.0, 150.0, 200.0, Inf]
|
|
||||||
|
|
||||||
declarePublicGauge waku_swap_peers_count, "number of swap peers"
|
|
||||||
declarePublicGauge waku_swap_errors, "number of swap protocol errors", ["type"]
|
|
||||||
declarePublicGauge waku_swap_messages, "number of swap messages received", ["type"]
|
|
||||||
declarePublicHistogram waku_peer_swap_account_balance, "Swap Account Balance for waku peers, aggregated into buckets based on threshold limits", buckets = swapAccountBalanceBuckets
|
|
||||||
|
|
||||||
logScope:
|
|
||||||
topics = "waku swap"
|
|
||||||
|
|
||||||
const WakuSwapCodec* = "/vac/waku/swap/2.0.0-beta1"
|
|
||||||
|
|
||||||
# Error types (metric label values)
|
|
||||||
const
|
|
||||||
dialFailure = "dial_failure"
|
|
||||||
decodeRpcFailure = "decode_rpc_failure"
|
|
||||||
|
|
||||||
# Serialization
|
|
||||||
# -------------------------------------------------------------------------------
|
|
||||||
proc encode*(handshake: Handshake): ProtoBuffer =
|
|
||||||
var output = initProtoBuffer()
|
|
||||||
|
|
||||||
output.write3(1, handshake.beneficiary)
|
|
||||||
|
|
||||||
output.finish3()
|
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
proc encode*(cheque: Cheque): ProtoBuffer =
|
|
||||||
var output = initProtoBuffer()
|
|
||||||
|
|
||||||
output.write3(1, cheque.beneficiary)
|
|
||||||
output.write3(2, cheque.date)
|
|
||||||
output.write3(3, cheque.amount)
|
|
||||||
output.write3(4, cheque.signature)
|
|
||||||
|
|
||||||
output.finish3()
|
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
proc init*(T: type Handshake, buffer: seq[byte]): ProtoResult[T] =
|
|
||||||
var beneficiary: seq[byte]
|
|
||||||
var handshake = Handshake()
|
|
||||||
let pb = initProtoBuffer(buffer)
|
|
||||||
|
|
||||||
discard ? pb.getField(1, handshake.beneficiary)
|
|
||||||
|
|
||||||
return ok(handshake)
|
|
||||||
|
|
||||||
proc init*(T: type Cheque, buffer: seq[byte]): ProtoResult[T] =
|
|
||||||
var beneficiary: seq[byte]
|
|
||||||
var date: uint32
|
|
||||||
var amount: uint32
|
|
||||||
var signature: seq[byte]
|
|
||||||
var cheque = Cheque()
|
|
||||||
let pb = initProtoBuffer(buffer)
|
|
||||||
|
|
||||||
discard ? pb.getField(1, cheque.beneficiary)
|
|
||||||
discard ? pb.getField(2, cheque.date)
|
|
||||||
discard ? pb.getField(3, cheque.amount)
|
|
||||||
discard ? pb.getField(4, cheque.signature)
|
|
||||||
|
|
||||||
return ok(cheque)
|
|
||||||
|
|
||||||
# Accounting
|
|
||||||
# -------------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# We credit and debits peers based on what for now is a form of Karma asset.
|
|
||||||
|
|
||||||
# TODO Test for credit/debit operations in succession
|
|
||||||
|
|
||||||
|
|
||||||
# TODO Assume we calculated cheque
|
|
||||||
proc sendCheque*(ws: WakuSwap, peerId: PeerID) {.async.} =
|
|
||||||
let connOpt = await ws.peerManager.dialPeer(peerId, WakuSwapCodec)
|
|
||||||
|
|
||||||
if connOpt.isNone():
|
|
||||||
# @TODO more sophisticated error handling here
|
|
||||||
error "failed to connect to remote peer"
|
|
||||||
waku_swap_errors.inc(labelValues = [dialFailure])
|
|
||||||
return
|
|
||||||
|
|
||||||
info "sendCheque"
|
|
||||||
|
|
||||||
# TODO We get this from the setup of swap setup, dynamic, should be part of setup
|
|
||||||
# TODO Add beneficiary, etc
|
|
||||||
var aliceSwapAddress = "0x6C3d502f1a97d4470b881015b83D9Dd1062172e1"
|
|
||||||
var aliceWalletAddress = "0x6C3d502f1a97d4470b881015b83D9Dd1062172e1"
|
|
||||||
var signature: string
|
|
||||||
|
|
||||||
var res = waku_swap_contracts.signCheque(aliceSwapAddress)
|
|
||||||
if res.isOk():
|
|
||||||
info "signCheque ", res=res[]
|
|
||||||
let json = res[]
|
|
||||||
signature = json["signature"].getStr()
|
|
||||||
else:
|
|
||||||
# To test code paths, this should look different in a production setting
|
|
||||||
warn "Something went wrong when signing cheque, sending anyway"
|
|
||||||
|
|
||||||
info "Signed Cheque", swapAddress = aliceSwapAddress, signature = signature, issuerAddress = aliceWalletAddress
|
|
||||||
let sigBytes = cast[seq[byte]](signature)
|
|
||||||
await connOpt.get().writeLP(Cheque(amount: 1, signature: sigBytes, issuerAddress: aliceWalletAddress).encode().buffer)
|
|
||||||
|
|
||||||
# Set new balance
|
|
||||||
ws.accounting[peerId] -= 1
|
|
||||||
info "New accounting state", accounting = ws.accounting[peerId]
|
|
||||||
|
|
||||||
# TODO Authenticate cheque, check beneficiary etc
|
|
||||||
proc handleCheque*(ws: WakuSwap, cheque: Cheque, peerId: PeerID) {.raises: [Defect, KeyError].} =
|
|
||||||
info "handle incoming cheque"
|
|
||||||
|
|
||||||
# Get the original signer using web3. For now, a static value (0x6C3d502f1a97d4470b881015b83D9Dd1062172e1) will be used.
|
|
||||||
# Check if web3.eth.personal.ecRecover(messageHash, signature); or an equivalent function has been implemented in nim-web3
|
|
||||||
let signer = "0x6C3d502f1a97d4470b881015b83D9Dd1062172e1"
|
|
||||||
|
|
||||||
# Verify that the Issuer was the signer of the signature
|
|
||||||
if signer != cheque.issuerAddress:
|
|
||||||
warn "Invalid cheque: The address of the issuer is different from the signer."
|
|
||||||
|
|
||||||
# TODO Redeem cheque here
|
|
||||||
var signature = cast[string](cheque.signature)
|
|
||||||
# TODO Where should Alice Swap Address come from? Handshake probably?
|
|
||||||
# Hacky for now
|
|
||||||
var aliceSwapAddress = "0x6C3d502f1a97d4470b881015b83D9Dd1062172e1"
|
|
||||||
info "Redeeming cheque with", swapAddress=aliceSwapAddress, signature=signature
|
|
||||||
var res = waku_swap_contracts.redeemCheque(aliceSwapAddress, signature)
|
|
||||||
if res.isOk():
|
|
||||||
info "redeemCheque ok", redeem=res[]
|
|
||||||
else:
|
|
||||||
info "Unable to redeem cheque"
|
|
||||||
|
|
||||||
# Check balance here
|
|
||||||
# TODO How do we get ERC20 address here?
|
|
||||||
# XXX This one is wrong
|
|
||||||
# Normally this would be part of initial setup, otherwise we need some temp persistence here
|
|
||||||
# Possibly as part of handshake?
|
|
||||||
var erc20address = "0x6C3d502f1a97d4470b881015b83D9Dd1062172e1"
|
|
||||||
let balRes = waku_swap_contracts.getERC20Balances(erc20address)
|
|
||||||
if balRes.isOk():
|
|
||||||
# XXX: Assumes Alice and Bob here...
|
|
||||||
var bobBalance = balRes[]["bobBalance"].getInt()
|
|
||||||
info "New balance is", balance = bobBalance
|
|
||||||
else:
|
|
||||||
info "Problem getting Bob balance"
|
|
||||||
|
|
||||||
# TODO Could imagine scenario where you don't cash cheque but leave it as credit
|
|
||||||
# In that case, we would probably update accounting state, but keep track of cheques
|
|
||||||
|
|
||||||
# When this is true we update accounting state anyway when node is offline,
|
|
||||||
# makes waku_swap test pass for now
|
|
||||||
# Consider desired logic here
|
|
||||||
var stateUpdateOverRide = true
|
|
||||||
|
|
||||||
if res.isOk():
|
|
||||||
info "Updating accounting state with redeemed cheque"
|
|
||||||
ws.accounting[peerId] += int(cheque.amount)
|
|
||||||
else:
|
|
||||||
if stateUpdateOverRide:
|
|
||||||
info "Updating accounting state with even if cheque failed"
|
|
||||||
ws.accounting[peerId] += int(cheque.amount)
|
|
||||||
else:
|
|
||||||
info "Not updating accounting state with due to bad cheque"
|
|
||||||
|
|
||||||
info "New accounting state", accounting = ws.accounting[peerId]
|
|
||||||
|
|
||||||
# Log Account Metrics
|
|
||||||
proc logAccountMetrics*(ws: Wakuswap, peer: PeerId) {.async.}=
|
|
||||||
waku_peer_swap_account_balance.observe(ws.accounting[peer].int64)
|
|
||||||
|
|
||||||
|
|
||||||
proc init*(wakuSwap: WakuSwap) =
|
|
||||||
info "wakuSwap init 1"
|
|
||||||
proc handle(conn: Connection, proto: string) {.async, gcsafe, closure.} =
|
|
||||||
info "swap handle incoming connection"
|
|
||||||
var message = await conn.readLp(MaxChequeSize.int)
|
|
||||||
# XXX This can be handshake, etc
|
|
||||||
var res = Cheque.init(message)
|
|
||||||
if res.isErr:
|
|
||||||
error "failed to decode rpc"
|
|
||||||
waku_swap_errors.inc(labelValues = [decodeRpcFailure])
|
|
||||||
return
|
|
||||||
|
|
||||||
info "received cheque", value=res.value
|
|
||||||
waku_swap_messages.inc(labelValues = ["Cheque"])
|
|
||||||
wakuSwap.handleCheque(res.value, conn.peerId)
|
|
||||||
|
|
||||||
proc credit(peerId: PeerID, n: int)
|
|
||||||
{.gcsafe, closure, raises: [Defect, KeyError, Exception].} =
|
|
||||||
|
|
||||||
info "Crediting peer: ", peer=peerId, amount=n
|
|
||||||
if wakuSwap.accounting.hasKey(peerId):
|
|
||||||
wakuSwap.accounting[peerId] -= n
|
|
||||||
else:
|
|
||||||
wakuSwap.accounting[peerId] = -n
|
|
||||||
info "Accounting state", accounting = wakuSwap.accounting[peerId]
|
|
||||||
wakuSwap.applyPolicy(peerId)
|
|
||||||
|
|
||||||
# TODO Debit and credit here for Karma asset
|
|
||||||
proc debit(peerId: PeerID, n: int)
|
|
||||||
{.gcsafe, closure, raises: [Defect, KeyError, Exception].} =
|
|
||||||
|
|
||||||
info "Debiting peer: ", peer=peerId, amount=n
|
|
||||||
if wakuSwap.accounting.hasKey(peerId):
|
|
||||||
wakuSwap.accounting[peerId] += n
|
|
||||||
else:
|
|
||||||
wakuSwap.accounting[peerId] = n
|
|
||||||
info "Accounting state", accounting = wakuSwap.accounting[peerId]
|
|
||||||
wakuSwap.applyPolicy(peerId)
|
|
||||||
|
|
||||||
proc applyPolicy(peerId: PeerID)
|
|
||||||
{.gcsafe, closure, raises: [Defect, KeyError, Exception].} =
|
|
||||||
|
|
||||||
# TODO Separate out depending on if policy is soft (accounting only) mock (send cheque but don't cash/verify) hard (actually send funds over testnet)
|
|
||||||
|
|
||||||
#Check if the Disconnect Threshold has been hit. Account Balance nears the disconnectThreshold after a Credit has been done
|
|
||||||
if wakuSwap.accounting[peerId] <= wakuSwap.config.disconnectThreshold:
|
|
||||||
warn "Disconnect threshhold has been reached: ", threshold=wakuSwap.config.disconnectThreshold, balance=wakuSwap.accounting[peerId]
|
|
||||||
else:
|
|
||||||
info "Disconnect threshhold not hit"
|
|
||||||
|
|
||||||
#Check if the Payment threshold has been hit. Account Balance nears the paymentThreshold after a Debit has been done
|
|
||||||
if wakuSwap.accounting[peerId] >= wakuSwap.config.paymentThreshold:
|
|
||||||
warn "Payment threshhold has been reached: ", threshold=wakuSwap.config.paymentThreshold, balance=wakuSwap.accounting[peerId]
|
|
||||||
#In soft phase we don't send cheques yet
|
|
||||||
if wakuSwap.config.mode == Mock:
|
|
||||||
discard wakuSwap.sendCheque(peerId)
|
|
||||||
else:
|
|
||||||
info "Payment threshhold not hit"
|
|
||||||
|
|
||||||
waitFor wakuSwap.logAccountMetrics(peerId)
|
|
||||||
|
|
||||||
wakuSwap.handler = handle
|
|
||||||
wakuSwap.codec = WakuSwapCodec
|
|
||||||
wakuSwap.credit = credit
|
|
||||||
wakuSwap.debit = debit
|
|
||||||
wakuswap.applyPolicy = applyPolicy
|
|
||||||
|
|
||||||
# TODO Expression return?
|
|
||||||
proc init*(T: type WakuSwap, peerManager: PeerManager, rng: ref rand.HmacDrbgContext, swapConfig: SwapConfig): T =
|
|
||||||
info "wakuSwap init 2"
|
|
||||||
let
|
|
||||||
accounting = initTable[PeerId, int]()
|
|
||||||
text = "test"
|
|
||||||
|
|
||||||
var ws = WakuSwap(rng: rng,
|
|
||||||
peerManager: peerManager,
|
|
||||||
accounting: accounting,
|
|
||||||
text: text,
|
|
||||||
config: swapConfig)
|
|
||||||
ws.init()
|
|
||||||
|
|
||||||
return ws
|
|
||||||
|
|
||||||
# TODO End to end communication
|
|
|
@ -1,90 +0,0 @@
|
||||||
# Glue code to interact with SWAP contracts module.
|
|
||||||
#
|
|
||||||
# Assumes swap-contracts-module node is running.
|
|
||||||
#
|
|
||||||
|
|
||||||
when (NimMajor, NimMinor) < (1, 4):
|
|
||||||
{.push raises: [Defect].}
|
|
||||||
else:
|
|
||||||
{.push raises: [].}
|
|
||||||
|
|
||||||
import
|
|
||||||
std/[osproc, strutils, json],
|
|
||||||
chronicles, stew/results
|
|
||||||
|
|
||||||
logScope:
|
|
||||||
topics = "waku swap"
|
|
||||||
|
|
||||||
# TODO Richer error types than string, overkill for now...
|
|
||||||
type NodeTaskJsonResult = Result[JsonNode, string]
|
|
||||||
|
|
||||||
# XXX In general this is not a great API, more a collection of hacky glue code for PoC.
|
|
||||||
|
|
||||||
# Interacts with node in sibling path and interacts with a local Hardhat node.
|
|
||||||
const taskPrelude = "npx hardhat --network localhost "
|
|
||||||
const cmdPrelude = "cd ../swap-contracts-module; " & taskPrelude
|
|
||||||
|
|
||||||
# proc execNodeTask(taskStr: string): tuple[output: string, exitCode: int] =
|
|
||||||
# let cmdString = $cmdPrelude & $taskStr
|
|
||||||
# debug "execNodeTask", cmdString
|
|
||||||
# return osproc.execCmdEx(cmdString)
|
|
||||||
|
|
||||||
proc execNodeTaskJson(taskStr: string): NodeTaskJsonResult =
|
|
||||||
let cmdString = $cmdPrelude & $taskStr
|
|
||||||
debug "execNodeTask", cmdString
|
|
||||||
|
|
||||||
try:
|
|
||||||
let (output, errC) = osproc.execCmdEx(cmdString)
|
|
||||||
if errC>0:
|
|
||||||
error "Error executing node task", output
|
|
||||||
return err(output)
|
|
||||||
|
|
||||||
debug "Command executed", output
|
|
||||||
|
|
||||||
try:
|
|
||||||
let json = parseJson(output)
|
|
||||||
return ok(json)
|
|
||||||
except JsonParsingError:
|
|
||||||
return err("Unable to parse JSON:" & $output)
|
|
||||||
except Exception:
|
|
||||||
return err("Unable to parse JSON:" & $output)
|
|
||||||
|
|
||||||
except OSError:
|
|
||||||
return err("Unable to execute command, OSError:" & $taskStr)
|
|
||||||
except Exception:
|
|
||||||
return err("Unable to execute command:" & $taskStr)
|
|
||||||
|
|
||||||
proc getBalance*(accountAddress: string): NodeTaskJsonResult =
|
|
||||||
let task = "balance --account " & $accountAddress
|
|
||||||
let res = execNodeTaskJson(task)
|
|
||||||
return res
|
|
||||||
|
|
||||||
proc setupSwap*(): NodeTaskJsonResult =
|
|
||||||
let task = "setupSwap"
|
|
||||||
let res = execNodeTaskJson(task)
|
|
||||||
return res
|
|
||||||
|
|
||||||
proc signCheque*(swapAddress: string): NodeTaskJsonResult =
|
|
||||||
let task = "signCheque --swapaddress '" & $swapAddress & "'"
|
|
||||||
var res = execNodeTaskJson(task)
|
|
||||||
return res
|
|
||||||
|
|
||||||
proc getERC20Balances*(erc20address: string): NodeTaskJsonResult =
|
|
||||||
let task = "getBalances --erc20address '" & $erc20address & "'"
|
|
||||||
let res = execNodeTaskJson(task)
|
|
||||||
debug "getERC20Balances", res
|
|
||||||
return res
|
|
||||||
|
|
||||||
proc redeemCheque*(swapAddress: string, signature: string): NodeTaskJsonResult =
|
|
||||||
let task = "redeemCheque --swapaddress '" & $swapAddress & "' --signature '" & $signature & "'"
|
|
||||||
let res = execNodeTaskJson(task)
|
|
||||||
return res
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
var aliceSwapAddress = "0x6C3d502f1a97d4470b881015b83D9Dd1062172e1"
|
|
||||||
var sigRes = signCheque(aliceSwapAddress)
|
|
||||||
if sigRes.isOk():
|
|
||||||
echo "All good"
|
|
||||||
echo "Signature ", sigRes[]
|
|
||||||
else:
|
|
||||||
echo sigRes
|
|
|
@ -1,63 +0,0 @@
|
||||||
when (NimMajor, NimMinor) < (1, 4):
|
|
||||||
{.push raises: [Defect].}
|
|
||||||
else:
|
|
||||||
{.push raises: [].}
|
|
||||||
|
|
||||||
import
|
|
||||||
std/tables,
|
|
||||||
bearssl/rand,
|
|
||||||
libp2p/protocols/protocol,
|
|
||||||
../../node/peer_manager
|
|
||||||
|
|
||||||
const
|
|
||||||
MaxChequeSize* = 64*1024 # Used for read buffers. 64kB should be more than enough for swap cheque
|
|
||||||
|
|
||||||
type
|
|
||||||
# The Swap Mode determines the functionality available in the swap protocol.
|
|
||||||
# Soft: Deals with the account balance (Credit and debit) of each peer.
|
|
||||||
# Mock: Includes the Send Cheque Functionality and peer disconnection upon failed signature verification or low balance.
|
|
||||||
# Hard: Includes interactions with Smart Contracts.
|
|
||||||
SwapMode* = enum
|
|
||||||
Soft,
|
|
||||||
Mock,
|
|
||||||
Hard
|
|
||||||
|
|
||||||
SwapConfig* = object
|
|
||||||
mode* : SwapMode
|
|
||||||
paymentThreshold* : int
|
|
||||||
disconnectThreshold* : int
|
|
||||||
|
|
||||||
Beneficiary* = seq[byte]
|
|
||||||
|
|
||||||
# TODO Consider adding payment threshhold and terms field
|
|
||||||
Handshake* = object
|
|
||||||
beneficiary*: Beneficiary
|
|
||||||
|
|
||||||
# TODO Look over these data structures again
|
|
||||||
Cheque* = object
|
|
||||||
issuerAddress*: string
|
|
||||||
beneficiary*: Beneficiary
|
|
||||||
date*: uint32
|
|
||||||
amount*: uint32
|
|
||||||
signature*: seq[byte]
|
|
||||||
|
|
||||||
CreditHandler* = proc (peerId: PeerID, amount: int) {.gcsafe, closure.}
|
|
||||||
DebitHandler* = proc (peerId: PeerID, amount: int) {.gcsafe, closure.}
|
|
||||||
ApplyPolicyHandler* = proc(peerId: PeerID) {.gcsafe, closure.}
|
|
||||||
|
|
||||||
WakuSwap* = ref object of LPProtocol
|
|
||||||
peerManager*: PeerManager
|
|
||||||
rng*: ref rand.HmacDrbgContext
|
|
||||||
text*: string
|
|
||||||
accounting*: Table[PeerId, int]
|
|
||||||
credit*: CreditHandler
|
|
||||||
debit*: DebitHandler
|
|
||||||
applyPolicy*: ApplyPolicyHandler
|
|
||||||
config*: SwapConfig
|
|
||||||
|
|
||||||
proc init*(_: type[SwapConfig]): SwapConfig =
|
|
||||||
SwapConfig(
|
|
||||||
mode: SwapMode.Soft,
|
|
||||||
paymentThreshold: 100,
|
|
||||||
disconnectThreshold: -100
|
|
||||||
)
|
|
Loading…
Reference in New Issue