-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexecutor.go
227 lines (195 loc) · 8.11 KB
/
executor.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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package main
import (
"bytes"
"context"
"fmt"
"repro/proto/github.com/0xPolygonHermez/zkevm-node/state/runtime/executor"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ledgerwatch/log/v3"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
type VerifierRequest struct {
BatchNumber uint64
StateRoot common.Hash
CheckCount int
}
type VerifierResponse struct {
BatchNumber uint64
Valid bool
Witness []byte
}
type Config struct {
GrpcUrls []string
Timeout time.Duration
}
type Payload struct {
Witness []byte // SMT partial tree, SCs, (indirectly) old state root
DataStream []byte // txs, old batch num, chain id, fork id, effective gas price, block header, index of L1 info tree (global exit root, min timestamp, ...)
Coinbase string // sequencer address
OldAccInputHash []byte // 0 for executor, required for the prover
// Used by injected/first batches (do not use it for regular batches)
L1InfoRoot []byte // 0 for executor, required for the prover
TimestampLimit uint64 // if 0, replace by now + 10 min internally
ForcedBlockhashL1 []byte // we need it, 0 in regular batches, hash in forced batches, also used in injected/first batches, 0 by now
ContextId string // batch ID to be shown in the executor traces, for your convenience: "Erigon_candidate_batch_N"
}
type RpcPayload struct {
Witness string `json:"witness"` // SMT partial tree, SCs, (indirectly) old state root
Coinbase string `json:"coinbase"` // sequencer address
OldAccInputHash string `json:"oldAccInputHash"` // 0 for executor, required for the prover
// Used by injected/first batches (do not use it for regular batches)
TimestampLimit uint64 `json:"timestampLimit"` // if 0, replace by now + 10 min internally
ForcedBlockhashL1 string `json:"forcedBlockhashL1"` // we need it, 0 in regular batches, hash in forced batches, also used in injected/first batches, 0 by now
}
type Executor struct {
grpcUrl string
conn *grpc.ClientConn
connCancel context.CancelFunc
client executor.ExecutorServiceClient
}
func NewExecutors(cfg Config) []*Executor {
executors := make([]*Executor, len(cfg.GrpcUrls))
var err error
for i, grpcUrl := range cfg.GrpcUrls {
executors[i], err = NewExecutor(grpcUrl, cfg.Timeout)
if err != nil {
log.Warn("Failed to create executor", "error", err)
}
}
return executors
}
func NewExecutor(grpcUrl string, timeout time.Duration) (*Executor, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
conn, err := grpc.DialContext(ctx, grpcUrl, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
if err != nil {
cancel()
return nil, fmt.Errorf("failed to dial grpc: %w", err)
}
client := executor.NewExecutorServiceClient(conn)
e := &Executor{
grpcUrl: grpcUrl,
conn: conn,
connCancel: cancel,
client: client,
}
return e, nil
}
func (e *Executor) Close() {
if e == nil || e.conn == nil {
return
}
e.connCancel()
err := e.conn.Close()
if err != nil {
log.Warn("Failed to close grpc connection", err)
}
}
func (e *Executor) Verify(p *Payload, request *VerifierRequest, oldStateRoot common.Hash) (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
log.Debug("Sending request to grpc server", "grpcUrl", e.grpcUrl)
size := 1024 * 1024 * 256 // 256mb maximum size - hack for now until trimmed witness is proved off
resp, err := e.client.ProcessStatelessBatchV2(ctx, &executor.ProcessStatelessBatchRequestV2{
Witness: p.Witness,
DataStream: p.DataStream,
Coinbase: p.Coinbase,
OldAccInputHash: p.OldAccInputHash,
L1InfoRoot: p.L1InfoRoot,
TimestampLimit: p.TimestampLimit,
ForcedBlockhashL1: p.ForcedBlockhashL1,
ContextId: p.ContextId,
//TraceConfig: &executor.TraceConfigV2{
// DisableStorage: 0,
// DisableStack: 0,
// EnableMemory: 0,
// EnableReturnData: 0,
// TxHashToGenerateFullTrace: nil,
//},
}, grpc.MaxCallSendMsgSize(size), grpc.MaxCallRecvMsgSize(size))
if err != nil {
return false, fmt.Errorf("failed to process stateless batch: %w", err)
}
counters := fmt.Sprintf("[SHA: %v]", resp.CntSha256Hashes)
counters += fmt.Sprintf("[A: %v]", resp.CntArithmetics)
counters += fmt.Sprintf("[B: %v]", resp.CntBinaries)
counters += fmt.Sprintf("[K: %v]", resp.CntKeccakHashes)
counters += fmt.Sprintf("[M: %v]", resp.CntMemAligns)
counters += fmt.Sprintf("[P: %v]", resp.CntPoseidonHashes)
counters += fmt.Sprintf("[S: %v]", resp.CntSteps)
counters += fmt.Sprintf("[D: %v]", resp.CntPoseidonPaddings)
log.Info("executor result",
"batch", request.BatchNumber,
"counters", counters,
"exec-root", common.BytesToHash(resp.NewStateRoot),
"our-root", request.StateRoot,
"exec-old-root", common.BytesToHash(resp.OldStateRoot),
"our-old-root", oldStateRoot)
log.Debug("Received response from executor", "grpcUrl", e.grpcUrl, "response", resp)
return responseCheck(resp, request.StateRoot)
}
func responseCheck(resp *executor.ProcessBatchResponseV2, erigonStateRoot common.Hash) (bool, error) {
if resp == nil {
return false, fmt.Errorf("nil response")
}
if resp.Error != executor.ExecutorError_EXECUTOR_ERROR_UNSPECIFIED &&
resp.Error != executor.ExecutorError_EXECUTOR_ERROR_NO_ERROR {
return false, fmt.Errorf("error in response: %s", resp.Error)
}
if !bytes.Equal(resp.NewStateRoot, erigonStateRoot.Bytes()) {
return false, fmt.Errorf("erigon state root mismatch: expected %s, got %s", erigonStateRoot, common.BytesToHash(resp.NewStateRoot))
}
return true, nil
}
func (e *Executor) VerifyTest(p *Payload, request *VerifierRequest) (bool, *executor.ProcessBatchResponseV2, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
log.Debug("Sending request to grpc server", "grpcUrl", e.grpcUrl)
size := 1024 * 1024 * 256 // 256mb maximum size - hack for now until trimmed witness is proved off
resp, err := e.client.ProcessStatelessBatchV2(ctx, &executor.ProcessStatelessBatchRequestV2{
Witness: p.Witness,
DataStream: p.DataStream,
Coinbase: p.Coinbase,
OldAccInputHash: p.OldAccInputHash,
L1InfoRoot: p.L1InfoRoot,
TimestampLimit: p.TimestampLimit,
ForcedBlockhashL1: p.ForcedBlockhashL1,
ContextId: p.ContextId,
//TraceConfig: &executor.TraceConfigV2{
// DisableStorage: 0,
// DisableStack: 0,
// EnableMemory: 0,
// EnableReturnData: 0,
// TxHashToGenerateFullTrace: nil,
//},
}, grpc.MaxCallSendMsgSize(size), grpc.MaxCallRecvMsgSize(size))
if err != nil {
return false, nil, fmt.Errorf("failed to process stateless batch: %w", err)
}
counters := fmt.Sprintf("[SHA: %v]", resp.CntSha256Hashes)
counters += fmt.Sprintf("[A: %v]", resp.CntArithmetics)
counters += fmt.Sprintf("[B: %v]", resp.CntBinaries)
counters += fmt.Sprintf("[K: %v]", resp.CntKeccakHashes)
counters += fmt.Sprintf("[M: %v]", resp.CntMemAligns)
counters += fmt.Sprintf("[P: %v]", resp.CntPoseidonHashes)
counters += fmt.Sprintf("[S: %v]", resp.CntSteps)
counters += fmt.Sprintf("[D: %v]", resp.CntPoseidonPaddings)
log.Info("executor result", "batch", request.BatchNumber, "counters", counters, "root", common.BytesToHash(resp.NewStateRoot), "our-root", request.StateRoot)
log.Debug("Received response from executor", "grpcUrl", e.grpcUrl, "response", resp)
respCheck, err := responseCheckTest(resp, request.StateRoot)
return respCheck, resp, err
}
func responseCheckTest(resp *executor.ProcessBatchResponseV2, erigonStateRoot common.Hash) (bool, error) {
if resp == nil {
return false, fmt.Errorf("nil response")
}
if resp.Error != executor.ExecutorError_EXECUTOR_ERROR_UNSPECIFIED &&
resp.Error != executor.ExecutorError_EXECUTOR_ERROR_NO_ERROR {
return false, fmt.Errorf("error in response: %s", resp.Error)
}
if !bytes.Equal(resp.NewStateRoot, erigonStateRoot.Bytes()) {
return false, fmt.Errorf("erigon state root mismatch: expected %s, got %s", erigonStateRoot, common.BytesToHash(resp.NewStateRoot))
}
return true, nil
}