Skip to content

Commit

Permalink
Merge pull request onflow#6488 from onflow/ramtin/evm-optimize-precom…
Browse files Browse the repository at this point in the history
…piled-call-tracking

[Flow EVM] adding a new encoding type for captured precompiled calls
  • Loading branch information
ramtinms authored Sep 27, 2024
2 parents c23d9b6 + 4387e74 commit 901e171
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 110 deletions.
8 changes: 2 additions & 6 deletions fvm/evm/emulator/emulator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1088,13 +1088,9 @@ func TestCallingExtraPrecompiles(t *testing.T) {
output := []byte{3, 4}
addr := testutils.RandomAddress(t)
capturedCall := &types.PrecompiledCalls{
Address: addr,
RequiredGasCalls: []types.RequiredGasCall{{
Input: input,
Output: uint64(10),
}},
Address: addr,
RequiredGasCalls: []uint64{10},
RunCalls: []types.RunCall{{
Input: input,
Output: output,
ErrorMsg: "",
}},
Expand Down
6 changes: 1 addition & 5 deletions fvm/evm/emulator/tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,7 @@ func (ct *CallTracker) CaptureRequiredGas(address types.Address, input []byte, o
ct.callsByAddress[address] = calls
}

calls.RequiredGasCalls = append(calls.RequiredGasCalls, types.RequiredGasCall{
Input: input,
Output: output,
})
calls.RequiredGasCalls = append(calls.RequiredGasCalls, output)
}

// CaptureRun captures a run calls
Expand All @@ -61,7 +58,6 @@ func (ct *CallTracker) CaptureRun(address types.Address, input []byte, output []
errMsg = err.Error()
}
calls.RunCalls = append(calls.RunCalls, types.RunCall{
Input: input,
Output: output,
ErrorMsg: errMsg,
})
Expand Down
27 changes: 20 additions & 7 deletions fvm/evm/emulator/tracker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,31 @@ func TestTracker(t *testing.T) {
apc := testutils.AggregatedPrecompiledCallsFixture(t)
var runCallCounter int
var requiredGasCallCounter int

reqGasCallInputs := make([][]byte, len(apc[0].RequiredGasCalls))
runCallInputs := make([][]byte, len(apc[0].RunCalls))

for i := range apc[0].RequiredGasCalls {
reqGasCallInputs[i] = testutils.RandomData(t)
}

for i := range apc[0].RunCalls {
runCallInputs[i] = testutils.RandomData(t)
}

pc := &MockedPrecompiled{
AddressFunc: func() types.Address {
return apc[0].Address
},
RequiredGasFunc: func(input []byte) uint64 {
res := apc[0].RequiredGasCalls[requiredGasCallCounter]
require.Equal(t, res.Input, input)
require.Equal(t, reqGasCallInputs[requiredGasCallCounter], input)
requiredGasCallCounter += 1
return res.Output
return res
},
RunFunc: func(input []byte) ([]byte, error) {
res := apc[0].RunCalls[runCallCounter]
require.Equal(t, res.Input, input)
require.Equal(t, runCallInputs[runCallCounter], input)
runCallCounter += 1
var err error
if len(res.ErrorMsg) > 0 {
Expand All @@ -40,12 +52,13 @@ func TestTracker(t *testing.T) {
wpc := tracker.RegisterPrecompiledContract(pc)

require.Equal(t, apc[0].Address, wpc.Address())

for _, pc := range apc {
for _, call := range pc.RequiredGasCalls {
require.Equal(t, call.Output, wpc.RequiredGas(call.Input))
for i, call := range pc.RequiredGasCalls {
require.Equal(t, call, wpc.RequiredGas(reqGasCallInputs[i]))
}
for _, call := range pc.RunCalls {
ret, err := wpc.Run(call.Input)
for i, call := range pc.RunCalls {
ret, err := wpc.Run(runCallInputs[i])
require.Equal(t, call.Output, ret)
errMsg := ""
if err != nil {
Expand Down
6 changes: 1 addition & 5 deletions fvm/evm/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,16 +668,12 @@ func TestHandler_COA(t *testing.T) {
require.Len(t, pc.RequiredGasCalls, 1)
require.Equal(t,
pc.RequiredGasCalls[0],
types.RequiredGasCall{
Input: precompiles.FlowBlockHeightFuncSig[:],
Output: precompiles.FlowBlockHeightFixedGas,
},
precompiles.FlowBlockHeightFixedGas,
)
require.Len(t, pc.RunCalls, 1)
require.Equal(t,
pc.RunCalls[0],
types.RunCall{
Input: precompiles.FlowBlockHeightFuncSig[:],
Output: ret.ReturnedData,
ErrorMsg: "",
},
Expand Down
9 changes: 1 addition & 8 deletions fvm/evm/precompiles/replayer.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package precompiles

import (
"bytes"
"errors"
"fmt"

Expand Down Expand Up @@ -54,10 +53,7 @@ func (p *ReplayerPrecompiledContract) RequiredGas(input []byte) (output uint64)
if p.requiredGasIndex > len(p.expectedCalls.RequiredGasCalls) {
panic(errUnexpectedCall)
}
if !bytes.Equal(p.expectedCalls.RequiredGasCalls[p.requiredGasIndex].Input, input) {
panic(errUnexpectedCall)
}
output = p.expectedCalls.RequiredGasCalls[p.requiredGasIndex].Output
output = p.expectedCalls.RequiredGasCalls[p.requiredGasIndex]
p.requiredGasIndex++
return
}
Expand All @@ -66,9 +62,6 @@ func (p *ReplayerPrecompiledContract) Run(input []byte) (output []byte, err erro
if p.runIndex > len(p.expectedCalls.RunCalls) {
panic(errUnexpectedCall)
}
if !bytes.Equal(p.expectedCalls.RunCalls[p.runIndex].Input, input) {
panic(errUnexpectedCall)
}
output = p.expectedCalls.RunCalls[p.runIndex].Output
errMsg := p.expectedCalls.RunCalls[p.runIndex].ErrorMsg
if len(errMsg) > 0 {
Expand Down
14 changes: 3 additions & 11 deletions fvm/evm/precompiles/replayer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,15 @@ func TestReplayer(t *testing.T) {

pc := &types.PrecompiledCalls{
Address: address,
RequiredGasCalls: []types.RequiredGasCall{
{
Input: input1,
Output: gas1,
},
{
Input: input2,
Output: gas2,
},
RequiredGasCalls: []uint64{
gas1,
gas2,
},
RunCalls: []types.RunCall{
{
Input: input1,
Output: output1,
},
{
Input: input2,
Output: output2,
ErrorMsg: errMsg2,
},
Expand Down
9 changes: 2 additions & 7 deletions fvm/evm/testutils/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,13 @@ func RandomResultFixture(t testing.TB) *types.Result {
func AggregatedPrecompiledCallsFixture(t testing.TB) types.AggregatedPrecompiledCalls {
return types.AggregatedPrecompiledCalls{
types.PrecompiledCalls{
Address: RandomAddress(t),
RequiredGasCalls: []types.RequiredGasCall{{
Input: RandomData(t),
Output: 2,
}},
Address: RandomAddress(t),
RequiredGasCalls: []uint64{2},
RunCalls: []types.RunCall{
{
Input: RandomData(t),
Output: RandomData(t),
},
{
Input: RandomData(t),
Output: []byte{},
ErrorMsg: "Some error msg",
},
Expand Down
63 changes: 53 additions & 10 deletions fvm/evm/types/precompiled.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"bytes"
"fmt"

gethVM "github.com/onflow/go-ethereum/core/vm"
"github.com/onflow/go-ethereum/rlp"
Expand All @@ -17,23 +18,16 @@ type PrecompiledContract interface {
Address() Address
}

// RunCall captures a call to the RequiredGas method of a precompiled contract
type RequiredGasCall struct {
Input []byte
Output uint64
}

// RunCall captures a call to the Run method of a precompiled contract
type RunCall struct {
Input []byte
Output []byte
ErrorMsg string
}

// PrecompiledCalls captures all the calls to a precompiled contract
type PrecompiledCalls struct {
Address Address
RequiredGasCalls []RequiredGasCall
RequiredGasCalls []uint64
RunCalls []RunCall
}

Expand All @@ -43,8 +37,8 @@ func (pc *PrecompiledCalls) IsEmpty() bool {
}

const (
AggregatedPrecompiledCallsEncodingVersion uint8 = 1
AggregatedPrecompiledCallsEncodingByteSize int = 1
AggregatedPrecompiledCallsEncodingVersion uint8 = 2 // current version
)

// AggregatedPrecompiledCalls aggregates a list of precompiled calls
Expand Down Expand Up @@ -85,5 +79,54 @@ func AggregatedPrecompileCallsFromEncoded(encoded []byte) (AggregatedPrecompiled
if len(encoded) == 0 {
return apc, nil
}
return apc, rlp.DecodeBytes(encoded[AggregatedPrecompiledCallsEncodingByteSize:], &apc)
switch int(encoded[0]) {
case 1:
return decodePrecompiledCallsV1(encoded)
case 2:
return apc, rlp.DecodeBytes(encoded[AggregatedPrecompiledCallsEncodingByteSize:], &apc)
default:
return nil, fmt.Errorf("unknown type for encoded AggregatedPrecompiledCalls received %d", int(encoded[0]))
}
}

func decodePrecompiledCallsV1(encoded []byte) (AggregatedPrecompiledCalls, error) {
legacy := make([]precompiledCallsV1, 0)
err := rlp.DecodeBytes(encoded[AggregatedPrecompiledCallsEncodingByteSize:], &legacy)
if err != nil {
return nil, err
}
apc := make([]PrecompiledCalls, len(legacy))
for i, ap := range legacy {
reqCalls := make([]uint64, len(ap.RequiredGasCalls))
for j, rc := range ap.RequiredGasCalls {
reqCalls[j] = rc.Output
}
runCalls := make([]RunCall, len(ap.RunCalls))
for j, rc := range ap.RunCalls {
runCalls[j] = RunCall{
Output: rc.Output,
ErrorMsg: rc.ErrorMsg,
}
}
apc[i] = PrecompiledCalls{
Address: ap.Address,
RequiredGasCalls: reqCalls,
RunCalls: runCalls,
}
}
return apc, nil
}

// legacy encoding types
type precompiledCallsV1 struct {
Address Address
RequiredGasCalls []struct {
Input []byte
Output uint64
}
RunCalls []struct {
Input []byte
Output []byte
ErrorMsg string
}
}
Loading

0 comments on commit 901e171

Please sign in to comment.