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 }