github.com/mavryk-network/mvgo@v1.19.9/mavryk/address.go (about) 1 // Copyright (c) 2020-2022 Blockwatch Data Inc. 2 // Author: alex@blockwatch.cc 3 4 package mavryk 5 6 import ( 7 "errors" 8 "fmt" 9 "io" 10 "strings" 11 12 "github.com/mavryk-network/mvgo/base58" 13 ) 14 15 var ( 16 // ErrChecksumMismatch describes an error where decoding failed due 17 // to a bad checksum. 18 ErrChecksumMismatch = errors.New("tezos: checksum mismatch") 19 20 // ErrUnknownAddressType describes an error where an address can not 21 // decoded as a specific address type due to the string encoding 22 // begining with an identifier byte unknown to any standard or 23 // registered (via Register) network. 24 ErrUnknownAddressType = errors.New("tezos: unknown address type") 25 26 // InvalidAddress is an empty invalid address 27 InvalidAddress = NewAddress(AddressTypeInvalid, nil) 28 29 // ZeroAddress is a mv1 address with all bytes zero 30 ZeroAddress = NewAddress(AddressTypeEd25519, make([]byte, HashTypePkhEd25519.Len)) 31 ZeroContract = NewAddress(AddressTypeContract, make([]byte, HashTypePkhNocurve.Len)) 32 33 // Burn Address 34 BurnAddress = MustParseAddress("mv2burnburnburnburnburnburnbur7hzNeg") 35 ) 36 37 const MAX_ADDRESS_LEN = 37 // tx rollup address 38 39 // AddressType represents the type of a Tezos signature. 40 type AddressType byte 41 42 // addressTypeData is an internal type used to store address related config 43 // options in a single place 44 type addressTypeData struct { 45 Id byte 46 Tag byte 47 Name string 48 HashType HashType 49 KeyType KeyType 50 } 51 52 const ( 53 AddressTypeInvalid AddressType = iota // 0 54 AddressTypeEd25519 // 1 55 AddressTypeSecp256k1 // 2 56 AddressTypeP256 // 3 57 AddressTypeContract // 4 58 AddressTypeBlinded // 5 59 AddressTypeBls12_381 // 6 60 AddressTypeTxRollup // 7 61 AddressTypeSmartRollup // 8 62 ) 63 64 var ( 65 addressTypes = []addressTypeData{ 66 {0, 255, "invalid", HashTypeInvalid, KeyTypeInvalid}, 67 {1, 0, "ed25519", HashTypePkhEd25519, KeyTypeEd25519}, 68 {2, 1, "secp256k1", HashTypePkhSecp256k1, KeyTypeSecp256k1}, 69 {3, 2, "p256", HashTypePkhP256, KeyTypeP256}, 70 {4, 255, "contract", HashTypePkhNocurve, KeyTypeInvalid}, 71 {5, 3, "blinded", HashTypePkhBlinded, KeyTypeInvalid}, 72 {6, 4, "bls12_381", HashTypePkhBls12_381, KeyTypeBls12_381}, 73 {7, 255, "tx_rollup", HashTypeTxRollupAddress, KeyTypeInvalid}, 74 {8, 255, "smart_rollup", HashTypeSmartRollupAddress, KeyTypeInvalid}, 75 } 76 77 addressTags = []AddressType{ 78 AddressTypeEd25519, // 0 79 AddressTypeSecp256k1, // 1 80 AddressTypeP256, // 2 81 AddressTypeBlinded, // 3 82 AddressTypeBls12_381, // 4 83 } 84 ) 85 86 func ParseAddressType(s string) AddressType { 87 for _, v := range addressTypes { 88 if s == v.HashType.B58Prefix || s == v.Name { 89 return AddressType(v.Id) 90 } 91 } 92 return AddressTypeInvalid 93 } 94 95 func (t AddressType) IsValid() bool { 96 return t != AddressTypeInvalid 97 } 98 99 func (t AddressType) String() string { 100 return addressTypes[int(t)].Name 101 } 102 103 func (t AddressType) Prefix() string { 104 return addressTypes[int(t)].HashType.B58Prefix 105 } 106 107 func (t AddressType) Tag() byte { 108 return addressTypes[int(t)].Tag 109 } 110 111 func (t AddressType) HashType() HashType { 112 return addressTypes[int(t)].HashType 113 } 114 115 func (t AddressType) KeyType() KeyType { 116 return addressTypes[int(t)].KeyType 117 } 118 119 func (t AddressType) asByte() byte { 120 return byte(t) 121 } 122 123 func parseAddressTag(b byte) byte { 124 t := AddressTypeInvalid 125 if int(b) < len(addressTags) { 126 t = addressTags[int(b)] 127 } 128 return t.asByte() 129 } 130 131 func (t *AddressType) UnmarshalText(data []byte) error { 132 typ := ParseAddressType(string(data)) 133 if !typ.IsValid() { 134 return ErrUnknownAddressType 135 } 136 *t = typ 137 return nil 138 } 139 140 func (t AddressType) MarshalText() ([]byte, error) { 141 return []byte(t.String()), nil 142 } 143 144 func HasAddressPrefix(s string) bool { 145 for _, typ := range addressTypes[1:] { 146 // ED25519_PUBLIC_KEY_HASH_PREFIX, // mv1 147 // SECP256K1_PUBLIC_KEY_HASH_PREFIX, // mv2 148 // P256_PUBLIC_KEY_HASH_PREFIX, // mv3 149 // NOCURVE_PUBLIC_KEY_HASH_PREFIX, // KT1 150 // BLINDED_PUBLIC_KEY_HASH_PREFIX, // bmv1 151 // BLS12_381_PUBLIC_KEY_HASH_PREFIX, // mv4 152 // TX_ROLLUP_ADDRESS_PREFIX, // txr1 153 // SMART_ROLLUP_ADDRESS_PREFIX, // sr1 154 if strings.HasPrefix(s, typ.HashType.B58Prefix) { 155 return true 156 } 157 } 158 return false 159 } 160 161 func DetectAddressType(s string) AddressType { 162 for _, typ := range addressTypes[1:] { 163 if strings.HasPrefix(s, typ.HashType.B58Prefix) { 164 return AddressType(typ.Id) 165 } 166 } 167 return AddressTypeInvalid 168 } 169 170 // Address represents a typed tezos address 171 type Address [21]byte 172 173 func NewAddress(typ AddressType, hash []byte) (a Address) { 174 isValid := typ.HashType().Len == len(hash) 175 a[0] = typ.asByte() * byte(b2i(isValid)) 176 copy(a[1:], hash) 177 return 178 } 179 180 func (a Address) Type() AddressType { 181 return AddressType(a[0]) 182 } 183 184 func (a Address) Hash() []byte { 185 return a[1:] 186 } 187 188 func (a Address) KeyType() KeyType { 189 return AddressType(a[0]).KeyType() 190 } 191 192 func (a Address) IsValid() bool { 193 return a.Type() != AddressTypeInvalid 194 } 195 196 func (a Address) IsEOA() bool { 197 return a.Type().KeyType().IsValid() 198 } 199 200 func (a Address) IsContract() bool { 201 return a.Type() == AddressTypeContract 202 } 203 204 func (a Address) IsRollup() bool { 205 return a.Type() == AddressTypeSmartRollup || a.Type() == AddressTypeTxRollup 206 } 207 208 func (a Address) Equal(b Address) bool { 209 return a == b 210 } 211 212 func (a Address) Clone() (b Address) { 213 copy(b[:], a[:]) 214 return 215 } 216 217 // String returns the string encoding of the address. 218 func (a Address) String() string { 219 return EncodeAddress(a.Type(), a[1:]) 220 } 221 222 func (a *Address) UnmarshalText(data []byte) error { 223 if len(data) > MAX_ADDRESS_LEN { 224 data = data[:MAX_ADDRESS_LEN] 225 } 226 astr, _, _ := strings.Cut(string(data), "%") 227 addr, err := ParseAddress(astr) 228 if err != nil { 229 return err 230 } 231 *a = addr 232 return nil 233 } 234 235 func (a Address) MarshalText() ([]byte, error) { 236 return []byte(a.String()), nil 237 } 238 239 // Bytes returns the 21 (implicit) or 22 byte (contract) tagged and optionally padded 240 // binary hash value of the address. 241 // func (a Address) Bytes() []byte { 242 func (a Address) Encode() []byte { 243 var buf [22]byte 244 switch a.Type() { 245 case AddressTypeInvalid: 246 return nil 247 case AddressTypeContract: 248 buf[0] = 1 249 copy(buf[1:], a[1:]) 250 case AddressTypeTxRollup: 251 buf[0] = 2 252 copy(buf[1:], a[1:]) 253 case AddressTypeSmartRollup: 254 buf[0] = 3 255 copy(buf[1:], a[1:]) 256 default: 257 // 21 byte version for implicit addresses 258 buf[0] = a.Type().Tag() 259 copy(buf[1:], a[1:]) 260 return buf[:21] 261 } 262 return buf[:] 263 } 264 265 // Bytes22 returns the 22 byte tagged and padded binary encoding for contracts 266 // and EOAs (mv1/2/3). In contrast to Bytes which outputs the 21 byte address for EOAs 267 // here we add a leading 0-byte. 268 // func (a Address) Bytes22() []byte { 269 func (a Address) EncodePadded() []byte { 270 var buf [22]byte 271 switch a.Type() { 272 case AddressTypeInvalid: 273 return nil 274 case AddressTypeContract: 275 buf[0] = 1 276 copy(buf[1:], a[1:]) 277 case AddressTypeTxRollup: 278 buf[0] = 2 279 copy(buf[1:], a[1:]) 280 case AddressTypeSmartRollup: 281 buf[0] = 3 282 copy(buf[1:], a[1:]) 283 default: 284 buf[1] = a.Type().Tag() 285 copy(buf[2:], a[1:]) 286 } 287 return buf[:] 288 } 289 290 // MarshalBinary outputs the 21 byte MvGo version of an address containing 291 // a one byte type tag and the 20 byte address hash. 292 func (a Address) MarshalBinary() ([]byte, error) { 293 return a[:], nil 294 } 295 296 // UnmarshalBinary reads the 21 byte MvGo version of an address containing 297 // a one byte type tag and the 20 byte address hash. 298 func (a *Address) UnmarshalBinary(b []byte) error { 299 if len(b) != 21 { 300 return io.ErrShortBuffer 301 } 302 copy(a[:], b) 303 return nil 304 } 305 306 // Decode reads a 21 byte or 22 byte address versions and is 307 // resilient to longer byte strings that contain extra padding or a suffix 308 // (e.g. an entrypoint suffix as found in smart contract data). 309 func (a *Address) Decode(b []byte) error { 310 a[0] = 0 311 switch { 312 case len(b) >= 22 && b[0] <= 3: 313 switch b[0] { 314 case 0: 315 a[0] = parseAddressTag(b[1]) 316 copy(a[1:], b[2:22]) 317 case 1: 318 a[0] = AddressTypeContract.asByte() 319 copy(a[1:], b[1:21]) 320 case 2: 321 a[0] = AddressTypeTxRollup.asByte() 322 copy(a[1:], b[1:21]) 323 case 3: 324 a[0] = AddressTypeSmartRollup.asByte() 325 copy(a[1:], b[1:21]) 326 default: 327 return fmt.Errorf("tezos: invalid binary address prefix %x", b[0]) 328 } 329 case len(b) >= 21: 330 a[0] = parseAddressTag(b[0]) 331 copy(a[1:], b[1:21]) 332 default: 333 return fmt.Errorf("tezos: invalid binary address length %d", len(b)) 334 } 335 if !a.IsValid() { 336 return ErrUnknownAddressType 337 } 338 return nil 339 } 340 341 // IsAddressBytes checks whether a buffer likely contains a binary encoded address. 342 func IsAddressBytes(b []byte) bool { 343 if len(b) < 21 { 344 return false 345 } 346 switch { 347 case len(b) == 22 && (b[0] <= 3): 348 return true 349 case len(b) == 21: 350 return parseAddressTag(b[0]) != AddressTypeInvalid.asByte() 351 default: 352 return false 353 } 354 } 355 356 // ContractAddress returns the string encoding of the address when used 357 // as originated contract. 358 func (a Address) ContractAddress() string { 359 return EncodeAddress(AddressTypeContract, a[1:]) 360 } 361 362 // TxRollupAddress returns the string encoding of the address when used 363 // as rollup contract. 364 func (a Address) TxRollupAddress() string { 365 return EncodeAddress(AddressTypeTxRollup, a[1:]) 366 } 367 368 // SmartRollupAddress returns the string encoding of the address when used 369 // as smart rollup contract. 370 func (a Address) SmartRollupAddress() string { 371 return EncodeAddress(AddressTypeSmartRollup, a[1:]) 372 } 373 374 // Set implements the flags.Value interface for use in command line argument parsing. 375 func (a *Address) Set(addr string) (err error) { 376 *a, err = ParseAddress(addr) 377 return 378 } 379 380 func MustParseAddress(addr string) Address { 381 a, err := ParseAddress(addr) 382 if err != nil { 383 panic(err) 384 } 385 return a 386 } 387 388 func ParseAddress(addr string) (a Address, err error) { 389 // accept empty strings, but return an invalid address 390 if len(addr) == 0 { 391 return 392 } 393 if len(addr) > MAX_ADDRESS_LEN { 394 err = fmt.Errorf("tezos: invalid base58 address length") 395 return 396 } 397 typ := DetectAddressType(addr) 398 if !typ.IsValid() { 399 err = fmt.Errorf("tezos: unknown address type for %q", addr) 400 return 401 } 402 ht := typ.HashType() 403 ibuf := bufPool32.Get() 404 dec, _, err2 := base58.CheckDecode(addr, len(ht.Id), ibuf.([]byte)) 405 if err2 != nil { 406 bufPool32.Put(ibuf) 407 if err2 == base58.ErrChecksum { 408 err = ErrChecksumMismatch 409 return 410 } 411 err = fmt.Errorf("tezos: invalid %s address: %w", typ, err2) 412 return 413 } 414 a[0] = typ.asByte() 415 copy(a[1:], dec) 416 bufPool32.Put(ibuf) 417 return 418 } 419 420 func EncodeAddress(typ AddressType, hash []byte) string { 421 if typ == AddressTypeInvalid { 422 return "" 423 } 424 return base58.CheckEncode(hash, typ.HashType().Id) 425 }