Skip to content

Commit

Permalink
Merge pull request onflow#5091 from onflow/gregor/evm/event-encoding
Browse files Browse the repository at this point in the history
[EVM] Add EVM type ID
  • Loading branch information
sideninja authored Jan 22, 2024
2 parents afb8708 + 010094d commit 420e295
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 38 deletions.
143 changes: 105 additions & 38 deletions fvm/evm/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@ package types

import (
"encoding/hex"
"encoding/json"
"fmt"
"strings"

gethCommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/onflow/cadence"
"github.com/onflow/cadence/runtime/stdlib"
"github.com/onflow/cadence/runtime/common"

"github.com/onflow/flow-go/model/flow"
)

const (
EventTypeBlockExecuted flow.EventType = "evm.BlockExecuted"
EventTypeTransactionExecuted flow.EventType = "evm.TransactionExecuted"
EventTypeBlockExecuted flow.EventType = "BlockExecuted"
EventTypeTransactionExecuted flow.EventType = "TransactionExecuted"
evmLocationPrefix = "evm"
locationDivider = "."
)

type EventPayload interface {
Expand All @@ -26,6 +31,72 @@ type Event struct {
Payload EventPayload
}

var _ common.Location = EVMLocation{}

type EVMLocation struct{}

func (l EVMLocation) TypeID(memoryGauge common.MemoryGauge, qualifiedIdentifier string) common.TypeID {
id := fmt.Sprintf("%s%s%s", evmLocationPrefix, locationDivider, qualifiedIdentifier)
common.UseMemory(memoryGauge, common.NewRawStringMemoryUsage(len(id)))

return common.TypeID(id)
}

func (l EVMLocation) QualifiedIdentifier(typeID common.TypeID) string {
pieces := strings.SplitN(string(typeID), locationDivider, 2)

if len(pieces) < 2 {
return ""
}

return pieces[1]
}

func (l EVMLocation) String() string {
return evmLocationPrefix
}

func (l EVMLocation) Description() string {
return evmLocationPrefix
}

func (l EVMLocation) ID() string {
return evmLocationPrefix
}

func (l EVMLocation) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Type string
}{
Type: "EVMLocation",
})
}

func init() {
common.RegisterTypeIDDecoder(
evmLocationPrefix,
func(_ common.MemoryGauge, typeID string) (common.Location, string, error) {
if typeID == "" {
return nil, "", fmt.Errorf("invalid EVM type location ID: missing type prefix")
}

parts := strings.SplitN(typeID, ".", 2)
prefix := parts[0]
if prefix != evmLocationPrefix {
return EVMLocation{}, "", fmt.Errorf("invalid EVM type location ID: invalid prefix")
}

var qualifiedIdentifier string
pieceCount := len(parts)
if pieceCount > 1 {
qualifiedIdentifier = parts[1]
}

return EVMLocation{}, qualifiedIdentifier, nil
},
)
}

// we might break this event into two (tx included /tx executed) if size becomes an issue
type TransactionExecutedPayload struct {
BlockHeight uint64
Expand All @@ -34,24 +105,6 @@ type TransactionExecutedPayload struct {
Result *Result
}

var transactionExecutedEventCadenceType = &cadence.EventType{
Location: stdlib.FlowLocation{},
QualifiedIdentifier: string(EventTypeTransactionExecuted),
Fields: []cadence.Field{
cadence.NewField("blockHeight", cadence.UInt64Type{}),
cadence.NewField("transactionHash", cadence.StringType{}),
cadence.NewField("transaction", cadence.StringType{}),
cadence.NewField("failed", cadence.BoolType{}),
cadence.NewField("transactionType", cadence.UInt8Type{}),
cadence.NewField("gasConsumed", cadence.UInt64Type{}),
cadence.NewField("deployedContractAddress", cadence.StringType{}),
cadence.NewField("returnedValue", cadence.StringType{}),
cadence.NewField("logs", cadence.StringType{}),
},
}

// todo add decoder for events from cadence to evm payload

func (p *TransactionExecutedPayload) CadenceEvent() (cadence.Event, error) {
var encodedLogs []byte
var err error
Expand All @@ -62,21 +115,35 @@ func (p *TransactionExecutedPayload) CadenceEvent() (cadence.Event, error) {
}
}

fields := []cadence.Value{
cadence.NewUInt64(p.BlockHeight),
cadence.String(p.TxHash.String()),
cadence.String(hex.EncodeToString(p.TxEncoded)),
cadence.NewBool(p.Result.Failed),
cadence.NewUInt8(p.Result.TxType),
cadence.NewUInt64(p.Result.GasConsumed),
cadence.String(hex.EncodeToString(p.Result.DeployedContractAddress.Bytes())),
cadence.String(hex.EncodeToString(p.Result.ReturnedValue)),
cadence.String(hex.EncodeToString(encodedLogs)),
}

return cadence.
NewEvent(fields).
WithType(transactionExecutedEventCadenceType), nil
return cadence.Event{
EventType: cadence.NewEventType(
EVMLocation{},
string(EventTypeTransactionExecuted),
[]cadence.Field{
cadence.NewField("blockHeight", cadence.UInt64Type{}),
cadence.NewField("transactionHash", cadence.StringType{}),
cadence.NewField("transaction", cadence.StringType{}),
cadence.NewField("failed", cadence.BoolType{}),
cadence.NewField("transactionType", cadence.UInt8Type{}),
cadence.NewField("gasConsumed", cadence.UInt64Type{}),
cadence.NewField("deployedContractAddress", cadence.StringType{}),
cadence.NewField("returnedValue", cadence.StringType{}),
cadence.NewField("logs", cadence.StringType{}),
},
nil,
),
Fields: []cadence.Value{
cadence.NewUInt64(p.BlockHeight),
cadence.String(p.TxHash.String()),
cadence.String(hex.EncodeToString(p.TxEncoded)),
cadence.NewBool(p.Result.Failed),
cadence.NewUInt8(p.Result.TxType),
cadence.NewUInt64(p.Result.GasConsumed),
cadence.String(hex.EncodeToString(p.Result.DeployedContractAddress.Bytes())),
cadence.String(hex.EncodeToString(p.Result.ReturnedValue)),
cadence.String(hex.EncodeToString(encodedLogs)),
},
}, nil
}

func NewTransactionExecutedEvent(
Expand All @@ -97,7 +164,7 @@ func NewTransactionExecutedEvent(
}

var blockExecutedEventCadenceType = &cadence.EventType{
Location: stdlib.FlowLocation{}, // todo create evm custom location
Location: EVMLocation{},
QualifiedIdentifier: string(EventTypeBlockExecuted),
Fields: []cadence.Field{
cadence.NewField("height", cadence.UInt64Type{}),
Expand All @@ -124,8 +191,8 @@ func (p *BlockExecutedEventPayload) CadenceEvent() (cadence.Event, error) {
fields := []cadence.Value{
cadence.NewUInt64(p.Block.Height),
cadence.NewUInt64(p.Block.TotalSupply),
cadence.String(p.Block.ReceiptRoot.String()),
cadence.String(p.Block.ParentBlockHash.String()),
cadence.String(p.Block.ReceiptRoot.String()),
cadence.NewArray(hashes).WithType(cadence.NewVariableSizedArrayType(cadence.StringType{})),
}

Expand Down
56 changes: 56 additions & 0 deletions fvm/fvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3156,4 +3156,60 @@ func TestEVM(t *testing.T) {
}
}),
)

t.Run("deploy contract code", newVMTest().
withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)).
run(func(
t *testing.T,
vm fvm.VM,
chain flow.Chain,
ctx fvm.Context,
snapshotTree snapshot.SnapshotTree,
) {
sc := systemcontracts.SystemContractsForChain(chain.ChainID())

txBody := flow.NewTransactionBody().
SetScript([]byte(fmt.Sprintf(`
import FungibleToken from %s
import FlowToken from %s
import EVM from %s
transaction() {
prepare(acc: AuthAccount) {
let vaultRef = acc.borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)
?? panic("Could not borrow reference to the owner's Vault!")
let acc <- EVM.createBridgedAccount()
let amount <- vaultRef.withdraw(amount: 0.0000001) as! @FlowToken.Vault
acc.deposit(from: <- amount)
destroy acc
}
}`,
sc.FungibleToken.Address.HexWithPrefix(),
sc.FlowToken.Address.HexWithPrefix(),
sc.FlowServiceAccount.Address.HexWithPrefix(), // TODO this should be sc.EVM.Address not found there???
))).
SetProposalKey(chain.ServiceAddress(), 0, 0).
AddAuthorizer(chain.ServiceAddress()).
SetPayer(chain.ServiceAddress())

err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain)
require.NoError(t, err)

ctx = fvm.NewContextFromParent(ctx, fvm.WithEVMEnabled(true))
_, output, err := vm.Run(
ctx,
fvm.Transaction(txBody, 0),
snapshotTree)

require.NoError(t, err)
require.NoError(t, output.Err)
require.Len(t, output.Events, 3)

evmLocation := types.EVMLocation{}
txExe, blockExe := output.Events[1], output.Events[2]
assert.Equal(t, evmLocation.TypeID(nil, string(types.EventTypeTransactionExecuted)), common.TypeID(txExe.Type))
assert.Equal(t, evmLocation.TypeID(nil, string(types.EventTypeBlockExecuted)), common.TypeID(blockExe.Type))
}),
)
}

0 comments on commit 420e295

Please sign in to comment.