mirror of
https://github.com/AthanorLabs/atomic-swap.git
synced 2026-01-10 06:38:04 -05:00
remove NotifyXMRLocked message, have xmrtaker check for lock independently (#341)
This commit is contained in:
@@ -22,7 +22,6 @@ const (
|
||||
QueryResponseType byte = iota
|
||||
SendKeysType
|
||||
NotifyETHLockedType
|
||||
NotifyXMRLockType
|
||||
)
|
||||
|
||||
// TypeToString converts a message type into a string.
|
||||
@@ -34,8 +33,6 @@ func TypeToString(t byte) string {
|
||||
return "SendKeysMessage"
|
||||
case NotifyETHLockedType:
|
||||
return "NotifyETHLocked"
|
||||
case NotifyXMRLockType:
|
||||
return "NotifyXMRLock"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
@@ -59,8 +56,6 @@ func DecodeMessage(b []byte) (common.Message, error) {
|
||||
msg = &SendKeysMessage{}
|
||||
case NotifyETHLockedType:
|
||||
msg = &NotifyETHLocked{}
|
||||
case NotifyXMRLockType:
|
||||
msg = &NotifyXMRLock{}
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid message type=%d", msgType)
|
||||
}
|
||||
@@ -173,29 +168,3 @@ func (m *NotifyETHLocked) Encode() ([]byte, error) {
|
||||
func (m *NotifyETHLocked) Type() byte {
|
||||
return NotifyETHLockedType
|
||||
}
|
||||
|
||||
// NotifyXMRLock is sent by XMRMaker to XMRTaker after locking his XMR.
|
||||
type NotifyXMRLock struct {
|
||||
Address *mcrypto.Address `json:"address" validate:"required"` // address the monero was sent to
|
||||
TxID types.Hash `json:"txID" validate:"required"` // Monero transaction ID (transaction hash in hex)
|
||||
}
|
||||
|
||||
// String ...
|
||||
func (m *NotifyXMRLock) String() string {
|
||||
return "NotifyXMRLock"
|
||||
}
|
||||
|
||||
// Encode ...
|
||||
func (m *NotifyXMRLock) Encode() ([]byte, error) {
|
||||
b, err := vjson.MarshalStruct(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append([]byte{NotifyXMRLockType}, b...), nil
|
||||
}
|
||||
|
||||
// Type ...
|
||||
func (m *NotifyXMRLock) Type() byte {
|
||||
return NotifyXMRLockType
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ func (s *swapState) handleEvent(event Event) {
|
||||
return
|
||||
}
|
||||
|
||||
err := s.handleEventETHLocked(e)
|
||||
err := s.handleNotifyETHLocked(e.message)
|
||||
if err != nil {
|
||||
e.errCh <- fmt.Errorf("failed to handle EventETHLocked: %w", err)
|
||||
if !s.fundsLocked {
|
||||
@@ -250,15 +250,6 @@ func (s *swapState) handleEvent(event Event) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *swapState) handleEventETHLocked(e *EventETHLocked) error {
|
||||
resp, err := s.handleNotifyETHLocked(e.message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.SendSwapMessage(resp, s.ID())
|
||||
}
|
||||
|
||||
func (s *swapState) handleEventContractReady() error {
|
||||
log.Debug("contract ready, attempting to claim funds...")
|
||||
close(s.readyCh)
|
||||
|
||||
@@ -58,7 +58,7 @@ func TestSwapState_handleEvent_EventETHRefunded(t *testing.T) {
|
||||
newSwap(t, s, [32]byte{}, refundKey, desiredAmount.BigInt(), duration)
|
||||
|
||||
// lock XMR
|
||||
_, err = s.lockFunds(coins.MoneroToPiconero(s.info.ProvidedAmount))
|
||||
err = s.lockFunds(coins.MoneroToPiconero(s.info.ProvidedAmount))
|
||||
require.NoError(t, err)
|
||||
|
||||
// call refund w/ XMRTaker's secret
|
||||
|
||||
@@ -146,11 +146,6 @@ func newTestInstanceAndDB(t *testing.T) (*Instance, *offers.MockDatabase) {
|
||||
return inst, db
|
||||
}
|
||||
|
||||
func newTestInstanceAndNet(t *testing.T) (*Instance, *mockNet) {
|
||||
inst, _, net := newTestInstanceAndDBAndNet(t)
|
||||
return inst, net
|
||||
}
|
||||
|
||||
func TestInstance_createOngoingSwap(t *testing.T) {
|
||||
inst, offerDB := newTestInstanceAndDB(t)
|
||||
rdb := inst.backend.RecoveryDB().(*backend.MockRecoveryDB)
|
||||
|
||||
@@ -86,20 +86,20 @@ func (s *swapState) setNextExpectedEvent(event EventType) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *swapState) handleNotifyETHLocked(msg *message.NotifyETHLocked) (common.Message, error) {
|
||||
func (s *swapState) handleNotifyETHLocked(msg *message.NotifyETHLocked) error {
|
||||
if msg.Address == (ethcommon.Address{}) {
|
||||
return nil, errMissingAddress
|
||||
return errMissingAddress
|
||||
}
|
||||
|
||||
if types.IsHashZero(msg.ContractSwapID) {
|
||||
return nil, errNilContractSwapID
|
||||
return errNilContractSwapID
|
||||
}
|
||||
|
||||
log.Infof("got NotifyETHLocked; address=%s contract swap ID=%s", msg.Address, msg.ContractSwapID)
|
||||
|
||||
// validate that swap ID == keccak256(swap struct)
|
||||
if err := checkContractSwapID(msg); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
s.contractSwapID = msg.ContractSwapID
|
||||
@@ -107,7 +107,7 @@ func (s *swapState) handleNotifyETHLocked(msg *message.NotifyETHLocked) (common.
|
||||
|
||||
receipt, err := s.Backend.ETHClient().Raw().TransactionReceipt(s.ctx, msg.TxHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
contractAddr := msg.Address
|
||||
@@ -116,11 +116,11 @@ func (s *swapState) handleNotifyETHLocked(msg *message.NotifyETHLocked) (common.
|
||||
// doesn't hurt though I suppose.
|
||||
_, err = contracts.CheckSwapFactoryContractCode(s.ctx, s.Backend.ETHClient().Raw(), contractAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.setContract(contractAddr); err != nil {
|
||||
return nil, fmt.Errorf("failed to instantiate contract instance: %w", err)
|
||||
return fmt.Errorf("failed to instantiate contract instance: %w", err)
|
||||
}
|
||||
|
||||
ethInfo := &db.EthereumSwapInfo{
|
||||
@@ -131,25 +131,25 @@ func (s *swapState) handleNotifyETHLocked(msg *message.NotifyETHLocked) (common.
|
||||
}
|
||||
|
||||
if err = s.Backend.RecoveryDB().PutContractSwapInfo(s.ID(), ethInfo); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.checkContract(msg.TxHash); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.checkAndSetTimeouts(msg.ContractSwap.Timeout0, msg.ContractSwap.Timeout1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
notifyXMRLocked, err := s.lockFunds(coins.MoneroToPiconero(s.info.ProvidedAmount))
|
||||
err = s.lockFunds(coins.MoneroToPiconero(s.info.ProvidedAmount))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to lock funds: %w", err)
|
||||
return fmt.Errorf("failed to lock funds: %w", err)
|
||||
}
|
||||
|
||||
go s.runT0ExpirationHandler()
|
||||
return notifyXMRLocked, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *swapState) runT0ExpirationHandler() {
|
||||
|
||||
@@ -510,14 +510,14 @@ func (s *swapState) setContract(address ethcommon.Address) error {
|
||||
// lockFunds locks XMRMaker's funds in the monero account specified by public key
|
||||
// (S_a + S_b), viewable with (V_a + V_b)
|
||||
// It accepts the amount to lock as the input
|
||||
func (s *swapState) lockFunds(amount *coins.PiconeroAmount) (*message.NotifyXMRLock, error) {
|
||||
func (s *swapState) lockFunds(amount *coins.PiconeroAmount) error {
|
||||
xmrtakerPublicKeys := mcrypto.NewPublicKeyPair(s.xmrtakerPublicSpendKey, s.xmrtakerPrivateViewKey.Public())
|
||||
swapDestAddr := mcrypto.SumSpendAndViewKeys(xmrtakerPublicKeys, s.pubkeys).Address(s.Env())
|
||||
log.Infof("going to lock XMR funds, amount=%s XMR", amount.AsMoneroString())
|
||||
|
||||
balance, err := s.XMRClient().GetBalance(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("total XMR balance: ", coins.FmtPiconeroAmtAsXMR(balance.Balance))
|
||||
@@ -526,19 +526,11 @@ func (s *swapState) lockFunds(amount *coins.PiconeroAmount) (*message.NotifyXMRL
|
||||
log.Infof("Starting lock of %s XMR in address %s", amount.AsMoneroString(), swapDestAddr)
|
||||
transfer, err := s.XMRClient().Transfer(s.ctx, swapDestAddr, 0, amount, monero.MinSpendConfirmations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Successfully locked XMR funds: txID=%s address=%s block=%d",
|
||||
transfer.TxID, swapDestAddr, transfer.Height)
|
||||
s.fundsLocked = true
|
||||
|
||||
txID, err := types.HexToHash(transfer.TxID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &message.NotifyXMRLock{
|
||||
Address: swapDestAddr,
|
||||
TxID: txID,
|
||||
}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func TestSwapStateOngoing_Refund(t *testing.T) {
|
||||
newSwap(t, s, [32]byte{}, refundKey, desiredAmount.BigInt(), duration)
|
||||
|
||||
// lock XMR
|
||||
_, err = s.lockFunds(coins.MoneroToPiconero(s.info.ProvidedAmount))
|
||||
err = s.lockFunds(coins.MoneroToPiconero(s.info.ProvidedAmount))
|
||||
require.NoError(t, err)
|
||||
s.cancel()
|
||||
|
||||
|
||||
@@ -45,27 +45,6 @@ func newTestSwapStateAndDB(t *testing.T) (*Instance, *swapState, *offers.MockDat
|
||||
return xmrmaker, swapState, db
|
||||
}
|
||||
|
||||
func newTestSwapStateAndNet(t *testing.T) (*Instance, *swapState, *mockNet) {
|
||||
xmrmaker, net := newTestInstanceAndNet(t)
|
||||
|
||||
swapState, err := newSwapStateFromStart(
|
||||
xmrmaker.backend,
|
||||
types.NewOffer(
|
||||
coins.ProvidesXMR,
|
||||
coins.StrToDecimal("0.1"),
|
||||
coins.StrToDecimal("1"),
|
||||
coins.StrToExchangeRate("0.1"),
|
||||
types.EthAssetETH,
|
||||
),
|
||||
&types.OfferExtra{},
|
||||
xmrmaker.offerManager,
|
||||
coins.MoneroToPiconero(coins.StrToDecimal("0.1")),
|
||||
desiredAmount,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return xmrmaker, swapState, net
|
||||
}
|
||||
|
||||
func newTestSwapState(t *testing.T) (*Instance, *swapState) {
|
||||
xmrmaker, swapState, _ := newTestSwapStateAndDB(t)
|
||||
return xmrmaker, swapState
|
||||
@@ -177,7 +156,7 @@ func TestSwapState_handleSendKeysMessage(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSwapState_HandleProtocolMessage_NotifyETHLocked_ok(t *testing.T) {
|
||||
_, s, net := newTestSwapStateAndNet(t)
|
||||
_, s := newTestSwapState(t)
|
||||
defer s.cancel()
|
||||
s.nextExpectedEvent = EventETHLockedType
|
||||
|
||||
@@ -208,12 +187,6 @@ func TestSwapState_HandleProtocolMessage_NotifyETHLocked_ok(t *testing.T) {
|
||||
|
||||
err = s.HandleProtocolMessage(msg)
|
||||
require.NoError(t, err)
|
||||
resp := net.LastSentMessage()
|
||||
require.NotNil(t, resp)
|
||||
require.Equal(t, message.NotifyXMRLockType, resp.Type())
|
||||
require.Equal(t, duration, s.t1.Sub(s.t0))
|
||||
require.Equal(t, EventContractReadyType, s.nextExpectedEvent)
|
||||
require.True(t, s.info.Status.IsOngoing())
|
||||
}
|
||||
|
||||
func TestSwapState_HandleProtocolMessage_NotifyETHLocked_timeout(t *testing.T) {
|
||||
@@ -279,7 +252,7 @@ func TestSwapState_handleRefund(t *testing.T) {
|
||||
newSwap(t, s, [32]byte{}, refundKey, desiredAmount.BigInt(), duration)
|
||||
|
||||
// lock XMR
|
||||
_, err = s.lockFunds(coins.MoneroToPiconero(s.info.ProvidedAmount))
|
||||
err = s.lockFunds(coins.MoneroToPiconero(s.info.ProvidedAmount))
|
||||
require.NoError(t, err)
|
||||
|
||||
// call refund w/ XMRTaker's spend key
|
||||
@@ -327,7 +300,7 @@ func TestSwapState_Exit_Reclaim(t *testing.T) {
|
||||
newSwap(t, s, [32]byte{}, refundKey, desiredAmount.BigInt(), duration)
|
||||
|
||||
// lock XMR
|
||||
_, err = s.lockFunds(coins.MoneroToPiconero(s.info.ProvidedAmount))
|
||||
err = s.lockFunds(coins.MoneroToPiconero(s.info.ProvidedAmount))
|
||||
require.NoError(t, err)
|
||||
|
||||
balAfterLock, err := s.XMRClient().GetBalance(0)
|
||||
|
||||
@@ -18,7 +18,6 @@ var (
|
||||
errCannotRefund = errors.New("swap is not at a stage where it can refund")
|
||||
errRefundInvalid = errors.New("cannot refund, swap does not exist")
|
||||
errRefundSwapCompleted = fmt.Errorf("cannot refund, %w", errSwapCompleted)
|
||||
errNoLockedXMRAddress = errors.New("got empty address for locked XMR")
|
||||
errCounterpartyKeysNotSet = errors.New("counterparty's keys aren't set")
|
||||
errSwapInstantiationNoLogs = errors.New("expected 1 log, got 0")
|
||||
errSwapCompleted = errors.New("swap is already completed")
|
||||
|
||||
@@ -131,8 +131,7 @@ func newEventKeysReceived(msg *message.SendKeysMessage) *EventKeysReceived {
|
||||
// EventXMRLocked is the second expected event. It represents XMR being locked
|
||||
// on-chain.
|
||||
type EventXMRLocked struct {
|
||||
message *message.NotifyXMRLock
|
||||
errCh chan error
|
||||
errCh chan error
|
||||
}
|
||||
|
||||
// Type ...
|
||||
@@ -140,10 +139,9 @@ func (*EventXMRLocked) Type() EventType {
|
||||
return EventXMRLockedType
|
||||
}
|
||||
|
||||
func newEventXMRLocked(msg *message.NotifyXMRLock) *EventXMRLocked {
|
||||
func newEventXMRLocked() *EventXMRLocked {
|
||||
return &EventXMRLocked{
|
||||
message: msg,
|
||||
errCh: make(chan error),
|
||||
errCh: make(chan error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +246,7 @@ func (s *swapState) handleEvent(event Event) {
|
||||
return
|
||||
}
|
||||
|
||||
err := s.handleEventXMRLocked(e)
|
||||
err := s.handleNotifyXMRLock()
|
||||
if err != nil {
|
||||
e.errCh <- fmt.Errorf("failed to handle %s: %w", e.Type(), err)
|
||||
return
|
||||
@@ -311,10 +309,6 @@ func (s *swapState) handleEventKeysReceived(event *EventKeysReceived) error {
|
||||
return s.SendSwapMessage(resp, s.ID())
|
||||
}
|
||||
|
||||
func (s *swapState) handleEventXMRLocked(event *EventXMRLocked) error {
|
||||
return s.handleNotifyXMRLock(event.message)
|
||||
}
|
||||
|
||||
func (s *swapState) handleEventETHClaimed(event *EventETHClaimed) error {
|
||||
_, err := s.claimMonero(event.sk)
|
||||
if err != nil {
|
||||
|
||||
@@ -32,15 +32,6 @@ func lockXMRAndCheckForReadyLog(t *testing.T, s *swapState, xmrAddr *mcrypto.Add
|
||||
t.Logf("Transferred %d pico XMR (fees %d) to account %s", transfer.Amount, transfer.Fee, xmrAddr)
|
||||
t.Logf("Transfer was mined at block=%d with %d confirmations", transfer.Height, transfer.Confirmations)
|
||||
|
||||
txID, err := types.HexToHash(transfer.TxID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// send notification that monero was locked
|
||||
lmsg := &message.NotifyXMRLock{
|
||||
Address: xmrAddr,
|
||||
TxID: txID,
|
||||
}
|
||||
|
||||
// assert that ready() is called, setup contract watcher
|
||||
ethHeader, err := backend.ETHClient().Raw().HeaderByNumber(backend.Ctx(), nil)
|
||||
require.NoError(t, err)
|
||||
@@ -58,17 +49,12 @@ func lockXMRAndCheckForReadyLog(t *testing.T, s *swapState, xmrAddr *mcrypto.Add
|
||||
err = readyWatcher.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
// now handle the NotifyXMRLock message
|
||||
err = s.HandleProtocolMessage(lmsg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, s.nextExpectedEvent, EventETHClaimedType)
|
||||
require.Equal(t, types.ContractReady, s.info.Status)
|
||||
|
||||
// goroutine in SendKeysMessage handler should handle the NotifyXMRLock message
|
||||
select {
|
||||
case log := <-logReadyCh:
|
||||
err = pcommon.CheckSwapID(&log, readyTopic, s.contractSwapID)
|
||||
require.NoError(t, err)
|
||||
case <-time.After(time.Second * 2):
|
||||
case <-time.After(time.Second * 5):
|
||||
t.Fatalf("didn't get ready logs in time")
|
||||
}
|
||||
}
|
||||
@@ -106,6 +92,11 @@ func TestSwapState_handleEvent_EventETHClaimed(t *testing.T) {
|
||||
kp := mcrypto.SumSpendAndViewKeys(s.pubkeys, s.pubkeys)
|
||||
xmrAddr := kp.Address(common.Mainnet)
|
||||
lockXMRAndCheckForReadyLog(t, s, xmrAddr)
|
||||
// give handleNotifyXMRLock some time to return, since the event watcher
|
||||
// sees the Ready event before swapState.ready() returns
|
||||
time.Sleep(time.Second * 2)
|
||||
require.Equal(t, EventETHClaimedType, s.nextExpectedEvent)
|
||||
require.Equal(t, types.ContractReady, s.info.Status)
|
||||
|
||||
// simulate xmrmaker calling claim
|
||||
// call swap.Swap.Claim() w/ b.privkeys.sk, revealing XMRMaker's secret spend key
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/coins"
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
"github.com/athanorlabs/atomic-swap/common/types"
|
||||
mcrypto "github.com/athanorlabs/atomic-swap/crypto/monero"
|
||||
@@ -29,13 +28,6 @@ func (s *swapState) HandleProtocolMessage(msg common.Message) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case *message.NotifyXMRLock:
|
||||
event := newEventXMRLocked(msg)
|
||||
s.eventCh <- event
|
||||
err := <-event.errCh
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errUnexpectedMessageType
|
||||
}
|
||||
@@ -138,6 +130,9 @@ func (s *swapState) handleSendKeysMessage(msg *message.SendKeysMessage) (common.
|
||||
// start goroutine to check that XMRMaker locks before t_0
|
||||
go s.runT0ExpirationHandler()
|
||||
|
||||
// start goroutine to check for xmr being locked
|
||||
go s.checkForXMRLock()
|
||||
|
||||
out := &message.NotifyETHLocked{
|
||||
Address: s.ContractAddr(),
|
||||
TxHash: txHash,
|
||||
@@ -148,6 +143,57 @@ func (s *swapState) handleSendKeysMessage(msg *message.SendKeysMessage) (common.
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *swapState) checkForXMRLock() {
|
||||
var checkForXMRLockInterval time.Duration
|
||||
if s.Env() == common.Development {
|
||||
checkForXMRLockInterval = time.Second
|
||||
} else {
|
||||
// monero block time is >1 minute, so this should be fine
|
||||
checkForXMRLockInterval = time.Minute
|
||||
}
|
||||
|
||||
// check that XMR was locked in expected account, and confirm amount
|
||||
lockedAddr, vk := s.expectedXMRLockAccount()
|
||||
|
||||
conf := s.XMRClient().CreateWalletConf("xmrtaker-swap-wallet-verify-funds")
|
||||
abViewCli, err := monero.CreateViewOnlyWalletFromKeys(conf, vk, lockedAddr, s.walletScanHeight)
|
||||
if err != nil {
|
||||
log.Errorf("failed to generate view-only wallet to verify locked XMR: %s", err)
|
||||
return
|
||||
}
|
||||
defer abViewCli.CloseAndRemoveWallet()
|
||||
|
||||
log.Debugf("generated view-only wallet to check funds: %s", abViewCli.WalletName())
|
||||
|
||||
timer := time.NewTicker(checkForXMRLockInterval)
|
||||
for {
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
case <-timer.C:
|
||||
balance, err := abViewCli.GetBalance(0)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get balance: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("checking locked wallet, address=%s balance=%d blocks-to-unlock=%d",
|
||||
lockedAddr, balance.Balance, balance.BlocksToUnlock)
|
||||
|
||||
if s.expectedPiconeroAmount().CmpU64(balance.UnlockedBalance) <= 0 {
|
||||
event := newEventXMRLocked()
|
||||
s.eventCh <- event
|
||||
err := <-event.errCh
|
||||
if err != nil {
|
||||
log.Errorf("eventXMRLocked errored: %s", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *swapState) runT0ExpirationHandler() {
|
||||
defer log.Debugf("returning from runT0ExpirationHandler")
|
||||
|
||||
@@ -189,51 +235,7 @@ func (s *swapState) expectedXMRLockAccount() (*mcrypto.Address, *mcrypto.Private
|
||||
return mcrypto.NewPublicKeyPair(sk, vk.Public()).Address(s.Env()), vk
|
||||
}
|
||||
|
||||
func (s *swapState) handleNotifyXMRLock(msg *message.NotifyXMRLock) error {
|
||||
if msg.Address == nil {
|
||||
return errNoLockedXMRAddress
|
||||
}
|
||||
|
||||
// check that XMR was locked in expected account, and confirm amount
|
||||
lockedAddr, vk := s.expectedXMRLockAccount()
|
||||
if !msg.Address.Equal(lockedAddr) {
|
||||
return fmt.Errorf("address received in message does not match expected address")
|
||||
}
|
||||
|
||||
conf := s.XMRClient().CreateWalletConf("xmrtaker-swap-wallet-verify-funds")
|
||||
abViewCli, err := monero.CreateViewOnlyWalletFromKeys(conf, vk, lockedAddr, s.walletScanHeight)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate view-only wallet to verify locked XMR: %w", err)
|
||||
}
|
||||
defer abViewCli.CloseAndRemoveWallet()
|
||||
|
||||
log.Debugf("generated view-only wallet to check funds: %s", abViewCli.WalletName())
|
||||
|
||||
balance, err := abViewCli.GetBalance(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get balance: %w", err)
|
||||
}
|
||||
|
||||
log.Debugf("checking locked wallet, address=%s balance=%d blocks-to-unlock=%d",
|
||||
lockedAddr, balance.Balance, balance.BlocksToUnlock)
|
||||
|
||||
if s.expectedPiconeroAmount().CmpU64(balance.Balance) > 0 {
|
||||
return fmt.Errorf("locked XMR amount is less than expected: got %s, expected %s",
|
||||
coins.FmtPiconeroAmtAsXMR(balance.Balance), s.ExpectedAmount().Text('f'))
|
||||
}
|
||||
|
||||
// Monero received from a transfer is locked for a minimum of 10 confirmations before
|
||||
// it can be spent again. The maker is required to wait for 10 confirmations before
|
||||
// notifying us that the XMR is locked and should not be adding additional wait
|
||||
// requirements. We give one block of leniency, in case the taker's node is not fully
|
||||
// synced. Our goal is to prevent double spends, issues due to block reorgs, and
|
||||
// prevent the maker from locking our funds until close to the heat death of the
|
||||
// universe (https://github.com/monero-project/research-lab/issues/78).
|
||||
if balance.BlocksToUnlock > 1 {
|
||||
return fmt.Errorf("received XMR funds are not unlocked as required (blocks-to-unlock=%d)",
|
||||
balance.BlocksToUnlock)
|
||||
}
|
||||
|
||||
func (s *swapState) handleNotifyXMRLock() error {
|
||||
close(s.xmrLockedCh)
|
||||
log.Info("XMR was locked successfully, setting contract to ready...")
|
||||
|
||||
|
||||
@@ -189,6 +189,10 @@ func newSwapStateFromOngoing(
|
||||
s.contractSwap = ethSwapInfo.Swap
|
||||
s.xmrmakerPublicSpendKey = makerSk
|
||||
s.xmrmakerPrivateViewKey = makerVk
|
||||
|
||||
if info.Status == types.ETHLocked {
|
||||
go s.checkForXMRLock()
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -239,15 +239,10 @@ func lockXMRFunds(
|
||||
wc monero.WalletClient,
|
||||
destAddr *mcrypto.Address,
|
||||
amount *coins.PiconeroAmount,
|
||||
) types.Hash {
|
||||
) {
|
||||
monero.MineMinXMRBalance(t, wc, amount)
|
||||
transfer, err := wc.Transfer(ctx, destAddr, 0, amount, monero.MinSpendConfirmations)
|
||||
_, err := wc.Transfer(ctx, destAddr, 0, amount, monero.MinSpendConfirmations)
|
||||
require.NoError(t, err)
|
||||
|
||||
txID, err := types.HexToHash(transfer.TxID)
|
||||
require.NoError(t, err)
|
||||
|
||||
return txID
|
||||
}
|
||||
|
||||
func TestSwapState_NotifyXMRLock(t *testing.T) {
|
||||
@@ -271,12 +266,10 @@ func TestSwapState_NotifyXMRLock(t *testing.T) {
|
||||
kp := mcrypto.SumSpendAndViewKeys(xmrmakerKeysAndProof.PublicKeyPair, s.pubkeys)
|
||||
xmrAddr := kp.Address(common.Development)
|
||||
|
||||
msg := &message.NotifyXMRLock{
|
||||
Address: xmrAddr,
|
||||
TxID: lockXMRFunds(t, s.ctx, s.XMRClient(), xmrAddr, s.expectedPiconeroAmount()),
|
||||
}
|
||||
|
||||
err = s.HandleProtocolMessage(msg)
|
||||
lockXMRFunds(t, s.ctx, s.XMRClient(), xmrAddr, s.expectedPiconeroAmount())
|
||||
event := newEventXMRLocked()
|
||||
s.eventCh <- event
|
||||
err = <-event.errCh
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, EventETHClaimedType, s.nextExpectedEvent)
|
||||
}
|
||||
@@ -305,12 +298,10 @@ func TestSwapState_NotifyXMRLock_Refund(t *testing.T) {
|
||||
kp := mcrypto.SumSpendAndViewKeys(xmrmakerKeysAndProof.PublicKeyPair, s.pubkeys)
|
||||
xmrAddr := kp.Address(common.Development)
|
||||
|
||||
msg := &message.NotifyXMRLock{
|
||||
Address: xmrAddr,
|
||||
TxID: lockXMRFunds(t, s.ctx, s.XMRClient(), xmrAddr, s.expectedPiconeroAmount()),
|
||||
}
|
||||
|
||||
err = s.HandleProtocolMessage(msg)
|
||||
lockXMRFunds(t, s.ctx, s.XMRClient(), xmrAddr, s.expectedPiconeroAmount())
|
||||
event := newEventXMRLocked()
|
||||
s.eventCh <- event
|
||||
err = <-event.errCh
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, EventETHClaimedType, s.nextExpectedEvent)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user