forked from ava-labs/avalanchego
-
Notifications
You must be signed in to change notification settings - Fork 4
/
tx.go
133 lines (112 loc) · 2.87 KB
/
tx.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
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package avm
import (
"context"
"errors"
"fmt"
"go.uber.org/zap"
"github.com/MetalBlockchain/metalgo/database"
"github.com/MetalBlockchain/metalgo/ids"
"github.com/MetalBlockchain/metalgo/snow/choices"
"github.com/MetalBlockchain/metalgo/snow/consensus/snowstorm"
"github.com/MetalBlockchain/metalgo/utils/set"
"github.com/MetalBlockchain/metalgo/vms/avm/txs"
"github.com/MetalBlockchain/metalgo/vms/avm/txs/executor"
)
var (
_ snowstorm.Tx = (*Tx)(nil)
errTxNotProcessing = errors.New("transaction is not processing")
errUnexpectedReject = errors.New("attempting to reject transaction")
)
type Tx struct {
vm *VM
tx *txs.Tx
}
func (tx *Tx) ID() ids.ID {
return tx.tx.ID()
}
func (tx *Tx) Accept(context.Context) error {
if s := tx.Status(); s != choices.Processing {
return fmt.Errorf("%w: %s", errTxNotProcessing, s)
}
if err := tx.vm.onAccept(tx.tx); err != nil {
return err
}
executor := &executor.Executor{
Codec: tx.vm.txBackend.Codec,
State: tx.vm.state,
Tx: tx.tx,
}
err := tx.tx.Unsigned.Visit(executor)
if err != nil {
return fmt.Errorf("error staging accepted state changes: %w", err)
}
tx.vm.state.AddTx(tx.tx)
commitBatch, err := tx.vm.state.CommitBatch()
if err != nil {
txID := tx.tx.ID()
return fmt.Errorf("couldn't create commitBatch while processing tx %s: %w", txID, err)
}
defer tx.vm.state.Abort()
err = tx.vm.ctx.SharedMemory.Apply(
executor.AtomicRequests,
commitBatch,
)
if err != nil {
txID := tx.tx.ID()
return fmt.Errorf("error committing accepted state changes while processing tx %s: %w", txID, err)
}
return tx.vm.metrics.MarkTxAccepted(tx.tx)
}
func (*Tx) Reject(context.Context) error {
return errUnexpectedReject
}
func (tx *Tx) Status() choices.Status {
txID := tx.tx.ID()
_, err := tx.vm.state.GetTx(txID)
switch err {
case nil:
return choices.Accepted
case database.ErrNotFound:
return choices.Processing
default:
tx.vm.ctx.Log.Error("failed looking up tx status",
zap.Stringer("txID", txID),
zap.Error(err),
)
return choices.Processing
}
}
func (tx *Tx) MissingDependencies() (set.Set[ids.ID], error) {
txIDs := set.Set[ids.ID]{}
for _, in := range tx.tx.Unsigned.InputUTXOs() {
if in.Symbolic() {
continue
}
txID, _ := in.InputSource()
_, err := tx.vm.state.GetTx(txID)
switch err {
case nil:
// Tx was already accepted
case database.ErrNotFound:
txIDs.Add(txID)
default:
return nil, err
}
}
return txIDs, nil
}
func (tx *Tx) Bytes() []byte {
return tx.tx.Bytes()
}
func (tx *Tx) Verify(context.Context) error {
if s := tx.Status(); s != choices.Processing {
return fmt.Errorf("%w: %s", errTxNotProcessing, s)
}
return tx.tx.Unsigned.Visit(&executor.SemanticVerifier{
Backend: tx.vm.txBackend,
State: tx.vm.state,
Tx: tx.tx,
})
}