Skip to content

Commit 19cf1c4

Browse files
hudem1rodrigo-pino
andauthored
fix(rpc): TransactionTrace matches rpc v8 specs (#2600)
* feat(rpcv6): Use rpcv6.TransactionTrace instead of vm.TransactionTrace * fix: Added tests for coverage and fix lint * fix: Use & instead of utils.Ptr() * Revert "fix: Use & instead of utils.Ptr()" This reverts commit 428b1ea. * fix: Use & instead of utils.Ptr when it makes a difference * fix: StateDiff field is mandatory for traces * fix: Actually trace.StateDiff is not mandatory! * fix: Add validation on transaction trace * fix: TransactionTrace.Events matches specs * fix: TransactionTrace.Messages matches specs * test: Add test for adapting vm l2 to l1 messages * fix: Use Sepolia instead of Goerli Integration for tests * fix: All TransactionTrace fields are mandatory * feat(rpc): TransactionTrace matches rpc v7 specs * fix: Add required fields marshalling test * fix: Add StateDiff nil check - Remove utils.Ptr where possible * fix: Set trace FunctionInvocation only for specific trace type * fix: Improve some v6 tests * fix: lint * feat: Bring back old Goerli Integration test (and keep new Sepolia test) * fix: Use & instead of utils.HeapPtr when better * fix: Make ExecutionResources.DataAvailability mandatory * feat(rpc): TransactionTrace matches rpc v8 specs * feat: Add isReverted field from VM and feeder * fix: Replace utils.Ptr by utils.HeapPtr * fix: Set trace FunctionInvocation only for specific trace type * feat: Bring back old Goerli Integration test (and keep new Sepolia test) * fix: Reverted INVOKE tx returns a non-nil ExecuteInvocation in trace * fix: lint * fix: Reverted INVOKE tx returns a non-nil ExecuteInvoc in v8 too * fix(PR reviews): fct inlining, divide tests, rename var --------- Co-authored-by: Rodrigo <[email protected]>
1 parent 73d8f7c commit 19cf1c4

File tree

14 files changed

+1245
-430
lines changed

14 files changed

+1245
-430
lines changed

rpc/v6/trace.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,7 @@ type OrderedL2toL1Message struct {
8484
// It follows the specification defined here:
8585
// https://github.com/starkware-libs/starknet-specs/blob/1ae810e0137cc5d175ace4554892a4f43052be56/api/starknet_trace_api_openrpc.json#L11
8686
func (h *Handler) TraceTransaction(ctx context.Context, hash felt.Felt) (*TransactionTrace, *jsonrpc.Error) {
87-
return h.traceTransaction(ctx, &hash)
88-
}
89-
90-
func (h *Handler) traceTransaction(ctx context.Context, hash *felt.Felt) (*TransactionTrace, *jsonrpc.Error) {
91-
_, blockHash, _, err := h.bcReader.Receipt(hash)
87+
_, blockHash, _, err := h.bcReader.Receipt(&hash)
9288
if err != nil {
9389
return nil, rpccore.ErrTxnHashNotFound
9490
}
@@ -114,7 +110,7 @@ func (h *Handler) traceTransaction(ctx context.Context, hash *felt.Felt) (*Trans
114110
}
115111

116112
txIndex := slices.IndexFunc(block.Transactions, func(tx core.Transaction) bool {
117-
return tx.Hash().Equal(hash)
113+
return tx.Hash().Equal(&hash)
118114
})
119115
if txIndex == -1 {
120116
return nil, rpccore.ErrTxnHashNotFound

rpc/v6/trace_test.go

Lines changed: 37 additions & 12 deletions
Large diffs are not rendered by default.

rpc/v7/trace.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,7 @@ func (t *TransactionTrace) allInvocations() []*rpcv6.FunctionInvocation {
8181
// It follows the specification defined here:
8282
// https://github.com/starkware-libs/starknet-specs/blob/1ae810e0137cc5d175ace4554892a4f43052be56/api/starknet_trace_api_openrpc.json#L11
8383
func (h *Handler) TraceTransaction(ctx context.Context, hash felt.Felt) (*TransactionTrace, http.Header, *jsonrpc.Error) {
84-
return h.traceTransaction(ctx, &hash)
85-
}
86-
87-
func (h *Handler) traceTransaction(ctx context.Context, hash *felt.Felt) (*TransactionTrace, http.Header, *jsonrpc.Error) {
88-
_, blockHash, _, err := h.bcReader.Receipt(hash)
84+
_, blockHash, _, err := h.bcReader.Receipt(&hash)
8985
httpHeader := http.Header{}
9086
httpHeader.Set(ExecutionStepsHeader, "0")
9187

@@ -112,7 +108,7 @@ func (h *Handler) traceTransaction(ctx context.Context, hash *felt.Felt) (*Trans
112108
}
113109

114110
txIndex := slices.IndexFunc(block.Transactions, func(tx core.Transaction) bool {
115-
return tx.Hash().Equal(hash)
111+
return tx.Hash().Equal(&hash)
116112
})
117113
if txIndex == -1 {
118114
return nil, httpHeader, rpccore.ErrTxnHashNotFound

rpc/v7/trace_test.go

Lines changed: 38 additions & 13 deletions
Large diffs are not rendered by default.

rpc/v8/adapters.go

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
package rpcv8
2+
3+
import (
4+
"errors"
5+
6+
"github.com/NethermindEth/juno/core/felt"
7+
rpcv6 "github.com/NethermindEth/juno/rpc/v6"
8+
"github.com/NethermindEth/juno/starknet"
9+
"github.com/NethermindEth/juno/utils"
10+
"github.com/NethermindEth/juno/vm"
11+
)
12+
13+
/****************************************************
14+
VM Adapters
15+
*****************************************************/
16+
17+
func AdaptVMTransactionTrace(trace *vm.TransactionTrace) TransactionTrace {
18+
var validateInvocation *FunctionInvocation
19+
if trace.ValidateInvocation != nil && trace.Type != vm.TxnL1Handler {
20+
validateInvocation = utils.HeapPtr(adaptVMFunctionInvocation(trace.ValidateInvocation))
21+
}
22+
23+
var feeTransferInvocation *FunctionInvocation
24+
if trace.FeeTransferInvocation != nil && trace.Type != vm.TxnL1Handler {
25+
feeTransferInvocation = utils.HeapPtr(adaptVMFunctionInvocation(trace.FeeTransferInvocation))
26+
}
27+
28+
var constructorInvocation *FunctionInvocation
29+
var executeInvocation *ExecuteInvocation
30+
var functionInvocation *FunctionInvocation
31+
32+
switch trace.Type {
33+
case vm.TxnDeployAccount, vm.TxnDeploy:
34+
if trace.ConstructorInvocation != nil {
35+
constructorInvocation = utils.HeapPtr(adaptVMFunctionInvocation(trace.ConstructorInvocation))
36+
}
37+
case vm.TxnInvoke:
38+
if trace.ExecuteInvocation != nil {
39+
executeInvocation = utils.HeapPtr(adaptVMExecuteInvocation(trace.ExecuteInvocation))
40+
}
41+
case vm.TxnL1Handler:
42+
if trace.FunctionInvocation != nil {
43+
functionInvocation = utils.HeapPtr(adaptVMFunctionInvocation(trace.FunctionInvocation))
44+
}
45+
}
46+
47+
var resources *ExecutionResources
48+
if trace.ExecutionResources != nil {
49+
resources = utils.HeapPtr(adaptVMExecutionResources(trace.ExecutionResources))
50+
}
51+
52+
var stateDiff *rpcv6.StateDiff
53+
if trace.StateDiff != nil {
54+
stateDiff = utils.HeapPtr(rpcv6.AdaptVMStateDiff(trace.StateDiff))
55+
}
56+
57+
return TransactionTrace{
58+
Type: TransactionType(trace.Type),
59+
ValidateInvocation: validateInvocation,
60+
ExecuteInvocation: executeInvocation,
61+
FeeTransferInvocation: feeTransferInvocation,
62+
ConstructorInvocation: constructorInvocation,
63+
FunctionInvocation: functionInvocation,
64+
StateDiff: stateDiff,
65+
ExecutionResources: resources,
66+
}
67+
}
68+
69+
func adaptVMExecuteInvocation(vmFnInvocation *vm.ExecuteInvocation) ExecuteInvocation {
70+
var functionInvocation *FunctionInvocation
71+
if vmFnInvocation.FunctionInvocation != nil {
72+
functionInvocation = utils.HeapPtr(adaptVMFunctionInvocation(vmFnInvocation.FunctionInvocation))
73+
}
74+
75+
return ExecuteInvocation{
76+
RevertReason: vmFnInvocation.RevertReason,
77+
FunctionInvocation: functionInvocation,
78+
}
79+
}
80+
81+
func adaptVMFunctionInvocation(vmFnInvocation *vm.FunctionInvocation) FunctionInvocation {
82+
// Adapt inner calls
83+
adaptedCalls := make([]FunctionInvocation, len(vmFnInvocation.Calls))
84+
for index := range vmFnInvocation.Calls {
85+
adaptedCalls[index] = adaptVMFunctionInvocation(&vmFnInvocation.Calls[index])
86+
}
87+
88+
// Adapt events
89+
adaptedEvents := make([]rpcv6.OrderedEvent, len(vmFnInvocation.Events))
90+
for index := range vmFnInvocation.Events {
91+
vmEvent := &vmFnInvocation.Events[index]
92+
93+
adaptedEvents[index] = rpcv6.OrderedEvent{
94+
Order: vmEvent.Order,
95+
Keys: vmEvent.Keys,
96+
Data: vmEvent.Data,
97+
}
98+
}
99+
100+
// Adapt messages
101+
adaptedMessages := make([]rpcv6.OrderedL2toL1Message, len(vmFnInvocation.Messages))
102+
for index := range vmFnInvocation.Messages {
103+
vmMessage := &vmFnInvocation.Messages[index]
104+
105+
toAddr, _ := new(felt.Felt).SetString(vmMessage.To)
106+
107+
adaptedMessages[index] = rpcv6.OrderedL2toL1Message{
108+
Order: vmMessage.Order,
109+
From: vmMessage.From,
110+
To: toAddr,
111+
Payload: vmMessage.Payload,
112+
}
113+
}
114+
115+
// Adapt execution resources
116+
var adaptedResources *InnerExecutionResources
117+
if r := vmFnInvocation.ExecutionResources; r != nil {
118+
adaptedResources = &InnerExecutionResources{
119+
L1Gas: r.L1Gas,
120+
L2Gas: r.L2Gas,
121+
}
122+
}
123+
124+
return FunctionInvocation{
125+
ContractAddress: vmFnInvocation.ContractAddress,
126+
EntryPointSelector: vmFnInvocation.EntryPointSelector,
127+
Calldata: vmFnInvocation.Calldata,
128+
CallerAddress: vmFnInvocation.CallerAddress,
129+
ClassHash: vmFnInvocation.ClassHash,
130+
EntryPointType: vmFnInvocation.EntryPointType,
131+
CallType: vmFnInvocation.CallType,
132+
Result: vmFnInvocation.Result,
133+
Calls: adaptedCalls,
134+
Events: adaptedEvents,
135+
Messages: adaptedMessages,
136+
ExecutionResources: adaptedResources,
137+
IsReverted: vmFnInvocation.IsReverted,
138+
}
139+
}
140+
141+
func adaptVMExecutionResources(r *vm.ExecutionResources) ExecutionResources {
142+
return ExecutionResources{
143+
InnerExecutionResources: InnerExecutionResources{
144+
L1Gas: r.L1Gas,
145+
L2Gas: r.L2Gas,
146+
},
147+
L1DataGas: r.L1DataGas,
148+
}
149+
}
150+
151+
/****************************************************
152+
Feeder Adapters
153+
*****************************************************/
154+
155+
func AdaptFeederBlockTrace(block *BlockWithTxs, blockTrace *starknet.BlockTrace) ([]TracedBlockTransaction, error) {
156+
if blockTrace == nil {
157+
return nil, nil
158+
}
159+
160+
if len(block.Transactions) != len(blockTrace.Traces) {
161+
return nil, errors.New("mismatched number of txs and traces")
162+
}
163+
164+
// Adapt every feeder block trace to rpc v8 trace
165+
adaptedTraces := make([]TracedBlockTransaction, len(blockTrace.Traces))
166+
for index := range blockTrace.Traces {
167+
feederTrace := &blockTrace.Traces[index]
168+
169+
trace := TransactionTrace{
170+
Type: block.Transactions[index].Type,
171+
}
172+
173+
if feederTrace.FeeTransferInvocation != nil && trace.Type != TxnL1Handler {
174+
trace.FeeTransferInvocation = utils.HeapPtr(adaptFeederFunctionInvocation(feederTrace.FeeTransferInvocation))
175+
}
176+
177+
if feederTrace.ValidateInvocation != nil && trace.Type != TxnL1Handler {
178+
trace.ValidateInvocation = utils.HeapPtr(adaptFeederFunctionInvocation(feederTrace.ValidateInvocation))
179+
}
180+
181+
var fnInvocation *FunctionInvocation
182+
if fct := feederTrace.FunctionInvocation; fct != nil {
183+
fnInvocation = utils.HeapPtr(adaptFeederFunctionInvocation(fct))
184+
}
185+
186+
switch trace.Type {
187+
case TxnDeploy, TxnDeployAccount:
188+
trace.ConstructorInvocation = fnInvocation
189+
case TxnInvoke:
190+
trace.ExecuteInvocation = new(ExecuteInvocation)
191+
if feederTrace.RevertError != "" {
192+
trace.ExecuteInvocation.RevertReason = feederTrace.RevertError
193+
} else {
194+
trace.ExecuteInvocation.FunctionInvocation = fnInvocation
195+
}
196+
case TxnL1Handler:
197+
trace.FunctionInvocation = fnInvocation
198+
}
199+
200+
adaptedTraces[index] = TracedBlockTransaction{
201+
TransactionHash: &feederTrace.TransactionHash,
202+
TraceRoot: &trace,
203+
}
204+
}
205+
206+
return adaptedTraces, nil
207+
}
208+
209+
func adaptFeederFunctionInvocation(snFnInvocation *starknet.FunctionInvocation) FunctionInvocation {
210+
// Adapt inner calls
211+
adaptedCalls := make([]FunctionInvocation, len(snFnInvocation.InternalCalls))
212+
for index := range snFnInvocation.InternalCalls {
213+
adaptedCalls[index] = adaptFeederFunctionInvocation(&snFnInvocation.InternalCalls[index])
214+
}
215+
216+
// Adapt events
217+
adaptedEvents := make([]rpcv6.OrderedEvent, len(snFnInvocation.Events))
218+
for index := range snFnInvocation.Events {
219+
snEvent := &snFnInvocation.Events[index]
220+
221+
adaptedEvents[index] = rpcv6.OrderedEvent{
222+
Order: snEvent.Order,
223+
Keys: utils.Map(snEvent.Keys, utils.HeapPtr[felt.Felt]),
224+
Data: utils.Map(snEvent.Data, utils.HeapPtr[felt.Felt]),
225+
}
226+
}
227+
228+
// Adapt messages
229+
adaptedMessages := make([]rpcv6.OrderedL2toL1Message, len(snFnInvocation.Messages))
230+
for index := range snFnInvocation.Messages {
231+
snMessage := &snFnInvocation.Messages[index]
232+
233+
toAddr, _ := new(felt.Felt).SetString(snMessage.ToAddr)
234+
235+
adaptedMessages[index] = rpcv6.OrderedL2toL1Message{
236+
Order: snMessage.Order,
237+
From: &snFnInvocation.ContractAddress,
238+
To: toAddr,
239+
Payload: utils.Map(snMessage.Payload, utils.HeapPtr[felt.Felt]),
240+
}
241+
}
242+
243+
return FunctionInvocation{
244+
ContractAddress: snFnInvocation.ContractAddress,
245+
EntryPointSelector: snFnInvocation.Selector,
246+
Calldata: snFnInvocation.Calldata,
247+
CallerAddress: snFnInvocation.CallerAddress,
248+
ClassHash: snFnInvocation.ClassHash,
249+
EntryPointType: snFnInvocation.EntryPointType,
250+
CallType: snFnInvocation.CallType,
251+
Result: snFnInvocation.Result,
252+
Calls: adaptedCalls,
253+
Events: adaptedEvents,
254+
Messages: adaptedMessages,
255+
ExecutionResources: utils.HeapPtr(adaptFeederExecutionResources(&snFnInvocation.ExecutionResources)),
256+
IsReverted: snFnInvocation.Failed,
257+
}
258+
}
259+
260+
func adaptFeederExecutionResources(resources *starknet.ExecutionResources) InnerExecutionResources {
261+
var l1Gas, l2Gas uint64
262+
if tgs := resources.TotalGasConsumed; tgs != nil {
263+
l1Gas = tgs.L1Gas
264+
l2Gas = tgs.L2Gas
265+
}
266+
267+
return InnerExecutionResources{
268+
L1Gas: l1Gas,
269+
L2Gas: l2Gas,
270+
}
271+
}

rpc/v8/simulation.go

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ func (s *SimulationFlag) UnmarshalJSON(bytes []byte) (err error) {
3939
}
4040

4141
type SimulatedTransaction struct {
42-
TransactionTrace *vm.TransactionTrace `json:"transaction_trace,omitempty"`
43-
FeeEstimation FeeEstimate `json:"fee_estimation,omitempty"`
42+
TransactionTrace *TransactionTrace `json:"transaction_trace,omitempty"`
43+
FeeEstimation FeeEstimate `json:"fee_estimation,omitempty"`
4444
}
4545

4646
type TracedBlockTransaction struct {
47-
TraceRoot *vm.TransactionTrace `json:"trace_root,omitempty"`
48-
TransactionHash *felt.Felt `json:"transaction_hash,omitempty"`
47+
TraceRoot *TransactionTrace `json:"trace_root,omitempty"`
48+
TransactionHash *felt.Felt `json:"transaction_hash,omitempty"`
4949
}
5050

5151
/****************************************************
@@ -112,7 +112,7 @@ func (h *Handler) simulateTransactions(id BlockID, transactions []BroadcastedTra
112112
func prepareTransactions(transactions []BroadcastedTransaction, network *utils.Network) (
113113
[]core.Transaction, []core.Class, []*felt.Felt, *jsonrpc.Error,
114114
) {
115-
txns := make([]core.Transaction, 0, len(transactions))
115+
txns := make([]core.Transaction, len(transactions))
116116
var classes []core.Class
117117
paidFeesOnL1 := make([]*felt.Felt, 0)
118118

@@ -126,7 +126,7 @@ func prepareTransactions(transactions []BroadcastedTransaction, network *utils.N
126126
paidFeesOnL1 = append(paidFeesOnL1, paidFeeOnL1)
127127
}
128128

129-
txns = append(txns, txn)
129+
txns[idx] = txn
130130
if declaredClass != nil {
131131
classes = append(classes, declaredClass)
132132
}
@@ -153,6 +153,7 @@ func createSimulatedTransactions(
153153
traces := executionResults.Traces
154154
gasConsumed := executionResults.GasConsumed
155155
daGas := executionResults.DataAvailability
156+
156157
if len(overallFees) != len(traces) || len(overallFees) != len(gasConsumed) ||
157158
len(overallFees) != len(daGas) || len(overallFees) != len(txns) {
158159
return nil, fmt.Errorf("inconsistent lengths: %d overall fees, %d traces, %d gas consumed, %d data availability, %d txns",
@@ -177,18 +178,19 @@ func createSimulatedTransactions(
177178

178179
simulatedTransactions := make([]SimulatedTransaction, len(overallFees))
179180
for i, overallFee := range overallFees {
180-
trace := traces[i]
181-
traces[i].ExecutionResources = &vm.ExecutionResources{
182-
L1Gas: gasConsumed[i].L1Gas,
183-
L1DataGas: gasConsumed[i].L1DataGas,
184-
L2Gas: gasConsumed[i].L2Gas,
185-
ComputationResources: trace.TotalComputationResources(),
186-
DataAvailability: &vm.DataAvailability{
187-
L1Gas: daGas[i].L1Gas,
188-
L1DataGas: daGas[i].L1DataGas,
181+
// Adapt transaction trace to rpc v8 trace
182+
trace := utils.HeapPtr(AdaptVMTransactionTrace(&traces[i]))
183+
184+
// Add root level execution resources
185+
trace.ExecutionResources = &ExecutionResources{
186+
InnerExecutionResources: InnerExecutionResources{
187+
L1Gas: gasConsumed[i].L1Gas,
188+
L2Gas: gasConsumed[i].L2Gas,
189189
},
190+
L1DataGas: gasConsumed[i].L1DataGas,
190191
}
191192

193+
// Compute data for FeeEstimate
192194
var l1GasPrice, l2GasPrice, l1DataGasPrice *felt.Felt
193195
feeUnit := feeUnit(txns[i])
194196
switch feeUnit {
@@ -202,8 +204,9 @@ func createSimulatedTransactions(
202204
l1DataGasPrice = l1DataGasPriceStrk
203205
}
204206

207+
// Append simulated transaction (trace + fee estimate)
205208
simulatedTransactions[i] = SimulatedTransaction{
206-
TransactionTrace: &traces[i],
209+
TransactionTrace: trace,
207210
FeeEstimation: FeeEstimate{
208211
L1GasConsumed: new(felt.Felt).SetUint64(gasConsumed[i].L1Gas),
209212
L1GasPrice: l1GasPrice,

0 commit comments

Comments
 (0)