-
Notifications
You must be signed in to change notification settings - Fork 667
/
standard_block.go
169 lines (150 loc) · 3.98 KB
/
standard_block.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
157
158
159
160
161
162
163
164
165
166
167
168
169
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package platformvm
import (
"fmt"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow/choices"
)
var (
_ Block = &StandardBlock{}
_ decision = &StandardBlock{}
)
// StandardBlock being accepted results in the transactions contained in the
// block to be accepted and committed to the chain.
type StandardBlock struct {
SingleDecisionBlock `serialize:"true"`
Txs []*Tx `serialize:"true" json:"txs"`
}
func (sb *StandardBlock) initialize(vm *VM, bytes []byte, status choices.Status, blk Block) error {
if err := sb.SingleDecisionBlock.initialize(vm, bytes, status, blk); err != nil {
return fmt.Errorf("failed to initialize: %w", err)
}
for _, tx := range sb.Txs {
if err := tx.Sign(vm.codec, nil); err != nil {
return fmt.Errorf("failed to sign block: %w", err)
}
}
return nil
}
// Verify this block performs a valid state transition.
//
// The parent block must be a proposal
//
// This function also sets onAcceptDB database if the verification passes.
func (sb *StandardBlock) Verify() error {
blkID := sb.ID()
if err := sb.SingleDecisionBlock.Verify(); err != nil {
if err := sb.Reject(); err != nil {
sb.vm.ctx.Log.Error(
"failed to reject standard block %s due to %s",
blkID,
err,
)
}
return err
}
parentIntf, err := sb.parent()
if err != nil {
return err
}
// StandardBlock is not a modifier on a proposal block, so its parent must
// be a decision.
parent, ok := parentIntf.(decision)
if !ok {
if err := sb.Reject(); err != nil {
sb.vm.ctx.Log.Error(
"failed to reject standard block %s due to %s",
blkID,
err,
)
}
return errInvalidBlockType
}
parentState := parent.onAccept()
sb.onAcceptState = newVersionedState(
parentState,
parentState.CurrentStakerChainState(),
parentState.PendingStakerChainState(),
)
funcs := make([]func() error, 0, len(sb.Txs))
for _, tx := range sb.Txs {
utx, ok := tx.UnsignedTx.(UnsignedDecisionTx)
if !ok {
return errWrongTxType
}
txID := tx.ID()
onAccept, err := utx.SemanticVerify(sb.vm, sb.onAcceptState, tx)
if err != nil {
sb.vm.droppedTxCache.Put(txID, err.Error()) // cache tx as dropped
if err := sb.Reject(); err != nil {
sb.vm.ctx.Log.Error(
"failed to reject standard block %s due to %s",
blkID,
err,
)
}
return err
}
sb.onAcceptState.AddTx(tx, Committed)
if onAccept != nil {
funcs = append(funcs, onAccept)
}
}
if numFuncs := len(funcs); numFuncs == 1 {
sb.onAcceptFunc = funcs[0]
} else if numFuncs > 1 {
sb.onAcceptFunc = func() error {
for _, f := range funcs {
if err := f(); err != nil {
return fmt.Errorf("failed to execute onAcceptFunc: %w", err)
}
}
return nil
}
}
sb.vm.currentBlocks[blkID] = sb
parentIntf.addChild(sb)
return nil
}
func (sb *StandardBlock) Reject() error {
sb.vm.ctx.Log.Verbo(
"Rejecting Standard Block %s at height %d with parent %s",
sb.ID(),
sb.Height(),
sb.ParentID(),
)
for _, tx := range sb.Txs {
if err := sb.vm.mempool.IssueTx(tx); err != nil {
sb.vm.ctx.Log.Debug(
"failed to reissue tx %q due to: %s",
tx.ID(),
err,
)
}
}
return sb.SingleDecisionBlock.Reject()
}
// newStandardBlock returns a new *StandardBlock where the block's parent, a
// decision block, has ID [parentID].
func (vm *VM) newStandardBlock(parentID ids.ID, height uint64, txs []*Tx) (*StandardBlock, error) {
sb := &StandardBlock{
SingleDecisionBlock: SingleDecisionBlock{
CommonDecisionBlock: CommonDecisionBlock{
CommonBlock: CommonBlock{
PrntID: parentID,
Hght: height,
},
},
},
Txs: txs,
}
// We serialize this block as a Block so that it can be deserialized into a
// Block
blk := Block(sb)
bytes, err := vm.codec.Marshal(codecVersion, &blk)
if err != nil {
return nil, fmt.Errorf("failed to marshal block: %w", err)
}
return sb, sb.SingleDecisionBlock.initialize(vm, bytes, choices.Processing, sb)
}