dedicated monero address type (#327)

Adds custom type for monero addresses with better validations.
This commit is contained in:
Dmitry Holodov
2023-02-25 11:18:16 -06:00
committed by GitHub
parent d913658446
commit 1d7c00a8ed
33 changed files with 569 additions and 247 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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 ...

View File

@@ -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
}

View 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
}

View 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")
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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())

View File

@@ -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)

View File

@@ -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"`
}

View File

@@ -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"`

View File

@@ -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",

View File

@@ -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")
}

View File

@@ -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())
}

View File

@@ -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 ...

View File

@@ -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

View File

@@ -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",

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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)

View File

@@ -536,7 +536,7 @@ func (s *swapState) lockFunds(amount *coins.PiconeroAmount) (*message.NotifyXMRL
}
return &message.NotifyXMRLock{
Address: string(swapDestAddr),
Address: swapDestAddr,
TxID: txID,
}, nil
}

View File

@@ -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)

View File

@@ -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{}

View File

@@ -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")
}

View File

@@ -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()),
}

View File

@@ -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")
}

View File

@@ -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

View File

@@ -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 ...

View File

@@ -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)

View File

@@ -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() {

View File

@@ -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