consensus/monero/address/address.go
DataHoarder b7b1344eea
All checks were successful
continuous-integration/drone/push Build is passing
Fix address.MarshalJSON
2023-07-23 01:10:43 +02:00

184 lines
4.3 KiB
Go

package address
import (
"bytes"
"errors"
"git.gammaspectra.live/P2Pool/moneroutil"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
"slices"
)
type Address struct {
SpendPub crypto.PublicKeyBytes
ViewPub crypto.PublicKeyBytes
Network uint8
hasChecksum bool
checksum moneroutil.Checksum
}
func (a *Address) Compare(b Interface) int {
//compare spend key
resultSpendKey := crypto.CompareConsensusPublicKeyBytes(&a.SpendPub, b.SpendPublicKey())
if resultSpendKey != 0 {
return resultSpendKey
}
// compare view key
return crypto.CompareConsensusPublicKeyBytes(&a.ViewPub, b.ViewPublicKey())
}
func (a *Address) PublicKeys() (spend, view crypto.PublicKey) {
return &a.SpendPub, &a.ViewPub
}
func (a *Address) SpendPublicKey() *crypto.PublicKeyBytes {
return &a.SpendPub
}
func (a *Address) ViewPublicKey() *crypto.PublicKeyBytes {
return &a.ViewPub
}
func (a *Address) ToAddress(network uint8, err ...error) *Address {
if a.Network != network || (len(err) > 0 && err[0] != nil) {
return nil
}
return a
}
func (a *Address) ToPackedAddress() PackedAddress {
return NewPackedAddressFromBytes(a.SpendPub, a.ViewPub)
}
func FromBase58(address string) *Address {
preAllocatedBuf := make([]byte, 0, 69)
raw := moneroutil.DecodeMoneroBase58PreAllocated(preAllocatedBuf, []byte(address))
if len(raw) != 69 {
return nil
}
switch raw[0] {
case moneroutil.MainNetwork, moneroutil.TestNetwork, moneroutil.StageNetwork:
break
case moneroutil.IntegratedMainNetwork, moneroutil.IntegratedTestNetwork, moneroutil.IntegratedStageNetwork:
return nil
case moneroutil.SubAddressMainNetwork, moneroutil.SubAddressTestNetwork, moneroutil.SubAddressStageNetwork:
return nil
default:
return nil
}
checksum := moneroutil.GetChecksum(raw[:65])
if bytes.Compare(checksum[:], raw[65:]) != 0 {
return nil
}
a := &Address{
Network: raw[0],
checksum: checksum,
hasChecksum: true,
}
copy(a.SpendPub[:], raw[1:33])
copy(a.ViewPub[:], raw[33:65])
return a
}
func FromBase58NoChecksumCheck(address []byte) *Address {
preAllocatedBuf := make([]byte, 0, 69)
raw := moneroutil.DecodeMoneroBase58PreAllocated(preAllocatedBuf, address)
if len(raw) != 69 {
return nil
}
switch raw[0] {
case moneroutil.MainNetwork, moneroutil.TestNetwork, moneroutil.StageNetwork:
break
case moneroutil.IntegratedMainNetwork, moneroutil.IntegratedTestNetwork, moneroutil.IntegratedStageNetwork:
return nil
case moneroutil.SubAddressMainNetwork, moneroutil.SubAddressTestNetwork, moneroutil.SubAddressStageNetwork:
return nil
default:
return nil
}
a := &Address{
Network: raw[0],
}
copy(a.checksum[:], slices.Clone(raw[65:]))
a.hasChecksum = true
copy(a.SpendPub[:], raw[1:33])
copy(a.ViewPub[:], raw[33:65])
return a
}
func FromRawAddress(network uint8, spend, view crypto.PublicKey) *Address {
var nice [69]byte
nice[0] = network
copy(nice[1:], spend.AsSlice())
copy(nice[33:], view.AsSlice())
//TODO: cache checksum?
checksum := crypto.PooledKeccak256(nice[:65])
a := &Address{
Network: nice[0],
}
copy(a.checksum[:], checksum[:4])
a.hasChecksum = true
a.SpendPub = spend.AsBytes()
a.ViewPub = view.AsBytes()
return a
}
func (a *Address) verifyChecksum() {
if !a.hasChecksum {
var nice [69]byte
nice[0] = a.Network
copy(nice[1:], a.SpendPub.AsSlice())
copy(nice[1+crypto.PublicKeySize:], a.ViewPub.AsSlice())
sum := crypto.PooledKeccak256(nice[:65])
//this race is ok
copy(a.checksum[:], sum[:4])
a.hasChecksum = true
}
}
func (a *Address) ToBase58() []byte {
a.verifyChecksum()
buf := make([]byte, 0, 95)
return moneroutil.EncodeMoneroBase58PreAllocated(buf, []byte{a.Network}, a.SpendPub.AsSlice(), a.ViewPub.AsSlice(), a.checksum[:])
}
func (a *Address) MarshalJSON() ([]byte, error) {
a.verifyChecksum()
buf := make([]byte, 95+2)
buf[0] = '"'
moneroutil.EncodeMoneroBase58PreAllocated(buf[1:1], []byte{a.Network}, a.SpendPub.AsSlice(), a.ViewPub.AsSlice(), a.checksum[:])
buf[len(buf)-1] = '"'
return buf, nil
}
func (a *Address) UnmarshalJSON(b []byte) error {
if len(b) < 2 {
return errors.New("unsupported length")
}
if addr := FromBase58NoChecksumCheck(b[1 : len(b)-1]); addr != nil {
a.Network = addr.Network
a.SpendPub = addr.SpendPub
a.ViewPub = addr.ViewPub
a.checksum = addr.checksum
a.hasChecksum = addr.hasChecksum
return nil
} else {
return errors.New("invalid address")
}
}