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