From 112ed74cfb5759b816aaad59cd884ec9b25fe3ce Mon Sep 17 00:00:00 2001 From: noot <36753753+noot@users.noreply.github.com> Date: Mon, 20 Feb 2023 22:33:49 +0100 Subject: [PATCH] fix: update xmrmaker refund path wallet to use address from xmrtaker swap keys (#304) Co-authored-by: Dmitry Holodov --- db/recovery_db.go | 23 ++++--- db/recovery_db_test.go | 10 +-- monero/test_support.go | 1 + monero/wallet_client_test.go | 38 +++++++++++ net/initiate_test.go | 1 - net/message/message.go | 6 +- protocol/backend/backend.go | 4 +- protocol/backend/mock_recovery_db.go | 60 +++++++++--------- protocol/claim_monero.go | 21 +++---- protocol/claim_monero_test.go | 4 +- protocol/xmrmaker/event_test.go | 7 ++- protocol/xmrmaker/instance.go | 16 ++--- protocol/xmrmaker/instance_test.go | 66 ++++++++++++++++++++ protocol/xmrmaker/message_handler.go | 8 +-- protocol/xmrmaker/swap_state.go | 41 +++++++----- protocol/xmrmaker/swap_state_ongoing_test.go | 16 ++++- protocol/xmrmaker/swap_state_test.go | 35 ++++++++--- protocol/xmrtaker/claim.go | 4 +- protocol/xmrtaker/instance.go | 4 +- protocol/xmrtaker/instance_test.go | 2 +- protocol/xmrtaker/swap_state.go | 6 +- protocol/xmrtaker/swap_state_ongoing_test.go | 2 +- protocol/xmrtaker/swap_state_test.go | 2 +- 23 files changed, 258 insertions(+), 119 deletions(-) diff --git a/db/recovery_db.go b/db/recovery_db.go index b886a3f5..ec00a768 100644 --- a/db/recovery_db.go +++ b/db/recovery_db.go @@ -14,7 +14,7 @@ const ( swapPrivateKeyPrefix = "privkey" counterpartySwapPrivateKeyPrefix = "cspriv" relayerInfoPrefix = "relayer" - xmrmakerKeysPrefix = "xmrmaker" + counterpartySwapKeysPrefix = "cskeys" ) // RecoveryDB contains information about ongoing swaps required for recovery @@ -152,14 +152,14 @@ func (db *RecoveryDB) GetCounterpartySwapPrivateKey(id types.Hash) (*mcrypto.Pri return sk, nil } -type xmrmakerKeys struct { +type counterpartyKeys struct { PublicSpendKey *mcrypto.PublicKey `json:"publicSpendKey" validate:"required"` PrivateViewKey *mcrypto.PrivateViewKey `json:"privateViewKey" validate:"required"` } -// PutXMRMakerSwapKeys is called by the xmrtaker to store the counterparty's swap keys. -func (db *RecoveryDB) PutXMRMakerSwapKeys(id types.Hash, sk *mcrypto.PublicKey, vk *mcrypto.PrivateViewKey) error { - val, err := vjson.MarshalStruct(&xmrmakerKeys{ +// PutCounterpartySwapKeys is used to store the counterparty's swap keys. +func (db *RecoveryDB) PutCounterpartySwapKeys(id types.Hash, sk *mcrypto.PublicKey, vk *mcrypto.PrivateViewKey) error { + val, err := vjson.MarshalStruct(&counterpartyKeys{ PublicSpendKey: sk, PrivateViewKey: vk, }) @@ -167,20 +167,19 @@ func (db *RecoveryDB) PutXMRMakerSwapKeys(id types.Hash, sk *mcrypto.PublicKey, return err } - key := getRecoveryDBKey(id, xmrmakerKeysPrefix) + key := getRecoveryDBKey(id, counterpartySwapKeysPrefix) return db.db.Put(key[:], val) } -// GetXMRMakerSwapKeys is called by the xmrtaker during recovery to retrieve the counterparty's -// swap keys. -func (db *RecoveryDB) GetXMRMakerSwapKeys(id types.Hash) (*mcrypto.PublicKey, *mcrypto.PrivateViewKey, error) { - key := getRecoveryDBKey(id, xmrmakerKeysPrefix) +// GetCounterpartySwapKeys is called during recovery to retrieve the counterparty's swap keys. +func (db *RecoveryDB) GetCounterpartySwapKeys(id types.Hash) (*mcrypto.PublicKey, *mcrypto.PrivateViewKey, error) { + key := getRecoveryDBKey(id, counterpartySwapKeysPrefix) value, err := db.db.Get(key) if err != nil { return nil, nil, err } - var info xmrmakerKeys + var info counterpartyKeys err = vjson.UnmarshalStruct(value, &info) if err != nil { return nil, nil, err @@ -196,7 +195,7 @@ func (db *RecoveryDB) DeleteSwap(id types.Hash) error { getRecoveryDBKey(id, contractSwapInfoPrefix), getRecoveryDBKey(id, swapPrivateKeyPrefix), getRecoveryDBKey(id, counterpartySwapPrivateKeyPrefix), - getRecoveryDBKey(id, xmrmakerKeysPrefix), + getRecoveryDBKey(id, counterpartySwapKeysPrefix), } for _, key := range keys { diff --git a/db/recovery_db_test.go b/db/recovery_db_test.go index eeba1026..b6706bb8 100644 --- a/db/recovery_db_test.go +++ b/db/recovery_db_test.go @@ -125,17 +125,17 @@ func TestRecoveryDB_SharedSwapPrivateKey(t *testing.T) { require.Equal(t, kp.SpendKey().String(), res.String()) } -func TestRecoveryDB_XMRMakerSwapKeys(t *testing.T) { +func TestRecoveryDB_CounterpartySwapKeys(t *testing.T) { rdb := newTestRecoveryDB(t) offerID := types.Hash{5, 6, 7, 8} kp, err := mcrypto.GenerateKeys() require.NoError(t, err) - err = rdb.PutXMRMakerSwapKeys(offerID, kp.SpendKey().Public(), kp.ViewKey()) + err = rdb.PutCounterpartySwapKeys(offerID, kp.SpendKey().Public(), kp.ViewKey()) require.NoError(t, err) - resSk, resVk, err := rdb.GetXMRMakerSwapKeys(offerID) + resSk, resVk, err := rdb.GetCounterpartySwapKeys(offerID) require.NoError(t, err) require.Equal(t, kp.SpendKey().Public().String(), resSk.String()) require.Equal(t, kp.ViewKey().String(), resVk.String()) @@ -178,7 +178,7 @@ func TestRecoveryDB_DeleteSwap(t *testing.T) { require.NoError(t, err) err = rdb.PutCounterpartySwapPrivateKey(offerID, kp.SpendKey()) require.NoError(t, err) - err = rdb.PutXMRMakerSwapKeys(offerID, kp.SpendKey().Public(), kp.ViewKey()) + err = rdb.PutCounterpartySwapKeys(offerID, kp.SpendKey().Public(), kp.ViewKey()) require.NoError(t, err) err = rdb.DeleteSwap(offerID) @@ -191,6 +191,6 @@ func TestRecoveryDB_DeleteSwap(t *testing.T) { require.EqualError(t, chaindb.ErrKeyNotFound, err.Error()) _, err = rdb.GetCounterpartySwapPrivateKey(offerID) require.EqualError(t, chaindb.ErrKeyNotFound, err.Error()) - _, _, err = rdb.GetXMRMakerSwapKeys(offerID) + _, _, err = rdb.GetCounterpartySwapKeys(offerID) require.EqualError(t, chaindb.ErrKeyNotFound, err.Error()) } diff --git a/monero/test_support.go b/monero/test_support.go index 384449cb..a4d060bc 100644 --- a/monero/test_support.go +++ b/monero/test_support.go @@ -134,6 +134,7 @@ func MineMinXMRBalance(t *testing.T, wc WalletClient, minBalance *coins.Piconero if balance.UnlockedBalance > minBalU64 { break } + _, err = daemonCli.GenerateBlocks(&daemon.GenerateBlocksRequest{ AmountOfBlocks: 32, WalletAddress: addr.Address, diff --git a/monero/wallet_client_test.go b/monero/wallet_client_test.go index b6834b95..2a754dcf 100644 --- a/monero/wallet_client_test.go +++ b/monero/wallet_client_test.go @@ -228,6 +228,44 @@ func TestCallGenerateFromKeys(t *testing.T) { t.Logf("Address %s", addr.Address) } +// this tests calling generateFromkeys passing an address derived in +// a non-standard manner; ie. the public view key in the address doesn't +// match the private view key passed in. +func TestCallGenerateFromKeys_UnusualAddress(t *testing.T) { + kp, err := mcrypto.GenerateKeys() + require.NoError(t, err) + + kp2, err := mcrypto.GenerateKeys() + require.NoError(t, err) + + // create keypair with priv spend key of kp, but a different priv view key + // use the address of this keypair in the call to `generateFromKeys` + kp3 := mcrypto.NewPrivateKeyPair(kp.SpendKey(), kp2.ViewKey()) + address := kp3.PublicKeyPair().Address(common.Mainnet) + t.Log("address", address) + + conf := &WalletClientConf{ + Env: common.Development, + WalletFilePath: path.Join(t.TempDir(), "wallet", "not-used"), + MoneroWalletRPCPath: moneroWalletRPCPath, + } + err = conf.Fill() + require.NoError(t, err) + + c, err := createWalletFromKeys( + conf, + 0, + kp.SpendKey(), + kp.ViewKey(), + kp3.Address(common.Mainnet), + ) + require.NoError(t, err) + + res, err := c.GetAddress(0) + require.NoError(t, err) + require.Equal(t, string(address), res.Address) +} + func Test_getMoneroWalletRPCBin(t *testing.T) { wd, err := os.Getwd() require.NoError(t, err) diff --git a/net/initiate_test.go b/net/initiate_test.go index b293c8be..4cc7f2b2 100644 --- a/net/initiate_test.go +++ b/net/initiate_test.go @@ -22,7 +22,6 @@ func createSendKeysMessage(t *testing.T) *message.SendKeysMessage { OfferID: types.Hash{}, ProvidedAmount: new(apd.Decimal), PublicSpendKey: keysAndProof.PublicKeyPair.SpendKey(), - PublicViewKey: keysAndProof.PublicKeyPair.ViewKey(), PrivateViewKey: keysAndProof.PrivateKeyPair.ViewKey(), DLEqProof: hex.EncodeToString(keysAndProof.DLEqProof.Proof()), Secp256k1PublicKey: keysAndProof.Secp256k1PublicKey, diff --git a/net/message/message.go b/net/message/message.go index ea0337fa..e22d29b2 100644 --- a/net/message/message.go +++ b/net/message/message.go @@ -106,8 +106,7 @@ type SendKeysMessage struct { OfferID types.Hash `json:"offerID"` ProvidedAmount *apd.Decimal `json:"providedAmount" validate:"required"` PublicSpendKey *mcrypto.PublicKey `json:"publicSpendKey" validate:"required"` - PublicViewKey *mcrypto.PublicKey `json:"publicViewKey"` - PrivateViewKey *mcrypto.PrivateViewKey `json:"privateViewKey"` + PrivateViewKey *mcrypto.PrivateViewKey `json:"privateViewKey" validate:"required"` DLEqProof string `json:"dleqProof" validate:"required"` Secp256k1PublicKey *secp256k1.PublicKey `json:"secp256k1PublicKey" validate:"required"` EthAddress ethcommon.Address `json:"ethAddress"` @@ -115,11 +114,10 @@ type SendKeysMessage struct { // String ... func (m *SendKeysMessage) String() string { - return fmt.Sprintf("SendKeysMessage OfferID=%s ProvidedAmount=%v PublicSpendKey=%s PublicViewKey=%s PrivateViewKey=%s DLEqProof=%s Secp256k1PublicKey=%s EthAddress=%s", //nolint:lll + return fmt.Sprintf("SendKeysMessage OfferID=%s ProvidedAmount=%v PublicSpendKey=%s PrivateViewKey=%s DLEqProof=%s Secp256k1PublicKey=%s EthAddress=%s", //nolint:lll m.OfferID, m.ProvidedAmount, m.PublicSpendKey, - m.PublicViewKey, m.PrivateViewKey, m.DLEqProof, m.Secp256k1PublicKey, diff --git a/protocol/backend/backend.go b/protocol/backend/backend.go index 72afb363..07bd23e7 100644 --- a/protocol/backend/backend.go +++ b/protocol/backend/backend.go @@ -47,8 +47,8 @@ type RecoveryDB interface { GetCounterpartySwapPrivateKey(id types.Hash) (*mcrypto.PrivateSpendKey, error) PutSwapRelayerInfo(id types.Hash, info *types.OfferExtra) error GetSwapRelayerInfo(id types.Hash) (*types.OfferExtra, error) - PutXMRMakerSwapKeys(id types.Hash, sk *mcrypto.PublicKey, vk *mcrypto.PrivateViewKey) error - GetXMRMakerSwapKeys(id types.Hash) (*mcrypto.PublicKey, *mcrypto.PrivateViewKey, error) + PutCounterpartySwapKeys(id types.Hash, sk *mcrypto.PublicKey, vk *mcrypto.PrivateViewKey) error + GetCounterpartySwapKeys(id types.Hash) (*mcrypto.PublicKey, *mcrypto.PrivateViewKey, error) DeleteSwap(id types.Hash) error } diff --git a/protocol/backend/mock_recovery_db.go b/protocol/backend/mock_recovery_db.go index de830ef7..ef165f26 100644 --- a/protocol/backend/mock_recovery_db.go +++ b/protocol/backend/mock_recovery_db.go @@ -67,6 +67,22 @@ func (mr *MockRecoveryDBMockRecorder) GetContractSwapInfo(arg0 interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContractSwapInfo", reflect.TypeOf((*MockRecoveryDB)(nil).GetContractSwapInfo), arg0) } +// GetCounterpartySwapKeys mocks base method. +func (m *MockRecoveryDB) GetCounterpartySwapKeys(arg0 common.Hash) (*mcrypto.PublicKey, *mcrypto.PrivateViewKey, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCounterpartySwapKeys", arg0) + ret0, _ := ret[0].(*mcrypto.PublicKey) + ret1, _ := ret[1].(*mcrypto.PrivateViewKey) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetCounterpartySwapKeys indicates an expected call of GetCounterpartySwapKeys. +func (mr *MockRecoveryDBMockRecorder) GetCounterpartySwapKeys(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCounterpartySwapKeys", reflect.TypeOf((*MockRecoveryDB)(nil).GetCounterpartySwapKeys), arg0) +} + // GetCounterpartySwapPrivateKey mocks base method. func (m *MockRecoveryDB) GetCounterpartySwapPrivateKey(arg0 common.Hash) (*mcrypto.PrivateSpendKey, error) { m.ctrl.T.Helper() @@ -112,22 +128,6 @@ func (mr *MockRecoveryDBMockRecorder) GetSwapRelayerInfo(arg0 interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSwapRelayerInfo", reflect.TypeOf((*MockRecoveryDB)(nil).GetSwapRelayerInfo), arg0) } -// GetXMRMakerSwapKeys mocks base method. -func (m *MockRecoveryDB) GetXMRMakerSwapKeys(arg0 common.Hash) (*mcrypto.PublicKey, *mcrypto.PrivateViewKey, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetXMRMakerSwapKeys", arg0) - ret0, _ := ret[0].(*mcrypto.PublicKey) - ret1, _ := ret[1].(*mcrypto.PrivateViewKey) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetXMRMakerSwapKeys indicates an expected call of GetXMRMakerSwapKeys. -func (mr *MockRecoveryDBMockRecorder) GetXMRMakerSwapKeys(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetXMRMakerSwapKeys", reflect.TypeOf((*MockRecoveryDB)(nil).GetXMRMakerSwapKeys), arg0) -} - // PutContractSwapInfo mocks base method. func (m *MockRecoveryDB) PutContractSwapInfo(arg0 common.Hash, arg1 *db.EthereumSwapInfo) error { m.ctrl.T.Helper() @@ -142,6 +142,20 @@ func (mr *MockRecoveryDBMockRecorder) PutContractSwapInfo(arg0, arg1 interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutContractSwapInfo", reflect.TypeOf((*MockRecoveryDB)(nil).PutContractSwapInfo), arg0, arg1) } +// PutCounterpartySwapKeys mocks base method. +func (m *MockRecoveryDB) PutCounterpartySwapKeys(arg0 common.Hash, arg1 *mcrypto.PublicKey, arg2 *mcrypto.PrivateViewKey) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PutCounterpartySwapKeys", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// PutCounterpartySwapKeys indicates an expected call of PutCounterpartySwapKeys. +func (mr *MockRecoveryDBMockRecorder) PutCounterpartySwapKeys(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCounterpartySwapKeys", reflect.TypeOf((*MockRecoveryDB)(nil).PutCounterpartySwapKeys), arg0, arg1, arg2) +} + // PutCounterpartySwapPrivateKey mocks base method. func (m *MockRecoveryDB) PutCounterpartySwapPrivateKey(arg0 common.Hash, arg1 *mcrypto.PrivateSpendKey) error { m.ctrl.T.Helper() @@ -183,17 +197,3 @@ func (mr *MockRecoveryDBMockRecorder) PutSwapRelayerInfo(arg0, arg1 interface{}) mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutSwapRelayerInfo", reflect.TypeOf((*MockRecoveryDB)(nil).PutSwapRelayerInfo), arg0, arg1) } - -// PutXMRMakerSwapKeys mocks base method. -func (m *MockRecoveryDB) PutXMRMakerSwapKeys(arg0 common.Hash, arg1 *mcrypto.PublicKey, arg2 *mcrypto.PrivateViewKey) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PutXMRMakerSwapKeys", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// PutXMRMakerSwapKeys indicates an expected call of PutXMRMakerSwapKeys. -func (mr *MockRecoveryDBMockRecorder) PutXMRMakerSwapKeys(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutXMRMakerSwapKeys", reflect.TypeOf((*MockRecoveryDB)(nil).PutXMRMakerSwapKeys), arg0, arg1, arg2) -} diff --git a/protocol/claim_monero.go b/protocol/claim_monero.go index c20ec004..97513ef0 100644 --- a/protocol/claim_monero.go +++ b/protocol/claim_monero.go @@ -41,39 +41,38 @@ func ClaimMonero( kpAB *mcrypto.PrivateKeyPair, depositAddr mcrypto.Address, transferBack bool, -) (mcrypto.Address, error) { - abAddr := kpAB.PublicKeyPair().Address(env) - +) error { conf := xmrClient.CreateWalletConf(fmt.Sprintf("swap-wallet-claim-%s", id)) abWalletCli, err := monero.CreateSpendWalletFromKeys(conf, kpAB, walletScanHeight) if err != nil { - return "", err + return err } + address := kpAB.PublicKeyPair().Address(env) if transferBack { defer abWalletCli.CloseAndRemoveWallet() } else { abWalletCli.Close() - log.Infof("monero claimed in account %s with wallet file %s", abAddr, conf.WalletFilePath) - return abAddr, nil + log.Infof("monero claimed in account %s with wallet file %s", address, conf.WalletFilePath) + return nil } log.Infof("monero claimed in account %s; transferring to deposit account %s", - abAddr, depositAddr) + address, depositAddr) err = mcrypto.ValidateAddress(string(depositAddr), env) if err != nil { log.Errorf( "failed to transfer XMR out of swap wallet, dest address %s is invalid: %s", - abAddr, + address, err, ) - return "", err + return err } transfers, err := abWalletCli.SweepAll(ctx, depositAddr, 0, monero.SweepToSelfConfirmations) if err != nil { - return "", fmt.Errorf("failed to send funds to deposit account: %w", err) + return fmt.Errorf("failed to send funds to deposit account: %w", err) } for _, transfer := range transfers { @@ -83,5 +82,5 @@ func ClaimMonero( ) } - return abAddr, nil + return nil } diff --git a/protocol/claim_monero_test.go b/protocol/claim_monero_test.go index 621a9ba9..c2d3a5ad 100644 --- a/protocol/claim_monero_test.go +++ b/protocol/claim_monero_test.go @@ -41,7 +41,7 @@ func TestClaimMonero_NoTransferBack(t *testing.T) { pnAmt := coins.MoneroToPiconero(xmrAmt) monero.MineMinXMRBalance(t, moneroCli, pnAmt) - _, err = ClaimMonero( + err = ClaimMonero( context.Background(), common.Development, [32]byte{}, @@ -81,7 +81,7 @@ func TestClaimMonero_WithTransferBack(t *testing.T) { require.NoError(t, err) depositAddr := kp2.PublicKeyPair().Address(env) - _, err = ClaimMonero( + err = ClaimMonero( context.Background(), common.Development, [32]byte{}, diff --git a/protocol/xmrmaker/event_test.go b/protocol/xmrmaker/event_test.go index e63941b4..cd875ec1 100644 --- a/protocol/xmrmaker/event_test.go +++ b/protocol/xmrmaker/event_test.go @@ -44,7 +44,12 @@ func TestSwapState_handleEvent_EventETHRefunded(t *testing.T) { xmrtakerKeysAndProof, err := generateKeys() require.NoError(t, err) - s.setXMRTakerPublicKeys(xmrtakerKeysAndProof.PublicKeyPair, xmrtakerKeysAndProof.Secp256k1PublicKey) + err = s.setXMRTakerKeys( + xmrtakerKeysAndProof.PublicKeyPair.SpendKey(), + xmrtakerKeysAndProof.PrivateKeyPair.ViewKey(), + xmrtakerKeysAndProof.Secp256k1PublicKey, + ) + require.NoError(t, err) duration, err := time.ParseDuration("10m") require.NoError(t, err) diff --git a/protocol/xmrmaker/instance.go b/protocol/xmrmaker/instance.go index 618c7990..ab23bdb5 100644 --- a/protocol/xmrmaker/instance.go +++ b/protocol/xmrmaker/instance.go @@ -198,13 +198,6 @@ func (inst *Instance) createOngoingSwap(s *swap.Info) error { // the swap was started). It will also only only recover to the primary wallet address, // not whatever address was used when the swap was started. func (inst *Instance) completeSwap(s *swap.Info, skA *mcrypto.PrivateSpendKey) error { - // calc counterparty's swap private view key - // TODO: if the counterparty sends a non-standard public view key, does this work? - vkA, err := skA.View() - if err != nil { - return err - } - // fetch our swap private spend key skB, err := inst.backend.RecoveryDB().GetSwapPrivateKey(s.ID) if err != nil { @@ -217,12 +210,19 @@ func (inst *Instance) completeSwap(s *swap.Info, skA *mcrypto.PrivateSpendKey) e return err } + // we save the counterparty's public keys in case they send public keys derived + // in a non-standard way. + _, vkA, err := inst.backend.RecoveryDB().GetCounterpartySwapKeys(s.ID) + if err != nil { + return err + } + kpAB := pcommon.GetClaimKeypair( skA, skB, vkA, vkB, ) - _, err = pcommon.ClaimMonero( + err = pcommon.ClaimMonero( inst.backend.Ctx(), inst.backend.Env(), s.ID, diff --git a/protocol/xmrmaker/instance_test.go b/protocol/xmrmaker/instance_test.go index 9e3d6e62..77c04bf3 100644 --- a/protocol/xmrmaker/instance_test.go +++ b/protocol/xmrmaker/instance_test.go @@ -18,6 +18,7 @@ import ( contracts "github.com/athanorlabs/atomic-swap/ethereum" "github.com/athanorlabs/atomic-swap/ethereum/extethclient" "github.com/athanorlabs/atomic-swap/monero" + pcommon "github.com/athanorlabs/atomic-swap/protocol" "github.com/athanorlabs/atomic-swap/protocol/backend" pswap "github.com/athanorlabs/atomic-swap/protocol/swap" "github.com/athanorlabs/atomic-swap/protocol/xmrmaker/offers" @@ -87,6 +88,7 @@ func newBackendAndNet(t *testing.T) (backend.Backend, *mockNet) { rdb.EXPECT().PutSwapPrivateKey(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() rdb.EXPECT().PutCounterpartySwapPrivateKey(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() rdb.EXPECT().PutSwapRelayerInfo(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + rdb.EXPECT().PutCounterpartySwapKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() rdb.EXPECT().DeleteSwap(gomock.Any()).Return(nil).AnyTimes() extendedEC, err := extethclient.NewEthClient(context.Background(), env, ec, pk) @@ -196,3 +198,67 @@ func TestInstance_createOngoingSwap(t *testing.T) { defer inst.swapMu.Unlock() close(inst.swapStates[s.ID].done) } + +func TestInstance_CompleteSwap(t *testing.T) { + monero.TestBackgroundMineBlocks(t) + + inst, _ := newTestInstanceAndDB(t) + rdb := inst.backend.RecoveryDB().(*backend.MockRecoveryDB) + + // our keypair + kp, err := mcrypto.GenerateKeys() + require.NoError(t, err) + + id := [32]byte{9, 9, 9} + rdb.EXPECT().GetSwapPrivateKey(id).Return(kp.SpendKey(), nil) + + // counterparty's keypair + kpOther, err := mcrypto.GenerateKeys() + require.NoError(t, err) + rdb.EXPECT().GetCounterpartySwapKeys(id).Return(kpOther.SpendKey().Public(), kpOther.ViewKey(), nil) + + height, err := inst.backend.XMRClient().GetHeight() + require.NoError(t, err) + sinfo := &pswap.Info{ + ID: id, + MoneroStartHeight: height, + Status: types.XMRLocked, + } + err = inst.backend.SwapManager().AddSwap(sinfo) + require.NoError(t, err) + + // the address of the "shared swap wallet" + address := mcrypto.SumSpendAndViewKeys( + kp.PublicKeyPair(), kpOther.PublicKeyPair(), + ).Address(common.Development) + + conf := &monero.WalletClientConf{ + Env: common.Development, + WalletFilePath: path.Join(t.TempDir(), "test-wallet-tcm"), + MoneroWalletRPCPath: monero.GetWalletRPCDirectory(t), + } + err = conf.Fill() + require.NoError(t, err) + + // mine some xmr to the "shared swap wallet" + kpAB := pcommon.GetClaimKeypair( + kp.SpendKey(), kpOther.SpendKey(), + kp.ViewKey(), kpOther.ViewKey(), + ) + moneroCli, err := monero.CreateSpendWalletFromKeys(conf, kpAB, 0) + require.NoError(t, err) + xmrAmt := coins.StrToDecimal("1") + pnAmt := coins.MoneroToPiconero(xmrAmt) + monero.MineMinXMRBalance(t, moneroCli, pnAmt) + + addrRes, err := moneroCli.GetAddress(0) + require.NoError(t, err) + require.Equal(t, string(address), addrRes.Address) + + err = inst.completeSwap(sinfo, kpOther.SpendKey()) + require.NoError(t, err) + + balance, err := moneroCli.GetBalance(0) + require.NoError(t, err) + require.Equal(t, uint64(0), balance.Balance) +} diff --git a/protocol/xmrmaker/message_handler.go b/protocol/xmrmaker/message_handler.go index 6fd32e61..e66a5ec5 100644 --- a/protocol/xmrmaker/message_handler.go +++ b/protocol/xmrmaker/message_handler.go @@ -10,7 +10,6 @@ import ( "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" "github.com/athanorlabs/atomic-swap/db" contracts "github.com/athanorlabs/atomic-swap/ethereum" "github.com/athanorlabs/atomic-swap/net/message" @@ -199,7 +198,7 @@ func (s *swapState) handleT0Expired() { } func (s *swapState) handleSendKeysMessage(msg *message.SendKeysMessage) error { - if msg.PublicSpendKey == nil || msg.PublicViewKey == nil { + if msg.PublicSpendKey == nil || msg.PrivateViewKey == nil { return errMissingKeys } @@ -209,8 +208,5 @@ func (s *swapState) handleSendKeysMessage(msg *message.SendKeysMessage) error { return err } - kp := mcrypto.NewPublicKeyPair(msg.PublicSpendKey, msg.PublicViewKey) - - s.setXMRTakerPublicKeys(kp, verifyResult.Secp256k1PublicKey) - return nil + return s.setXMRTakerKeys(msg.PublicSpendKey, msg.PrivateViewKey, verifyResult.Secp256k1PublicKey) } diff --git a/protocol/xmrmaker/swap_state.go b/protocol/xmrmaker/swap_state.go index f4ef381c..2b37aa42 100644 --- a/protocol/xmrmaker/swap_state.go +++ b/protocol/xmrmaker/swap_state.go @@ -5,6 +5,7 @@ package xmrmaker import ( "context" "encoding/hex" + "fmt" "math/big" "time" @@ -63,7 +64,8 @@ type swapState struct { t0, t1 time.Time // XMRTaker's keys for this session - xmrtakerPublicKeys *mcrypto.PublicKeyPair + xmrtakerPublicSpendKey *mcrypto.PublicKey + xmrtakerPrivateViewKey *mcrypto.PrivateViewKey xmrtakerSecp256K1PublicKey *secp256k1.PublicKey moneroStartHeight uint64 // height of the monero blockchain when the swap is started @@ -412,21 +414,24 @@ func (s *swapState) exit() error { func (s *swapState) reclaimMonero(skA *mcrypto.PrivateSpendKey) error { // write counterparty swap privkey to disk in case something goes wrong - if err := s.Backend.RecoveryDB().PutCounterpartySwapPrivateKey(s.ID(), skA); err != nil { - return err - } - - vkA, err := skA.View() + err := s.Backend.RecoveryDB().PutCounterpartySwapPrivateKey(s.ID(), skA) if err != nil { return err } + if s.xmrtakerPublicSpendKey == nil || s.xmrtakerPrivateViewKey == nil { + s.xmrtakerPublicSpendKey, s.xmrtakerPrivateViewKey, err = s.RecoveryDB().GetCounterpartySwapKeys(s.ID()) + if err != nil { + return fmt.Errorf("failed to get counterparty public keypair: %w", err) + } + } + kpAB := pcommon.GetClaimKeypair( skA, s.privkeys.SpendKey(), - vkA, s.privkeys.ViewKey(), + s.xmrtakerPrivateViewKey, s.privkeys.ViewKey(), ) - _, err = pcommon.ClaimMonero( + return pcommon.ClaimMonero( s.ctx, s.Env(), s.ID(), @@ -436,11 +441,6 @@ func (s *swapState) reclaimMonero(skA *mcrypto.PrivateSpendKey) error { s.XMRClient().PrimaryAddress(), true, // always sweep back to our primary address ) - if err != nil { - return err - } - - return nil } // generateKeys generates XMRMaker's spend and view keys (s_b, v_b) @@ -479,10 +479,16 @@ func (s *swapState) getSecret() [32]byte { return secret } -// setXMRTakerPublicKeys sets XMRTaker's public spend and view keys -func (s *swapState) setXMRTakerPublicKeys(sk *mcrypto.PublicKeyPair, secp256k1Pub *secp256k1.PublicKey) { - s.xmrtakerPublicKeys = sk +// setXMRTakerKeys sets XMRTaker's public spend and private view key +func (s *swapState) setXMRTakerKeys( + sk *mcrypto.PublicKey, + vk *mcrypto.PrivateViewKey, + secp256k1Pub *secp256k1.PublicKey, +) error { + s.xmrtakerPublicSpendKey = sk + s.xmrtakerPrivateViewKey = vk s.xmrtakerSecp256K1PublicKey = secp256k1Pub + return s.RecoveryDB().PutCounterpartySwapKeys(s.ID(), sk, vk) } // setContract sets the contract in which XMRTaker has locked her ETH. @@ -504,7 +510,8 @@ func (s *swapState) setContract(address ethcommon.Address) error { // (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) { - swapDestAddr := mcrypto.SumSpendAndViewKeys(s.xmrtakerPublicKeys, s.pubkeys).Address(s.Env()) + 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) diff --git a/protocol/xmrmaker/swap_state_ongoing_test.go b/protocol/xmrmaker/swap_state_ongoing_test.go index cccac147..7df2c53c 100644 --- a/protocol/xmrmaker/swap_state_ongoing_test.go +++ b/protocol/xmrmaker/swap_state_ongoing_test.go @@ -10,6 +10,7 @@ import ( "github.com/athanorlabs/atomic-swap/common/types" "github.com/athanorlabs/atomic-swap/db" "github.com/athanorlabs/atomic-swap/ethereum/block" + "github.com/athanorlabs/atomic-swap/protocol/backend" "github.com/athanorlabs/atomic-swap/tests" "github.com/stretchr/testify/require" @@ -63,12 +64,17 @@ func TestSwapStateOngoing_ClaimFunds(t *testing.T) { } func TestSwapStateOngoing_Refund(t *testing.T) { - _, s, offerDB := newTestSwapStateAndDB(t) + inst, s, offerDB := newTestSwapStateAndDB(t) offerDB.EXPECT().PutOffer(s.offer) xmrtakerKeysAndProof, err := generateKeys() require.NoError(t, err) - s.setXMRTakerPublicKeys(xmrtakerKeysAndProof.PublicKeyPair, xmrtakerKeysAndProof.Secp256k1PublicKey) + err = s.setXMRTakerKeys( + xmrtakerKeysAndProof.PublicKeyPair.SpendKey(), + xmrtakerKeysAndProof.PrivateKeyPair.ViewKey(), + xmrtakerKeysAndProof.Secp256k1PublicKey, + ) + require.NoError(t, err) duration, err := time.ParseDuration("10m") require.NoError(t, err) @@ -106,6 +112,12 @@ func TestSwapStateOngoing_Refund(t *testing.T) { } s.info.Status = types.XMRLocked + rdb := inst.backend.RecoveryDB().(*backend.MockRecoveryDB) + rdb.EXPECT().GetCounterpartySwapKeys(s.ID()).Return( + xmrtakerKeysAndProof.PublicKeyPair.SpendKey(), + xmrtakerKeysAndProof.PrivateKeyPair.ViewKey(), + nil, + ) t.Log("creating swap state again...") ss, err := newSwapStateFromOngoing( diff --git a/protocol/xmrmaker/swap_state_test.go b/protocol/xmrmaker/swap_state_test.go index 75d7c300..7c70ba98 100644 --- a/protocol/xmrmaker/swap_state_test.go +++ b/protocol/xmrmaker/swap_state_test.go @@ -78,7 +78,7 @@ func newTestXMRTakerSendKeysMessage(t *testing.T) (*message.SendKeysMessage, *pc msg := &message.SendKeysMessage{ PublicSpendKey: keysAndProof.PublicKeyPair.SpendKey(), - PublicViewKey: keysAndProof.PublicKeyPair.ViewKey(), + PrivateViewKey: keysAndProof.PrivateKeyPair.ViewKey(), DLEqProof: hex.EncodeToString(keysAndProof.DLEqProof.Proof()), Secp256k1PublicKey: keysAndProof.Secp256k1PublicKey, } @@ -168,13 +168,12 @@ func TestSwapState_handleSendKeysMessage(t *testing.T) { require.Equal(t, errMissingKeys, err) msg, xmrtakerKeysAndProof := newTestXMRTakerSendKeysMessage(t) - xmrtakerPubKeys := xmrtakerKeysAndProof.PublicKeyPair err = s.handleSendKeysMessage(msg) require.NoError(t, err) require.Equal(t, EventETHLockedType, s.nextExpectedEvent) - require.Equal(t, xmrtakerPubKeys.SpendKey().String(), s.xmrtakerPublicKeys.SpendKey().String()) - require.Equal(t, xmrtakerPubKeys.ViewKey().String(), s.xmrtakerPublicKeys.ViewKey().String()) + require.Equal(t, xmrtakerKeysAndProof.PublicKeyPair.SpendKey().String(), s.xmrtakerPublicSpendKey.String()) + require.Equal(t, xmrtakerKeysAndProof.PrivateKeyPair.ViewKey().String(), s.xmrtakerPrivateViewKey.String()) require.True(t, s.info.Status.IsOngoing()) } @@ -185,7 +184,12 @@ func TestSwapState_HandleProtocolMessage_NotifyETHLocked_ok(t *testing.T) { xmrtakerKeysAndProof, err := generateKeys() require.NoError(t, err) - s.setXMRTakerPublicKeys(xmrtakerKeysAndProof.PublicKeyPair, xmrtakerKeysAndProof.Secp256k1PublicKey) + err = s.setXMRTakerKeys( + xmrtakerKeysAndProof.PublicKeyPair.SpendKey(), + xmrtakerKeysAndProof.PrivateKeyPair.ViewKey(), + xmrtakerKeysAndProof.Secp256k1PublicKey, + ) + require.NoError(t, err) msg := &message.NotifyETHLocked{} err = s.HandleProtocolMessage(msg) @@ -219,7 +223,12 @@ func TestSwapState_HandleProtocolMessage_NotifyETHLocked_timeout(t *testing.T) { xmrtakerKeysAndProof, err := generateKeys() require.NoError(t, err) - s.setXMRTakerPublicKeys(xmrtakerKeysAndProof.PublicKeyPair, xmrtakerKeysAndProof.Secp256k1PublicKey) + err = s.setXMRTakerKeys( + xmrtakerKeysAndProof.PublicKeyPair.SpendKey(), + xmrtakerKeysAndProof.PrivateKeyPair.ViewKey(), + xmrtakerKeysAndProof.Secp256k1PublicKey, + ) + require.NoError(t, err) msg := &message.NotifyETHLocked{} err = s.HandleProtocolMessage(msg) @@ -257,7 +266,12 @@ func TestSwapState_handleRefund(t *testing.T) { xmrtakerKeysAndProof, err := generateKeys() require.NoError(t, err) - s.setXMRTakerPublicKeys(xmrtakerKeysAndProof.PublicKeyPair, xmrtakerKeysAndProof.Secp256k1PublicKey) + err = s.setXMRTakerKeys( + xmrtakerKeysAndProof.PublicKeyPair.SpendKey(), + xmrtakerKeysAndProof.PrivateKeyPair.ViewKey(), + xmrtakerKeysAndProof.Secp256k1PublicKey, + ) + require.NoError(t, err) duration, err := time.ParseDuration("10m") require.NoError(t, err) @@ -300,7 +314,12 @@ func TestSwapState_Exit_Reclaim(t *testing.T) { xmrtakerKeysAndProof, err := generateKeys() require.NoError(t, err) - s.setXMRTakerPublicKeys(xmrtakerKeysAndProof.PublicKeyPair, xmrtakerKeysAndProof.Secp256k1PublicKey) + err = s.setXMRTakerKeys( + xmrtakerKeysAndProof.PublicKeyPair.SpendKey(), + xmrtakerKeysAndProof.PrivateKeyPair.ViewKey(), + xmrtakerKeysAndProof.Secp256k1PublicKey, + ) + require.NoError(t, err) duration, err := time.ParseDuration("10m") require.NoError(t, err) diff --git a/protocol/xmrtaker/claim.go b/protocol/xmrtaker/claim.go index 6e2641e0..8cdd8d9d 100644 --- a/protocol/xmrtaker/claim.go +++ b/protocol/xmrtaker/claim.go @@ -101,7 +101,7 @@ func (s *swapState) claimMonero(skB *mcrypto.PrivateSpendKey) (mcrypto.Address, s.xmrmakerPrivateViewKey, s.privkeys.ViewKey(), ) - abAddr, err := pcommon.ClaimMonero( + err = pcommon.ClaimMonero( s.ctx, s.Env(), s.info.ID, @@ -116,5 +116,5 @@ func (s *swapState) claimMonero(skB *mcrypto.PrivateSpendKey) (mcrypto.Address, } close(s.claimedCh) - return abAddr, nil + return kpAB.PublicKeyPair().Address(s.Env()), nil } diff --git a/protocol/xmrtaker/instance.go b/protocol/xmrtaker/instance.go index 78eb9faf..56b3d127 100644 --- a/protocol/xmrtaker/instance.go +++ b/protocol/xmrtaker/instance.go @@ -175,7 +175,7 @@ func (inst *Instance) completeSwap(s *swap.Info, skB *mcrypto.PrivateSpendKey) e } // fetch counterparty's private view key - _, vkB, err := inst.backend.RecoveryDB().GetXMRMakerSwapKeys(s.ID) + _, vkB, err := inst.backend.RecoveryDB().GetCounterpartySwapKeys(s.ID) if err != nil { return err } @@ -185,7 +185,7 @@ func (inst *Instance) completeSwap(s *swap.Info, skB *mcrypto.PrivateSpendKey) e vkA, vkB, ) - _, err = pcommon.ClaimMonero( + err = pcommon.ClaimMonero( inst.backend.Ctx(), inst.backend.Env(), s.ID, diff --git a/protocol/xmrtaker/instance_test.go b/protocol/xmrtaker/instance_test.go index 90156cfe..7e9e9d4b 100644 --- a/protocol/xmrtaker/instance_test.go +++ b/protocol/xmrtaker/instance_test.go @@ -72,7 +72,7 @@ func TestInstance_createOngoingSwap(t *testing.T) { rdb.EXPECT().GetSwapPrivateKey(s.ID).Return( sk.SpendKey(), nil, ) - rdb.EXPECT().GetXMRMakerSwapKeys(s.ID).Return( + rdb.EXPECT().GetCounterpartySwapKeys(s.ID).Return( makerKeys.SpendKey().Public(), makerKeys.ViewKey(), nil, ) diff --git a/protocol/xmrtaker/swap_state.go b/protocol/xmrtaker/swap_state.go index ef5cb344..ac20752d 100644 --- a/protocol/xmrtaker/swap_state.go +++ b/protocol/xmrtaker/swap_state.go @@ -161,7 +161,7 @@ func newSwapStateFromOngoing( return nil, errInvalidStageForRecovery } - makerSk, makerVk, err := b.RecoveryDB().GetXMRMakerSwapKeys(info.ID) + makerSk, makerVk, err := b.RecoveryDB().GetCounterpartySwapKeys(info.ID) if err != nil { return nil, fmt.Errorf("failed to get xmrmaker swap keys from db: %w", err) } @@ -309,7 +309,7 @@ func (s *swapState) waitForSendKeysMessage() { func (s *swapState) SendKeysMessage() common.Message { return &message.SendKeysMessage{ PublicSpendKey: s.pubkeys.SpendKey(), - PublicViewKey: s.pubkeys.ViewKey(), + PrivateViewKey: s.privkeys.ViewKey(), DLEqProof: hex.EncodeToString(s.dleqProof.Proof()), Secp256k1PublicKey: s.secp256k1Pub, } @@ -535,7 +535,7 @@ func (s *swapState) setXMRMakerKeys( s.xmrmakerPublicSpendKey = sk s.xmrmakerPrivateViewKey = vk s.xmrmakerSecp256k1PublicKey = secp256k1Pub - return s.Backend.RecoveryDB().PutXMRMakerSwapKeys(s.info.ID, sk, vk) + return s.Backend.RecoveryDB().PutCounterpartySwapKeys(s.info.ID, sk, vk) } func (s *swapState) approveToken() error { diff --git a/protocol/xmrtaker/swap_state_ongoing_test.go b/protocol/xmrtaker/swap_state_ongoing_test.go index 224627f5..ba6ee246 100644 --- a/protocol/xmrtaker/swap_state_ongoing_test.go +++ b/protocol/xmrtaker/swap_state_ongoing_test.go @@ -40,7 +40,7 @@ func setupSwapStateUntilETHLocked(t *testing.T) (*swapState, uint64) { // shutdown swap state, re-create from ongoing s.cancel() - rdb.EXPECT().GetXMRMakerSwapKeys(s.info.ID).Return( + rdb.EXPECT().GetCounterpartySwapKeys(s.info.ID).Return( makerKeys.PublicKeyPair.SpendKey(), makerKeys.PrivateKeyPair.ViewKey(), nil, diff --git a/protocol/xmrtaker/swap_state_test.go b/protocol/xmrtaker/swap_state_test.go index 78bec2c5..0e7b4763 100644 --- a/protocol/xmrtaker/swap_state_test.go +++ b/protocol/xmrtaker/swap_state_test.go @@ -87,7 +87,7 @@ func newBackendAndNet(t *testing.T) (backend.Backend, *mockNet) { rdb.EXPECT().PutContractSwapInfo(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() rdb.EXPECT().PutSwapPrivateKey(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() rdb.EXPECT().PutCounterpartySwapPrivateKey(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - rdb.EXPECT().PutXMRMakerSwapKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + rdb.EXPECT().PutCounterpartySwapKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() rdb.EXPECT().DeleteSwap(gomock.Any()).Return(nil).AnyTimes() extendedEC, err := extethclient.NewEthClient(context.Background(), env, ec, pk)