-
Notifications
You must be signed in to change notification settings - Fork 95
/
input_context.go
144 lines (121 loc) · 4.82 KB
/
input_context.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
package iotago
import (
"fmt"
"sort"
"github.com/iotaledger/hive.go/constraints"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/serializer/v2"
)
// ContextInputType defines the type of context inputs.
type ContextInputType byte
const (
// ContextInputCommitment is a type of input which references a commitment.
ContextInputCommitment ContextInputType = iota
// ContextInputBlockIssuanceCredit is a type of input which references the block issuance credit from a specific account and commitment, the latter being provided by a commitment input.
ContextInputBlockIssuanceCredit
// ContextInputReward is a type of input which references an Account or Delegation Input for which to claim rewards.
ContextInputReward
)
func (inputType ContextInputType) String() string {
if int(inputType) >= len(contextInputNames) {
return fmt.Sprintf("unknown input type: %d", inputType)
}
return contextInputNames[inputType]
}
var contextInputNames = [ContextInputReward + 1]string{"CommitmentInput", "BlockIssuanceCreditInput", "RewardInput"}
// ContextInput provides an additional contextual input for transaction validation.
type ContextInput interface {
Sizer
constraints.Cloneable[ContextInput]
constraints.Comparable[ContextInput]
ProcessableObject
// Type returns the type of ContextInput.
Type() ContextInputType
}
// ContextInputs is a slice of ContextInput.
type ContextInputs[T ContextInput] []T
func (in ContextInputs[T]) Clone() ContextInputs[T] {
cpy := make(ContextInputs[T], len(in))
for idx, input := range in {
//nolint:forcetypeassert // we can safely assume that this is of type T
cpy[idx] = input.Clone().(T)
}
return cpy
}
func (in ContextInputs[T]) WorkScore(workScoreParameters *WorkScoreParameters) (WorkScore, error) {
var workScoreContextInputs WorkScore
for _, input := range in {
workScoreInput, err := input.WorkScore(workScoreParameters)
if err != nil {
return 0, err
}
workScoreContextInputs, err = workScoreContextInputs.Add(workScoreInput)
if err != nil {
return 0, err
}
}
return workScoreContextInputs, nil
}
func (in ContextInputs[T]) Size() int {
sum := serializer.UInt16ByteSize
for _, i := range in {
sum += i.Size()
}
return sum
}
// Sort sorts the Context Inputs in lexical order.
func (in ContextInputs[T]) Sort() {
sort.Slice(in, func(i, j int) bool {
return in[i].Compare(in[j]) < 0
})
}
// ContextInputsRewardInputMaxIndex returns a ElementValidationFunc
// which checks that every Reward Input references an index <= max inputs count.
func ContextInputsRewardInputMaxIndex(inputsCount uint16) ElementValidationFunc[ContextInput] {
return func(index int, input ContextInput) error {
switch castInput := input.(type) {
case *CommitmentInput, *BlockIssuanceCreditInput:
case *RewardInput:
utxoIndex := castInput.Index
if utxoIndex >= inputsCount {
return ierrors.WithMessagef(ErrInputRewardIndexExceedsMaxInputsCount, "reward input %d references index %d which is equal or greater than the inputs count %d",
index, utxoIndex, inputsCount)
}
default:
// We're switching on the Go context input type here, so we can only run into the default case
// if we added a new context input type and have not handled it above or a user constructed a type
// implementing the interface (only possible when iota.go is used as a library).
// In both cases we want to panic.
panic("all supported context input types should be handled above")
}
return nil
}
}
// ContextInputsCommitmentInputRequirement returns an ElementValidationFunc which
// checks that a Commitment Input is present if a BIC or Reward Input is present.
func ContextInputsCommitmentInputRequirement() ElementValidationFunc[ContextInput] {
// Once we see the first BIC or Reward Input and there was no Commitment Input before, then due to lexical ordering
// a commitment input cannot appear later, so we can error immediately.
seenCommitmentInput := false
return func(index int, input ContextInput) error {
switch input.(type) {
case *CommitmentInput:
seenCommitmentInput = true
case *BlockIssuanceCreditInput:
if !seenCommitmentInput {
return ierrors.WithMessagef(ErrCommitmentInputMissing, "block issuance credit input at index %d requires a commitment input", index)
}
case *RewardInput:
if !seenCommitmentInput {
return ierrors.WithMessagef(ErrCommitmentInputMissing, "reward input at index %d requires a commitment input", index)
}
default:
// We're switching on the Go context input type here, so we can only run into the default case
// if we added a new context input type and have not handled it above or a user constructed a type
// implementing the interface (only possible when iota.go is used as a library).
// In both cases we want to panic.
panic("all supported context input types should be handled above")
}
return nil
}
}