-
Notifications
You must be signed in to change notification settings - Fork 175
/
engine.go
145 lines (120 loc) · 3.85 KB
/
engine.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
package scripts
import (
"context"
"encoding/hex"
"fmt"
"github.com/rs/zerolog"
"github.com/onflow/flow-go/engine"
"github.com/onflow/flow-go/engine/execution"
"github.com/onflow/flow-go/engine/execution/computation/query"
"github.com/onflow/flow-go/engine/execution/state"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/state/protocol"
)
var ErrStateCommitmentPruned = fmt.Errorf("state commitment not found")
type Engine struct {
unit *engine.Unit
log zerolog.Logger
state protocol.State
queryExecutor query.Executor
execState state.ScriptExecutionState
}
var _ execution.ScriptExecutor = (*Engine)(nil)
func New(
logger zerolog.Logger,
state protocol.State,
queryExecutor query.Executor,
execState state.ScriptExecutionState,
) *Engine {
return &Engine{
unit: engine.NewUnit(),
log: logger.With().Str("engine", "scripts").Logger(),
state: state,
execState: execState,
queryExecutor: queryExecutor,
}
}
func (e *Engine) Ready() <-chan struct{} {
return e.unit.Ready()
}
func (e *Engine) Done() <-chan struct{} {
return e.unit.Done()
}
func (e *Engine) ExecuteScriptAtBlockID(
ctx context.Context,
script []byte,
arguments [][]byte,
blockID flow.Identifier,
) ([]byte, error) {
stateCommit, err := e.execState.StateCommitmentByBlockID(ctx, blockID)
if err != nil {
return nil, fmt.Errorf("failed to get state commitment for block (%s): %w", blockID, err)
}
// return early if state with the given state commitment is not in memory
// and already purged. This reduces allocations for scripts targeting old blocks.
if !e.execState.HasState(stateCommit) {
return nil, fmt.Errorf(
"failed to execute script at block (%s): %w (%s). "+
"this error usually happens if the reference "+
"block for this script is not set to a recent block.",
blockID.String(),
ErrStateCommitmentPruned,
hex.EncodeToString(stateCommit[:]),
)
}
header, err := e.state.AtBlockID(blockID).Head()
if err != nil {
return nil, fmt.Errorf("failed to get header (%s): %w", blockID, err)
}
blockSnapshot := e.execState.NewStorageSnapshot(stateCommit)
return e.queryExecutor.ExecuteScript(
ctx,
script,
arguments,
header,
blockSnapshot)
}
func (e *Engine) GetRegisterAtBlockID(
ctx context.Context,
owner, key []byte,
blockID flow.Identifier,
) ([]byte, error) {
stateCommit, err := e.execState.StateCommitmentByBlockID(ctx, blockID)
if err != nil {
return nil, fmt.Errorf("failed to get state commitment for block (%s): %w", blockID, err)
}
blockSnapshot := e.execState.NewStorageSnapshot(stateCommit)
id := flow.NewRegisterID(string(owner), string(key))
data, err := blockSnapshot.Get(id)
if err != nil {
return nil, fmt.Errorf("failed to get the register (%s): %w", id, err)
}
return data, nil
}
func (e *Engine) GetAccount(
ctx context.Context,
addr flow.Address,
blockID flow.Identifier,
) (*flow.Account, error) {
stateCommit, err := e.execState.StateCommitmentByBlockID(ctx, blockID)
if err != nil {
return nil, fmt.Errorf("failed to get state commitment for block (%s): %w", blockID, err)
}
// return early if state with the given state commitment is not in memory
// and already purged. This reduces allocations for get accounts targeting old blocks.
if !e.execState.HasState(stateCommit) {
return nil, fmt.Errorf(
"failed to get account at block (%s): %w (%s). "+
"this error usually happens if the reference "+
"block for this script is not set to a recent block.",
blockID.String(),
ErrStateCommitmentPruned,
hex.EncodeToString(stateCommit[:]))
}
block, err := e.state.AtBlockID(blockID).Head()
if err != nil {
return nil, fmt.Errorf("failed to get block (%s): %w", blockID, err)
}
blockSnapshot := e.execState.NewStorageSnapshot(stateCommit)
return e.queryExecutor.GetAccount(ctx, addr, block, blockSnapshot)
}