git.gammaspectra.live/P2Pool/consensus/v3@v3.8.0/monero/address/address.go (about)

     1  package address
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"git.gammaspectra.live/P2Pool/consensus/v3/monero"
     7  	"git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto"
     8  	base58 "git.gammaspectra.live/P2Pool/monero-base58"
     9  	"slices"
    10  )
    11  
    12  type Address struct {
    13  	SpendPub    crypto.PublicKeyBytes
    14  	ViewPub     crypto.PublicKeyBytes
    15  	Network     uint8
    16  	hasChecksum bool
    17  	checksum    Checksum
    18  }
    19  
    20  const ChecksumLength = 4
    21  
    22  type Checksum [ChecksumLength]byte
    23  
    24  func (a *Address) Compare(b Interface) int {
    25  	//compare spend key
    26  
    27  	resultSpendKey := crypto.CompareConsensusPublicKeyBytes(&a.SpendPub, b.SpendPublicKey())
    28  	if resultSpendKey != 0 {
    29  		return resultSpendKey
    30  	}
    31  
    32  	// compare view key
    33  	return crypto.CompareConsensusPublicKeyBytes(&a.ViewPub, b.ViewPublicKey())
    34  }
    35  
    36  func (a *Address) PublicKeys() (spend, view crypto.PublicKey) {
    37  	return &a.SpendPub, &a.ViewPub
    38  }
    39  
    40  func (a *Address) SpendPublicKey() *crypto.PublicKeyBytes {
    41  	return &a.SpendPub
    42  }
    43  
    44  func (a *Address) ViewPublicKey() *crypto.PublicKeyBytes {
    45  	return &a.ViewPub
    46  }
    47  
    48  func (a *Address) ToAddress(network uint8, err ...error) *Address {
    49  	if a.Network != network || (len(err) > 0 && err[0] != nil) {
    50  		return nil
    51  	}
    52  	return a
    53  }
    54  
    55  func (a *Address) ToPackedAddress() PackedAddress {
    56  	return NewPackedAddressFromBytes(a.SpendPub, a.ViewPub)
    57  }
    58  
    59  func FromBase58(address string) *Address {
    60  	preAllocatedBuf := make([]byte, 0, 69)
    61  	raw := base58.DecodeMoneroBase58PreAllocated(preAllocatedBuf, []byte(address))
    62  
    63  	if len(raw) != 69 {
    64  		return nil
    65  	}
    66  
    67  	switch raw[0] {
    68  	case monero.MainNetwork, monero.TestNetwork, monero.StageNetwork:
    69  		break
    70  	case monero.IntegratedMainNetwork, monero.IntegratedTestNetwork, monero.IntegratedStageNetwork:
    71  		return nil
    72  	case monero.SubAddressMainNetwork, monero.SubAddressTestNetwork, monero.SubAddressStageNetwork:
    73  		return nil
    74  	default:
    75  		return nil
    76  	}
    77  
    78  	checksum := crypto.PooledKeccak256(raw[:65])
    79  	a := &Address{
    80  		Network: raw[0],
    81  	}
    82  	copy(a.checksum[:], checksum[:ChecksumLength])
    83  	a.hasChecksum = true
    84  
    85  	if bytes.Compare(a.checksum[:], raw[65:]) != 0 {
    86  		return nil
    87  	}
    88  
    89  	copy(a.SpendPub[:], raw[1:33])
    90  	copy(a.ViewPub[:], raw[33:65])
    91  
    92  	return a
    93  }
    94  
    95  func FromBase58NoChecksumCheck(address []byte) *Address {
    96  	preAllocatedBuf := make([]byte, 0, 69)
    97  	raw := base58.DecodeMoneroBase58PreAllocated(preAllocatedBuf, address)
    98  
    99  	if len(raw) != 69 {
   100  		return nil
   101  	}
   102  
   103  	switch raw[0] {
   104  	case monero.MainNetwork, monero.TestNetwork, monero.StageNetwork:
   105  		break
   106  	case monero.IntegratedMainNetwork, monero.IntegratedTestNetwork, monero.IntegratedStageNetwork:
   107  		return nil
   108  	case monero.SubAddressMainNetwork, monero.SubAddressTestNetwork, monero.SubAddressStageNetwork:
   109  		return nil
   110  	default:
   111  		return nil
   112  	}
   113  
   114  	a := &Address{
   115  		Network: raw[0],
   116  	}
   117  	copy(a.checksum[:], slices.Clone(raw[65:]))
   118  	a.hasChecksum = true
   119  
   120  	copy(a.SpendPub[:], raw[1:33])
   121  	copy(a.ViewPub[:], raw[33:65])
   122  
   123  	return a
   124  }
   125  
   126  func FromRawAddress(network uint8, spend, view crypto.PublicKey) *Address {
   127  	var nice [69]byte
   128  	nice[0] = network
   129  	copy(nice[1:], spend.AsSlice())
   130  	copy(nice[33:], view.AsSlice())
   131  
   132  	//TODO: cache checksum?
   133  	checksum := crypto.PooledKeccak256(nice[:65])
   134  	a := &Address{
   135  		Network: nice[0],
   136  	}
   137  	copy(a.checksum[:], checksum[:ChecksumLength])
   138  	a.hasChecksum = true
   139  
   140  	a.SpendPub = spend.AsBytes()
   141  	a.ViewPub = view.AsBytes()
   142  
   143  	return a
   144  }
   145  
   146  func (a *Address) verifyChecksum() {
   147  	if !a.hasChecksum {
   148  		var nice [69]byte
   149  		nice[0] = a.Network
   150  		copy(nice[1:], a.SpendPub.AsSlice())
   151  		copy(nice[1+crypto.PublicKeySize:], a.ViewPub.AsSlice())
   152  		sum := crypto.PooledKeccak256(nice[:65])
   153  		//this race is ok
   154  		copy(a.checksum[:], sum[:ChecksumLength])
   155  		a.hasChecksum = true
   156  	}
   157  }
   158  
   159  func (a *Address) ToBase58() []byte {
   160  	a.verifyChecksum()
   161  	buf := make([]byte, 0, 95)
   162  	return base58.EncodeMoneroBase58PreAllocated(buf, []byte{a.Network}, a.SpendPub.AsSlice(), a.ViewPub.AsSlice(), a.checksum[:])
   163  }
   164  
   165  func (a *Address) MarshalJSON() ([]byte, error) {
   166  	a.verifyChecksum()
   167  	buf := make([]byte, 95+2)
   168  	buf[0] = '"'
   169  	base58.EncodeMoneroBase58PreAllocated(buf[1:1], []byte{a.Network}, a.SpendPub.AsSlice(), a.ViewPub.AsSlice(), a.checksum[:])
   170  	buf[len(buf)-1] = '"'
   171  	return buf, nil
   172  }
   173  
   174  func (a *Address) UnmarshalJSON(b []byte) error {
   175  	if len(b) < 2 {
   176  		return errors.New("unsupported length")
   177  	}
   178  
   179  	if addr := FromBase58NoChecksumCheck(b[1 : len(b)-1]); addr != nil {
   180  		a.Network = addr.Network
   181  		a.SpendPub = addr.SpendPub
   182  		a.ViewPub = addr.ViewPub
   183  		a.checksum = addr.checksum
   184  		a.hasChecksum = addr.hasChecksum
   185  		return nil
   186  	} else {
   187  		return errors.New("invalid address")
   188  	}
   189  }