-
Notifications
You must be signed in to change notification settings - Fork 651
/
output_owners.go
156 lines (134 loc) · 4.44 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
148
149
150
151
152
153
154
155
156
// Copyright (C) 2019-2022, 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")
errMarshal = errors.New("cannot marshal without ctx")
_ verify.State = (*OutputOwners)(nil)
)
type OutputOwners struct {
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 assigns the OutputOwners.ctx object to given [ctx] object
// Must be called at least once for MarshalJSON to work successfully
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 serialise OutputOwners struct
func (out *OutputOwners) Fields() (map[string]interface{}, error) {
addrsLen := len(out.Addrs)
// we need out.ctx to do this, if its absent, throw error
if addrsLen > 0 && out.ctx == nil {
return nil, errMarshal
}
addresses := make([]string, addrsLen)
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] {
set := set.NewSet[ids.ShortID](len(out.Addrs))
set.Add(out.Addrs...)
return set
}
// 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.IsSortedAndUniqueSortable(out.Addrs):
return errAddrsNotSortedUnique
default:
return nil
}
}
func (out *OutputOwners) VerifyState() error {
return out.Verify()
}
func (out *OutputOwners) Sort() {
utils.Sort(out.Addrs)
}
// formatAddress formats a given [addr] into human readable format using
// [ChainID] and [NetworkID] from the provided [ctx].
func formatAddress(ctx *snow.Context, addr ids.ShortID) (string, error) {
chainIDAlias, err := ctx.BCLookup.PrimaryAlias(ctx.ChainID)
if err != nil {
return "", err
}
hrp := constants.GetHRP(ctx.NetworkID)
return address.Format(chainIDAlias, hrp, addr.Bytes())
}