ETH <-> XMR atomic swap prototype

ETH-XMR Atomic Swaps

This is a prototype of ETH<->XMR atomic swaps, which was worked on during ETHLisbon.

Instructions

Start ganache-cli with determinstic keys:

ganache-cli -d

Start monerod for regtest:

./monerod --regtest --fixed-difficulty=1 --rpc-bind-port 18081 --offline

Start monero-wallet-rpc for Bob with some wallet that has regtest monero:

./monero-wallet-rpc  --rpc-bind-port 18083 --password "" --disable-rpc-login --wallet-file test-wallet

Start monero-wallet-rpc for Alice:

./monero-wallet-rpc  --rpc-bind-port 18084 --password "" --disable-rpc-login --wallet-dir .
Compiling contract bindings

Download solc v0.8.9

Set SOLC_BIN to the downloaded binary

export SOLC_BIN=solc

Generate the bindings

./scripts/generate-bindings.sh
Testing

To run tests on the go bindings, execute

go test ./swap-contract
Owner
Elizabeth // Software Engineer at @ChainSafe
null
Comments
  • Solidity contracts + testing revamp

    Solidity contracts + testing revamp

    The main goal of this was to abstract the specific cryptographic implementation for the swap from the swap logic itself in order to minimise code duplication, which is now the case. Swap.sol is an abstract contract that can be instantiated needing only a verifySecret function that contains the verification logic.

    In between a bunch of other stuff happened and in this PR are now also:

    • consistent error reporting
    • testing logic in the smart contracts is now also independent of the impl files
    • scalable hardhat tests
    • simplified crypto - at least for secp256k1 now only the bit that actually matters is included

    I was halfway through doing this when I realised the DLEQ approach isn't actually integrated with the rest of the code - that is still the idea though, no? It's way faster, works in principle and I for one would like to play around with @Lederstrumpf's stuff.

    TODO:

    • propagate new error messages to Go tests
    • rename all instances of Swap.sol to SwapOnChain.sol in Go code
    • run integration tests (the new script is awesome, thanks :D)
    • integrate the DLEQ option into the actual program (not for this PR...)

    Other than that, here are some preliminary gas measurements for the two approaches:

    ·----------------------------------------|----------------------------|-------------|-----------------------------·
    |          Solc version: 0.8.5           ·  Optimizer enabled: false  ·  Runs: 200  ·  Block limit: 30000000 gas  │
    ·········································|····························|·············|······························
    |  Methods                                                                                                        │
    ····················|····················|··············|·············|·············|···············|··············
    |  Contract         ·  Method            ·  Min         ·  Max        ·  Avg        ·  # calls      ·  eur (avg)  │
    ····················|····················|··············|·············|·············|···············|··············
    |  SwapDLEQMock     ·  testVerifySecret  ·       30304  ·      30316  ·      30310  ·            2  ·          -  │
    ····················|····················|··············|·············|·············|···············|··············
    |  SwapOnChainMock  ·  testVerifySecret  ·      930673  ·     950395  ·     938818  ·            5  ·          -  │
    ····················|····················|··············|·············|·············|···············|··············
    |  Deployments                           ·                                          ·  % of limit   ·             │
    ·········································|··············|·············|·············|···············|··············
    |  SwapDLEQMock                          ·           -  ·          -  ·    1350801  ·        4.5 %  ·          -  │
    ·········································|··············|·············|·············|···············|··············
    |  SwapOnChainMock                       ·           -  ·          -  ·    1855645  ·        6.2 %  ·          -  │
    ·----------------------------------------|--------------|-------------|-------------|---------------|-------------·
    

    So that'd indeed be roughly a 30x improvement.

  • mismatched nonce in TestSwapFactory_NewSwap and TestSwap_Refund_afterT1

    mismatched nonce in TestSwapFactory_NewSwap and TestSwap_Refund_afterT1

    I'm trying to get up-to-speed on the code base. One of the first things I tried was the ./scripts/run-unit-tests.sh script called by make test.

    === RUN   TestSwapFactory_NewSwap
        swap_factory_test.go:41: gas cost to deploy SwapFactory.sol: 782308
        swap_factory_test.go:46: 
            	Error Trace:	swap_factory_test.go:46
            	Error:      	Received unexpected error:
            	            	the tx doesn't have the correct nonce. account has nonce of: 3 tx has nonce of: 2
            	Test:       	TestSwapFactory_NewSwap
    
    === RUN   TestSwap_Refund_afterT1
        swap_factory_test.go:272: gas cost to deploy SwapFactory.sol: 782308
        swap_factory_test.go:277: 
                Error Trace:    swap_factory_test.go:277
                Error:          Received unexpected error:
                                the tx doesn't have the correct nonce. account has nonce of: 20 tx has nonce of: 19
                Test:           TestSwap_Refund_afterT1
    --- FAIL: TestSwap_Refund_afterT1 (0.95s)
    

    Is this a known issue? Is there any other information I can provide?

    I'm on Ubuntu 22.04:

    $ go version
    go version go1.18.3 linux/amd64
    
    $ /usr/local/bin/ganache-cli --version
    Ganache CLI v6.12.2 (ganache-core: 2.13.2)
    
  • integrate DLEq into protocol

    integrate DLEq into protocol

    to save on gas, a secp256k1 ScalarBaseMult can occur in the swap contract instead of a ed25519 ScalarBaseMult to verify the secret corresponding to the public key, as this is cheaper to do with secp256k1. a DLEq proof can be used to then verify that the ed25519 public key and secp256k1 public key correspond to the same secret scalar. this needs to be provided by both Alice and Bob parties, as both their public keys are stored in the contract.

    to perform this integration:

    • swap contract needs to be updated to perform a secp256k1 ScalarBaseMult
    • Alice needs to store the secp256k1 keys in the contract instead of the ed25519 keys
    • both parties must generate a secp256k1 key as well as an ed25519 key in the keygen step of the protocol
    • DLEq proof also needs to be generated in the keygen step of the protocol
    • the proof needs to be verified in the handling of SendKeysMessage, and the swap aborted if it isn't valid
  • implement relayer/metatx support for swap contract `claim` function

    implement relayer/metatx support for swap contract `claim` function

    currently, Bob needs to have some ETH to pay for gas when calling Claim() to get his ETH. this is a pretty old ETH problem (eg. I have DAI but no ETH, so the DAI is trapped) - I'm sure there is likely some existing solution to this, just need to explore what's out there.

  • integration of DLEQ generation & verification

    integration of DLEQ generation & verification

    Currently, secp256k1 Point calculation in Solidity still doesn't match external calculation, so this must be debugged.

    Likewise, this PR currently replaces prior implementation, which is NOT the intention - it was just more efficient to work like this, so the onchain version should still be readded.

  • protocol optimization: verify hash that derives view key in contract, instead of the public key, saving on *lots* of gas

    protocol optimization: verify hash that derives view key in contract, instead of the public key, saving on *lots* of gas

    I propose an update to the existing swap protocol that will dramatically reduce the gas costs to deploy the contract and call Claim/Refund.

    the current protocol verifies that the secret passed to the contract (either s_a or s_b) corresponds to the public key of either Alice or Bob (that was set when the contract was deployed) by performing an ed25519 scalar base multiplication and checking that the resulting point == the public key. this is expensive (around 1mil gas currently)

    however, this can be replaced by only verifying a keccak256 hash (30 gas!!)

    for background, a monero private view key is derived from the private spend key as follows:

    func (k *PrivateSpendKey) View() (*PrivateViewKey, error) {
    	h := Keccak256(k.key.Bytes())
    	vk, err := ed25519.NewScalar().SetBytesWithClamping(h[:])
    	if err != nil {
    		return nil, err
    	}
    
    	return &PrivateViewKey{
    		key: vk,
    	}, nil
    }
    

    first, the spend key is hashed using keccak256 and then set to a point on the ed25519 curve using clamping. the resulting scalar is the view key.

    the updated protocol is as follows:

    1. in the key exchange step, Alice and Bob exchange public spend keys (P_a and P_b) and private view keys (v_a and v_b), and additionally the keccak256-hashes of their private spend keys (h_a and h_b) that are used to derive their private view keys. when each party receives the other party's keys, they verify that the hash they receive corresponds to the view key.
    2. Alice deploys a Swap contract storing the keccak256 hashes h_a and h_b in the contract. Claim can be called by revealing the pre-image to h_b (ie. s_b) and Refund can be called by revealing the pre-image to h_a (ie. s_a)
    3. the rest of the protocol proceeds as usual - Bob locks his XMR in the account P_a + P_b, which is viewable with v_a + v_b and spendable with s_a + s_b. either Claim or Refund will be called on the contract, and thus one party will end up with the spend key to the account at completion of the protocol.

    note: we don't verify in step 1 that the view key corresponds to the public spend key, as it's impossible to do so. however, if the public spend key does not correspond to the view key, then the funds cannot be viewed. before Bob locks his XMR, he can check that he can generate a view-only wallet using the summation of the two view keys. if he cannot, he should abort the protocol as the view keys and public keys do not correspond. or, on Alice's side, if she cannot view the funds after they are locked (as the view keys are wrong), she should abort.

    implications:

    • the only drawback I see to this method is that both Alice and Bob now have the secret view key to the account with the locked XMR, so at the end of the protocol, now both Alice and Bob can view when the funds are transferred out. I don't see this being an issue, as Alice can just transfer the funds to another account once the swap is done if she wishes. either way, both parties know of the amount being swapped to begin with, so it doesn't really reduce privacy imo.
    • actually, as the libp2p messages aren't encrypted, there's a chance anyone can get the secret view keys and create a view-only wallet for the account. however if someone intercepts the messages between Alice and Bob, they also already know of the amounts transferred and where. so, as long as Alice transfers her funds out after the swap (if she cares about someone viewing the account), I think this is ok.

    I'll work on updating the contract/code for this and reply with the benchmark updates. and of course, let me know if there are security issuse with this proposed update.

  • Adaptor Signature based swap for reduced tx fees

    Adaptor Signature based swap for reduced tx fees

    Adaptor signatures are used in the XMR/BTC atomic swap reference client More In Depth Reading HERE

    Adaptor signatures

    The setup for an adaptor signature involves a secret value, an adaptor signature, and a “normal" signature. Knowing any two of these data is enough to calculate the third This allows us to verify the signatures across different curves in a way that secrets can be revealed upon the final redeem transaction.

    Ethereum cost advantages

    ed25519 inside of a smart contract is costly. An adaptor signature swap allows for native secp256k1 inside the contract through sha256() and ecrecover() calls. These also cut down on the needed storage for each instance of the swap which reduces cots. This approach also enables ETH to be pre-deposited to a market contract which enables on chain signaling for easier defi integration.

    Adaptor Signature Process

    This is based earlier draft Classic Alice and Bob want to swap eth for xmr

    • 1- Alice:Create Deposit of eth into swap smart contract
    • 2- Bob:Fund xmr address and key split priv Key (K) into 2 parts (aK & bK)
    • 3- Bob:Submit adaptor sig to Alice (OffChain)
    • 3- Bob:Submit hash(aK) to contract (OnChain contract interaction)
    • 4- Alice:Lock withdraw address to B's + time lock with auto revert on expiry (OnChain contract interaction)
    • 5- Bob: claim eth deposit in contract using aK and signing it as part of the tx (OnChain contract interaction)
    • 6- Alice: calculates bK from signature and adaptor sig. Is able to spend xmr since they now have K

    *step 4 is needed to prevent front running attacks since only the withdraw address is able to withdraw. Time lock reverts funds to original funder after x amount of time. Financial incentive makes B claim deposit To further add privacy on the eth side, funds are pooled in the smart contract allowing for an anon set to increase the more atomic swaps that happen. (similar to tornado cash)

    Key advantage in this means that no zero-knowledge proofs are needed inside the smart contract as it all happens offchain. This reduces costs significantly and reduces the need for trusted key setups and complexity. The smart contract is basically a simple escrow contract which is locked for a period of time and reverts if nothing happens after x amount of time. The contract in a locked state can release funds by the withdrew address interacting with it and providing the Ak key shard as a claim key. The transaction that does this also contains a signature so this enables Alice to then calculate the monero priv key K from the shard received earlier (Ak) and adaptor sig + signature in tx to calculate bK.

    The contract not only time locks to allow enough time for the swap to happen, but it also locks the address. This is done to prevent the potential of a party brute forcing hashes for AK. This lowers the privacy a bit since the address is on chain in the contract but I think it isn't a big deal since it would be public when they withdrew anyways. In the future it could incorporate some zero knowledge proofs in contract but that's a later conversation.

    Comments

    While I understand the highlevel use of adaptor signatures I am having some trouble implementing them. I know that there are reference clients in rust that were used for the XMR/BTC swap, we can use the same functions for creating the adaptor sigs and for calculating secrets once Bob claims eth deposit. This should seriously cut down on associated costs for the swap as the computational expensive cryptography is done off chain and the on chain contract acts as a simple escrow contract.

    Also if there is anything wrong in my foundational understanding of adaptor signatures please call me out on it. I wasn't able to get as much feedback and those specific crypto concepts are not super common.

    references

    Golang adaptor sig libary XMR/BTC atomic swap reference client XMR/BTC atomic swap POC client Encrypted Signature whitepaper

  • refactor net package to separate generic and swap-specific functionality

    refactor net package to separate generic and swap-specific functionality

    • create net/swapnet package which implements a swap-specific host; ie. a host that supports network messages used in the swap.
    • update net to be a more generic libp2p host that supports discovery and the ability to register stream handlers

    related to #209

  • add FakeDLEq; add pure go + darwin build commands

    add FakeDLEq; add pure go + darwin build commands

    this PR attempts to fix the build for non-linux platforms by removing the Rust dependency completely.

    it adds make commands make build-go which builds the pure go project and make build-go-darwin which builds the pure go project for GOOS=darwin and GOARCH=arm64.

    WARNING: the pure go build should not be used in reality, as the DLEq prover has been stubbed out and now proves nothing.

  • panic on database shutdown

    panic on database shutdown

    By accident I typed swapd instead of swapcli and discovered this panic. It's unrelated to what I'm working on now, so just ticketing it. Based on the output, it looks related to the database close.

    $ ./swapd balance --swapd-port 5001
    2022-10-22T17:17:55.443-0500	ERROR	cmd	swapd/main.go:270	failed to make daemon: dial tcp 127.0.0.1:8545: connect: connection refused
    protocol complete, shutting down...
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xfa6081]
    
    goroutine 1 [running]:
    github.com/athanorlabs/atomic-swap/db.(*Database).Close(...)
    	/home/dima/dev/atomic-swap/db/database.go:36
    main.(*daemon).stop(0xc000a27e80)
    	/home/dima/dev/atomic-swap/cmd/swapd/main.go:286 +0x21
    main.runDaemon(0xc000a27e00)
    	/home/dima/dev/atomic-swap/cmd/swapd/main.go:277 +0x15e
    github.com/urfave/cli/v2.(*App).RunContext(0x1f29d00, {0x1733c80?, 0xc0000400d0}, {0xc00003c080, 0x4, 0x4})
    	/home/dima/go/pkg/mod/github.com/urfave/cli/[email protected]/app.go:350 +0x9d3
    github.com/urfave/cli/v2.(*App).Run(...)
    	/home/dima/go/pkg/mod/github.com/urfave/cli/[email protected]/app.go:247
    main.main()
    	/home/dima/dev/atomic-swap/cmd/swapd/main.go:201 +0x47
    
  • TestValidateAddress rare failure

    TestValidateAddress rare failure

    I just got this error in CI, unrelated to current code changes. My initial guess is that GenerateKeys has an issue that rarely shows itself.

    === RUN   TestValidateAddress
        address_test.go:17: 
            	Error Trace:	/home/runner/work/atomic-swap/atomic-swap/crypto/monero/address_test.go:17
            	Error:      	Received unexpected error:
            	            	invalid monero address length: got 68, expected 69
            	Test:       	TestValidateAddress
    --- FAIL: TestValidateAddress (0.00s)
    
  • remove previous net/* contents, move net/swapnet/* to net/

    remove previous net/* contents, move net/swapnet/* to net/

    as title says, diff looks messed up because net/swapnet was moved to net/. previous net/ contents were moved here: https://github.com/AthanorLabs/go-p2p-net

    also feel free to suggest on package naming, right now the package in net/ is still called swapnet because the package in the above repo is called net

    related to #209

  • Fixed Point Math

    Fixed Point Math

    This PR updates our code to use the fixed point match library apd.Decimal. Using floats for money, especially ethereum, is problematic because:

    • Conversion back and forth between base2 to base10 is lossy.
    • ETHER requires 18 decimal places of precision, but float64 only handles around 16 decimals.

    Note: Before this PR, the piconero, wei and ERC20 types where in common and the exchange rate type was in common/types. This was causing problems, because support methods for all 4 types required access to the same constants and math helper functions. To solve this issue, I pulled the types out into a new, top-level coins package.

  • implement suggested exchange rate

    implement suggested exchange rate

    implement suggested exchange rate feature by querying the on-chain Chainlink price feeds (if we're on mainnet).

    if we're on stagenet/dev, then 1 is returned.

    closes #205

  • add prometheus metrics

    add prometheus metrics

    it would be nice to add metrics to the daemon for additional monitoring/debugging/crash notifications.

    example metrics:

    • network peer count
    • network num open streams on each protocol
    • network num streams per peer
    • network messages received and sent
    • ongoing swap count
    • average swap duration
    • swap success/refund/abort rate
  • swapd should transfer xmr back to original account if a refund occurs

    swapd should transfer xmr back to original account if a refund occurs

    currently, refunded xmr is left in the generated swap wallet. instead, the protocol should transfer funds back to the original account if a refund occurs.

Related tags
x-crafter is used to quickly create templates from your prototype, also come with a builder to quickly regenerate your code

XCrafter ?? x-crafter is used to quickly create templates from your prototype, also come with a builder to quickly regenerate your code. Install Using

Nov 29, 2021
A prototype of a plugin system in Go using syscalls (execve)

Talking binaries Creating a viable plugin system in Go is challenging. Some avenues (and architectural examples) I considered are: go-plugin Go plugin

Jan 24, 2022
A mining pool proxy tool, support BTC, ETH, ETC, XMR mining pool, etc.

Tier2Pool A mining pool proxy tool, support BTC, ETH, ETC, XMR mining pool, etc. Build I use Ubuntu as a demo. sudo update sudo apt install git make s

Jul 29, 2022
Atomic: a go package for atomic file writing

atomic import "github.com/natefinch/atomic" atomic is a go package for atomic file writing By default, writing to a file in go (and generally any lan

Nov 10, 2021
:chart_with_upwards_trend: Monitors Go MemStats + System stats such as Memory, Swap and CPU and sends via UDP anywhere you want for logging etc...

Package stats Package stats allows for gathering of statistics regarding your Go application and system it is running on and sent them via UDP to a se

Nov 10, 2022
k8s-image-swapper Mirror images into your own registry and swap image references automatically.
k8s-image-swapper Mirror images into your own registry and swap image references automatically.

k8s-image-swapper Mirror images into your own registry and swap image references automatically. k8s-image-swapper is a mutating webhook for Kubernetes

Dec 27, 2022
Allows you to swap usernames at https://solo.to, I swapped @/city and @/lose with this program :)
Allows you to swap usernames at https://solo.to, I swapped @/city and @/lose with this program :)

Solo.To-Username-Swapper added the exe version for people who do not have Go installed, how ever I reccomend installing it at https://golang.org Getti

Sep 29, 2021
Hot-swap Kubernetes clusters while keeping your microservices up and running.

Okra Okra is a Kubernetes controller and a set of CRDs which provide advanced multi-cluster appilcation rollout capabilities, such as canary deploymen

Nov 23, 2022
Get all the swap details within the block range

go-swap-statistics get all the swap details within the block range get started git clone https://github.com/huahuayu/go-swap-statistics.git cd go-swap

Jul 22, 2022
A Console Application Use Pancakeswap To Swap Token
A Console Application Use Pancakeswap To Swap Token

Pancakeswap Console A Pancakeswap Application Why It is a pity that some areas do not have access to the functions of PancakeSwap. I developed this to

Dec 30, 2021
A Dero service to sell Eth for Dero

ETH Seller - a Dero Service This is a Dero service for the Stargate R2 testnet, written for the dARCH 2021 Event 0.5 competition

Jul 18, 2022
🎯 ENS (.eth domain) batch domain resolver

ENS batch domain resolver (.eth domain) A simple program to check a batch of ENS domains availability. Configure Configs store in config.yaml file nex

Mar 15, 2022
Compares the BTC and ETH buy and sell prices between two exchanges.

CryptoComparer Repo for the crypto comparer, where I compare prices between two exchanges(Blockchain and Binance), and tell you which is the best plac

Oct 28, 2021
Eth-based smartcontracts framework

description status Advance EVM-Based framework development PENTA GO PENTA GO Is one of several PENTA ARCH services created to facilitate the developme

Dec 8, 2021
Go-serverless-eth-event-listener - Go serverless, ethereum contract event listener with a sample contract

go-serverless-eth-event-listener This repository is for showing how to listen sm

May 19, 2022
Dynatomic is a library for using dynamodb as an atomic counter

Dynatomic Dynatomic is a library for using dynamodb as an atomic counter Dynatomic Motivation Usage Development Contributing Motivation The dynatomic

Sep 26, 2022
atomic measures + Prometheus exposition library

About Atomic measures with Prometheus exposition for the Go programming language. This is free and unencumbered software released into the public doma

Sep 27, 2022
All the missing AMD64 atomic instructions

atomics All the AMD64 atomic instructions, exposed as Go functions: ⚠️ PRE-ALPHA ⚠️ Features LOCK {ADD,ADC,AND,BTC,BTR,BTS,CMPXCHG,CMPXCH8B,CMPXCHG16B

Sep 20, 2021
An atomic counter that also tracks and calculates a rate written in Go.

An atomic counter that also tracks and calculates a rate written in Go.

Oct 27, 2021