mirror of
https://github.com/AthanorLabs/atomic-swap.git
synced 2026-01-09 14:18:03 -05:00
138 lines
3.9 KiB
Go
138 lines
3.9 KiB
Go
// Copyright 2023 The AthanorLabs/atomic-swap Authors
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
package xmrmaker
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math/big"
|
|
"time"
|
|
|
|
ethcommon "github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/athanorlabs/atomic-swap/common"
|
|
"github.com/athanorlabs/atomic-swap/common/types"
|
|
contracts "github.com/athanorlabs/atomic-swap/ethereum"
|
|
)
|
|
|
|
// checkContract checks the contract's balance and Claim/Refund keys. If the
|
|
// balance doesn't match what we're expecting to receive, or the public keys in
|
|
// the contract aren't what we expect, we error and abort the swap.
|
|
func (s *swapState) checkContract(txHash ethcommon.Hash) error {
|
|
tx, _, err := s.ETHClient().Raw().TransactionByHash(s.ctx, txHash)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get newSwap transaction %s by hash: %w", txHash, err)
|
|
}
|
|
|
|
if tx.To() == nil || *(tx.To()) != s.swapCreatorAddr {
|
|
return errInvalidETHLockedTransaction
|
|
}
|
|
|
|
receipt, err := s.ETHClient().WaitForReceipt(s.ctx, txHash)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get receipt for New transaction: %w", err)
|
|
}
|
|
|
|
if receipt.Status == 0 {
|
|
// swap transaction reverted
|
|
return errLockTxReverted
|
|
}
|
|
|
|
// check that New log was emitted
|
|
if len(receipt.Logs) == 0 {
|
|
return errCannotFindNewLog
|
|
}
|
|
|
|
var event *contracts.SwapCreatorNew
|
|
for _, log := range receipt.Logs {
|
|
event, err = s.SwapCreator().ParseNew(*log)
|
|
if err == nil {
|
|
break
|
|
}
|
|
}
|
|
if err != nil {
|
|
return errCannotFindNewLog
|
|
}
|
|
|
|
if !bytes.Equal(event.SwapID[:], s.contractSwapID[:]) {
|
|
return errUnexpectedSwapID
|
|
}
|
|
|
|
// check that contract was constructed with correct secp256k1 keys
|
|
skOurs := s.secp256k1Pub.Keccak256()
|
|
if !bytes.Equal(event.ClaimKey[:], skOurs[:]) {
|
|
return fmt.Errorf("contract claim key is not expected: got 0x%x, expected 0x%x", event.ClaimKey, skOurs)
|
|
}
|
|
|
|
skTheirs := s.xmrtakerSecp256K1PublicKey.Keccak256()
|
|
if !bytes.Equal(event.RefundKey[:], skTheirs[:]) {
|
|
return fmt.Errorf("contract refund key is not expected: got 0x%x, expected 0x%x", event.RefundKey, skTheirs)
|
|
}
|
|
|
|
// check asset of created swap
|
|
if types.EthAsset(s.contractSwap.Asset) != types.EthAsset(event.Asset) {
|
|
return fmt.Errorf("swap asset is not expected: got %v, expected %v", event.Asset, s.contractSwap.Asset)
|
|
}
|
|
|
|
// check value of created swap
|
|
if s.contractSwap.Value.Cmp(event.Value) != 0 {
|
|
// this should never happen
|
|
return fmt.Errorf("swap value and event value don't match: got %v, expected %v", event.Value, s.contractSwap.Value)
|
|
}
|
|
|
|
expectedAmount, err := getEthAssetAmount(
|
|
s.ctx,
|
|
s.ETHClient(),
|
|
s.info.ExpectedAmount,
|
|
types.EthAsset(s.contractSwap.Asset),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if s.contractSwap.Value.Cmp(expectedAmount.BigInt()) != 0 {
|
|
return fmt.Errorf("swap value is not expected: got %v, expected %v",
|
|
s.contractSwap.Value,
|
|
expectedAmount.BigInt(),
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// checkAndSetTimeouts checks that the timeouts set by the counterparty when
|
|
// initiating a swap are not too short or long. We expect the timeout to be of a
|
|
// certain length (1 hour for mainnet/stagenet), and allow a 3 minute variation
|
|
// between now and the expected time until the first timeout t1, to allow for
|
|
// block confirmations. The time between t1 and t2 should always be the exact
|
|
// length we expect.
|
|
func (s *swapState) checkAndSetTimeouts(t1, t2 *big.Int) error {
|
|
s.setTimeouts(t1, t2)
|
|
|
|
// we ignore the timeout for development, as unit tests and integration tests
|
|
// often set different timeouts.
|
|
if s.Backend.Env() == common.Development {
|
|
return nil
|
|
}
|
|
|
|
expectedTimeout := common.SwapTimeoutFromEnv(s.Backend.Env())
|
|
allowableTimeDiff := expectedTimeout / 20
|
|
|
|
if s.t2.Sub(s.t1) != expectedTimeout {
|
|
return errInvalidT2
|
|
}
|
|
|
|
if time.Now().Add(expectedTimeout).Sub(s.t1).Abs() > allowableTimeDiff {
|
|
return errInvalidT1
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *swapState) setTimeouts(t1, t2 *big.Int) {
|
|
s.t1 = time.Unix(t1.Int64(), 0)
|
|
s.t2 = time.Unix(t2.Int64(), 0)
|
|
s.info.SetTimeouts(&s.t1, &s.t2)
|
|
}
|