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  }