github.com/ava-labs/avalanchego@v1.11.11/vms/secp256k1fx/output_owners.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package secp256k1fx 5 6 import ( 7 "encoding/json" 8 "errors" 9 10 "github.com/ava-labs/avalanchego/ids" 11 "github.com/ava-labs/avalanchego/snow" 12 "github.com/ava-labs/avalanchego/utils" 13 "github.com/ava-labs/avalanchego/utils/constants" 14 "github.com/ava-labs/avalanchego/utils/formatting/address" 15 "github.com/ava-labs/avalanchego/utils/set" 16 "github.com/ava-labs/avalanchego/vms/components/verify" 17 ) 18 19 var ( 20 ErrNilOutput = errors.New("nil output") 21 ErrOutputUnspendable = errors.New("output is unspendable") 22 ErrOutputUnoptimized = errors.New("output representation should be optimized") 23 ErrAddrsNotSortedUnique = errors.New("addresses not sorted and unique") 24 ) 25 26 type OutputOwners struct { 27 verify.IsNotState `json:"-"` 28 29 Locktime uint64 `serialize:"true" json:"locktime"` 30 Threshold uint32 `serialize:"true" json:"threshold"` 31 Addrs []ids.ShortID `serialize:"true" json:"addresses"` 32 33 // ctx is used in MarshalJSON to convert Addrs into human readable 34 // format with ChainID and NetworkID. Unexported because we don't use 35 // it outside this object. 36 ctx *snow.Context 37 } 38 39 // InitCtx allows addresses to be formatted into their human readable format 40 // during json marshalling. 41 func (out *OutputOwners) InitCtx(ctx *snow.Context) { 42 out.ctx = ctx 43 } 44 45 // MarshalJSON marshals OutputOwners as JSON with human readable addresses. 46 // OutputOwners.InitCtx must be called before marshalling this or one of 47 // the parent objects to json. Uses the OutputOwners.ctx method to format 48 // the addresses. Returns errMarshal error if OutputOwners.ctx is not set. 49 func (out *OutputOwners) MarshalJSON() ([]byte, error) { 50 result, err := out.Fields() 51 if err != nil { 52 return nil, err 53 } 54 55 return json.Marshal(result) 56 } 57 58 // Fields returns JSON keys in a map that can be used with marshal JSON 59 // to serialize OutputOwners struct 60 func (out *OutputOwners) Fields() (map[string]interface{}, error) { 61 addresses := make([]string, len(out.Addrs)) 62 for i, addr := range out.Addrs { 63 // for each [addr] in [Addrs] we attempt to format it given 64 // the [out.ctx] object 65 fAddr, err := formatAddress(out.ctx, addr) 66 if err != nil { 67 // we expect these addresses to be valid, return error 68 // if they are not 69 return nil, err 70 } 71 addresses[i] = fAddr 72 } 73 result := map[string]interface{}{ 74 "locktime": out.Locktime, 75 "threshold": out.Threshold, 76 "addresses": addresses, 77 } 78 79 return result, nil 80 } 81 82 // Addresses returns the addresses that manage this output 83 func (out *OutputOwners) Addresses() [][]byte { 84 addrs := make([][]byte, len(out.Addrs)) 85 for i, addr := range out.Addrs { 86 addrs[i] = addr.Bytes() 87 } 88 return addrs 89 } 90 91 // AddressesSet returns addresses as a set 92 func (out *OutputOwners) AddressesSet() set.Set[ids.ShortID] { 93 return set.Of(out.Addrs...) 94 } 95 96 // Equals returns true if the provided owners create the same condition 97 func (out *OutputOwners) Equals(other *OutputOwners) bool { 98 if out == other { 99 return true 100 } 101 if out == nil || other == nil || out.Locktime != other.Locktime || out.Threshold != other.Threshold || len(out.Addrs) != len(other.Addrs) { 102 return false 103 } 104 for i, addr := range out.Addrs { 105 otherAddr := other.Addrs[i] 106 if addr != otherAddr { 107 return false 108 } 109 } 110 return true 111 } 112 113 func (out *OutputOwners) Verify() error { 114 switch { 115 case out == nil: 116 return ErrNilOutput 117 case out.Threshold > uint32(len(out.Addrs)): 118 return ErrOutputUnspendable 119 case out.Threshold == 0 && len(out.Addrs) > 0: 120 return ErrOutputUnoptimized 121 case !utils.IsSortedAndUnique(out.Addrs): 122 return ErrAddrsNotSortedUnique 123 default: 124 return nil 125 } 126 } 127 128 func (out *OutputOwners) Sort() { 129 utils.Sort(out.Addrs) 130 } 131 132 // formatAddress formats a given [addr] into human readable format using 133 // [ChainID] and [NetworkID] if a non-nil [ctx] is provided. If [ctx] is not 134 // provided, the address will be returned in cb58 format. 135 func formatAddress(ctx *snow.Context, addr ids.ShortID) (string, error) { 136 if ctx == nil { 137 return addr.String(), nil 138 } 139 140 chainIDAlias, err := ctx.BCLookup.PrimaryAlias(ctx.ChainID) 141 if err != nil { 142 return "", err 143 } 144 145 hrp := constants.GetHRP(ctx.NetworkID) 146 return address.Format(chainIDAlias, hrp, addr.Bytes()) 147 }