Skip to content

Commit 08e28b9

Browse files
authored
Merge pull request #6199 from onflow/ramtin/evm-delay-block-commit
[Flow EVM] delay EVM block proposal commitment till system chunk execution
2 parents 9e7fe2c + 926230a commit 08e28b9

File tree

16 files changed

+292
-238
lines changed

16 files changed

+292
-238
lines changed

engine/execution/computation/computer/computer_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ func TestBlockExecutor_ExecuteBlock(t *testing.T) {
378378

379379
// include all fees. System chunk should ignore them
380380
contextOptions := []fvm.Option{
381+
fvm.WithEVMEnabled(true),
381382
fvm.WithTransactionFeesEnabled(true),
382383
fvm.WithAccountStorageLimit(true),
383384
fvm.WithBlocks(&environment.NoopBlockFinder{}),
@@ -1223,6 +1224,7 @@ func (f *FixedAddressGenerator) AddressCount() uint64 {
12231224
func Test_ExecutingSystemCollection(t *testing.T) {
12241225

12251226
execCtx := fvm.NewContext(
1227+
fvm.WithEVMEnabled(true),
12261228
fvm.WithChain(flow.Localnet.Chain()),
12271229
fvm.WithBlocks(&environment.NoopBlockFinder{}),
12281230
)
@@ -1245,8 +1247,8 @@ func Test_ExecutingSystemCollection(t *testing.T) {
12451247

12461248
noopCollector := metrics.NewNoopCollector()
12471249

1248-
expectedNumberOfEvents := 3
1249-
expectedEventSize := 1497
1250+
expectedNumberOfEvents := 4
1251+
expectedEventSize := 1961
12501252

12511253
// bootstrapping does not cache programs
12521254
expectedCachedPrograms := 0

engine/execution/computation/programs_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ func TestPrograms_TestBlockForks(t *testing.T) {
207207
chain := flow.Emulator.Chain()
208208
vm := fvm.NewVirtualMachine()
209209
execCtx := fvm.NewContext(
210+
fvm.WithEVMEnabled(true),
210211
fvm.WithBlockHeader(block.Header),
211212
fvm.WithBlocks(blockProvider{map[uint64]*flow.Block{0: &block}}),
212213
fvm.WithChain(chain))

engine/execution/state/bootstrap/bootstrap_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func TestBootstrapLedger(t *testing.T) {
5353
}
5454

5555
func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) {
56-
expectedStateCommitmentBytes, _ := hex.DecodeString("48460c42a43f700562d6c17408af8b52823774cc24fe795148c8e37b62f77615")
56+
expectedStateCommitmentBytes, _ := hex.DecodeString("a738c835be7196f62900595d186557ba56d5ee4221b0a108782967c5d1d110d7")
5757
expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes)
5858
require.NoError(t, err)
5959

fvm/blueprints/scripts/systemChunkTransactionTemplate.cdc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import FlowEpoch from "FlowEpoch"
22
import NodeVersionBeacon from "NodeVersionBeacon"
33
import RandomBeaconHistory from "RandomBeaconHistory"
4+
import EVM from "EVM"
45

56
transaction {
67
prepare(serviceAccount: auth(BorrowValue) &Account) {
@@ -17,5 +18,10 @@ transaction {
1718
.borrow<&RandomBeaconHistory.Heartbeat>(from: RandomBeaconHistory.HeartbeatStoragePath)
1819
?? panic("Couldn't borrow RandomBeaconHistory.Heartbeat Resource")
1920
randomBeaconHistoryHeartbeat.heartbeat(randomSourceHistory: randomSourceHistory())
21+
22+
let evmHeartbeat = serviceAccount.storage.borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat)
23+
if evmHeartbeat != nil { // skip if not available
24+
evmHeartbeat!.heartbeat()
25+
}
2026
}
2127
}

fvm/blueprints/system.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package blueprints
22

33
import (
44
_ "embed"
5+
"strings"
56

67
"github.com/onflow/flow-core-contracts/lib/go/templates"
78

@@ -17,17 +18,31 @@ const SystemChunkTransactionGasLimit = 100_000_000
1718
//go:embed scripts/systemChunkTransactionTemplate.cdc
1819
var systemChunkTransactionTemplate string
1920

21+
// TODO: when the EVM contract is moved to the flow-core-contracts, we can
22+
// just directly use the replace address functionality of the templates package.
23+
24+
var placeholderEVMAddress = "\"EVM\""
25+
26+
func prepareSystemContractCode(chainID flow.ChainID) string {
27+
sc := systemcontracts.SystemContractsForChain(chainID)
28+
code := templates.ReplaceAddresses(
29+
systemChunkTransactionTemplate,
30+
sc.AsTemplateEnv(),
31+
)
32+
code = strings.ReplaceAll(
33+
code,
34+
placeholderEVMAddress,
35+
sc.EVMContract.Address.HexWithPrefix(),
36+
)
37+
return code
38+
}
39+
2040
// SystemChunkTransaction creates and returns the transaction corresponding to the
2141
// system chunk for the given chain.
2242
func SystemChunkTransaction(chain flow.Chain) (*flow.TransactionBody, error) {
23-
contracts := systemcontracts.SystemContractsForChain(chain.ChainID())
24-
2543
tx := flow.NewTransactionBody().
2644
SetScript(
27-
[]byte(templates.ReplaceAddresses(
28-
systemChunkTransactionTemplate,
29-
contracts.AsTemplateEnv(),
30-
)),
45+
[]byte(prepareSystemContractCode(chain.ChainID())),
3146
).
3247
// The heartbeat resources needed by the system tx have are on the service account,
3348
// therefore, the service account is the only authorizer needed.

fvm/blueprints/system_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ func TestSystemChunkTransactionHash(t *testing.T) {
1818

1919
// this is formatted in a way that the resulting error message is easy to copy-paste into the test.
2020
expectedHashes := []chainHash{
21-
{chainId: "flow-mainnet", expectedHash: "0a7ea89ad32d79a30b91b4c1202230a1e29310e1b92e01c76d036d2e3839159b"},
22-
{chainId: "flow-testnet", expectedHash: "368434cb7c792c3c35647f30aa90aae5798a45efcf2ff6abb7123b70c1e7850c"},
23-
{chainId: "flow-previewnet", expectedHash: "e90268cb6e8385d9eb50f2956f47c1c5f77a7b3111de2f66756b2a48855e05ce"},
24-
{chainId: "flow-emulator", expectedHash: "c6ccd6b805adcfaa6f9719f1dc71c831c40712977f12d82332ba23e2cb499475"},
21+
{chainId: "flow-mainnet", expectedHash: "0e56f890392ad3f2a0dcc7a0e859b6d41f3c17556aa85a0f8831ae25a6537e39"},
22+
{chainId: "flow-testnet", expectedHash: "69c922177af520e8ecaf0ba97cb174a3ebbd03de6d5b5d1f6b6c043a9638dba3"},
23+
{chainId: "flow-previewnet", expectedHash: "2ec3b6b7e7d70a651600eb80f775cb57613a0a168c847b70040c469f09066a55"},
24+
{chainId: "flow-emulator", expectedHash: "7dcc0daaecebc7be33b068ed5c8da0620c89fd896abfc498fc0cc32a261aab1f"},
2525
}
2626

2727
var actualHashes []chainHash

fvm/evm/evm_test.go

Lines changed: 84 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import (
77
"math/big"
88
"testing"
99

10-
"github.com/onflow/cadence/runtime/common"
11-
1210
"github.com/onflow/cadence/encoding/ccf"
1311
gethTypes "github.com/onflow/go-ethereum/core/types"
1412
gethParams "github.com/onflow/go-ethereum/params"
@@ -103,49 +101,25 @@ func TestEVMRun(t *testing.T) {
103101
require.NoError(t, err)
104102
require.NoError(t, output.Err)
105103
require.NotEmpty(t, state.WriteSet)
104+
snapshot = snapshot.Append(state)
106105

107-
// assert event fiedls are correct
108-
require.Len(t, output.Events, 2)
109-
110-
blockEvent := output.Events[1]
111-
112-
assert.Equal(
113-
t,
114-
common.NewAddressLocation(
115-
nil,
116-
common.Address(sc.EVMContract.Address),
117-
string(types.EventTypeBlockExecuted),
118-
).ID(),
119-
string(blockEvent.Type),
120-
)
121-
122-
ev, err := ccf.Decode(nil, blockEvent.Payload)
123-
require.NoError(t, err)
124-
cadenceEvent, ok := ev.(cadence.Event)
125-
require.True(t, ok)
126-
127-
blockEventPayload, err := types.DecodeBlockEventPayload(cadenceEvent)
128-
require.NoError(t, err)
129-
require.NotEmpty(t, blockEventPayload.Hash)
130-
106+
// assert event fields are correct
107+
require.Len(t, output.Events, 1)
131108
txEvent := output.Events[0]
132109

133-
assert.Equal(
134-
t,
135-
common.NewAddressLocation(
136-
nil,
137-
common.Address(sc.EVMContract.Address),
138-
string(types.EventTypeTransactionExecuted),
139-
).ID(),
140-
string(txEvent.Type),
141-
)
110+
// commit block
111+
blockEventPayload, snapshot := callEVMHeartBeat(t,
112+
ctx,
113+
vm,
114+
snapshot)
142115

143-
ev, err = ccf.Decode(nil, txEvent.Payload)
144-
require.NoError(t, err)
145-
cadenceEvent, ok = ev.(cadence.Event)
146-
require.True(t, ok)
116+
require.NotEmpty(t, blockEventPayload.Hash)
117+
require.Equal(t, uint64(43785), blockEventPayload.TotalGasUsed)
118+
require.NotEmpty(t, blockEventPayload.Hash)
119+
require.Len(t, blockEventPayload.TransactionHashes, 1)
120+
require.NotEmpty(t, blockEventPayload.ReceiptRoot)
147121

148-
txEventPayload, err := types.DecodeTransactionEventPayload(cadenceEvent)
122+
txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address)
149123
require.NoError(t, err)
150124

151125
txPayload, err := types.CadenceUInt8ArrayValueToBytes(txEventPayload.Payload)
@@ -388,16 +362,11 @@ func TestEVMRun(t *testing.T) {
388362
require.NotEmpty(t, state.WriteSet)
389363

390364
txEvent := output.Events[0]
391-
ev, err := ccf.Decode(nil, txEvent.Payload)
392-
require.NoError(t, err)
393-
cadenceEvent, ok := ev.(cadence.Event)
394-
require.True(t, ok)
365+
txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address)
395366

396-
event, err := types.DecodeTransactionEventPayload(cadenceEvent)
397-
require.NoError(t, err)
398-
require.NotEmpty(t, event.Hash)
367+
require.NotEmpty(t, txEventPayload.Hash)
399368

400-
encodedLogs, err := types.CadenceUInt8ArrayValueToBytes(event.Logs)
369+
encodedLogs, err := types.CadenceUInt8ArrayValueToBytes(txEventPayload.Logs)
401370
require.NoError(t, err)
402371

403372
var logs []*gethTypes.Log
@@ -491,7 +460,10 @@ func TestEVMBatchRun(t *testing.T) {
491460
require.NoError(t, output.Err)
492461
require.NotEmpty(t, state.WriteSet)
493462

494-
require.Len(t, output.Events, batchCount+1) // +1 block executed
463+
// append the state
464+
snapshot = snapshot.Append(state)
465+
466+
require.Len(t, output.Events, batchCount)
495467
for i, event := range output.Events {
496468
if i == batchCount { // last one is block executed
497469
continue
@@ -519,31 +491,15 @@ func TestEVMBatchRun(t *testing.T) {
519491
assert.Equal(t, storedValues[i], last.Big().Int64())
520492
}
521493

522-
// last one is block executed, make sure TotalGasUsed is non-zero
523-
blockEvent := output.Events[batchCount]
524-
525-
assert.Equal(
526-
t,
527-
common.NewAddressLocation(
528-
nil,
529-
common.Address(sc.EVMContract.Address),
530-
string(types.EventTypeBlockExecuted),
531-
).ID(),
532-
string(blockEvent.Type),
533-
)
534-
535-
ev, err := ccf.Decode(nil, blockEvent.Payload)
536-
require.NoError(t, err)
537-
cadenceEvent, ok := ev.(cadence.Event)
538-
require.True(t, ok)
494+
// commit block
495+
blockEventPayload, snapshot := callEVMHeartBeat(t,
496+
ctx,
497+
vm,
498+
snapshot)
539499

540-
blockEventPayload, err := types.DecodeBlockEventPayload(cadenceEvent)
541-
require.NoError(t, err)
542500
require.NotEmpty(t, blockEventPayload.Hash)
543501
require.Equal(t, uint64(155513), blockEventPayload.TotalGasUsed)
544-
545-
// append the state
546-
snapshot = snapshot.Append(state)
502+
require.Len(t, blockEventPayload.TransactionHashes, 5)
547503

548504
// retrieve the values
549505
retrieveCode := []byte(fmt.Sprintf(
@@ -1003,38 +959,31 @@ func TestEVMAddressDeposit(t *testing.T) {
1003959
bal := getEVMAccountBalance(t, ctx, vm, snapshot, addr)
1004960
require.Equal(t, expectedBalance, bal)
1005961

1006-
// block executed event, make sure TotalGasUsed is non-zero
1007-
blockEvent := output.Events[3]
1008-
1009-
assert.Equal(
1010-
t,
1011-
common.NewAddressLocation(
1012-
nil,
1013-
common.Address(sc.EVMContract.Address),
1014-
string(types.EventTypeBlockExecuted),
1015-
).ID(),
1016-
string(blockEvent.Type),
1017-
)
1018-
1019-
ev, err := ccf.Decode(nil, blockEvent.Payload)
962+
// tx executed event
963+
txEvent := output.Events[2]
964+
txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address)
1020965
require.NoError(t, err)
1021-
cadenceEvent, ok := ev.(cadence.Event)
1022-
require.True(t, ok)
1023-
1024-
blockEventPayload, err := types.DecodeBlockEventPayload(cadenceEvent)
1025-
require.NoError(t, err)
1026-
require.NotEmpty(t, blockEventPayload.Hash)
1027-
require.Equal(t, uint64(21000), blockEventPayload.TotalGasUsed)
1028966

1029967
// deposit event
1030-
depositEvent := output.Events[4]
968+
depositEvent := output.Events[3]
1031969
depEv, err := types.FlowEventToCadenceEvent(depositEvent)
1032970
require.NoError(t, err)
1033971

1034972
depEvPayload, err := types.DecodeFLOWTokensDepositedEventPayload(depEv)
1035973
require.NoError(t, err)
1036974

1037975
require.Equal(t, types.OneFlow, depEvPayload.BalanceAfterInAttoFlow.Value)
976+
977+
// commit block
978+
blockEventPayload, _ := callEVMHeartBeat(t,
979+
ctx,
980+
vm,
981+
snapshot)
982+
983+
require.NotEmpty(t, blockEventPayload.Hash)
984+
require.Equal(t, uint64(21000), blockEventPayload.TotalGasUsed)
985+
require.Len(t, blockEventPayload.TransactionHashes, 1)
986+
require.Equal(t, txEventPayload.Hash, string(blockEventPayload.TransactionHashes[0]))
1038987
})
1039988
}
1040989

@@ -1197,7 +1146,7 @@ func TestCadenceOwnedAccountFunctionalities(t *testing.T) {
11971146
require.NoError(t, err)
11981147
require.NoError(t, output.Err)
11991148

1200-
withdrawEvent := output.Events[10]
1149+
withdrawEvent := output.Events[7]
12011150

12021151
ev, err := types.FlowEventToCadenceEvent(withdrawEvent)
12031152
require.NoError(t, err)
@@ -2557,12 +2506,52 @@ func setupCOA(
25572506
snap = snap.Append(es)
25582507

25592508
// 3rd event is the cadence owned account created event
2560-
coaAddress, err := types.COAAddressFromFlowCOACreatedEvent(sc.EVMContract.Address, output.Events[2])
2509+
coaAddress, err := types.COAAddressFromFlowCOACreatedEvent(sc.EVMContract.Address, output.Events[1])
25612510
require.NoError(t, err)
25622511

25632512
return coaAddress, snap
25642513
}
25652514

2515+
func callEVMHeartBeat(
2516+
t *testing.T,
2517+
ctx fvm.Context,
2518+
vm fvm.VM,
2519+
snap snapshot.SnapshotTree,
2520+
) (*types.BlockEventPayload, snapshot.SnapshotTree) {
2521+
sc := systemcontracts.SystemContractsForChain(ctx.Chain.ChainID())
2522+
2523+
heartBeatCode := []byte(fmt.Sprintf(
2524+
`
2525+
import EVM from %s
2526+
transaction {
2527+
prepare(serviceAccount: auth(BorrowValue) &Account) {
2528+
let evmHeartbeat = serviceAccount.storage
2529+
.borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat)
2530+
?? panic("Couldn't borrow EVM.Heartbeat Resource")
2531+
evmHeartbeat.heartbeat()
2532+
}
2533+
}
2534+
`,
2535+
sc.EVMContract.Address.HexWithPrefix(),
2536+
))
2537+
tx := fvm.Transaction(
2538+
flow.NewTransactionBody().
2539+
SetScript(heartBeatCode).
2540+
AddAuthorizer(sc.FlowServiceAccount.Address),
2541+
0)
2542+
2543+
state, output, err := vm.Run(ctx, tx, snap)
2544+
require.NoError(t, err)
2545+
require.NoError(t, output.Err)
2546+
require.NotEmpty(t, state.WriteSet)
2547+
snap = snap.Append(state)
2548+
2549+
// validate block event
2550+
require.Len(t, output.Events, 1)
2551+
blockEvent := output.Events[0]
2552+
return BlockEventToPayload(t, blockEvent, sc.EVMContract.Address), snap
2553+
}
2554+
25662555
func getFlowAccountBalance(
25672556
t *testing.T,
25682557
ctx fvm.Context,

0 commit comments

Comments
 (0)