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  }