mirror of
https://github.com/AthanorLabs/atomic-swap.git
synced 2026-01-08 21:58:07 -05:00
dedicated monero address type (#327)
Adds custom type for monero addresses with better validations.
This commit is contained in:
@@ -21,6 +21,7 @@ linters-settings:
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
|
||||
dupl:
|
||||
# tokens count to trigger issue, 150 by default
|
||||
threshold: 100
|
||||
@@ -29,12 +30,19 @@ linters-settings:
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||
|
||||
tagliatelle:
|
||||
# Check the struct tag name case.
|
||||
case:
|
||||
rules:
|
||||
# See https://github.com/ldez/tagliatelle for values and examples:
|
||||
json: goCamel
|
||||
|
||||
gocritic:
|
||||
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint` run to see all tags and checks.
|
||||
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
|
||||
enabled-tags:
|
||||
- performance
|
||||
|
||||
settings: # settings passed to gocritic
|
||||
captLocal: # must be valid enabled check name
|
||||
paramsOnly: true
|
||||
@@ -64,6 +72,7 @@ linters:
|
||||
- predeclared
|
||||
- revive
|
||||
- staticcheck
|
||||
- tagliatelle
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
|
||||
@@ -421,7 +421,7 @@ func runETHAddress(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Ethereum address: %s\n", balances.EthAddress)
|
||||
code, err := qrcode.New(balances.EthAddress, qrcode.Medium)
|
||||
code, err := qrcode.New(balances.EthAddress.String(), qrcode.Medium)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -436,7 +436,7 @@ func runXMRAddress(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Monero address: %s\n", balances.MoneroAddress)
|
||||
code, err := qrcode.New(balances.MoneroAddress, qrcode.Medium)
|
||||
code, err := qrcode.New(balances.MoneroAddress.String(), qrcode.Medium)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/coins"
|
||||
"github.com/athanorlabs/atomic-swap/common/types"
|
||||
mcrypto "github.com/athanorlabs/atomic-swap/crypto/monero"
|
||||
)
|
||||
|
||||
// JSON RPC method names that we serve on the localhost server
|
||||
@@ -87,15 +88,15 @@ type MakeOfferRequest struct {
|
||||
|
||||
// MakeOfferResponse ...
|
||||
type MakeOfferResponse struct {
|
||||
PeerID peer.ID `json:"peerID"`
|
||||
OfferID types.Hash `json:"offerID"`
|
||||
PeerID peer.ID `json:"peerID" validate:"required"`
|
||||
OfferID types.Hash `json:"offerID" validate:"required"`
|
||||
}
|
||||
|
||||
// SignerRequest initiates the signer_subscribe handler from the front-end
|
||||
type SignerRequest struct {
|
||||
OfferID types.Hash `json:"offerID"`
|
||||
EthAddress string `json:"ethAddress"`
|
||||
XMRAddress string `json:"xmrAddress"`
|
||||
OfferID types.Hash `json:"offerID" validate:"required"`
|
||||
EthAddress ethcommon.Address `json:"ethAddress" validate:"required"`
|
||||
XMRAddress *mcrypto.Address `json:"xmrAddress" validate:"required"`
|
||||
}
|
||||
|
||||
// SignerResponse sends a tx to be signed to the front-end
|
||||
@@ -114,12 +115,12 @@ type SignerTxSigned struct {
|
||||
|
||||
// BalancesResponse holds the response for the combined Monero and Ethereum Balances request
|
||||
type BalancesResponse struct {
|
||||
MoneroAddress string `json:"moneroAddress"`
|
||||
PiconeroBalance *coins.PiconeroAmount `json:"piconeroBalance"`
|
||||
PiconeroUnlockedBalance *coins.PiconeroAmount `json:"piconeroUnlockedBalance"`
|
||||
MoneroAddress *mcrypto.Address `json:"moneroAddress" validate:"required"`
|
||||
PiconeroBalance *coins.PiconeroAmount `json:"piconeroBalance" validate:"required"`
|
||||
PiconeroUnlockedBalance *coins.PiconeroAmount `json:"piconeroUnlockedBalance" validate:"required"`
|
||||
BlocksToUnlock uint64 `json:"blocksToUnlock"`
|
||||
EthAddress string `json:"ethAddress"`
|
||||
WeiBalance *coins.WeiAmount `json:"weiBalance"`
|
||||
EthAddress ethcommon.Address `json:"ethAddress" validate:"required"`
|
||||
WeiBalance *coins.WeiAmount `json:"weiBalance" validate:"required"`
|
||||
}
|
||||
|
||||
// AddressesResponse ...
|
||||
|
||||
@@ -9,43 +9,150 @@ import (
|
||||
"github.com/athanorlabs/atomic-swap/crypto"
|
||||
)
|
||||
|
||||
// Network is the Monero network type
|
||||
type Network string
|
||||
|
||||
// Monero networks
|
||||
const (
|
||||
addressPrefixMainnet byte = 18
|
||||
addressPrefixStagenet byte = 24
|
||||
Mainnet Network = "mainnet"
|
||||
Stagenet Network = "stagenet"
|
||||
Testnet Network = "testnet"
|
||||
)
|
||||
|
||||
// AddressType is the type of Monero address: Standard or Subaddress
|
||||
type AddressType string
|
||||
|
||||
// Monero address types. We don't support Integrated.
|
||||
const (
|
||||
Standard AddressType = "standard"
|
||||
Subaddress AddressType = "subaddress"
|
||||
)
|
||||
|
||||
// Network prefix byte. The 1st decoded byte of a monero address defines both
|
||||
// the network (mainnet, stagenet, testnet) and the type of address (standard,
|
||||
// integrated, and subaddress).
|
||||
const (
|
||||
netPrefixStdAddrMainnet = 18
|
||||
netPrefixSubAddrMainnet = 42
|
||||
netPrefixStdAddrStagenet = 24
|
||||
netPrefixSubAddrStagenet = 36
|
||||
netPrefixStdAddrTestnet = 53
|
||||
netPrefixSubAddrTestnet = 63
|
||||
)
|
||||
|
||||
var (
|
||||
errAddressNotInitialized = errors.New("monero address is not initialized")
|
||||
errChecksumMismatch = errors.New("invalid address checksum")
|
||||
errInvalidAddressLength = errors.New("invalid monero address length")
|
||||
errInvalidAddressEncoding = errors.New("invalid monero address encoding")
|
||||
errInvalidPrefixGotMainnet = errors.New("invalid monero address: expected stagenet, got mainnet")
|
||||
errInvalidPrefixGotStagenet = errors.New("invalid monero address: expected mainnet, got stagenet")
|
||||
errInvalidPrefixGotTestnet = errors.New("invalid monero address: monero testnet not yet supported")
|
||||
)
|
||||
|
||||
// Address represents a base58-encoded string
|
||||
type Address string
|
||||
// Address represents a Monero address
|
||||
type Address struct {
|
||||
// decoded is the bytes (prefix, pub spend key, pub view key, checksum) that
|
||||
// get base58 encoded. Package private, as it is a semi-arbitrary
|
||||
// implementation detail.
|
||||
decoded [addressBytesLen]byte
|
||||
}
|
||||
|
||||
// ValidateAddress checks if the given address is valid
|
||||
func ValidateAddress(addr string, env common.Environment) error {
|
||||
b, err := MoneroAddrBase58ToBytes(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
// NewAddress converts a string to a monero Address with validation.
|
||||
func NewAddress(addrStr string, env common.Environment) (*Address, error) {
|
||||
addr := new(Address)
|
||||
if err := addr.UnmarshalText([]byte(addrStr)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch env {
|
||||
case common.Mainnet, common.Development:
|
||||
if b[0] != addressPrefixMainnet {
|
||||
return errInvalidPrefixGotStagenet
|
||||
}
|
||||
case common.Stagenet:
|
||||
if b[0] != addressPrefixStagenet {
|
||||
return addr, addr.ValidateEnv(env)
|
||||
}
|
||||
|
||||
func (a *Address) String() string {
|
||||
return addrBytesToBase58(a.decoded[:])
|
||||
}
|
||||
|
||||
// Network returns the Monero network of the address
|
||||
func (a *Address) Network() Network {
|
||||
switch a.decoded[0] {
|
||||
case netPrefixStdAddrMainnet, netPrefixSubAddrMainnet:
|
||||
return Mainnet
|
||||
case netPrefixStdAddrStagenet, netPrefixSubAddrStagenet:
|
||||
return Stagenet
|
||||
case netPrefixStdAddrTestnet, netPrefixSubAddrTestnet:
|
||||
return Testnet
|
||||
default:
|
||||
// Our methods to deserialize and create Address values all verify
|
||||
// that the address byte is valid
|
||||
panic("address has invalid network prefix")
|
||||
}
|
||||
}
|
||||
|
||||
// Type returns the Address type
|
||||
func (a *Address) Type() AddressType {
|
||||
switch a.decoded[0] {
|
||||
case netPrefixStdAddrMainnet, netPrefixStdAddrStagenet, netPrefixStdAddrTestnet:
|
||||
return Standard
|
||||
case netPrefixSubAddrTestnet, netPrefixSubAddrStagenet, netPrefixSubAddrMainnet:
|
||||
return Subaddress
|
||||
default:
|
||||
// Our methods to deserialize and create Address values all verify
|
||||
// that the address byte is valid
|
||||
panic("address has invalid network prefix")
|
||||
}
|
||||
}
|
||||
|
||||
// validateDecoded ensures that the checksum and network prefix of the address
|
||||
// are valid. The Network() and Type() methods are not safe to use until
|
||||
// this base level validation is performed.
|
||||
func (a *Address) validateDecoded() error {
|
||||
checksum := getChecksum(a.decoded[:65])
|
||||
if !bytes.Equal(checksum[:], a.decoded[65:69]) {
|
||||
return errChecksumMismatch
|
||||
}
|
||||
|
||||
netPrefix := a.decoded[0]
|
||||
switch netPrefix {
|
||||
case netPrefixStdAddrMainnet, netPrefixSubAddrMainnet,
|
||||
netPrefixStdAddrStagenet, netPrefixSubAddrStagenet,
|
||||
netPrefixStdAddrTestnet, netPrefixSubAddrTestnet:
|
||||
// we are good, do nothing
|
||||
default:
|
||||
return fmt.Errorf("monero address has unknown network prefix %d", netPrefix)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Equal returns true if the addresses are identical, otherwise false.
|
||||
func (a *Address) Equal(b *Address) bool {
|
||||
if b == nil {
|
||||
return false
|
||||
}
|
||||
return a.decoded == b.decoded
|
||||
}
|
||||
|
||||
// ValidateEnv validates that the monero network matches the passed environment.
|
||||
// This validation can't be performed when decoding JSON, as the environment is
|
||||
// not known at that time.
|
||||
func (a *Address) ValidateEnv(env common.Environment) error {
|
||||
if a == nil || a.decoded == new(Address).decoded {
|
||||
return errAddressNotInitialized
|
||||
}
|
||||
|
||||
switch a.Network() {
|
||||
case Mainnet:
|
||||
if env != common.Mainnet && env != common.Development {
|
||||
return errInvalidPrefixGotMainnet
|
||||
}
|
||||
}
|
||||
|
||||
checksum := getChecksum(b[:65])
|
||||
if !bytes.Equal(checksum[:], b[65:69]) {
|
||||
return errChecksumMismatch
|
||||
case Stagenet:
|
||||
if env != common.Stagenet {
|
||||
return errInvalidPrefixGotStagenet
|
||||
}
|
||||
case Testnet:
|
||||
return errInvalidPrefixGotTestnet
|
||||
default:
|
||||
panic("unhandled network")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -57,28 +164,16 @@ func getChecksum(data ...[]byte) (result [4]byte) {
|
||||
return
|
||||
}
|
||||
|
||||
// AddressBytes returns the address as bytes for a PrivateKeyPair with the given environment (ie. mainnet or stagenet)
|
||||
func (kp *PrivateKeyPair) AddressBytes(env common.Environment) []byte {
|
||||
return kp.PublicKeyPair().AddressBytes(env)
|
||||
}
|
||||
|
||||
// Address returns the base58-encoded address for a PrivateKeyPair with the given environment
|
||||
// (ie. mainnet or stagenet)
|
||||
func (kp *PrivateKeyPair) Address(env common.Environment) Address {
|
||||
return Address(MoneroAddrBytesToBase58(kp.AddressBytes(env)))
|
||||
}
|
||||
|
||||
// AddressBytes returns the address as bytes for a PublicKeyPair with the given environment (ie. mainnet or stagenet)
|
||||
func (kp *PublicKeyPair) AddressBytes(env common.Environment) []byte {
|
||||
psk := kp.sk.key.Bytes()
|
||||
pvk := kp.vk.key.Bytes()
|
||||
// Address returns the address as bytes for a PublicKeyPair with the given environment (ie. mainnet or stagenet)
|
||||
func (kp *PublicKeyPair) Address(env common.Environment) *Address {
|
||||
address := new(Address)
|
||||
|
||||
var prefix byte
|
||||
switch env {
|
||||
case common.Mainnet, common.Development:
|
||||
prefix = addressPrefixMainnet
|
||||
prefix = netPrefixStdAddrMainnet
|
||||
case common.Stagenet:
|
||||
prefix = addressPrefixStagenet
|
||||
prefix = netPrefixStdAddrStagenet
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled env %d", env))
|
||||
}
|
||||
@@ -86,17 +181,11 @@ func (kp *PublicKeyPair) AddressBytes(env common.Environment) []byte {
|
||||
// address encoding is:
|
||||
// (network_prefix) + (32-byte public spend key) + (32-byte-byte public view key)
|
||||
// + first_4_Bytes(Hash(network_prefix + (32-byte public spend key) + (32-byte public view key)))
|
||||
addr := append(append([]byte{prefix}, psk...), pvk...)
|
||||
checksum := getChecksum(addr)
|
||||
addrWithChecksum := append(addr, checksum[:4]...)
|
||||
if len(addrWithChecksum) != 69 { // 1 (prefix) + 32 (pub spend key) + 32 (pub view key) + 4 (checksum)
|
||||
panic(fmt.Sprintf("monero address %d instead of 69", len(addrWithChecksum)))
|
||||
}
|
||||
return addrWithChecksum
|
||||
}
|
||||
address.decoded[0] = prefix // 1-byte network prefix
|
||||
copy(address.decoded[1:33], kp.sk.Bytes()) // 32-byte public spend key
|
||||
copy(address.decoded[33:65], kp.vk.Bytes()) // 32-byte public view key
|
||||
checksum := getChecksum(address.decoded[0:65])
|
||||
copy(address.decoded[65:69], checksum[:])
|
||||
|
||||
// Address returns the base58-encoded address for a PublicKeyPair with the given environment
|
||||
// (ie. mainnet or stagenet)
|
||||
func (kp *PublicKeyPair) Address(env common.Environment) Address {
|
||||
return Address(MoneroAddrBytesToBase58(kp.AddressBytes(env)))
|
||||
return address
|
||||
}
|
||||
|
||||
36
crypto/monero/address_marshal.go
Normal file
36
crypto/monero/address_marshal.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package mcrypto
|
||||
|
||||
// MarshalText serializes the Monero Address type with some extra validation.
|
||||
func (a *Address) MarshalText() ([]byte, error) {
|
||||
if err := a.validateDecoded(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(addrBytesToBase58(a.decoded[:])), nil
|
||||
}
|
||||
|
||||
// UnmarshalText converts a base58 encoded monero address to our Address type.
|
||||
// The encoding, length and checksum are all validated, but not the network, as
|
||||
// it is unknown by the JSON parser. Empty strings are not allowed. Use an
|
||||
// address pointer in your serialized types if the Address is optional.
|
||||
func (a *Address) UnmarshalText(base58Input []byte) error {
|
||||
base58Str := string(base58Input)
|
||||
addrBytes, err := addrBase58ToBytes(base58Str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newAddr := new(Address)
|
||||
n := copy(newAddr.decoded[:], addrBytes)
|
||||
if n != addressBytesLen {
|
||||
// addrBase58ToBytes already verified the decoded length
|
||||
panic("bytes to address conversion is broken")
|
||||
}
|
||||
|
||||
if err := newAddr.validateDecoded(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// No more errors possible, overwrite the existing value
|
||||
*a = *newAddr
|
||||
return nil
|
||||
}
|
||||
85
crypto/monero/address_marshal_test.go
Normal file
85
crypto/monero/address_marshal_test.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package mcrypto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
)
|
||||
|
||||
func TestAddress_MarshalText_roundTrip(t *testing.T) {
|
||||
keys, err := GenerateKeys()
|
||||
require.NoError(t, err)
|
||||
addr := keys.PublicKeyPair().Address(common.Development)
|
||||
|
||||
type MyStruct struct {
|
||||
XMRAddress *Address `json:"xmrAddress"`
|
||||
}
|
||||
|
||||
s1 := &MyStruct{XMRAddress: addr}
|
||||
data, err := json.Marshal(s1)
|
||||
require.NoError(t, err)
|
||||
|
||||
s2 := new(MyStruct)
|
||||
err = json.Unmarshal(data, s2)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, s1.XMRAddress.Equal(s2.XMRAddress))
|
||||
}
|
||||
|
||||
func TestAddress_UnmarshalText(t *testing.T) {
|
||||
for _, test := range addressEncodingTests {
|
||||
address := new(Address)
|
||||
err := address.UnmarshalText([]byte(test.address))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.network, address.Network())
|
||||
require.Equal(t, test.addressType, address.Type())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddress_UnmarshalText_fail(t *testing.T) {
|
||||
address := new(Address) // non-initialized address should not marshal
|
||||
_, err := address.MarshalText()
|
||||
require.ErrorIs(t, err, errChecksumMismatch)
|
||||
}
|
||||
|
||||
func TestAddress_UnmarshalText_badChecksum(t *testing.T) {
|
||||
keys, err := GenerateKeys()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Generate a good address, then change the checksum to create
|
||||
// a new address with a bad checksum
|
||||
address := keys.PublicKeyPair().Address(common.Development)
|
||||
address.decoded[addressBytesLen-1]++ // overflow fine, 255 goes to 0
|
||||
badChecksumAddr := address.String()
|
||||
|
||||
err = address.UnmarshalText([]byte(badChecksumAddr))
|
||||
require.ErrorIs(t, err, errChecksumMismatch)
|
||||
}
|
||||
|
||||
func TestAddress_UnmarshalText_badNetworkPrefix(t *testing.T) {
|
||||
keys, err := GenerateKeys()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Generate a good address, then change the network prefix and adjust the
|
||||
// checksum to get an address that is otherwise good, except for the prefix.
|
||||
address := keys.PublicKeyPair().Address(common.Development)
|
||||
address.decoded[0] = 255
|
||||
checksum := getChecksum(address.decoded[0:65])
|
||||
copy(address.decoded[65:69], checksum[:])
|
||||
|
||||
badPrefixAddr := address.String()
|
||||
|
||||
err = address.UnmarshalText([]byte(badPrefixAddr))
|
||||
require.ErrorContains(t, err, "monero address has unknown network prefix 255")
|
||||
}
|
||||
|
||||
func TestAddress_UnmarshalText_integratedAddress(t *testing.T) {
|
||||
const integratedAddress = "4BxSHvcgTwu25WooY4BVmgdcKwZu5EksVZSZkDd6ooxSVVqQ4ubxXkhLF6hEqtw96i9cf3cVfLw8UWe95bdDKfRQeYtPwLm1Jiw7AKt2LY" //nolint:lll
|
||||
address := new(Address)
|
||||
err := address.UnmarshalText([]byte(integratedAddress))
|
||||
require.ErrorIs(t, err, errInvalidAddressLength)
|
||||
require.ErrorContains(t, err, "integrated addresses not supported")
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package mcrypto
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
@@ -10,23 +8,41 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewAddress(t *testing.T) {
|
||||
const addrStr = "42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm"
|
||||
addr, err := NewAddress(addrStr, common.Mainnet)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, addrStr, addr.String())
|
||||
}
|
||||
|
||||
func TestNewAddress_fail(t *testing.T) {
|
||||
_, err := NewAddress("fake", common.Mainnet)
|
||||
require.ErrorIs(t, err, errInvalidAddressLength)
|
||||
}
|
||||
|
||||
func TestValidateAddress(t *testing.T) {
|
||||
kp, err := GenerateKeys()
|
||||
require.NoError(t, err)
|
||||
addr := kp.Address(common.Mainnet)
|
||||
err = ValidateAddress(string(addr), common.Mainnet)
|
||||
require.NoError(t, err)
|
||||
err = ValidateAddress(string(addr), common.Stagenet)
|
||||
require.Equal(t, err, errInvalidPrefixGotMainnet)
|
||||
pubKeys := kp.PublicKeyPair()
|
||||
|
||||
addr = kp.Address(common.Stagenet)
|
||||
err = ValidateAddress(string(addr), common.Stagenet)
|
||||
require.NoError(t, err)
|
||||
err = ValidateAddress(string(addr), common.Mainnet)
|
||||
require.Equal(t, err, errInvalidPrefixGotStagenet)
|
||||
// mainnet address checks
|
||||
addr := pubKeys.Address(common.Mainnet)
|
||||
require.NoError(t, addr.ValidateEnv(common.Mainnet))
|
||||
require.ErrorIs(t, addr.ValidateEnv(common.Stagenet), errInvalidPrefixGotMainnet)
|
||||
|
||||
err = ValidateAddress("fake", common.Mainnet)
|
||||
require.True(t, errors.Is(err, errInvalidAddressLength))
|
||||
// stagenet address checks
|
||||
addr = pubKeys.Address(common.Stagenet)
|
||||
require.NoError(t, addr.ValidateEnv(common.Stagenet))
|
||||
require.ErrorIs(t, addr.ValidateEnv(common.Mainnet), errInvalidPrefixGotStagenet)
|
||||
|
||||
// testnet address check
|
||||
const testnetAddress = "9ujeXrjzf7bfeK3KZdCqnYaMwZVFuXemPU8Ubw335rj2FN1CdMiWNyFV3ksEfMFvRp9L9qum5UxkP5rN9aLcPxbH1au4WAB" //nolint:lll
|
||||
require.NoError(t, addr.UnmarshalText([]byte(testnetAddress)))
|
||||
require.ErrorIs(t, addr.ValidateEnv(common.Mainnet), errInvalidPrefixGotTestnet)
|
||||
|
||||
// uninitialized address validation
|
||||
addr = new(Address) // empty
|
||||
require.ErrorIs(t, addr.ValidateEnv(common.Development), errAddressNotInitialized)
|
||||
}
|
||||
|
||||
func TestValidateAddress_loop(t *testing.T) {
|
||||
@@ -34,9 +50,30 @@ func TestValidateAddress_loop(t *testing.T) {
|
||||
for i := 0; i < 1000; i++ {
|
||||
kp, err := GenerateKeys() // create random key
|
||||
require.NoError(t, err)
|
||||
addrHex := hex.EncodeToString(kp.AddressBytes(common.Mainnet))
|
||||
addr := kp.Address(common.Mainnet) // generates a base58 encoded address
|
||||
err = ValidateAddress(string(addr), common.Mainnet) // decodes base58 as part of validation
|
||||
require.NoError(t, err, addrHex) // save this hex address if the test fails!
|
||||
// Generate the address, convert it to its base58 string form,
|
||||
// then convert the base58 form back into a new address, then
|
||||
// verify that the bytes of the 2 addresses are identical.
|
||||
addr1 := kp.PublicKeyPair().Address(common.Mainnet)
|
||||
addr2, err := NewAddress(addr1.String(), common.Mainnet)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, addr1.String(), addr2.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddress_Equal(t *testing.T) {
|
||||
kp, err := GenerateKeys() // create random key
|
||||
require.NoError(t, err)
|
||||
pubKeys := kp.PublicKeyPair()
|
||||
|
||||
addr1 := pubKeys.Address(common.Mainnet)
|
||||
addr2 := pubKeys.Address(common.Mainnet)
|
||||
addr3 := pubKeys.Address(common.Stagenet)
|
||||
|
||||
require.False(t, addr1.Equal(nil))
|
||||
require.True(t, addr1.Equal(addr1)) // identity
|
||||
|
||||
require.False(t, addr1 == addr2) // the pointers are unique,
|
||||
require.True(t, addr1.Equal(addr2)) // but the values are the same
|
||||
|
||||
require.False(t, addr1.Equal(addr3)) // same keys, but different network
|
||||
}
|
||||
|
||||
@@ -1,31 +1,38 @@
|
||||
package mcrypto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil/base58"
|
||||
)
|
||||
|
||||
const (
|
||||
// AddressBytesLen is the length (69) of a Monero address in raw bytes:
|
||||
// addressBytesLen is the length (69) of a Monero address in raw bytes:
|
||||
// 1 - Network byte
|
||||
// 32 - Public spend key
|
||||
// 32 - Public view key
|
||||
// 4 - First 4 bytes of keccak-256 checksum of previous bytes
|
||||
AddressBytesLen = 1 + 32 + 32 + 4
|
||||
addressBytesLen = 1 + 32 + 32 + 4
|
||||
|
||||
// EncodedAddressLen is the length (95) of a base58 encoded Monero address:
|
||||
// encodedAddressLen is the length (95) of a base58 encoded Monero address:
|
||||
// 88 - Eight, 11-symbol base58 blocks each representing 8 binary bytes (64 binary bytes total)
|
||||
// 7 - Remaining base58 block representing 5 binary bytes
|
||||
EncodedAddressLen = 8*11 + 1*7
|
||||
encodedAddressLen = 8*11 + 1*7
|
||||
|
||||
// encodedIntegratedAddrLen is only for giving better error messages. We don't support
|
||||
// integrated addresses. In the byte form, they have an additional 8-byte payment ID
|
||||
// between the public view key and the checksum. The additional 8 bytes converts to
|
||||
// an additional 11 bytes in base58.
|
||||
encodedIntegratedAddrLen = encodedAddressLen + 11
|
||||
)
|
||||
|
||||
// MoneroAddrBytesToBase58 takes a 69-byte binary monero address (including the 4-byte
|
||||
// addrBytesToBase58 takes a 69-byte binary monero address (including the 4-byte
|
||||
// checksum) and returns it encoded using Monero's unique base58 algorithm. It is the
|
||||
// caller's responsibility to only pass 65 byte input slices.
|
||||
func MoneroAddrBytesToBase58(addrBytes []byte) string {
|
||||
if len(addrBytes) != AddressBytesLen {
|
||||
panic("MoneroAddrBytesToBase58 passed non-addrBytes value")
|
||||
func addrBytesToBase58(addrBytes []byte) string {
|
||||
if len(addrBytes) != addressBytesLen {
|
||||
panic("addrBytesToBase58 passed non-addrBytes value")
|
||||
}
|
||||
|
||||
var encodedAddr string
|
||||
@@ -33,7 +40,7 @@ func MoneroAddrBytesToBase58(addrBytes []byte) string {
|
||||
// Handle the first 64 binary bytes in 8 byte chunks yielding exactly 88 (8 * 11)
|
||||
// base58 characters.
|
||||
for i := 0; i < 8; i++ {
|
||||
// Encoded block will be 11 characters or fewer. If less, we pad to 11 characters.
|
||||
// Each encoded block will be 11 characters or fewer. If less, we pad to 11.
|
||||
block := base58.Encode(addrBytes[i*8 : i*8+8]) // yields 11 or fewer characters
|
||||
if len(block) < 11 {
|
||||
// Prepend "1"'s (zero in base58) as padding to get exactly 11 characters.
|
||||
@@ -59,14 +66,18 @@ func MoneroAddrBytesToBase58(addrBytes []byte) string {
|
||||
return encodedAddr
|
||||
}
|
||||
|
||||
// MoneroAddrBase58ToBytes decodes a monero base58 encoded address into a byte slice
|
||||
func MoneroAddrBase58ToBytes(encodedAddress string) ([]byte, error) {
|
||||
if len(encodedAddress) != EncodedAddressLen {
|
||||
// addrBase58ToBytes decodes a monero base58 encoded address into a byte slice.
|
||||
// Only decoding is done here, the checksum should be verified after this decoding.
|
||||
func addrBase58ToBytes(encodedAddress string) ([]byte, error) {
|
||||
if len(encodedAddress) != encodedAddressLen {
|
||||
err := errInvalidAddressLength
|
||||
if len(encodedAddress) == encodedIntegratedAddrLen {
|
||||
err = fmt.Errorf("integrated addresses not supported: %w", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]byte, 0, EncodedAddressLen)
|
||||
result := make([]byte, 0, addressBytesLen)
|
||||
|
||||
// Handle the first 88 bytes in 11-byte base58 chunks. Each 11 byte chunk converts to
|
||||
// 8 binary bytes.
|
||||
@@ -91,5 +102,9 @@ func MoneroAddrBase58ToBytes(encodedAddress string) ([]byte, error) {
|
||||
lastBlock = lastBlock[len(lastBlock)-5:] // strip any leading zeros
|
||||
result = append(result, lastBlock...)
|
||||
|
||||
if len(result) != addressBytesLen {
|
||||
panic("base58 address decoder is broken")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -13,59 +13,81 @@ import (
|
||||
// Hex values were computed here:
|
||||
// https://xmr.llcoins.net/addresstests.html
|
||||
var addressEncodingTests = []struct {
|
||||
name string // Address description
|
||||
addressHex string // Base16 encoded address
|
||||
address string // Base58 encoded address
|
||||
name string // Address description
|
||||
network Network // Mainnet, Stagenet, Testnet
|
||||
addressType AddressType // Standard, Integrated, Subaddress
|
||||
addressHex string // Base16 encoded address
|
||||
address string // Base58 encoded address
|
||||
}{
|
||||
{
|
||||
name: "mainnet primary (1)",
|
||||
addressHex: "121b3bd040020d3712ab84992b773d0a965134eb2df0392fb84af95de8a17be2ab231c9bf8341c6a870d92e3fb98063a90a355fb8dbf74a8561b9d7f9273247e9956e6f1b0", //nolint:lll
|
||||
address: "42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm",
|
||||
name: "mainnet primary (1)",
|
||||
network: Mainnet,
|
||||
addressType: Standard,
|
||||
addressHex: "121b3bd040020d3712ab84992b773d0a965134eb2df0392fb84af95de8a17be2ab231c9bf8341c6a870d92e3fb98063a90a355fb8dbf74a8561b9d7f9273247e9956e6f1b0", //nolint:lll
|
||||
address: "42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm",
|
||||
},
|
||||
{
|
||||
name: "mainnet primary (2)",
|
||||
addressHex: "1247335f3ceae62690c602dc20cdb6bd461dfb409f7322844e0092dbb4000c796cb8954f72ccc4bf16d93600d0cfba6be32def0ca114bf7147c20c42769bef4cfc605c9bed", //nolint:lll
|
||||
address: "44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW",
|
||||
name: "mainnet primary (2)",
|
||||
network: Mainnet,
|
||||
addressType: Standard,
|
||||
addressHex: "1247335f3ceae62690c602dc20cdb6bd461dfb409f7322844e0092dbb4000c796cb8954f72ccc4bf16d93600d0cfba6be32def0ca114bf7147c20c42769bef4cfc605c9bed", //nolint:lll
|
||||
address: "44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW",
|
||||
},
|
||||
{
|
||||
name: "testnet primary",
|
||||
addressHex: "3543ca04c0bac1fee7087d0779959c89c773e1d4d4a477f2a2316cb431018ee955dd951a02750dcaa7af680fd3fd148331cd980eda5e1d881d00bf1e35865f40052e237e80", //nolint:lll
|
||||
address: "9ujeXrjzf7bfeK3KZdCqnYaMwZVFuXemPU8Ubw335rj2FN1CdMiWNyFV3ksEfMFvRp9L9qum5UxkP5rN9aLcPxbH1au4WAB",
|
||||
name: "testnet primary",
|
||||
network: Testnet,
|
||||
addressType: Standard,
|
||||
addressHex: "3543ca04c0bac1fee7087d0779959c89c773e1d4d4a477f2a2316cb431018ee955dd951a02750dcaa7af680fd3fd148331cd980eda5e1d881d00bf1e35865f40052e237e80", //nolint:lll
|
||||
address: "9ujeXrjzf7bfeK3KZdCqnYaMwZVFuXemPU8Ubw335rj2FN1CdMiWNyFV3ksEfMFvRp9L9qum5UxkP5rN9aLcPxbH1au4WAB",
|
||||
},
|
||||
{
|
||||
name: "stagenet primary",
|
||||
addressHex: "18365f7c1aa6cc01def62e128fffd8e1035d64cea20211b5b85e313737f28e14941bbc4ca71a085b5bb8390ab800b53e81be2abab23f63740ef8a450804c6de96fe16e7338", //nolint:lll
|
||||
address: "53teqCAESLxeJ1REzGMAat1ZeHvuajvDiXqboEocPaDRRmqWoVPzy46GLo866qRFjbNhfkNckyhST3WEvBviDwpUDd7DSzB",
|
||||
name: "stagenet primary",
|
||||
network: Stagenet,
|
||||
addressType: Standard,
|
||||
addressHex: "18365f7c1aa6cc01def62e128fffd8e1035d64cea20211b5b85e313737f28e14941bbc4ca71a085b5bb8390ab800b53e81be2abab23f63740ef8a450804c6de96fe16e7338", //nolint:lll
|
||||
address: "53teqCAESLxeJ1REzGMAat1ZeHvuajvDiXqboEocPaDRRmqWoVPzy46GLo866qRFjbNhfkNckyhST3WEvBviDwpUDd7DSzB",
|
||||
},
|
||||
{
|
||||
name: "mainnet subaddress (1)",
|
||||
addressHex: "2ade13d5e57591933d61237e94bfca6b3fd239c9d53c5582a592eeeb8d8986c71b0d4d160c2c2ef02c3d0dd3b8646fbbef9dadc5d54002e69cb78b74ace989510210defc2a", //nolint:lll
|
||||
address: "8AsN91rznfkBGTY8psSNkJBg9SZgxxGGRUhGwRptBhgr5XSQ1XzmA9m8QAnoxydecSh5aLJXdrgXwTDMMZ1AuXsN1EX5Mtm",
|
||||
name: "mainnet subaddress (1)",
|
||||
network: Mainnet,
|
||||
addressType: Subaddress,
|
||||
addressHex: "2ade13d5e57591933d61237e94bfca6b3fd239c9d53c5582a592eeeb8d8986c71b0d4d160c2c2ef02c3d0dd3b8646fbbef9dadc5d54002e69cb78b74ace989510210defc2a", //nolint:lll
|
||||
address: "8AsN91rznfkBGTY8psSNkJBg9SZgxxGGRUhGwRptBhgr5XSQ1XzmA9m8QAnoxydecSh5aLJXdrgXwTDMMZ1AuXsN1EX5Mtm",
|
||||
},
|
||||
{
|
||||
name: "mainnet subaddress (2)",
|
||||
addressHex: "2a71521fb4561485775aa8e3b2398e8b7e9a6dcfb70da20abb6f8dae06d43a3ab28c3572ef0c82ae42b38586c7404e1587373aef49d6d94ef190e8feec603fd1dc7643061d", //nolint:lll
|
||||
address: "86kKnBKFqzCLxtK1Jmx2BkNBDBSMDEVaRYMMyVbeURYDWs8uNGDZURKCA5yRcyMxHzPcmCf1q2fSdhQVcaKsFrtGRsdGfNk",
|
||||
name: "mainnet subaddress (2)",
|
||||
network: Mainnet,
|
||||
addressType: Subaddress,
|
||||
addressHex: "2a71521fb4561485775aa8e3b2398e8b7e9a6dcfb70da20abb6f8dae06d43a3ab28c3572ef0c82ae42b38586c7404e1587373aef49d6d94ef190e8feec603fd1dc7643061d", //nolint:lll
|
||||
address: "86kKnBKFqzCLxtK1Jmx2BkNBDBSMDEVaRYMMyVbeURYDWs8uNGDZURKCA5yRcyMxHzPcmCf1q2fSdhQVcaKsFrtGRsdGfNk",
|
||||
},
|
||||
{
|
||||
name: "testnet subaddress (1)",
|
||||
addressHex: "3f87b78ea79e0d8542392dd48f5f7bf81ef674947434bb8035ee96b69947af9c61fa2829886abb638b9fdbc89f17b4e28be1c47dc2d89c7a5ab09cf4d0fa465ea4c6ec3a0b", //nolint:lll
|
||||
address: "BdKg9udkvckC5T58a8Nmtb6BNsgRAxs7uA2D49sWNNX5HPW5Us6Wxu8QMXrnSx3xPBQQ2iu9kwEcRGAoiz6EPmcZKbF62GS",
|
||||
name: "testnet subaddress (1)",
|
||||
network: Testnet,
|
||||
addressType: Subaddress,
|
||||
addressHex: "3f87b78ea79e0d8542392dd48f5f7bf81ef674947434bb8035ee96b69947af9c61fa2829886abb638b9fdbc89f17b4e28be1c47dc2d89c7a5ab09cf4d0fa465ea4c6ec3a0b", //nolint:lll
|
||||
address: "BdKg9udkvckC5T58a8Nmtb6BNsgRAxs7uA2D49sWNNX5HPW5Us6Wxu8QMXrnSx3xPBQQ2iu9kwEcRGAoiz6EPmcZKbF62GS",
|
||||
},
|
||||
{
|
||||
name: "testnet subaddress (2)",
|
||||
addressHex: "3f6b9ed65b32362dacaa7c48c8a7f82526d96f599897fefd7f04cb8b7fd4ea5e2658a1145569ef5bf0c949048a6ede19484f1221a2ba01df6e83e95741d2dd0fbc69e7a9ec", //nolint:lll
|
||||
address: "BcFvPa3fT4gVt5QyRDe5Vv7VtUFao9ci8NFEy3r254KF7R1N2cNB5FYhGvrHbMStv4D6VDzZ5xtxeKV8vgEPMnDcNFuwZb9",
|
||||
name: "testnet subaddress (2)",
|
||||
network: Testnet,
|
||||
addressType: Subaddress,
|
||||
addressHex: "3f6b9ed65b32362dacaa7c48c8a7f82526d96f599897fefd7f04cb8b7fd4ea5e2658a1145569ef5bf0c949048a6ede19484f1221a2ba01df6e83e95741d2dd0fbc69e7a9ec", //nolint:lll
|
||||
address: "BcFvPa3fT4gVt5QyRDe5Vv7VtUFao9ci8NFEy3r254KF7R1N2cNB5FYhGvrHbMStv4D6VDzZ5xtxeKV8vgEPMnDcNFuwZb9",
|
||||
},
|
||||
{
|
||||
name: "stagenet subaddress (1)",
|
||||
addressHex: "241ce1f6265ca2c855d32c7160ee0c648e108b11323d8edb1f808e4c7b4c6c7d8911e6a21cc33534e41f2592beafd78e1e7b30fe95c596dafdd869a7fd15f034c09cb4dcb9", //nolint:lll
|
||||
address: "73LhUiix4DVFMcKhsPRG51QmCsv8dYYbL6GcQoLwEEFvPvkVvc7BhebfA4pnEFF9Lq66hwvLqBvpHjTcqvpJMHmmNjPPBqa",
|
||||
name: "stagenet subaddress (1)",
|
||||
network: Stagenet,
|
||||
addressType: Subaddress,
|
||||
addressHex: "241ce1f6265ca2c855d32c7160ee0c648e108b11323d8edb1f808e4c7b4c6c7d8911e6a21cc33534e41f2592beafd78e1e7b30fe95c596dafdd869a7fd15f034c09cb4dcb9", //nolint:lll
|
||||
address: "73LhUiix4DVFMcKhsPRG51QmCsv8dYYbL6GcQoLwEEFvPvkVvc7BhebfA4pnEFF9Lq66hwvLqBvpHjTcqvpJMHmmNjPPBqa",
|
||||
},
|
||||
{
|
||||
name: "stagenet subaddress (2)",
|
||||
addressHex: "24ccc5703d9109e9c619bc427e9874f740ce43c25e5466e743e1cc4a6cf6d4908f3c79ff40b5b8fb281e7b379a652c36e0b74129684f43473be6cac960f124b9fe5d74bcfa", //nolint:lll
|
||||
address: "7A1Hr63MfgUa8pkWxueD5xBqhQczkusYiCMYMnJGcGmuQxa7aDBxN1G7iCuLCNB3VPeb2TW7U9FdxB27xKkWKfJ8VhUZthF",
|
||||
name: "stagenet subaddress (2)",
|
||||
network: Stagenet,
|
||||
addressType: Subaddress,
|
||||
addressHex: "24ccc5703d9109e9c619bc427e9874f740ce43c25e5466e743e1cc4a6cf6d4908f3c79ff40b5b8fb281e7b379a652c36e0b74129684f43473be6cac960f124b9fe5d74bcfa", //nolint:lll
|
||||
address: "7A1Hr63MfgUa8pkWxueD5xBqhQczkusYiCMYMnJGcGmuQxa7aDBxN1G7iCuLCNB3VPeb2TW7U9FdxB27xKkWKfJ8VhUZthF",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -73,14 +95,14 @@ func TestMoneroAddrBytesToBase58(t *testing.T) {
|
||||
for _, tt := range addressEncodingTests {
|
||||
addrBytes, err := hex.DecodeString(tt.addressHex)
|
||||
require.NoError(t, err, tt.name)
|
||||
addr := MoneroAddrBytesToBase58(addrBytes)
|
||||
addr := addrBytesToBase58(addrBytes)
|
||||
require.Equal(t, tt.address, addr, tt.name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMoneroAddrBase58ToBytes(t *testing.T) {
|
||||
for _, tt := range addressEncodingTests {
|
||||
addrBytes, err := MoneroAddrBase58ToBytes(tt.address)
|
||||
addrBytes, err := addrBase58ToBytes(tt.address)
|
||||
require.NoError(t, err, tt.name)
|
||||
addrHex := hex.EncodeToString(addrBytes)
|
||||
require.Equal(t, tt.addressHex, addrHex, tt.name)
|
||||
@@ -88,23 +110,23 @@ func TestMoneroAddrBase58ToBytes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMoneroAddrBase58ToBytes_BadLength(t *testing.T) {
|
||||
_, err := MoneroAddrBase58ToBytes("")
|
||||
_, err := addrBase58ToBytes("")
|
||||
require.ErrorIs(t, err, errInvalidAddressLength)
|
||||
}
|
||||
|
||||
func TestMoneroAddrBase58ToBytes_BadEncoding(t *testing.T) {
|
||||
_, err := MoneroAddrBase58ToBytes("")
|
||||
_, err := addrBase58ToBytes("")
|
||||
require.ErrorIs(t, err, errInvalidAddressLength)
|
||||
|
||||
// Different code handles invalid encoding in the last block, so we add a non-valid base58
|
||||
// character in both the first and last block
|
||||
validAddr := "42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm"
|
||||
firstBlockBad := "l" + validAddr[1:]
|
||||
lastBlockBad := validAddr[:EncodedAddressLen-1] + "l"
|
||||
lastBlockBad := validAddr[:encodedAddressLen-1] + "l"
|
||||
|
||||
_, err = MoneroAddrBase58ToBytes(firstBlockBad)
|
||||
_, err = addrBase58ToBytes(firstBlockBad)
|
||||
require.ErrorIs(t, err, errInvalidAddressEncoding)
|
||||
|
||||
_, err = MoneroAddrBase58ToBytes(lastBlockBad)
|
||||
_, err = addrBase58ToBytes(lastBlockBad)
|
||||
require.ErrorIs(t, err, errInvalidAddressEncoding)
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ func TestPrivateKeyPairToAddress(t *testing.T) {
|
||||
pvk, err := hex.DecodeString(pvkBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
// test MoneroAddrBase58ToBytes
|
||||
address := "49oFJna6jrkJYvmupQktXKXmhnktf1aCvUmwp8HJGvY7fdXpLMTVeqmZLWQLkyHXuU9Z8mZ78LordCmp3Nqx5T9GFdEGueB"
|
||||
addressBytes, err := MoneroAddrBase58ToBytes(address)
|
||||
// test addrBase58ToBytes
|
||||
addressStr := "49oFJna6jrkJYvmupQktXKXmhnktf1aCvUmwp8HJGvY7fdXpLMTVeqmZLWQLkyHXuU9Z8mZ78LordCmp3Nqx5T9GFdEGueB"
|
||||
addressBytes, err := addrBase58ToBytes(addressStr)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, psk, addressBytes[1:33])
|
||||
require.Equal(t, pvk, addressBytes[33:65])
|
||||
@@ -42,8 +42,10 @@ func TestPrivateKeyPairToAddress(t *testing.T) {
|
||||
// give the correct public keys
|
||||
kp, err := NewPrivateKeyPairFromBytes(sk, vk)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, addressBytes, kp.AddressBytes(common.Mainnet))
|
||||
require.Equal(t, Address(address), kp.Address(common.Mainnet))
|
||||
address := kp.PublicKeyPair().Address(common.Mainnet)
|
||||
|
||||
require.EqualValues(t, addressBytes, address.decoded[:])
|
||||
require.Equal(t, addressStr, address.String())
|
||||
|
||||
// check public key derivation
|
||||
require.Equal(t, "0x"+pskBytes, kp.sk.Public().String())
|
||||
|
||||
@@ -50,20 +50,20 @@ func TestRecoveryDB_ContractSwapInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
expectedStr := `{
|
||||
"start_number": 12345,
|
||||
"swap_id": "0x0102030400000000000000000000000000000000000000000000000000000000",
|
||||
"startNumber": 12345,
|
||||
"swapID": "0x0102030400000000000000000000000000000000000000000000000000000000",
|
||||
"swap": {
|
||||
"owner": "0xda9dfa130df4de4673b89022ee50ff26f6ea73cf",
|
||||
"claimer": "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8",
|
||||
"pub_key_claim": "0x5ab9467e70d4e98567991f0179d1f82a3096ed7973f7aff9ea50f649cafa88b9",
|
||||
"pub_key_refund": "0x4897bc3b9e02c2a8cd6353b9b29377157bf2694daaf52b59c0b42daa39877f14",
|
||||
"pubKeyClaim": "0x5ab9467e70d4e98567991f0179d1f82a3096ed7973f7aff9ea50f649cafa88b9",
|
||||
"pubKeyRefund": "0x4897bc3b9e02c2a8cd6353b9b29377157bf2694daaf52b59c0b42daa39877f14",
|
||||
"timeout0": 1672531200,
|
||||
"timeout1": 1672545600,
|
||||
"asset": "0x0000000000000000000000000000000000000000",
|
||||
"value": 9876,
|
||||
"nonce": 1234
|
||||
},
|
||||
"contract_address": "0xd2b5d6252d0645e4cf4bb547e82a485f527befb7"
|
||||
"contractAddress": "0xd2b5d6252d0645e4cf4bb547e82a485f527befb7"
|
||||
}`
|
||||
jsonData, err := vjson.MarshalStruct(si)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
type EthereumSwapInfo struct {
|
||||
// StartNumber the block number of the `newSwap` transaction.
|
||||
// The same for both maker/taker.
|
||||
StartNumber *big.Int `json:"start_number"`
|
||||
StartNumber *big.Int `json:"startNumber"`
|
||||
// SwapID is the swap ID used by the swap contract; not the same as the swap/offer ID
|
||||
// used by swapd.
|
||||
// It's the hash of the ABI encoded `contracts.SwapFactorySwap` struct.
|
||||
SwapID types.Hash `json:"swap_id"`
|
||||
SwapID types.Hash `json:"swapID"`
|
||||
// Swap is the `Swap` structure inside SwapFactory.sol.
|
||||
Swap *contracts.SwapFactorySwap `json:"swap"`
|
||||
ContractAddress ethcommon.Address `json:"contract_address"`
|
||||
ContractAddress ethcommon.Address `json:"contractAddress"`
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
type swap struct {
|
||||
Owner common.Address `json:"owner"`
|
||||
Claimer common.Address `json:"claimer"`
|
||||
PubKeyClaim types.Hash `json:"pub_key_claim"`
|
||||
PubKeyRefund types.Hash `json:"pub_key_refund"`
|
||||
PubKeyClaim types.Hash `json:"pubKeyClaim"`
|
||||
PubKeyRefund types.Hash `json:"pubKeyRefund"`
|
||||
Timeout0 *big.Int `json:"timeout0" validate:"required"`
|
||||
Timeout1 *big.Int `json:"timeout1" validate:"required"`
|
||||
Asset common.Address `json:"asset"`
|
||||
|
||||
@@ -29,8 +29,8 @@ func TestSwapFactorySwap_JSON(t *testing.T) {
|
||||
expectedJSON := `{
|
||||
"owner": "0xda9dfa130df4de4673b89022ee50ff26f6ea73cf",
|
||||
"claimer": "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8",
|
||||
"pub_key_claim": "0x5ab9467e70d4e98567991f0179d1f82a3096ed7973f7aff9ea50f649cafa88b9",
|
||||
"pub_key_refund": "0x4897bc3b9e02c2a8cd6353b9b29377157bf2694daaf52b59c0b42daa39877f14",
|
||||
"pubKeyClaim": "0x5ab9467e70d4e98567991f0179d1f82a3096ed7973f7aff9ea50f649cafa88b9",
|
||||
"pubKeyRefund": "0x4897bc3b9e02c2a8cd6353b9b29377157bf2694daaf52b59c0b42daa39877f14",
|
||||
"timeout0": 1672531200,
|
||||
"timeout1": 1672545600,
|
||||
"asset": "0xdac17f958d2ee523a2206206994597c13d831ec7",
|
||||
|
||||
@@ -42,18 +42,18 @@ const (
|
||||
type WalletClient interface {
|
||||
GetAccounts() (*wallet.GetAccountsResponse, error)
|
||||
GetAddress(idx uint64) (*wallet.GetAddressResponse, error)
|
||||
PrimaryAddress() mcrypto.Address
|
||||
PrimaryAddress() *mcrypto.Address
|
||||
GetBalance(idx uint64) (*wallet.GetBalanceResponse, error)
|
||||
Transfer(
|
||||
ctx context.Context,
|
||||
to mcrypto.Address,
|
||||
to *mcrypto.Address,
|
||||
accountIdx uint64,
|
||||
amount *coins.PiconeroAmount,
|
||||
numConfirmations uint64,
|
||||
) (*wallet.Transfer, error)
|
||||
SweepAll(
|
||||
ctx context.Context,
|
||||
to mcrypto.Address,
|
||||
to *mcrypto.Address,
|
||||
accountIdx uint64,
|
||||
numConfirmations uint64,
|
||||
) ([]*wallet.Transfer, error)
|
||||
@@ -129,7 +129,7 @@ type walletClient struct {
|
||||
wRPC wallet.Wallet // full monero-wallet-rpc API (larger than the WalletClient interface)
|
||||
dRPC monerodaemon.Daemon // full monerod RPC API
|
||||
endpoint string
|
||||
walletAddr mcrypto.Address
|
||||
walletAddr *mcrypto.Address
|
||||
conf *WalletClientConf
|
||||
rpcProcess *os.Process // monero-wallet-rpc process that we create
|
||||
}
|
||||
@@ -189,7 +189,13 @@ func NewWalletClient(conf *WalletClientConf) (WalletClient, error) {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
c.walletAddr = mcrypto.Address(acctResp.Address)
|
||||
|
||||
c.walletAddr, err = mcrypto.NewAddress(acctResp.Address, conf.Env)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.conf = conf
|
||||
return c, nil
|
||||
}
|
||||
@@ -267,7 +273,7 @@ func (c *walletClient) waitForReceipt(req *waitForReceiptRequest) (*wallet.Trans
|
||||
|
||||
func (c *walletClient) Transfer(
|
||||
ctx context.Context,
|
||||
to mcrypto.Address,
|
||||
to *mcrypto.Address,
|
||||
accountIdx uint64,
|
||||
amount *coins.PiconeroAmount,
|
||||
numConfirmations uint64,
|
||||
@@ -281,7 +287,7 @@ func (c *walletClient) Transfer(
|
||||
reqResp, err := c.wRPC.Transfer(&wallet.TransferRequest{
|
||||
Destinations: []wallet.Destination{{
|
||||
Amount: amt,
|
||||
Address: string(to),
|
||||
Address: to.String(),
|
||||
}},
|
||||
AccountIndex: accountIdx,
|
||||
})
|
||||
@@ -309,7 +315,7 @@ func (c *walletClient) Transfer(
|
||||
|
||||
func (c *walletClient) SweepAll(
|
||||
ctx context.Context,
|
||||
to mcrypto.Address,
|
||||
to *mcrypto.Address,
|
||||
accountIdx uint64,
|
||||
numConfirmations uint64,
|
||||
) ([]*wallet.Transfer, error) {
|
||||
@@ -336,7 +342,7 @@ func (c *walletClient) SweepAll(
|
||||
|
||||
reqResp, err := c.wRPC.SweepAll(&wallet.SweepAllRequest{
|
||||
AccountIndex: accountIdx,
|
||||
Address: string(to),
|
||||
Address: to.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sweep_all from %s failed: %w", from, err)
|
||||
@@ -386,7 +392,7 @@ func createWalletFromKeys(
|
||||
walletRestoreHeight uint64,
|
||||
privateSpendKey *mcrypto.PrivateSpendKey, // nil for a view-only wallet
|
||||
privateViewKey *mcrypto.PrivateViewKey,
|
||||
address mcrypto.Address,
|
||||
address *mcrypto.Address,
|
||||
) (WalletClient, error) {
|
||||
if conf.WalletPort == 0 { // swap wallets need randomized ports, so we expect this to be zero
|
||||
var err error
|
||||
@@ -431,8 +437,14 @@ func createWalletFromKeys(
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
c.walletAddr = mcrypto.Address(acctResp.Address)
|
||||
if c.walletAddr != address {
|
||||
|
||||
c.walletAddr, err = mcrypto.NewAddress(acctResp.Address, conf.Env)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !c.walletAddr.Equal(address) {
|
||||
c.Close()
|
||||
return nil, fmt.Errorf("provided address %s does not match monero-wallet-rpc computed address %s",
|
||||
address, c.walletAddr)
|
||||
@@ -462,7 +474,7 @@ func CreateSpendWalletFromKeys(
|
||||
) (WalletClient, error) {
|
||||
privateViewKey := privateKeyPair.ViewKey()
|
||||
privateSpendKey := privateKeyPair.SpendKey()
|
||||
address := privateKeyPair.Address(conf.Env)
|
||||
address := privateKeyPair.PublicKeyPair().Address(conf.Env)
|
||||
return createWalletFromKeys(conf, restoreHeight, privateSpendKey, privateViewKey, address)
|
||||
}
|
||||
|
||||
@@ -471,7 +483,7 @@ func CreateSpendWalletFromKeys(
|
||||
func CreateViewOnlyWalletFromKeys(
|
||||
conf *WalletClientConf,
|
||||
privateViewKey *mcrypto.PrivateViewKey,
|
||||
address mcrypto.Address,
|
||||
address *mcrypto.Address,
|
||||
restoreHeight uint64,
|
||||
) (WalletClient, error) {
|
||||
return createWalletFromKeys(conf, restoreHeight, nil, privateViewKey, address)
|
||||
@@ -480,7 +492,7 @@ func CreateViewOnlyWalletFromKeys(
|
||||
func (c *walletClient) generateFromKeys(
|
||||
sk *mcrypto.PrivateSpendKey,
|
||||
vk *mcrypto.PrivateViewKey,
|
||||
address mcrypto.Address,
|
||||
address *mcrypto.Address,
|
||||
restoreHeight uint64,
|
||||
filename,
|
||||
password string,
|
||||
@@ -497,7 +509,7 @@ func (c *walletClient) generateFromKeys(
|
||||
|
||||
res, err := c.wRPC.GenerateFromKeys(&wallet.GenerateFromKeysRequest{
|
||||
Filename: filename,
|
||||
Address: string(address),
|
||||
Address: address.String(),
|
||||
RestoreHeight: restoreHeight,
|
||||
Viewkey: vk.Hex(),
|
||||
Spendkey: spendKey,
|
||||
@@ -537,8 +549,8 @@ func (c *walletClient) CreateWallet(filename, password string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (c *walletClient) PrimaryAddress() mcrypto.Address {
|
||||
if c.walletAddr == "" {
|
||||
func (c *walletClient) PrimaryAddress() *mcrypto.Address {
|
||||
if c.walletAddr == nil {
|
||||
// Initialised in constructor function, so this shouldn't ever happen
|
||||
panic("primary wallet address was not initialised")
|
||||
}
|
||||
|
||||
@@ -139,7 +139,9 @@ func Test_walletClient_SweepAll_nothingToSweepReturnsError(t *testing.T) {
|
||||
|
||||
addrResp, err := takerWallet.GetAddress(0)
|
||||
require.NoError(t, err)
|
||||
destAddr := mcrypto.Address(addrResp.Address)
|
||||
|
||||
destAddr, err := mcrypto.NewAddress(addrResp.Address, common.Development)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = emptyWallet.SweepAll(context.Background(), destAddr, 0, SweepToSelfConfirmations)
|
||||
require.ErrorContains(t, err, "no balance to sweep")
|
||||
@@ -216,7 +218,7 @@ func TestCallGenerateFromKeys(t *testing.T) {
|
||||
err = c.(*walletClient).generateFromKeys(
|
||||
kp.SpendKey(),
|
||||
kp.ViewKey(),
|
||||
kp.Address(common.Mainnet),
|
||||
kp.PublicKeyPair().Address(common.Mainnet),
|
||||
height,
|
||||
"swap-deposit-wallet",
|
||||
"",
|
||||
@@ -257,13 +259,13 @@ func TestCallGenerateFromKeys_UnusualAddress(t *testing.T) {
|
||||
0,
|
||||
kp.SpendKey(),
|
||||
kp.ViewKey(),
|
||||
kp3.Address(common.Mainnet),
|
||||
kp3.PublicKeyPair().Address(common.Mainnet),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := c.GetAddress(0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(address), res.Address)
|
||||
require.Equal(t, address.String(), res.Address)
|
||||
}
|
||||
|
||||
func Test_getMoneroWalletRPCBin(t *testing.T) {
|
||||
@@ -326,7 +328,8 @@ func Test_walletClient_waitForConfirmations_contextCancelled(t *testing.T) {
|
||||
const numConfirmations = 999999999 // won't be achieved before our context is cancelled
|
||||
|
||||
minBal := coins.MoneroToPiconero(coins.StrToDecimal("10.01")) // add a little extra for fees
|
||||
destAddr := mcrypto.Address(blockRewardAddress)
|
||||
destAddr, err := mcrypto.NewAddress(blockRewardAddress, common.Development)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := CreateWalletClient(t)
|
||||
MineMinXMRBalance(t, c, minBal)
|
||||
@@ -335,7 +338,7 @@ func Test_walletClient_waitForConfirmations_contextCancelled(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := c.Transfer(ctx, destAddr, 0, coins.NewPiconeroAmount(amount), numConfirmations)
|
||||
_, err = c.Transfer(ctx, destAddr, 0, coins.NewPiconeroAmount(amount), numConfirmations)
|
||||
require.ErrorIs(t, err, context.DeadlineExceeded)
|
||||
}
|
||||
|
||||
@@ -359,5 +362,5 @@ func TestCreateWalletFromKeys(t *testing.T) {
|
||||
abCli, err := CreateSpendWalletFromKeys(conf, kp, height)
|
||||
require.NoError(t, err)
|
||||
defer abCli.CloseAndRemoveWallet()
|
||||
require.Equal(t, kp.Address(common.Development), abCli.PrimaryAddress())
|
||||
require.Equal(t, kp.PublicKeyPair().Address(common.Development).String(), abCli.PrimaryAddress().String())
|
||||
}
|
||||
|
||||
@@ -176,8 +176,8 @@ func (m *NotifyETHLocked) Type() byte {
|
||||
|
||||
// NotifyXMRLock is sent by XMRMaker to XMRTaker after locking his XMR.
|
||||
type NotifyXMRLock struct {
|
||||
Address string `json:"address" validate:"required"` // address the monero was sent to
|
||||
TxID types.Hash `json:"txID"` // Monero transaction ID (transaction hash in hex)
|
||||
Address *mcrypto.Address `json:"address" validate:"required"` // address the monero was sent to
|
||||
TxID types.Hash `json:"txID"` // Monero transaction ID (transaction hash in hex)
|
||||
}
|
||||
|
||||
// String ...
|
||||
|
||||
@@ -74,13 +74,13 @@ type Backend interface {
|
||||
Contract() *contracts.SwapFactory
|
||||
ContractAddr() ethcommon.Address
|
||||
SwapTimeout() time.Duration
|
||||
XMRDepositAddress(id *types.Hash) (mcrypto.Address, error)
|
||||
XMRDepositAddress(id *types.Hash) (*mcrypto.Address, error)
|
||||
|
||||
// setters
|
||||
SetSwapTimeout(timeout time.Duration)
|
||||
SetXMRDepositAddress(mcrypto.Address, types.Hash)
|
||||
SetXMRDepositAddress(*mcrypto.Address, types.Hash)
|
||||
ClearXMRDepositAddress(types.Hash)
|
||||
SetBaseXMRDepositAddress(mcrypto.Address)
|
||||
SetBaseXMRDepositAddress(*mcrypto.Address)
|
||||
|
||||
// relayer functions
|
||||
DiscoverRelayers() ([]peer.ID, error)
|
||||
@@ -100,7 +100,7 @@ type backend struct {
|
||||
// monero deposit address (used if xmrtaker has transferBack set to true)
|
||||
sync.RWMutex
|
||||
baseXMRDepositAddr *mcrypto.Address
|
||||
xmrDepositAddrs map[types.Hash]mcrypto.Address
|
||||
xmrDepositAddrs map[types.Hash]*mcrypto.Address
|
||||
|
||||
// swap contract
|
||||
contract *contracts.SwapFactory
|
||||
@@ -148,7 +148,7 @@ func NewBackend(cfg *Config) (Backend, error) {
|
||||
swapManager: cfg.SwapManager,
|
||||
swapTimeout: common.SwapTimeoutFromEnv(cfg.Environment),
|
||||
MessageSender: cfg.Net,
|
||||
xmrDepositAddrs: make(map[types.Hash]mcrypto.Address),
|
||||
xmrDepositAddrs: make(map[types.Hash]*mcrypto.Address),
|
||||
recoveryDB: cfg.RecoveryDB,
|
||||
rnet: cfg.RelayerHost,
|
||||
}, nil
|
||||
@@ -204,21 +204,21 @@ func (b *backend) SetSwapTimeout(timeout time.Duration) {
|
||||
b.swapTimeout = timeout
|
||||
}
|
||||
|
||||
func (b *backend) XMRDepositAddress(id *types.Hash) (mcrypto.Address, error) {
|
||||
func (b *backend) XMRDepositAddress(id *types.Hash) (*mcrypto.Address, error) {
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
if id == nil && b.baseXMRDepositAddr == nil {
|
||||
return "", errNoXMRDepositAddress
|
||||
return nil, errNoXMRDepositAddress
|
||||
} else if id == nil {
|
||||
return *b.baseXMRDepositAddr, nil
|
||||
return b.baseXMRDepositAddr, nil
|
||||
}
|
||||
|
||||
addr, has := b.xmrDepositAddrs[*id]
|
||||
if !has && b.baseXMRDepositAddr == nil {
|
||||
return "", errNoXMRDepositAddress
|
||||
return nil, errNoXMRDepositAddress
|
||||
} else if !has {
|
||||
return *b.baseXMRDepositAddr, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
@@ -228,11 +228,11 @@ func (b *backend) NewSwapFactory(addr ethcommon.Address) (*contracts.SwapFactory
|
||||
return contracts.NewSwapFactory(addr, b.ethClient.Raw())
|
||||
}
|
||||
|
||||
func (b *backend) SetBaseXMRDepositAddress(addr mcrypto.Address) {
|
||||
b.baseXMRDepositAddr = &addr
|
||||
func (b *backend) SetBaseXMRDepositAddress(addr *mcrypto.Address) {
|
||||
b.baseXMRDepositAddr = addr
|
||||
}
|
||||
|
||||
func (b *backend) SetXMRDepositAddress(addr mcrypto.Address, id types.Hash) {
|
||||
func (b *backend) SetXMRDepositAddress(addr *mcrypto.Address, id types.Hash) {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
b.xmrDepositAddrs[id] = addr
|
||||
|
||||
@@ -39,7 +39,7 @@ func ClaimMonero(
|
||||
xmrClient monero.WalletClient,
|
||||
walletScanHeight uint64,
|
||||
kpAB *mcrypto.PrivateKeyPair,
|
||||
depositAddr mcrypto.Address,
|
||||
depositAddr *mcrypto.Address,
|
||||
transferBack bool,
|
||||
) error {
|
||||
conf := xmrClient.CreateWalletConf(fmt.Sprintf("swap-wallet-claim-%s", id))
|
||||
@@ -60,7 +60,7 @@ func ClaimMonero(
|
||||
log.Infof("monero claimed in account %s; transferring to deposit account %s",
|
||||
address, depositAddr)
|
||||
|
||||
err = mcrypto.ValidateAddress(string(depositAddr), env)
|
||||
err = depositAddr.ValidateEnv(env)
|
||||
if err != nil {
|
||||
log.Errorf(
|
||||
"failed to transfer XMR out of swap wallet, dest address %s is invalid: %s",
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestClaimMonero_NoTransferBack(t *testing.T) {
|
||||
moneroCli,
|
||||
height,
|
||||
kp,
|
||||
"",
|
||||
nil, // deposit address can be nil, as transferBack is false
|
||||
false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -259,14 +259,21 @@ func (inst *Instance) GetOngoingSwapState(id types.Hash) common.SwapState {
|
||||
|
||||
// GetMoneroBalance returns the primary wallet address, and current balance of the user's monero
|
||||
// wallet.
|
||||
func (inst *Instance) GetMoneroBalance() (string, *wallet.GetBalanceResponse, error) {
|
||||
addr, err := inst.backend.XMRClient().GetAddress(0)
|
||||
func (inst *Instance) GetMoneroBalance() (*mcrypto.Address, *wallet.GetBalanceResponse, error) {
|
||||
addrResp, err := inst.backend.XMRClient().GetAddress(0)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
balance, err := inst.backend.XMRClient().GetBalance(0)
|
||||
|
||||
addr, err := mcrypto.NewAddress(addrResp.Address, inst.backend.Env())
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
return addr.Address, balance, nil
|
||||
|
||||
balanceResp, err := inst.backend.XMRClient().GetBalance(0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return addr, balanceResp, nil
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ func TestInstance_CompleteSwap(t *testing.T) {
|
||||
|
||||
addrRes, err := moneroCli.GetAddress(0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(address), addrRes.Address)
|
||||
require.Equal(t, address.String(), addrRes.Address)
|
||||
|
||||
err = inst.completeSwap(sinfo, kpOther.SpendKey())
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -536,7 +536,7 @@ func (s *swapState) lockFunds(amount *coins.PiconeroAmount) (*message.NotifyXMRL
|
||||
}
|
||||
|
||||
return &message.NotifyXMRLock{
|
||||
Address: string(swapDestAddr),
|
||||
Address: swapDestAddr,
|
||||
TxID: txID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -76,23 +76,23 @@ func (s *swapState) filterForClaim() (*mcrypto.PrivateSpendKey, error) {
|
||||
return sa, nil
|
||||
}
|
||||
|
||||
func (s *swapState) claimMonero(skB *mcrypto.PrivateSpendKey) (mcrypto.Address, error) {
|
||||
func (s *swapState) claimMonero(skB *mcrypto.PrivateSpendKey) (*mcrypto.Address, error) {
|
||||
if !s.info.Status.IsOngoing() {
|
||||
return "", errSwapCompleted
|
||||
return nil, errSwapCompleted
|
||||
}
|
||||
|
||||
// write counterparty swap privkey to disk in case something goes wrong
|
||||
err := s.Backend.RecoveryDB().PutCounterpartySwapPrivateKey(s.ID(), skB)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var depositAddr mcrypto.Address
|
||||
var depositAddr *mcrypto.Address
|
||||
if s.transferBack {
|
||||
id := s.ID()
|
||||
depositAddr, err = s.XMRDepositAddress(&id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ func (s *swapState) claimMonero(skB *mcrypto.PrivateSpendKey) (mcrypto.Address,
|
||||
s.transferBack,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
close(s.claimedCh)
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
pcommon "github.com/athanorlabs/atomic-swap/protocol"
|
||||
)
|
||||
|
||||
func lockXMRAndCheckForReadyLog(t *testing.T, s *swapState, xmrAddr mcrypto.Address) {
|
||||
func lockXMRAndCheckForReadyLog(t *testing.T, s *swapState, xmrAddr *mcrypto.Address) {
|
||||
// backend simulates the xmrmaker's instance
|
||||
backend := newBackend(t)
|
||||
monero.MineMinXMRBalance(t, backend.XMRClient(), coins.MoneroToPiconero(coins.StrToDecimal("1")))
|
||||
@@ -37,7 +37,7 @@ func lockXMRAndCheckForReadyLog(t *testing.T, s *swapState, xmrAddr mcrypto.Addr
|
||||
|
||||
// send notification that monero was locked
|
||||
lmsg := &message.NotifyXMRLock{
|
||||
Address: string(xmrAddr),
|
||||
Address: xmrAddr,
|
||||
TxID: txID,
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func TestSwapState_handleEvent_EventETHClaimed(t *testing.T) {
|
||||
|
||||
// test transferBack functionality
|
||||
s.transferBack = true
|
||||
s.Backend.SetBaseXMRDepositAddress(s.Backend.XMRClient().PrimaryAddress())
|
||||
s.Backend.SetXMRDepositAddress(s.Backend.XMRClient().PrimaryAddress(), s.ID())
|
||||
|
||||
// invalid SendKeysMessage should result in an error
|
||||
msg := &message.SendKeysMessage{}
|
||||
|
||||
@@ -183,20 +183,20 @@ func (s *swapState) runT0ExpirationHandler() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *swapState) expectedXMRLockAccount() (mcrypto.Address, *mcrypto.PrivateViewKey) {
|
||||
func (s *swapState) expectedXMRLockAccount() (*mcrypto.Address, *mcrypto.PrivateViewKey) {
|
||||
vk := mcrypto.SumPrivateViewKeys(s.xmrmakerPrivateViewKey, s.privkeys.ViewKey())
|
||||
sk := mcrypto.SumPublicKeys(s.xmrmakerPublicSpendKey, s.pubkeys.SpendKey())
|
||||
return mcrypto.NewPublicKeyPair(sk, vk.Public()).Address(s.Env()), vk
|
||||
}
|
||||
|
||||
func (s *swapState) handleNotifyXMRLock(msg *message.NotifyXMRLock) error {
|
||||
if msg.Address == "" {
|
||||
if msg.Address == nil {
|
||||
return errNoLockedXMRAddress
|
||||
}
|
||||
|
||||
// check that XMR was locked in expected account, and confirm amount
|
||||
lockedAddr, vk := s.expectedXMRLockAccount()
|
||||
if msg.Address != string(lockedAddr) {
|
||||
if !msg.Address.Equal(lockedAddr) {
|
||||
return fmt.Errorf("address received in message does not match expected address")
|
||||
}
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ func lockXMRFunds(
|
||||
t *testing.T,
|
||||
ctx context.Context, //nolint:revive
|
||||
wc monero.WalletClient,
|
||||
destAddr mcrypto.Address,
|
||||
destAddr *mcrypto.Address,
|
||||
amount *coins.PiconeroAmount,
|
||||
) types.Hash {
|
||||
monero.MineMinXMRBalance(t, wc, amount)
|
||||
@@ -273,7 +273,7 @@ func TestSwapState_NotifyXMRLock(t *testing.T) {
|
||||
xmrAddr := kp.Address(common.Development)
|
||||
|
||||
msg := &message.NotifyXMRLock{
|
||||
Address: string(xmrAddr),
|
||||
Address: xmrAddr,
|
||||
TxID: lockXMRFunds(t, s.ctx, s.XMRClient(), xmrAddr, s.expectedPiconeroAmount()),
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ func TestSwapState_NotifyXMRLock_Refund(t *testing.T) {
|
||||
xmrAddr := kp.Address(common.Development)
|
||||
|
||||
msg := &message.NotifyXMRLock{
|
||||
Address: string(xmrAddr),
|
||||
Address: xmrAddr,
|
||||
TxID: lockXMRFunds(t, s.ctx, s.XMRClient(), xmrAddr, s.expectedPiconeroAmount()),
|
||||
}
|
||||
|
||||
|
||||
@@ -46,19 +46,19 @@ func (*mockNet) ConnectedPeers() []string {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (*mockNet) Discover(provides string, searchTime time.Duration) ([]peer.ID, error) {
|
||||
func (*mockNet) Discover(_ string, _ time.Duration) ([]peer.ID, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockNet) Query(who peer.ID) (*message.QueryResponse, error) {
|
||||
func (*mockNet) Query(_ peer.ID) (*message.QueryResponse, error) {
|
||||
return &message.QueryResponse{Offers: []*types.Offer{{ID: testSwapID}}}, nil
|
||||
}
|
||||
|
||||
func (*mockNet) Initiate(who peer.AddrInfo, msg common.Message, s common.SwapStateNet) error {
|
||||
func (*mockNet) Initiate(_ peer.AddrInfo, _ common.Message, _ common.SwapStateNet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*mockNet) CloseProtocolStream(types.Hash) {
|
||||
func (*mockNet) CloseProtocolStream(_ types.Hash) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func (*mockSwapManager) GetPastIDs() ([]types.Hash, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (*mockSwapManager) GetPastSwap(id types.Hash) (*swap.Info, error) {
|
||||
func (*mockSwapManager) GetPastSwap(_ types.Hash) (*swap.Info, error) {
|
||||
return &swap.Info{}, nil
|
||||
}
|
||||
|
||||
@@ -98,11 +98,11 @@ func (*mockSwapManager) GetOngoingSwap(id types.Hash) (swap.Info, error) {
|
||||
), nil
|
||||
}
|
||||
|
||||
func (*mockSwapManager) AddSwap(*swap.Info) error {
|
||||
func (*mockSwapManager) AddSwap(_ *swap.Info) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (*mockSwapManager) CompleteOngoingSwap(*swap.Info) error {
|
||||
func (*mockSwapManager) CompleteOngoingSwap(_ *swap.Info) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
@@ -112,15 +112,15 @@ func (*mockXMRTaker) Provides() coins.ProvidesCoin {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (*mockXMRTaker) GetOngoingSwapState(types.Hash) common.SwapState {
|
||||
func (*mockXMRTaker) GetOngoingSwapState(_ types.Hash) common.SwapState {
|
||||
return new(mockSwapState)
|
||||
}
|
||||
|
||||
func (*mockXMRTaker) InitiateProtocol(providesAmount *apd.Decimal, _ *types.Offer) (common.SwapState, error) {
|
||||
func (*mockXMRTaker) InitiateProtocol(_ *apd.Decimal, _ *types.Offer) (common.SwapState, error) {
|
||||
return new(mockSwapState), nil
|
||||
}
|
||||
|
||||
func (*mockXMRTaker) Refund(types.Hash) (ethcommon.Hash, error) {
|
||||
func (*mockXMRTaker) Refund(_ types.Hash) (ethcommon.Hash, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
@@ -138,11 +138,11 @@ func (m *mockXMRMaker) Provides() coins.ProvidesCoin {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (m *mockXMRMaker) GetOngoingSwapState(hash types.Hash) common.SwapState {
|
||||
func (m *mockXMRMaker) GetOngoingSwapState(_ types.Hash) common.SwapState {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (*mockXMRMaker) MakeOffer(offer *types.Offer, _ string, _ *apd.Decimal) (*types.OfferExtra, error) {
|
||||
func (*mockXMRMaker) MakeOffer(_ *types.Offer, _ string, _ *apd.Decimal) (*types.OfferExtra, error) {
|
||||
offerExtra := &types.OfferExtra{
|
||||
StatusCh: make(chan types.Status, 1),
|
||||
}
|
||||
@@ -154,17 +154,17 @@ func (*mockXMRMaker) GetOffers() []*types.Offer {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (*mockXMRMaker) ClearOffers([]types.Hash) error {
|
||||
func (*mockXMRMaker) ClearOffers(_ []types.Hash) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (*mockXMRMaker) GetMoneroBalance() (string, *wallet.GetBalanceResponse, error) {
|
||||
func (*mockXMRMaker) GetMoneroBalance() (*mcrypto.Address, *wallet.GetBalanceResponse, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
type mockSwapState struct{}
|
||||
|
||||
func (*mockSwapState) HandleProtocolMessage(msg common.Message) error {
|
||||
func (*mockSwapState) HandleProtocolMessage(_ common.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ func (*mockProtocolBackend) Env() common.Environment {
|
||||
return common.Development
|
||||
}
|
||||
|
||||
func (*mockProtocolBackend) SetSwapTimeout(timeout time.Duration) {
|
||||
func (*mockProtocolBackend) SetSwapTimeout(_ time.Duration) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ func (b *mockProtocolBackend) SwapManager() swap.Manager {
|
||||
return b.sm
|
||||
}
|
||||
|
||||
func (*mockProtocolBackend) SetXMRDepositAddress(mcrypto.Address, types.Hash) {
|
||||
func (*mockProtocolBackend) SetXMRDepositAddress(*mcrypto.Address, types.Hash) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ func (s *PersonalService) Balances(_ *http.Request, _ *interface{}, resp *rpctyp
|
||||
PiconeroBalance: coins.NewPiconeroAmount(mBal.Balance),
|
||||
PiconeroUnlockedBalance: coins.NewPiconeroAmount(mBal.UnlockedBalance),
|
||||
BlocksToUnlock: mBal.BlocksToUnlock,
|
||||
EthAddress: s.pb.ETHClient().Address().String(),
|
||||
EthAddress: s.pb.ETHClient().Address(),
|
||||
WeiBalance: coins.NewWeiAmount(eBal),
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -165,7 +165,7 @@ type ProtocolBackend interface {
|
||||
SetSwapTimeout(timeout time.Duration)
|
||||
SwapTimeout() time.Duration
|
||||
SwapManager() swap.Manager
|
||||
SetXMRDepositAddress(mcrypto.Address, types.Hash)
|
||||
SetXMRDepositAddress(*mcrypto.Address, types.Hash)
|
||||
ClearXMRDepositAddress(types.Hash)
|
||||
ETHClient() extethclient.EthClient
|
||||
}
|
||||
@@ -184,7 +184,7 @@ type XMRMaker interface {
|
||||
MakeOffer(offer *types.Offer, relayerEndpoint string, relayerCommission *apd.Decimal) (*types.OfferExtra, error)
|
||||
GetOffers() []*types.Offer
|
||||
ClearOffers([]types.Hash) error
|
||||
GetMoneroBalance() (string, *wallet.GetBalanceResponse, error)
|
||||
GetMoneroBalance() (*mcrypto.Address, *wallet.GetBalanceResponse, error)
|
||||
}
|
||||
|
||||
// SwapManager ...
|
||||
|
||||
20
rpc/ws.go
20
rpc/ws.go
@@ -20,7 +20,7 @@ var upgrader = websocket.Upgrader{
|
||||
CheckOrigin: checkOriginFunc,
|
||||
}
|
||||
|
||||
func checkOriginFunc(r *http.Request) bool {
|
||||
func checkOriginFunc(_ *http.Request) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (s *wsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
defer conn.Close() //nolint:errcheck
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
for {
|
||||
_, message, err := conn.ReadMessage()
|
||||
@@ -150,20 +150,24 @@ func (s *wsServer) handleRequest(conn *websocket.Conn, req *rpctypes.Request) er
|
||||
}
|
||||
}
|
||||
|
||||
func (s *wsServer) handleSigner(ctx context.Context, conn *websocket.Conn, offerID types.Hash, ethAddress,
|
||||
xmrAddr string) error {
|
||||
|
||||
func (s *wsServer) handleSigner(
|
||||
ctx context.Context,
|
||||
conn *websocket.Conn,
|
||||
offerID types.Hash,
|
||||
ethAddress ethcommon.Address,
|
||||
xmrAddr *mcrypto.Address,
|
||||
) error {
|
||||
signer, err := s.taker.ExternalSender(offerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mcrypto.ValidateAddress(xmrAddr, s.backend.Env()); err != nil {
|
||||
if err = xmrAddr.ValidateEnv(s.backend.Env()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.backend.ETHClient().SetAddress(ethcommon.HexToAddress(ethAddress))
|
||||
s.backend.SetXMRDepositAddress(mcrypto.Address(xmrAddr), offerID)
|
||||
s.backend.ETHClient().SetAddress(ethAddress)
|
||||
s.backend.SetXMRDepositAddress(xmrAddr, offerID)
|
||||
defer s.backend.ClearXMRDepositAddress(offerID)
|
||||
|
||||
txsOutCh := signer.OngoingCh(offerID)
|
||||
|
||||
@@ -137,7 +137,7 @@ start-daemons() {
|
||||
|
||||
# Give time for Bob and Charlie's swapd instances to fully start
|
||||
# and do peer discovery.
|
||||
sleep 15
|
||||
sleep 20
|
||||
}
|
||||
|
||||
stop-daemons() {
|
||||
|
||||
@@ -94,7 +94,7 @@ func mineMinXMRMakerBalance(t *testing.T, minBalance *coins.PiconeroAmount) {
|
||||
}
|
||||
_, err = daemonCli.GenerateBlocks(&daemon.GenerateBlocksRequest{
|
||||
AmountOfBlocks: 32,
|
||||
WalletAddress: balances.MoneroAddress,
|
||||
WalletAddress: balances.MoneroAddress.String(),
|
||||
})
|
||||
if err != nil && err.Error() == "Block not accepted" {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user