mirror of
https://github.com/AthanorLabs/atomic-swap.git
synced 2026-04-22 03:00:02 -04:00
add relayer support for claim contract function (#204)
This commit is contained in:
@@ -65,6 +65,7 @@ type Backend interface {
|
||||
TxOpts() (*bind.TransactOpts, error)
|
||||
SwapManager() swap.Manager
|
||||
EthAddress() ethcommon.Address
|
||||
EthPrivateKey() *ecdsa.PrivateKey
|
||||
Contract() *contracts.SwapFactory
|
||||
ContractAddr() ethcommon.Address
|
||||
Net() net.MessageSender
|
||||
@@ -80,8 +81,6 @@ type Backend interface {
|
||||
SetXMRDepositAddress(mcrypto.Address, types.Hash)
|
||||
ClearXMRDepositAddress(types.Hash)
|
||||
SetBaseXMRDepositAddress(mcrypto.Address)
|
||||
SetContract(*contracts.SwapFactory)
|
||||
SetContractAddress(ethcommon.Address)
|
||||
}
|
||||
|
||||
type backend struct {
|
||||
@@ -228,6 +227,10 @@ func (b *backend) EthClient() *ethclient.Client {
|
||||
return b.ethClient
|
||||
}
|
||||
|
||||
func (b *backend) EthPrivateKey() *ecdsa.PrivateKey {
|
||||
return b.ethPrivKey
|
||||
}
|
||||
|
||||
func (b *backend) Net() net.MessageSender {
|
||||
return b.MessageSender
|
||||
}
|
||||
@@ -398,15 +401,3 @@ func (b *backend) ClearXMRDepositAddress(id types.Hash) {
|
||||
defer b.Unlock()
|
||||
delete(b.xmrDepositAddrs, id)
|
||||
}
|
||||
|
||||
// NOTE: this is called when a swap is initiated and the XMR-taker specifies the contract
|
||||
// address they will be using.
|
||||
// the contract bytecode is validated in the calling code, but this should never be called
|
||||
// for unvalidated contracts.
|
||||
func (b *backend) SetContract(contract *contracts.SwapFactory) {
|
||||
b.contract = contract
|
||||
}
|
||||
|
||||
func (b *backend) SetContractAddress(addr ethcommon.Address) {
|
||||
b.contractAddr = addr
|
||||
}
|
||||
|
||||
52
protocol/check.go
Normal file
52
protocol/check.go
Normal file
File diff suppressed because one or more lines are too long
@@ -1,8 +1,7 @@
|
||||
package xmrmaker
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
contracts "github.com/athanorlabs/atomic-swap/ethereum"
|
||||
@@ -10,15 +9,10 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheckContractCode(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
b := NewMockBackend(ctrl)
|
||||
|
||||
ec, chainID := tests.NewEthClient(t)
|
||||
ctx := context.Background()
|
||||
pk := tests.GetMakerTestKey(t)
|
||||
@@ -26,17 +20,26 @@ func TestCheckContractCode(t *testing.T) {
|
||||
txOpts, err := bind.NewKeyedTransactorWithChainID(pk, chainID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, tx, _, err := contracts.DeploySwapFactory(txOpts, ec)
|
||||
_, tx, _, err := contracts.DeploySwapFactory(txOpts, ec, ethcommon.Address{})
|
||||
require.NoError(t, err)
|
||||
|
||||
addr, err := bind.WaitDeployed(ctx, ec, tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
b.EXPECT().CodeAt(context.Background(), addr, nil).
|
||||
DoAndReturn(func(ctx context.Context, account ethcommon.Address, _ *big.Int) ([]byte, error) {
|
||||
return ec.CodeAt(ctx, account, nil)
|
||||
})
|
||||
err = CheckContractCode(ctx, ec, addr)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = checkContractCode(ctx, b, addr)
|
||||
// deploy with some arbitrary trustedForwarder address
|
||||
_, tx, _, err = contracts.DeploySwapFactory(
|
||||
txOpts,
|
||||
ec,
|
||||
ethcommon.HexToAddress("0x64e902cD8A29bBAefb9D4e2e3A24d8250C606ee7"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
addr, err = bind.WaitDeployed(ctx, ec, tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = CheckContractCode(ctx, ec, addr)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -8,10 +8,15 @@ import (
|
||||
)
|
||||
|
||||
// MakeOffer makes a new swap offer.
|
||||
func (b *Instance) MakeOffer(o *types.Offer) (*types.OfferExtra, error) {
|
||||
func (b *Instance) MakeOffer(
|
||||
o *types.Offer,
|
||||
relayerEndpoint string,
|
||||
relayerCommission float64,
|
||||
) (*types.OfferExtra, error) {
|
||||
b.backend.LockClient()
|
||||
defer b.backend.UnlockClient()
|
||||
|
||||
// get monero balance
|
||||
balance, err := b.backend.GetBalance(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -22,7 +27,7 @@ func (b *Instance) MakeOffer(o *types.Offer) (*types.OfferExtra, error) {
|
||||
return nil, errUnlockedBalanceTooLow{unlockedBalance.AsMonero(), o.MaximumAmount}
|
||||
}
|
||||
|
||||
extra, err := b.offerManager.AddOffer(o)
|
||||
extra, err := b.offerManager.AddOffer(o, relayerEndpoint, relayerCommission)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
223
protocol/xmrmaker/claim.go
Normal file
223
protocol/xmrmaker/claim.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package xmrmaker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
"github.com/athanorlabs/atomic-swap/common/types"
|
||||
contracts "github.com/athanorlabs/atomic-swap/ethereum"
|
||||
"github.com/athanorlabs/atomic-swap/ethereum/block"
|
||||
"github.com/athanorlabs/atomic-swap/relayer"
|
||||
)
|
||||
|
||||
var numEtherUnitsFloat = big.NewFloat(math.Pow(10, 18))
|
||||
|
||||
func (s *swapState) tryClaim() (ethcommon.Hash, error) {
|
||||
stage, err := s.Contract().Swaps(s.CallOpts(), s.contractSwapID)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
switch stage {
|
||||
case contracts.StageInvalid:
|
||||
return ethcommon.Hash{}, errClaimInvalid
|
||||
case contracts.StageCompleted:
|
||||
return ethcommon.Hash{}, errClaimSwapComplete
|
||||
case contracts.StagePending, contracts.StageReady:
|
||||
// do nothing
|
||||
default:
|
||||
panic("Unhandled stage value")
|
||||
}
|
||||
|
||||
ts, err := s.LatestBlockTimestamp(s.ctx)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
// The block that our claim transaction goes into needs a timestamp that is strictly less
|
||||
// than T1. Since the minimum interval between blocks is 1 second, the current block must
|
||||
// be at least 2 seconds before T1 for a non-zero chance of the next block having a
|
||||
// timestamp that is strictly less than T1.
|
||||
if ts.After(s.t1.Add(-2 * time.Second)) {
|
||||
// We've passed t1, so the only way we can regain control of the locked XMR is for
|
||||
// XMRTaker to call refund on the contract.
|
||||
return ethcommon.Hash{}, errClaimPastTime
|
||||
}
|
||||
|
||||
if ts.Before(s.t0) && stage != contracts.StageReady {
|
||||
// TODO: t0 could be 24 hours from now. Don't we want to poll the stage periodically? (#163)
|
||||
// we need to wait until t0 to claim
|
||||
log.Infof("waiting until time %s to claim, time now=%s", s.t0, time.Now())
|
||||
err = s.WaitForTimestamp(s.ctx, s.t0)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return s.claimFunds()
|
||||
}
|
||||
|
||||
// claimFunds redeems XMRMaker's ETH funds by calling Claim() on the contract
|
||||
func (s *swapState) claimFunds() (ethcommon.Hash, error) {
|
||||
addr := s.EthAddress()
|
||||
|
||||
var (
|
||||
symbol string
|
||||
decimals uint8
|
||||
err error
|
||||
)
|
||||
if types.EthAsset(s.contractSwap.Asset) != types.EthAssetETH {
|
||||
_, symbol, decimals, err = s.ERC20Info(s.ctx, s.contractSwap.Asset)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, fmt.Errorf("failed to get ERC20 info: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if types.EthAsset(s.contractSwap.Asset) == types.EthAssetETH {
|
||||
balance, err := s.BalanceAt(s.ctx, addr, nil) //nolint:govet
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
log.Infof("balance before claim: %v ETH", common.EtherAmount(*balance).AsEther())
|
||||
} else {
|
||||
balance, err := s.ERC20BalanceAt(s.ctx, s.contractSwap.Asset, addr, nil) //nolint:govet
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
log.Infof("balance before claim: %v %s", common.EtherAmount(*balance).ToDecimals(decimals), symbol)
|
||||
}
|
||||
|
||||
var (
|
||||
txHash ethcommon.Hash
|
||||
)
|
||||
|
||||
// call swap.Swap.Claim() w/ b.privkeys.sk, revealing XMRMaker's secret spend key
|
||||
if s.offerExtra.RelayerEndpoint != "" {
|
||||
// relayer endpoint is set, claim using relayer
|
||||
// TODO: eventually update when relayer discovery is implemented
|
||||
txHash, err = s.claimRelayer()
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
} else {
|
||||
// claim and wait for tx to be included
|
||||
sc := s.getSecret()
|
||||
txHash, _, err = s.sender.Claim(s.contractSwap, sc)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("sent claim transaction, tx hash=%s", txHash)
|
||||
|
||||
if types.EthAsset(s.contractSwap.Asset) == types.EthAssetETH {
|
||||
balance, err := s.BalanceAt(s.ctx, addr, nil)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
log.Infof("balance after claim: %v ETH", common.EtherAmount(*balance).AsEther())
|
||||
} else {
|
||||
balance, err := s.ERC20BalanceAt(s.ctx, s.contractSwap.Asset, addr, nil)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
log.Infof("balance after claim: %v %s", common.EtherAmount(*balance).ToDecimals(decimals), symbol)
|
||||
}
|
||||
|
||||
return txHash, nil
|
||||
}
|
||||
|
||||
func (s *swapState) claimRelayer() (ethcommon.Hash, error) {
|
||||
return claimRelayer(
|
||||
s.Ctx(),
|
||||
s.EthPrivateKey(),
|
||||
s.Contract(),
|
||||
s.contractAddr,
|
||||
s.EthClient(),
|
||||
s.offerExtra.RelayerEndpoint,
|
||||
s.offerExtra.RelayerCommission,
|
||||
&s.contractSwap,
|
||||
s.getSecret(),
|
||||
)
|
||||
}
|
||||
|
||||
// claimRelayer claims the ETH funds via relayer.
|
||||
func claimRelayer(
|
||||
ctx context.Context,
|
||||
sk *ecdsa.PrivateKey,
|
||||
contract *contracts.SwapFactory,
|
||||
contractAddr ethcommon.Address,
|
||||
ec *ethclient.Client,
|
||||
relayerEndpoint string,
|
||||
relayerCommission float64,
|
||||
contractSwap *contracts.SwapFactorySwap,
|
||||
secret [32]byte,
|
||||
) (ethcommon.Hash, error) {
|
||||
forwarderAddress, err := contract.TrustedForwarder(&bind.CallOpts{})
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
rc, err := relayer.NewClient(sk, ec, relayerEndpoint, forwarderAddress)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
abi, err := abi.JSON(strings.NewReader(contracts.SwapFactoryABI))
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
feeValue, err := calculateRelayerCommissionValue(contractSwap.Value, relayerCommission)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
calldata, err := abi.Pack("claimRelayer", *contractSwap, secret, feeValue)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
txHash, err := rc.SubmitTransaction(contractAddr, calldata)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
// wait for inclusion
|
||||
receipt, err := block.WaitForReceipt(ctx, ec, txHash)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
if receipt.Status == 0 {
|
||||
return ethcommon.Hash{}, fmt.Errorf("transaction failed")
|
||||
}
|
||||
|
||||
return txHash, nil
|
||||
}
|
||||
|
||||
// swapValue is in wei
|
||||
// relayerCommission is a percentage (ie must be much less than 1)
|
||||
// error if it's greater than 0.1 (10%) - arbitrary, just a sanity check
|
||||
func calculateRelayerCommissionValue(swapValue *big.Int, relayerCommission float64) (*big.Int, error) {
|
||||
if relayerCommission > 0.1 {
|
||||
return nil, errRelayerCommissionTooHigh
|
||||
}
|
||||
|
||||
swapValueF := big.NewFloat(0).SetInt(swapValue)
|
||||
relayerCommissionF := big.NewFloat(relayerCommission)
|
||||
feeValue := big.NewFloat(0).Mul(swapValueF, relayerCommissionF)
|
||||
wei, _ := feeValue.Int(nil)
|
||||
return wei, nil
|
||||
}
|
||||
197
protocol/xmrmaker/claim_test.go
Normal file
197
protocol/xmrmaker/claim_test.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package xmrmaker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
rcommon "github.com/AthanorLabs/go-relayer/common"
|
||||
"github.com/AthanorLabs/go-relayer/impls/gsnforwarder"
|
||||
"github.com/AthanorLabs/go-relayer/relayer"
|
||||
rrpc "github.com/AthanorLabs/go-relayer/rpc"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
"github.com/athanorlabs/atomic-swap/common/types"
|
||||
"github.com/athanorlabs/atomic-swap/dleq"
|
||||
contracts "github.com/athanorlabs/atomic-swap/ethereum"
|
||||
"github.com/athanorlabs/atomic-swap/ethereum/block"
|
||||
"github.com/athanorlabs/atomic-swap/tests"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultTestTimeoutDuration = big.NewInt(60 * 5)
|
||||
defaultRelayerEndpoint = "http://127.0.0.1:7799"
|
||||
relayerCommission = float64(0.01)
|
||||
)
|
||||
|
||||
func runRelayer(
|
||||
t *testing.T,
|
||||
ec *ethclient.Client,
|
||||
forwarderAddress ethcommon.Address,
|
||||
sk *ecdsa.PrivateKey,
|
||||
chainID *big.Int,
|
||||
) {
|
||||
iforwarder, err := gsnforwarder.NewIForwarder(forwarderAddress, ec)
|
||||
require.NoError(t, err)
|
||||
fw := gsnforwarder.NewIForwarderWrapped(iforwarder)
|
||||
|
||||
key := rcommon.NewKeyFromPrivateKey(sk)
|
||||
|
||||
cfg := &relayer.Config{
|
||||
Ctx: context.Background(),
|
||||
EthClient: ec,
|
||||
Forwarder: fw,
|
||||
Key: key,
|
||||
ChainID: chainID,
|
||||
NewForwardRequestFunc: gsnforwarder.NewIForwarderForwardRequest,
|
||||
}
|
||||
|
||||
r, err := relayer.NewRelayer(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
rpcCfg := &rrpc.Config{
|
||||
Port: 7799,
|
||||
Relayer: r,
|
||||
}
|
||||
server, err := rrpc.NewServer(rpcCfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
_ = server.Start()
|
||||
t.Cleanup(func() {
|
||||
// TODO stop server
|
||||
})
|
||||
}
|
||||
|
||||
func TestSwapState_ClaimRelayer(t *testing.T) {
|
||||
sk := tests.GetMakerTestKey(t)
|
||||
relayerSk := tests.GetTestKeyByIndex(t, 1)
|
||||
require.NotEqual(t, sk, relayerSk)
|
||||
conn, chainID := tests.NewEthClient(t)
|
||||
|
||||
txOpts, err := bind.NewKeyedTransactorWithChainID(sk, chainID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// generate claim secret and public key
|
||||
dleq := &dleq.CGODLEq{}
|
||||
proof, err := dleq.Prove()
|
||||
require.NoError(t, err)
|
||||
res, err := dleq.Verify(proof)
|
||||
require.NoError(t, err)
|
||||
|
||||
// hash public key of claim secret
|
||||
cmt := res.Secp256k1PublicKey().Keccak256()
|
||||
|
||||
pub := sk.Public().(*ecdsa.PublicKey)
|
||||
addr := crypto.PubkeyToAddress(*pub)
|
||||
|
||||
// deploy forwarder
|
||||
forwarderAddress, tx, forwarderContract, err := gsnforwarder.DeployForwarder(txOpts, conn)
|
||||
require.NoError(t, err)
|
||||
receipt, err := block.WaitForReceipt(context.Background(), conn, tx.Hash())
|
||||
require.NoError(t, err)
|
||||
t.Logf("gas cost to deploy Forwarder.sol: %d", receipt.GasUsed)
|
||||
|
||||
tx, err = forwarderContract.RegisterDomainSeparator(txOpts, gsnforwarder.DefaultName, gsnforwarder.DefaultVersion)
|
||||
require.NoError(t, err)
|
||||
receipt, err = block.WaitForReceipt(context.Background(), conn, tx.Hash())
|
||||
require.NoError(t, err)
|
||||
t.Logf("gas cost to call RegisterDomainSeparator: %d", receipt.GasUsed)
|
||||
|
||||
// start relayer
|
||||
runRelayer(t, conn, forwarderAddress, relayerSk, chainID)
|
||||
|
||||
// deploy swap contract with claim key hash
|
||||
contractAddr, tx, contract, err := contracts.DeploySwapFactory(txOpts, conn, forwarderAddress)
|
||||
require.NoError(t, err)
|
||||
receipt, err = block.WaitForReceipt(context.Background(), conn, tx.Hash())
|
||||
require.NoError(t, err)
|
||||
t.Logf("gas cost to deploy SwapFactory.sol: %d", receipt.GasUsed)
|
||||
|
||||
value := big.NewInt(100000000000)
|
||||
nonce := big.NewInt(0)
|
||||
txOpts.Value = value
|
||||
|
||||
tx, err = contract.NewSwap(txOpts, cmt, [32]byte{}, addr,
|
||||
defaultTestTimeoutDuration, types.EthAssetETH.Address(), value, nonce)
|
||||
require.NoError(t, err)
|
||||
receipt, err = block.WaitForReceipt(context.Background(), conn, tx.Hash())
|
||||
require.NoError(t, err)
|
||||
t.Logf("gas cost to call new_swap: %d", receipt.GasUsed)
|
||||
txOpts.Value = big.NewInt(0)
|
||||
|
||||
require.Equal(t, 1, len(receipt.Logs))
|
||||
id, err := contracts.GetIDFromLog(receipt.Logs[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
t0, t1, err := contracts.GetTimeoutsFromLog(receipt.Logs[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
swap := contracts.SwapFactorySwap{
|
||||
Owner: addr,
|
||||
Claimer: addr,
|
||||
PubKeyClaim: cmt,
|
||||
PubKeyRefund: [32]byte{},
|
||||
Timeout0: t0,
|
||||
Timeout1: t1,
|
||||
Asset: types.EthAssetETH.Address(),
|
||||
Value: value,
|
||||
Nonce: nonce,
|
||||
}
|
||||
|
||||
// set contract to Ready
|
||||
tx, err = contract.SetReady(txOpts, swap)
|
||||
require.NoError(t, err)
|
||||
receipt, err = block.WaitForReceipt(context.Background(), conn, tx.Hash())
|
||||
t.Logf("gas cost to call SetReady: %d", receipt.GasUsed)
|
||||
require.NoError(t, err)
|
||||
|
||||
// now let's try to claim
|
||||
var s [32]byte
|
||||
secret := proof.Secret()
|
||||
copy(s[:], common.Reverse(secret[:]))
|
||||
|
||||
txHash, err := claimRelayer(
|
||||
context.Background(),
|
||||
sk,
|
||||
contract,
|
||||
contractAddr,
|
||||
conn,
|
||||
defaultRelayerEndpoint,
|
||||
relayerCommission,
|
||||
&swap,
|
||||
s,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
receipt, err = block.WaitForReceipt(context.Background(), conn, txHash)
|
||||
require.NoError(t, err)
|
||||
t.Logf("gas cost to call Claim via relayer: %d", receipt.GasUsed)
|
||||
|
||||
// expected 1 Claimed log
|
||||
require.Equal(t, 1, len(receipt.Logs))
|
||||
|
||||
stage, err := contract.Swaps(nil, id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, contracts.StageCompleted, stage)
|
||||
}
|
||||
|
||||
func TestCalculateRelayerCommissionValue(t *testing.T) {
|
||||
swapValueF := big.NewFloat(0).Mul(big.NewFloat(4.567), numEtherUnitsFloat)
|
||||
swapValue, _ := swapValueF.Int(nil)
|
||||
|
||||
relayerCommission := float64(0.01398)
|
||||
|
||||
expectedF := big.NewFloat(0).Mul(big.NewFloat(0.06384666), numEtherUnitsFloat)
|
||||
expected, _ := expectedF.Int(nil)
|
||||
|
||||
val, err := calculateRelayerCommissionValue(swapValue, relayerCommission)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, val)
|
||||
}
|
||||
@@ -24,10 +24,10 @@ var (
|
||||
errClaimTxHasNoLogs = errors.New("claim transaction has no logs")
|
||||
errCannotFindNewLog = errors.New("cannot find New log")
|
||||
errUnexpectedSwapID = errors.New("unexpected swap ID was emitted by New log")
|
||||
errInvalidSwapContract = errors.New("given contract address does not contain correct code")
|
||||
errSwapIDMismatch = errors.New("hash of swap struct does not match swap ID")
|
||||
errLockTxReverted = errors.New("other party failed to lock ETH asset (transaction reverted)")
|
||||
errInvalidETHLockedTransaction = errors.New("eth locked tx was not to correct contract address")
|
||||
errRelayerCommissionTooHigh = errors.New("relayer commission must be less than 0.1 (10%)")
|
||||
|
||||
// protocol initiation errors
|
||||
errProtocolAlreadyInProgress = errors.New("protocol already in progress")
|
||||
|
||||
@@ -84,8 +84,8 @@ func (s *swapState) HandleProtocolMessage(msg net.Message) (net.Message, bool, e
|
||||
func (s *swapState) clearNextExpectedMessage(status types.Status) {
|
||||
s.nextExpectedMessage = nil
|
||||
s.info.SetStatus(status)
|
||||
if s.statusCh != nil {
|
||||
s.statusCh <- status
|
||||
if s.offerExtra.StatusCh != nil {
|
||||
s.offerExtra.StatusCh <- status
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@ func (s *swapState) setNextExpectedMessage(msg net.Message) {
|
||||
|
||||
s.nextExpectedMessage = msg
|
||||
stage := pcommon.GetStatus(msg.Type())
|
||||
if s.statusCh != nil && stage != types.UnknownStatus {
|
||||
s.statusCh <- stage
|
||||
if s.offerExtra.StatusCh != nil && stage != types.UnknownStatus {
|
||||
s.offerExtra.StatusCh <- stage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,12 +149,12 @@ func (s *swapState) handleNotifyETHLocked(msg *message.NotifyETHLocked) (net.Mes
|
||||
s.contractSwapID = msg.ContractSwapID
|
||||
s.contractSwap = convertContractSwap(msg.ContractSwap)
|
||||
|
||||
if err := pcommon.WriteContractSwapToFile(s.infoFile, s.contractSwapID, s.contractSwap); err != nil {
|
||||
if err := pcommon.WriteContractSwapToFile(s.offerExtra.InfoFile, s.contractSwapID, s.contractSwap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contractAddr := ethcommon.HexToAddress(msg.Address)
|
||||
if err := checkContractCode(s.ctx, s, contractAddr); err != nil {
|
||||
if err := pcommon.CheckContractCode(s.ctx, s.Backend.EthClient(), contractAddr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ func (s *swapState) handleNotifyETHLocked(msg *message.NotifyETHLocked) (net.Mes
|
||||
return nil, fmt.Errorf("failed to instantiate contract instance: %w", err)
|
||||
}
|
||||
|
||||
if err := pcommon.WriteContractAddressToFile(s.infoFile, msg.Address); err != nil {
|
||||
if err := pcommon.WriteContractAddressToFile(s.offerExtra.InfoFile, msg.Address); err != nil {
|
||||
return nil, fmt.Errorf("failed to write contract address to file: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package xmrmaker
|
||||
|
||||
import (
|
||||
context "context"
|
||||
ecdsa "crypto/ecdsa"
|
||||
big "math/big"
|
||||
reflect "reflect"
|
||||
time "time"
|
||||
@@ -292,6 +293,20 @@ func (mr *MockBackendMockRecorder) EthClient() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthClient", reflect.TypeOf((*MockBackend)(nil).EthClient))
|
||||
}
|
||||
|
||||
// EthPrivateKey mocks base method.
|
||||
func (m *MockBackend) EthPrivateKey() *ecdsa.PrivateKey {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "EthPrivateKey")
|
||||
ret0, _ := ret[0].(*ecdsa.PrivateKey)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// EthPrivateKey indicates an expected call of EthPrivateKey.
|
||||
func (mr *MockBackendMockRecorder) EthPrivateKey() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthPrivateKey", reflect.TypeOf((*MockBackend)(nil).EthPrivateKey))
|
||||
}
|
||||
|
||||
// FilterLogs mocks base method.
|
||||
func (m *MockBackend) FilterLogs(arg0 context.Context, arg1 ethereum.FilterQuery) ([]types0.Log, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -44,8 +44,7 @@ func (b *Instance) initiate(
|
||||
// checks passed, delete offer for now
|
||||
b.offerManager.DeleteOffer(offer.GetID())
|
||||
|
||||
s, err := newSwapState(b.backend, offer, b.offerManager, offerExtra.StatusCh,
|
||||
offerExtra.InfoFile, providesAmount, desiredAmount)
|
||||
s, err := newSwapState(b.backend, offer, offerExtra, b.offerManager, providesAmount, desiredAmount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestXMRMaker_HandleInitiateMessage(t *testing.T) {
|
||||
|
||||
b.net.(*MockHost).EXPECT().Advertise()
|
||||
|
||||
_, err := b.MakeOffer(offer)
|
||||
_, err := b.MakeOffer(offer, "", 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
msg, _ := newTestXMRTakerSendKeysMessage(t)
|
||||
|
||||
@@ -78,7 +78,11 @@ func (m *Manager) GetOffer(id types.Hash) (*types.Offer, *types.OfferExtra, erro
|
||||
}
|
||||
|
||||
// AddOffer adds a new offer to the manager and returns its OffersExtra data
|
||||
func (m *Manager) AddOffer(o *types.Offer) (*types.OfferExtra, error) {
|
||||
func (m *Manager) AddOffer(
|
||||
o *types.Offer,
|
||||
relayerEndpoint string,
|
||||
relayerCommission float64,
|
||||
) (*types.OfferExtra, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ func Test_Manager(t *testing.T) {
|
||||
offer := types.NewOffer(types.ProvidesXMR, float64(i), float64(i), types.ExchangeRate(i),
|
||||
types.EthAssetETH)
|
||||
db.EXPECT().PutOffer(offer)
|
||||
offerExtra, err := mgr.AddOffer(offer)
|
||||
offerExtra, err := mgr.AddOffer(offer, "", 0)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, offerExtra)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,9 @@ func NewRecoveryState(b backend.Backend, dataDir string, secret *mcrypto.Private
|
||||
dleqProof: dleq.NewProofWithSecret(sc),
|
||||
contractSwapID: contractSwapID,
|
||||
contractSwap: contractSwap,
|
||||
infoFile: pcommon.GetSwapRecoveryFilepath(dataDir),
|
||||
offerExtra: &types.OfferExtra{
|
||||
InfoFile: pcommon.GetSwapRecoveryFilepath(dataDir),
|
||||
},
|
||||
}
|
||||
|
||||
if err := s.setContract(contractAddr); err != nil {
|
||||
|
||||
@@ -39,15 +39,14 @@ type swapState struct {
|
||||
backend.Backend
|
||||
sender txsender.Sender
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
stateMu sync.Mutex
|
||||
infoFile string
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
stateMu sync.Mutex
|
||||
|
||||
info *pswap.Info
|
||||
offer *types.Offer
|
||||
offerExtra *types.OfferExtra
|
||||
offerManager *offers.Manager
|
||||
statusCh chan types.Status
|
||||
|
||||
// our keys for this session
|
||||
dleqProof *dleq.Proof
|
||||
@@ -55,7 +54,9 @@ type swapState struct {
|
||||
privkeys *mcrypto.PrivateKeyPair
|
||||
pubkeys *mcrypto.PublicKeyPair
|
||||
|
||||
// swap contract and timeouts in it; set once contract is deployed
|
||||
// swap contract and timeouts in it
|
||||
contract *contracts.SwapFactory
|
||||
contractAddr ethcommon.Address
|
||||
contractSwapID [32]byte
|
||||
contractSwap contracts.SwapFactorySwap
|
||||
t0, t1 time.Time
|
||||
@@ -79,20 +80,19 @@ type swapState struct {
|
||||
func newSwapState(
|
||||
b backend.Backend,
|
||||
offer *types.Offer,
|
||||
offerExtra *types.OfferExtra,
|
||||
om *offers.Manager,
|
||||
statusCh chan types.Status,
|
||||
infoFile string,
|
||||
providesAmount common.MoneroAmount,
|
||||
desiredAmount common.EtherAmount,
|
||||
) (*swapState, error) {
|
||||
exchangeRate := types.ExchangeRate(providesAmount.AsMonero() / desiredAmount.AsEther())
|
||||
stage := types.ExpectingKeys
|
||||
if statusCh == nil {
|
||||
statusCh = make(chan types.Status, 7)
|
||||
if offerExtra.StatusCh == nil {
|
||||
offerExtra.StatusCh = make(chan types.Status, 7)
|
||||
}
|
||||
statusCh <- stage
|
||||
offerExtra.StatusCh <- stage
|
||||
info := pswap.NewInfo(offer.GetID(), types.ProvidesXMR, providesAmount.AsMonero(), desiredAmount.AsEther(),
|
||||
exchangeRate, offer.EthAsset, stage, statusCh)
|
||||
exchangeRate, offer.EthAsset, stage, offerExtra.StatusCh)
|
||||
if err := b.SwapManager().AddSwap(info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -123,12 +123,11 @@ func newSwapState(
|
||||
Backend: b,
|
||||
sender: sender,
|
||||
offer: offer,
|
||||
offerExtra: offerExtra,
|
||||
offerManager: om,
|
||||
infoFile: infoFile,
|
||||
nextExpectedMessage: &net.SendKeysMessage{},
|
||||
readyCh: make(chan struct{}),
|
||||
info: info,
|
||||
statusCh: statusCh,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
@@ -161,7 +160,7 @@ func (s *swapState) SendKeysMessage() (*net.SendKeysMessage, error) {
|
||||
|
||||
// InfoFile returns the swap's infoFile path
|
||||
func (s *swapState) InfoFile() string {
|
||||
return s.infoFile
|
||||
return s.offerExtra.InfoFile
|
||||
}
|
||||
|
||||
// ReceivedAmount returns the amount received, or expected to be received, at the end of the swap
|
||||
@@ -208,7 +207,7 @@ func (s *swapState) exit() error {
|
||||
|
||||
if s.info.Status() != types.CompletedSuccess {
|
||||
// re-add offer, as it wasn't taken successfully
|
||||
_, err := s.offerManager.AddOffer(s.offer)
|
||||
_, err := s.offerManager.AddOffer(s.offer, s.offerExtra.RelayerEndpoint, s.offerExtra.RelayerCommission)
|
||||
if err != nil {
|
||||
log.Warnf("failed to re-add offer %s: %s", s.offer.GetID(), err)
|
||||
}
|
||||
@@ -299,7 +298,7 @@ func (s *swapState) reclaimMonero(skA *mcrypto.PrivateSpendKey) (mcrypto.Address
|
||||
kpAB := mcrypto.NewPrivateKeyPair(skAB, vkAB)
|
||||
|
||||
// write keys to file in case something goes wrong
|
||||
if err = pcommon.WriteSharedSwapKeyPairToFile(s.infoFile, kpAB, s.Env()); err != nil {
|
||||
if err = pcommon.WriteSharedSwapKeyPairToFile(s.offerExtra.InfoFile, kpAB, s.Env()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -353,50 +352,6 @@ func (s *swapState) filterForRefund() (*mcrypto.PrivateSpendKey, error) {
|
||||
return sa, nil
|
||||
}
|
||||
|
||||
func (s *swapState) tryClaim() (ethcommon.Hash, error) {
|
||||
stage, err := s.Contract().Swaps(s.CallOpts(), s.contractSwapID)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
switch stage {
|
||||
case contracts.StageInvalid:
|
||||
return ethcommon.Hash{}, errClaimInvalid
|
||||
case contracts.StageCompleted:
|
||||
return ethcommon.Hash{}, errClaimSwapComplete
|
||||
case contracts.StagePending, contracts.StageReady:
|
||||
// do nothing
|
||||
default:
|
||||
panic("Unhandled stage value")
|
||||
}
|
||||
|
||||
ts, err := s.LatestBlockTimestamp(s.ctx)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
// The block that our claim transaction goes into needs a timestamp that is strictly less
|
||||
// than T1. Since the minimum interval between blocks is 1 second, the current block must
|
||||
// be at least 2 seconds before T1 for a non-zero chance of the next block having a
|
||||
// timestamp that is strictly less than T1.
|
||||
if ts.After(s.t1.Add(-2 * time.Second)) {
|
||||
// We've passed t1, so the only way we can regain control of the locked XMR is for
|
||||
// XMRTaker to call refund on the contract.
|
||||
return ethcommon.Hash{}, errClaimPastTime
|
||||
}
|
||||
|
||||
if ts.Before(s.t0) && stage != contracts.StageReady {
|
||||
// TODO: t0 could be 24 hours from now. Don't we want to poll the stage periodically? (#163)
|
||||
// we need to wait until t0 to claim
|
||||
log.Infof("waiting until time %s to claim, time now=%s", s.t0, time.Now())
|
||||
err = s.WaitForTimestamp(s.ctx, s.t0)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return s.claimFunds()
|
||||
}
|
||||
|
||||
// generateKeys generates XMRMaker's spend and view keys (s_b, v_b)
|
||||
// It returns XMRMaker's public spend key and his private view key, so that XMRTaker can see
|
||||
// if the funds are locked.
|
||||
@@ -419,7 +374,7 @@ func (s *swapState) generateAndSetKeys() error {
|
||||
s.privkeys = keysAndProof.PrivateKeyPair
|
||||
s.pubkeys = keysAndProof.PublicKeyPair
|
||||
|
||||
return pcommon.WriteKeysToFile(s.infoFile, s.privkeys, s.Env())
|
||||
return pcommon.WriteKeysToFile(s.offerExtra.InfoFile, s.privkeys, s.Env())
|
||||
}
|
||||
|
||||
func generateKeys() (*pcommon.KeysAndProof, error) {
|
||||
@@ -442,17 +397,16 @@ func (s *swapState) setXMRTakerPublicKeys(sk *mcrypto.PublicKeyPair, secp256k1Pu
|
||||
|
||||
// setContract sets the contract in which XMRTaker has locked her ETH.
|
||||
func (s *swapState) setContract(address ethcommon.Address) error {
|
||||
s.contractAddr = address
|
||||
|
||||
var err error
|
||||
// note: this overrides the backend contract
|
||||
s.SetContractAddress(address)
|
||||
contract, err := s.NewSwapFactory(address)
|
||||
s.contract, err = s.NewSwapFactory(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.SetContract(contract)
|
||||
s.sender.SetContractAddress(address)
|
||||
s.sender.SetContract(contract)
|
||||
s.sender.SetContract(s.contract)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -470,7 +424,7 @@ func (s *swapState) checkContract(txHash ethcommon.Hash) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if tx.To() == nil || *(tx.To()) != s.ContractAddr() {
|
||||
if tx.To() == nil || *(tx.To()) != s.contractAddr {
|
||||
return errInvalidETHLockedTransaction
|
||||
}
|
||||
|
||||
@@ -570,60 +524,3 @@ func (s *swapState) lockFunds(amount common.MoneroAmount) (mcrypto.Address, erro
|
||||
log.Infof("successfully locked XMR funds: address=%s", address)
|
||||
return address, nil
|
||||
}
|
||||
|
||||
// claimFunds redeems XMRMaker's ETH funds by calling Claim() on the contract
|
||||
func (s *swapState) claimFunds() (ethcommon.Hash, error) {
|
||||
addr := s.EthAddress()
|
||||
|
||||
var (
|
||||
symbol string
|
||||
decimals uint8
|
||||
err error
|
||||
)
|
||||
if types.EthAsset(s.contractSwap.Asset) != types.EthAssetETH {
|
||||
_, symbol, decimals, err = s.ERC20Info(s.ctx, s.contractSwap.Asset)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, fmt.Errorf("failed to get ERC20 info: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if types.EthAsset(s.contractSwap.Asset) == types.EthAssetETH {
|
||||
balance, err := s.BalanceAt(s.ctx, addr, nil) //nolint:govet
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
log.Infof("balance before claim: %v ETH", common.EtherAmount(*balance).AsEther())
|
||||
} else {
|
||||
balance, err := s.ERC20BalanceAt(s.ctx, s.contractSwap.Asset, addr, nil) //nolint:govet
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
log.Infof("balance before claim: %v %s", common.EtherAmount(*balance).ToDecimals(decimals), symbol)
|
||||
}
|
||||
|
||||
// call swap.Swap.Claim() w/ b.privkeys.sk, revealing XMRMaker's secret spend key
|
||||
sc := s.getSecret()
|
||||
txHash, _, err := s.sender.Claim(s.contractSwap, sc)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
log.Infof("sent claim tx, tx hash=%s", txHash)
|
||||
|
||||
if types.EthAsset(s.contractSwap.Asset) == types.EthAssetETH {
|
||||
balance, err := s.BalanceAt(s.ctx, addr, nil)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
log.Infof("balance after claim: %v ETH", common.EtherAmount(*balance).AsEther())
|
||||
} else {
|
||||
balance, err := s.ERC20BalanceAt(s.ctx, s.contractSwap.Asset, addr, nil)
|
||||
if err != nil {
|
||||
return ethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
log.Infof("balance after claim: %v %s", common.EtherAmount(*balance).ToDecimals(decimals), symbol)
|
||||
}
|
||||
|
||||
return txHash, nil
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ func newTestXMRMakerAndDB(t *testing.T) (*Instance, *offers.MockDatabase) {
|
||||
txOpts, err := bind.NewKeyedTransactorWithChainID(pk, chainID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, tx, contract, err := contracts.DeploySwapFactory(txOpts, ec)
|
||||
var forwarderAddress ethcommon.Address
|
||||
_, tx, contract, err := contracts.DeploySwapFactory(txOpts, ec, forwarderAddress)
|
||||
require.NoError(t, err)
|
||||
|
||||
addr, err := bind.WaitDeployed(context.Background(), ec, tx)
|
||||
@@ -112,12 +113,14 @@ func newTestXMRMakerAndDB(t *testing.T) (*Instance, *offers.MockDatabase) {
|
||||
func newTestInstanceAndDB(t *testing.T) (*Instance, *swapState, *offers.MockDatabase) {
|
||||
xmrmaker, db := newTestXMRMakerAndDB(t)
|
||||
infoFile := path.Join(t.TempDir(), "test.keys")
|
||||
oe := &types.OfferExtra{
|
||||
InfoFile: infoFile,
|
||||
}
|
||||
|
||||
swapState, err := newSwapState(xmrmaker.backend,
|
||||
types.NewOffer("", 0, 0, 0, types.EthAssetETH), xmrmaker.offerManager, nil, infoFile,
|
||||
types.NewOffer("", 0, 0, 0, types.EthAssetETH), oe, xmrmaker.offerManager,
|
||||
common.MoneroAmount(33), desiredAmount)
|
||||
require.NoError(t, err)
|
||||
swapState.SetContract(xmrmaker.backend.Contract())
|
||||
swapState.SetContractAddress(xmrmaker.backend.ContractAddr())
|
||||
return xmrmaker, swapState, db
|
||||
}
|
||||
|
||||
@@ -311,7 +314,7 @@ func TestSwapState_HandleProtocolMessage_NotifyETHLocked_timeout(t *testing.T) {
|
||||
require.Equal(t, duration, s.t1.Sub(s.t0))
|
||||
require.Equal(t, &message.NotifyReady{}, s.nextExpectedMessage)
|
||||
|
||||
for status := range s.statusCh {
|
||||
for status := range s.offerExtra.StatusCh {
|
||||
if status == types.CompletedSuccess {
|
||||
break
|
||||
} else if !status.IsOngoing() {
|
||||
@@ -525,7 +528,7 @@ func TestSwapState_Exit_Refunded(t *testing.T) {
|
||||
|
||||
s.offer = types.NewOffer(types.ProvidesXMR, 0.1, 0.2, 0.1, types.EthAssetETH)
|
||||
db.EXPECT().PutOffer(s.offer)
|
||||
b.MakeOffer(s.offer)
|
||||
b.MakeOffer(s.offer, "", 0)
|
||||
|
||||
s.info.SetStatus(types.CompletedRefund)
|
||||
err := s.Exit()
|
||||
|
||||
@@ -1,30 +1,10 @@
|
||||
package xmrmaker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
contracts "github.com/athanorlabs/atomic-swap/ethereum"
|
||||
"github.com/athanorlabs/atomic-swap/net/message"
|
||||
"github.com/athanorlabs/atomic-swap/protocol/backend"
|
||||
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func checkContractCode(ctx context.Context, b backend.Backend, contractAddr ethcommon.Address) error {
|
||||
code, err := b.CodeAt(ctx, contractAddr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expectedCode := ethcommon.FromHex(contracts.SwapFactoryMetaData.Bin)
|
||||
if !bytes.Contains(expectedCode, code) {
|
||||
return errInvalidSwapContract
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertContractSwap(msg *message.ContractSwap) contracts.SwapFactorySwap {
|
||||
return contracts.SwapFactorySwap{
|
||||
Owner: msg.Owner,
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -56,7 +57,8 @@ func newBackend(t *testing.T) backend.Backend {
|
||||
txOpts, err := bind.NewKeyedTransactorWithChainID(pk, chainID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, tx, contract, err := contracts.DeploySwapFactory(txOpts, ec)
|
||||
var forwarderAddress ethcommon.Address
|
||||
_, tx, contract, err := contracts.DeploySwapFactory(txOpts, ec, forwarderAddress)
|
||||
require.NoError(t, err)
|
||||
|
||||
addr, err := bind.WaitDeployed(ctx, ec, tx)
|
||||
@@ -79,32 +81,6 @@ func newBackend(t *testing.T) backend.Backend {
|
||||
return b
|
||||
}
|
||||
|
||||
func newXMRMakerBackend(t *testing.T) backend.Backend {
|
||||
pk := tests.GetMakerTestKey(t)
|
||||
ec, chainID := tests.NewEthClient(t)
|
||||
|
||||
txOpts, err := bind.NewKeyedTransactorWithChainID(pk, chainID)
|
||||
require.NoError(t, err)
|
||||
addr, _, contract, err := contracts.DeploySwapFactory(txOpts, ec)
|
||||
require.NoError(t, err)
|
||||
|
||||
bcfg := &backend.Config{
|
||||
Ctx: context.Background(),
|
||||
MoneroClient: monero.CreateWalletClient(t),
|
||||
EthereumClient: ec,
|
||||
EthereumPrivateKey: pk,
|
||||
Environment: common.Development,
|
||||
SwapManager: pswap.NewManager(),
|
||||
SwapContract: contract,
|
||||
SwapContractAddress: addr,
|
||||
Net: new(mockNet),
|
||||
}
|
||||
|
||||
b, err := backend.NewBackend(bcfg)
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}
|
||||
|
||||
func newTestInstance(t *testing.T) *swapState {
|
||||
b := newBackend(t)
|
||||
swapState, err := newSwapState(b, types.Hash{}, infofile, false,
|
||||
@@ -306,11 +282,11 @@ func TestSwapState_NotifyClaimed(t *testing.T) {
|
||||
s.SetSwapTimeout(time.Minute * 2)
|
||||
|
||||
// close swap-deposit-wallet
|
||||
maker := newXMRMakerBackend(t)
|
||||
err := maker.CreateWallet("test-wallet", "")
|
||||
backend := newBackend(t)
|
||||
err := backend.CreateWallet("test-wallet", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
monero.MineMinXMRBalance(t, maker, common.MoneroToPiconero(1))
|
||||
monero.MineMinXMRBalance(t, backend, common.MoneroToPiconero(1))
|
||||
|
||||
// invalid SendKeysMessage should result in an error
|
||||
msg := &net.SendKeysMessage{}
|
||||
@@ -340,7 +316,7 @@ func TestSwapState_NotifyClaimed(t *testing.T) {
|
||||
xmrAddr := kp.Address(common.Mainnet)
|
||||
|
||||
// lock xmr
|
||||
tResp, err := maker.Transfer(xmrAddr, 0, uint64(amt))
|
||||
tResp, err := backend.Transfer(xmrAddr, 0, uint64(amt))
|
||||
require.NoError(t, err)
|
||||
t.Logf("transferred %d pico XMR (fees %d) to account %s", tResp.Amount, tResp.Fee, xmrAddr)
|
||||
require.Equal(t, uint64(amt), tResp.Amount)
|
||||
|
||||
Reference in New Issue
Block a user