From 115ccee9cae920ee5bc309537cf43a9b152a1cf9 Mon Sep 17 00:00:00 2001 From: Ramtin Mehdizadeh Seraj Date: Thu, 10 Dec 2020 16:26:47 -0800 Subject: [PATCH] Adding event limit per transaction (#128) --- .../computation/computer/computer.go | 10 +- .../computation/computer/computer_test.go | 17 +- engine/execution/testutil/fixtures.go | 12 +- fvm/README.md | 6 +- fvm/account.go | 1 + fvm/accounts_test.go | 57 ++++--- fvm/astCache_test.go | 14 +- fvm/bootstrap.go | 4 + fvm/context.go | 27 ++- fvm/env.go | 137 ++++++++++----- fvm/errors.go | 21 +++ fvm/fees.go | 3 +- fvm/fvm_test.go | 158 ++++++++++++++---- fvm/script.go | 4 +- fvm/transaction.go | 31 +--- fvm/transaction_test.go | 4 +- fvm/uuids.go | 1 - go.mod | 2 +- go.sum | 4 +- integration/go.mod | 2 +- integration/go.sum | 4 +- module/chunks/chunkVerifier.go | 8 +- 22 files changed, 351 insertions(+), 176 deletions(-) diff --git a/engine/execution/computation/computer/computer.go b/engine/execution/computation/computer/computer.go index 355042a99ff..10af27d6faa 100644 --- a/engine/execution/computation/computer/computer.go +++ b/engine/execution/computation/computer/computer.go @@ -241,7 +241,7 @@ func (e *blockComputer) executeTransaction( txView := collectionView.NewChild() - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, txIndex) err := e.vm.Run(ctx, tx, txView) @@ -255,12 +255,6 @@ func (e *blockComputer) executeTransaction( return nil, flow.TransactionResult{}, 0, fmt.Errorf("failed to execute transaction: %w", err) } - txEvents, err := tx.ConvertEvents(txIndex) - - if err != nil { - return nil, flow.TransactionResult{}, 0, fmt.Errorf("failed to create flow events: %w", err) - } - txResult := flow.TransactionResult{ TransactionID: tx.ID, } @@ -282,5 +276,5 @@ func (e *blockComputer) executeTransaction( collectionView.MergeView(txView) } - return txEvents, txResult, tx.GasUsed, nil + return tx.Events, txResult, tx.GasUsed, nil } diff --git a/engine/execution/computation/computer/computer_test.go b/engine/execution/computation/computer/computer_test.go index 7013122f969..71db0b3bb4a 100644 --- a/engine/execution/computation/computer/computer_test.go +++ b/engine/execution/computation/computer/computer_test.go @@ -6,7 +6,6 @@ import ( "math/rand" "testing" - "github.com/onflow/cadence" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -94,15 +93,13 @@ func TestBlockExecutor_ExecuteBlock(t *testing.T) { // create a block with 2 collections with 2 transactions each block := generateBlock(collectionCount, transactionsPerCollection) - // create dummy events - events := generateEvents(eventsPerTransaction) - vm.On("Run", mock.Anything, mock.Anything, mock.Anything). Run(func(args mock.Arguments) { tx := args[1].(*fvm.TransactionProcedure) tx.Err = &fvm.MissingPayerError{} - tx.Events = events + // create dummy events + tx.Events = generateEvents(eventsPerTransaction, tx.TxIndex) }). Return(nil). Times(totalTransactionCount) @@ -125,7 +122,7 @@ func TestBlockExecutor_ExecuteBlock(t *testing.T) { for expectedTxIndex := 0; expectedTxIndex < totalTransactionCount; expectedTxIndex++ { for expectedEventIndex := 0; expectedEventIndex < eventsPerTransaction; expectedEventIndex++ { e := result.Events[k] - assert.EqualValues(t, expectedEventIndex, e.EventIndex) + assert.EqualValues(t, expectedEventIndex, int(e.EventIndex)) assert.EqualValues(t, expectedTxIndex, e.TransactionIndex) k++ } @@ -194,13 +191,11 @@ func generateCollection(transactionCount int) *entity.CompleteCollection { } } -func generateEvents(eventCount int) []cadence.Event { - events := make([]cadence.Event, eventCount) +func generateEvents(eventCount int, txIndex uint32) []flow.Event { + events := make([]flow.Event, eventCount) for i := 0; i < eventCount; i++ { // creating some dummy event - event := cadence.Event{EventType: &cadence.EventType{ - Identifier: "whatever", - }} + event := flow.Event{Type: "whatever", EventIndex: uint32(i), TransactionIndex: txIndex} events[i] = event } return events diff --git a/engine/execution/testutil/fixtures.go b/engine/execution/testutil/fixtures.go index 9717d2b8569..30e58b8cfb1 100644 --- a/engine/execution/testutil/fixtures.go +++ b/engine/execution/testutil/fixtures.go @@ -168,7 +168,7 @@ func CreateAccountsWithSimpleAddresses( serviceAddress := chain.ServiceAddress() - for _, privateKey := range privateKeys { + for i, privateKey := range privateKeys { accountKey := privateKey.PublicKey(fvm.AccountKeyWeightThreshold) encAccountKey, _ := flow.EncodeRuntimeAccountPublicKey(accountKey) cadAccountKey := BytesToCadenceArray(encAccountKey) @@ -179,7 +179,7 @@ func CreateAccountsWithSimpleAddresses( AddArgument(encCadAccountKey). AddAuthorizer(serviceAddress) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, uint32(i)) err := vm.Run(ctx, tx, ledger) if err != nil { return nil, err @@ -192,8 +192,12 @@ func CreateAccountsWithSimpleAddresses( var addr flow.Address for _, event := range tx.Events { - if event.EventType.ID() == string(flow.EventAccountCreated) { - addr = event.Fields[0].ToGoValue().([8]byte) + if event.Type == flow.EventAccountCreated { + data, err := jsoncdc.Decode(event.Payload) + if err != nil { + return nil, errors.New("error decoding events") + } + addr = flow.Address(data.(cadence.Event).Fields[0].(cadence.Address)) break } diff --git a/fvm/README.md b/fvm/README.md index 58a07429419..76d0c5ce835 100644 --- a/fvm/README.md +++ b/fvm/README.md @@ -23,7 +23,8 @@ tx := flow.NewTransactionBody(). ctx := fvm.NewContext() ledger := state.NewMapLedger() -txProc := fvm.Transaction(tx) +txIndex := uint32(0) +txProc := fvm.Transaction(tx, txIndex) err := vm.Run(ctx, txProc, ledger) if err != nil { @@ -48,8 +49,9 @@ A transaction procedure can be created from a `flow.TransactionBody`: ```go var tx flow.TransactionBody +var txIndex uint32 -i := fvm.Transaction(tx) +i := fvm.Transaction(tx, txIndex) ``` A transaction procedure has the following steps: diff --git a/fvm/account.go b/fvm/account.go index f7cf61bbe2d..e46cf75a115 100644 --- a/fvm/account.go +++ b/fvm/account.go @@ -77,6 +77,7 @@ func initFlowTokenTransaction(accountAddress, serviceAddress flow.Address) *Tran flow.NewTransactionBody(). SetScript([]byte(fmt.Sprintf(initFlowTokenTransactionTemplate, serviceAddress))). AddAuthorizer(accountAddress), + 0, ) } diff --git a/fvm/accounts_test.go b/fvm/accounts_test.go index f369730361c..ef8fe3b9a65 100644 --- a/fvm/accounts_test.go +++ b/fvm/accounts_test.go @@ -28,15 +28,17 @@ func createAccount(t *testing.T, vm *fvm.VirtualMachine, chain flow.Chain, ctx f SetScript([]byte(createAccountTransaction)). AddAuthorizer(chain.ServiceAddress()) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err := vm.Run(ctx, tx, ledger) require.NoError(t, err) require.NoError(t, tx.Err) - require.Equal(t, string(flow.EventAccountCreated), tx.Events[0].EventType.TypeID) + require.Equal(t, flow.EventAccountCreated, tx.Events[0].Type) - address := flow.Address(tx.Events[0].Fields[0].(cadence.Address)) + data, err := jsoncdc.Decode(tx.Events[0].Payload) + require.NoError(t, err) + address := flow.Address(data.(cadence.Event).Fields[0].(cadence.Address)) return address } @@ -55,7 +57,7 @@ func addAccountKey( AddArgument(cadencePublicKey). AddAuthorizer(address) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err := vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -83,7 +85,7 @@ func addAccountCreator( SetScript(script). AddAuthorizer(chain.ServiceAddress()) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err := vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -110,7 +112,7 @@ func removeAccountCreator( SetScript(script). AddAuthorizer(chain.ServiceAddress()) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err := vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -238,7 +240,7 @@ func TestCreateAccount(t *testing.T) { SetScript([]byte(createAccountTransaction)). AddAuthorizer(payer) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err := vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -246,9 +248,12 @@ func TestCreateAccount(t *testing.T) { assert.NoError(t, tx.Err) require.Len(t, tx.Events, 1) - assert.Equal(t, string(flow.EventAccountCreated), tx.Events[0].EventType.TypeID) - address := flow.Address(tx.Events[0].Fields[0].(cadence.Address)) + require.Equal(t, flow.EventAccountCreated, tx.Events[0].Type) + + data, err := jsoncdc.Decode(tx.Events[0].Payload) + require.NoError(t, err) + address := flow.Address(data.(cadence.Event).Fields[0].(cadence.Address)) account, err := vm.GetAccount(ctx, address, ledger) require.NoError(t, err) @@ -266,7 +271,7 @@ func TestCreateAccount(t *testing.T) { SetScript([]byte(createMultipleAccountsTransaction)). AddAuthorizer(payer) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err := vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -276,9 +281,11 @@ func TestCreateAccount(t *testing.T) { require.Len(t, tx.Events, count) for i := 0; i < count; i++ { - require.Equal(t, string(flow.EventAccountCreated), tx.Events[i].EventType.TypeID) + require.Equal(t, flow.EventAccountCreated, tx.Events[i].Type) - address := flow.Address(tx.Events[i].Fields[0].(cadence.Address)) + data, err := jsoncdc.Decode(tx.Events[i].Payload) + require.NoError(t, err) + address := flow.Address(data.(cadence.Event).Fields[0].(cadence.Address)) account, err := vm.GetAccount(ctx, address, ledger) require.NoError(t, err) @@ -303,7 +310,7 @@ func TestCreateAccount_WithRestrictedAccountCreation(t *testing.T) { SetScript([]byte(createAccountTransaction)). AddAuthorizer(payer) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err := vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -318,7 +325,7 @@ func TestCreateAccount_WithRestrictedAccountCreation(t *testing.T) { SetScript([]byte(createAccountTransaction)). AddAuthorizer(chain.ServiceAddress()) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err := vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -337,7 +344,7 @@ func TestCreateAccount_WithRestrictedAccountCreation(t *testing.T) { SetPayer(payer). AddAuthorizer(payer) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err := vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -355,7 +362,7 @@ func TestCreateAccount_WithRestrictedAccountCreation(t *testing.T) { SetScript([]byte(createAccountTransaction)). AddAuthorizer(payer) - validTx := fvm.Transaction(txBody) + validTx := fvm.Transaction(txBody, 0) err := vm.Run(ctx, validTx, ledger) require.NoError(t, err) @@ -364,7 +371,7 @@ func TestCreateAccount_WithRestrictedAccountCreation(t *testing.T) { removeAccountCreator(t, vm, chain, ctx, ledger, payer) - invalidTx := fvm.Transaction(txBody) + invalidTx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, invalidTx, ledger) require.NoError(t, err) @@ -411,7 +418,7 @@ func TestAddAccountKey(t *testing.T) { AddArgument(cadencePublicKey). AddAuthorizer(address) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -449,7 +456,7 @@ func TestAddAccountKey(t *testing.T) { AddArgument(publicKey2Arg). AddAuthorizer(address) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -490,7 +497,7 @@ func TestAddAccountKey(t *testing.T) { AddArgument(invalidPublicKeyArg). AddAuthorizer(address) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -521,7 +528,7 @@ func TestAddAccountKey(t *testing.T) { AddArgument(publicKey2Arg). AddAuthorizer(address) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -570,7 +577,7 @@ func TestRemoveAccountKey(t *testing.T) { require.NoError(t, err) assert.Len(t, before.Keys, keyCount) - for _, keyIndex := range []int{-1, keyCount, keyCount + 1} { + for i, keyIndex := range []int{-1, keyCount, keyCount + 1} { keyIndexArg, err := jsoncdc.Encode(cadence.NewInt(keyIndex)) require.NoError(t, err) @@ -579,7 +586,7 @@ func TestRemoveAccountKey(t *testing.T) { AddArgument(keyIndexArg). AddAuthorizer(address) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, uint32(i)) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -620,7 +627,7 @@ func TestRemoveAccountKey(t *testing.T) { AddArgument(keyIndexArg). AddAuthorizer(address) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -664,7 +671,7 @@ func TestRemoveAccountKey(t *testing.T) { txBody.AddArgument(keyIndexArg) } - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) diff --git a/fvm/astCache_test.go b/fvm/astCache_test.go index e1354f11b82..a2b4d8b320e 100644 --- a/fvm/astCache_test.go +++ b/fvm/astCache_test.go @@ -46,7 +46,7 @@ func TestTransactionASTCache(t *testing.T) { ledger := testutil.RootBootstrappedLedger(vm, ctx) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -150,7 +150,7 @@ func TestTransactionWithProgramASTCache(t *testing.T) { // Run the Use import (FT Vault resource) transaction - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -224,7 +224,7 @@ func TestTransactionWithProgramASTCacheConsistentRegTouches(t *testing.T) { // Run the Use import (FT Vault resource) transaction - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -294,9 +294,9 @@ func BenchmarkTransactionWithProgramASTCache(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - for _, txBody := range txs { + for j, txBody := range txs { // Run the Use import (FT Vault resource) transaction. - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, uint32(j)) err := vm.Run(ctx, tx, ledger) assert.NoError(b, err) @@ -365,9 +365,9 @@ func BenchmarkTransactionWithoutProgramASTCache(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - for _, txBody := range txs { + for j, txBody := range txs { // Run the Use import (FT Vault resource) transaction. - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, uint32(j)) err := vm.Run(ctx, tx, ledger) assert.NoError(b, err) diff --git a/fvm/bootstrap.go b/fvm/bootstrap.go index b8202d2ef60..6c7dd002665 100644 --- a/fvm/bootstrap.go +++ b/fvm/bootstrap.go @@ -240,6 +240,7 @@ func deployContractTransaction(address flow.Address, contract []byte, contractNa flow.NewTransactionBody(). SetScript([]byte(fmt.Sprintf(deployContractTransactionTemplate, contractName, hex.EncodeToString(contract)))). AddAuthorizer(address), + 0, ) } @@ -249,6 +250,7 @@ func deployFlowTokenTransaction(flowToken, service flow.Address, contract []byte SetScript([]byte(fmt.Sprintf(deployFlowTokenTransactionTemplate, hex.EncodeToString(contract)))). AddAuthorizer(flowToken). AddAuthorizer(service), + 0, ) } @@ -258,6 +260,7 @@ func deployFlowFeesTransaction(flowFees, service flow.Address, contract []byte) SetScript([]byte(fmt.Sprintf(deployFlowFeesTransactionTemplate, hex.EncodeToString(contract)))). AddAuthorizer(flowFees). AddAuthorizer(service), + 0, ) } @@ -275,6 +278,7 @@ func mintFlowTokenTransaction( SetScript([]byte(fmt.Sprintf(mintFlowTokenTransactionTemplate, fungibleToken, flowToken))). AddArgument(initialSupplyArg). AddAuthorizer(service), + 0, ) } diff --git a/fvm/context.go b/fvm/context.go index 86e5a9ef9a9..9e69ac17071 100644 --- a/fvm/context.go +++ b/fvm/context.go @@ -14,10 +14,12 @@ type Context struct { Blocks Blocks Metrics *MetricsCollector GasLimit uint64 + EventCollectionByteSizeLimit uint64 BlockHeader *flow.Header ServiceAccountEnabled bool RestrictedAccountCreationEnabled bool RestrictedDeploymentEnabled bool + CadenceLoggingEnabled bool SetValueHandler SetValueHandler SignatureVerifier SignatureVerifier TransactionProcessors []TransactionProcessor @@ -48,7 +50,9 @@ func newContext(ctx Context, opts ...Option) Context { const AccountKeyWeightThreshold = 1000 -const defaultGasLimit = 100000 +const defaultGasLimit = 100_000 + +const defaultEventCollectionByteSizeLimit = 128_000 // 128KB func defaultContext(logger zerolog.Logger) Context { return Context{ @@ -57,10 +61,12 @@ func defaultContext(logger zerolog.Logger) Context { Blocks: nil, Metrics: nil, GasLimit: defaultGasLimit, + EventCollectionByteSizeLimit: defaultEventCollectionByteSizeLimit, BlockHeader: nil, ServiceAccountEnabled: true, RestrictedAccountCreationEnabled: true, RestrictedDeploymentEnabled: true, + CadenceLoggingEnabled: false, SetValueHandler: nil, SignatureVerifier: NewDefaultSignatureVerifier(), TransactionProcessors: []TransactionProcessor{ @@ -103,6 +109,14 @@ func WithGasLimit(limit uint64) Option { } } +// WithEventCollectionSizeLimit sets the event collection byte size limit for a virtual machine context. +func WithEventCollectionSizeLimit(limit uint64) Option { + return func(ctx Context) Context { + ctx.EventCollectionByteSizeLimit = limit + return ctx + } +} + // WithBlockHeader sets the block header for a virtual machine context. // // The VM uses the header to provide current block information to the Cadence runtime, @@ -135,7 +149,7 @@ func WithMetricsCollector(mc *MetricsCollector) Option { } } -// WithTransactionSignatureVerifier sets the transaction processors for a +// WithTransactionProcessors sets the transaction processors for a // virtual machine context. func WithTransactionProcessors(processors ...TransactionProcessor) Option { return func(ctx Context) Context { @@ -161,6 +175,15 @@ func WithRestrictedDeployment(enabled bool) Option { } } +// WithCadenceLogging enables or disables Cadence logging for a +// virtual machine context. +func WithCadenceLogging(enabled bool) Option { + return func(ctx Context) Context { + ctx.CadenceLoggingEnabled = enabled + return ctx + } +} + // WithRestrictedAccountCreation enables or disables restricted account creation for a // virtual machine context func WithRestrictedAccountCreation(enabled bool) Option { diff --git a/fvm/env.go b/fvm/env.go index 499d20054d0..cde923210ae 100644 --- a/fvm/env.go +++ b/fvm/env.go @@ -29,22 +29,21 @@ type hostEnv struct { accounts *state.Accounts addressGenerator flow.AddressGenerator uuidGenerator *UUIDGenerator - runtime.Metrics - - events []cadence.Event - logs []string - - transactionEnv *transactionEnv - rng *rand.Rand + events []flow.Event + totalEventByteSize uint64 + logs []string + totalGasUsed uint64 + transactionEnv *transactionEnv + rng *rand.Rand } -func (e *hostEnv) Hash(data []byte, hashAlgorithm string) []byte { +func (e *hostEnv) Hash(data []byte, hashAlgorithm string) ([]byte, error) { hasher, err := crypto.NewHasher(crypto.StringToHashAlgorithm(hashAlgorithm)) if err != nil { panic(fmt.Errorf("cannot create hasher: %w", err)) } - return hasher.ComputeHash(data) + return hasher.ComputeHash(data), nil } func newEnvironment(ctx Context, ledger state.Ledger) (*hostEnv, error) { @@ -58,12 +57,13 @@ func newEnvironment(ctx Context, ledger state.Ledger) (*hostEnv, error) { uuidGenerator := NewUUIDGenerator(uuids) env := &hostEnv{ - ctx: ctx, - ledger: ledger, - Metrics: &noopMetricsCollector{}, - accounts: accounts, - addressGenerator: generator, - uuidGenerator: uuidGenerator, + ctx: ctx, + ledger: ledger, + Metrics: &noopMetricsCollector{}, + accounts: accounts, + addressGenerator: generator, + uuidGenerator: uuidGenerator, + totalEventByteSize: uint64(0), } if ctx.BlockHeader != nil { @@ -85,7 +85,7 @@ func (e *hostEnv) seedRNG(header *flow.Header) { e.rng = rand.New(source) } -func (e *hostEnv) setTransaction(vm *VirtualMachine, tx *flow.TransactionBody) { +func (e *hostEnv) setTransaction(vm *VirtualMachine, tx *flow.TransactionBody, txIndex uint32) { e.transactionEnv = newTransactionEnv( vm, e.ctx, @@ -93,10 +93,11 @@ func (e *hostEnv) setTransaction(vm *VirtualMachine, tx *flow.TransactionBody) { e.accounts, e.addressGenerator, tx, + txIndex, ) } -func (e *hostEnv) getEvents() []cadence.Event { +func (e *hostEnv) getEvents() []flow.Event { return e.events } @@ -140,7 +141,7 @@ func (e *hostEnv) GetStorageCapacity(_ common.Address) (value uint64, err error) func (e *hostEnv) ResolveLocation( identifiers []runtime.Identifier, location runtime.Location, -) []runtime.ResolvedLocation { +) ([]runtime.ResolvedLocation, error) { addressLocation, isAddress := location.(runtime.AddressLocation) @@ -153,7 +154,7 @@ func (e *hostEnv) ResolveLocation( Location: location, Identifiers: identifiers, }, - } + }, nil } // if the location is an address, @@ -171,7 +172,7 @@ func (e *hostEnv) ResolveLocation( // then return no resolved locations if len(contractNames) == 0 { - return nil + return nil, nil } identifiers = make([]ast.Identifier, len(contractNames)) @@ -198,7 +199,7 @@ func (e *hostEnv) ResolveLocation( } } - return resolvedLocations + return resolvedLocations, nil } func (e *hostEnv) GetCode(location runtime.Location) ([]byte, error) { @@ -244,22 +245,48 @@ func (e *hostEnv) CacheProgram(location ast.Location, program *ast.Program) erro return e.ctx.ASTCache.SetProgram(location, program) } -func (e *hostEnv) Log(message string) { - e.logs = append(e.logs, message) +func (e *hostEnv) Log(message string) error { + if e.ctx.CadenceLoggingEnabled { + e.logs = append(e.logs, message) + } + return nil } -func (e *hostEnv) EmitEvent(event cadence.Event) { - e.events = append(e.events, event) -} +func (e *hostEnv) EmitEvent(event cadence.Event) error { -func (e *hostEnv) GenerateUUID() uint64 { - uuid, err := e.uuidGenerator.GenerateUUID() + payload, err := jsoncdc.Encode(event) if err != nil { - // TODO - Return error once Cadence interface accommodates it - panic(fmt.Errorf("cannot get UUID: %w", err)) + return fmt.Errorf("failed to json encode a cadence event: %w", err) + } + + e.totalEventByteSize += uint64(len(payload)) + + // skip limit if payer is service account + if e.transactionEnv.tx.Payer != e.ctx.Chain.ServiceAddress() { + if e.totalEventByteSize > e.ctx.EventCollectionByteSizeLimit { + return &EventLimitExceededError{ + TotalByteSize: e.totalEventByteSize, + Limit: e.ctx.EventCollectionByteSizeLimit, + } + } } - return uuid + flowEvent := flow.Event{ + Type: flow.EventType(event.EventType.ID()), + TransactionID: e.transactionEnv.TxID(), + TransactionIndex: e.transactionEnv.TxIndex(), + EventIndex: uint32(len(e.events)), + Payload: payload, + } + + e.events = append(e.events, flowEvent) + return nil +} + +func (e *hostEnv) GenerateUUID() (uint64, error) { + // TODO add not supported + uuid, err := e.uuidGenerator.GenerateUUID() + return uuid, err } func (e *hostEnv) GetComputationLimit() uint64 { @@ -270,11 +297,16 @@ func (e *hostEnv) GetComputationLimit() uint64 { return e.ctx.GasLimit } +func (e *hostEnv) SetComputationUsed(used uint64) error { + e.totalGasUsed = used + return nil +} + func (e *hostEnv) DecodeArgument(b []byte, t cadence.Type) (cadence.Value, error) { return jsoncdc.Decode(b) } -func (e *hostEnv) Events() []cadence.Event { +func (e *hostEnv) Events() []flow.Event { return e.events } @@ -289,7 +321,7 @@ func (e *hostEnv) VerifySignature( rawPublicKey []byte, rawSigAlgo string, rawHashAlgo string, -) bool { +) (bool, error) { valid, err := verifySignatureFromRuntime( e.ctx.SignatureVerifier, signature, @@ -305,7 +337,7 @@ func (e *hostEnv) VerifySignature( panic(err) } - return valid + return valid, nil } func (e *hostEnv) HighLevelStorageEnabled() bool { @@ -319,24 +351,22 @@ func (e *hostEnv) SetCadenceValue(owner common.Address, key string, value cadenc // Block Environment Functions // GetCurrentBlockHeight returns the current block height. -func (e *hostEnv) GetCurrentBlockHeight() uint64 { +func (e *hostEnv) GetCurrentBlockHeight() (uint64, error) { if e.ctx.BlockHeader == nil { - panic("GetCurrentBlockHeight is not supported by this environment") + return 0, errors.New("GetCurrentBlockHeight is not supported by this environment") } - - return e.ctx.BlockHeader.Height + return e.ctx.BlockHeader.Height, nil } // UnsafeRandom returns a random uint64, where the process of random number derivation is not cryptographically // secure. -func (e *hostEnv) UnsafeRandom() uint64 { +func (e *hostEnv) UnsafeRandom() (uint64, error) { if e.rng == nil { - panic("UnsafeRandom is not supported by this environment") + return 0, errors.New("UnsafeRandom is not supported by this environment") } - buf := make([]byte, 8) _, _ = e.rng.Read(buf) // Always succeeds, no need to check error - return binary.LittleEndian.Uint64(buf) + return binary.LittleEndian.Uint64(buf), nil } func runtimeBlockFromHeader(header *flow.Header) runtime.Block { @@ -449,12 +479,12 @@ func (e *transactionEnv) RemoveAccountContractCode(address runtime.Address, name return e.accounts.DeleteContract(name, accountAddress) } -func (e *hostEnv) GetSigningAccounts() []runtime.Address { +func (e *hostEnv) GetSigningAccounts() ([]runtime.Address, error) { if e.transactionEnv == nil { - panic("GetSigningAccounts is not supported by this environment") + return nil, errors.New("GetSigningAccounts is not supported by this environment") } - return e.transactionEnv.GetSigningAccounts() + return e.transactionEnv.GetSigningAccounts(), nil } // Transaction Environment @@ -466,7 +496,10 @@ type transactionEnv struct { accounts *state.Accounts addressGenerator flow.AddressGenerator - tx *flow.TransactionBody + tx *flow.TransactionBody + txIndex uint32 + txID flow.Identifier + authorizers []runtime.Address } @@ -477,6 +510,8 @@ func newTransactionEnv( accounts *state.Accounts, addressGenerator flow.AddressGenerator, tx *flow.TransactionBody, + txIndex uint32, + ) *transactionEnv { return &transactionEnv{ vm: vm, @@ -485,6 +520,8 @@ func newTransactionEnv( accounts: accounts, addressGenerator: addressGenerator, tx: tx, + txIndex: txIndex, + txID: tx.ID(), } } @@ -500,6 +537,14 @@ func (e *transactionEnv) GetSigningAccounts() []runtime.Address { return e.authorizers } +func (e *transactionEnv) TxIndex() uint32 { + return e.txIndex +} + +func (e *transactionEnv) TxID() flow.Identifier { + return e.txID +} + func (e *transactionEnv) GetComputationLimit() uint64 { return e.tx.GasLimit } diff --git a/fvm/errors.go b/fvm/errors.go index d1610391ef0..de64ac69b78 100644 --- a/fvm/errors.go +++ b/fvm/errors.go @@ -25,6 +25,8 @@ const ( errCodeInvalidHashAlgorithm = 10 + errCodeEventLimitExceededError = 20 + errCodeExecution = 100 ) @@ -198,6 +200,25 @@ func (e *InvalidProposalKeyMissingSignatureError) Code() uint32 { return errCodeInvalidProposalKeyMissingSignature } +// EventLimitExceededError indicates that the transaction has produced events with size more than limit. +type EventLimitExceededError struct { + TotalByteSize uint64 + Limit uint64 +} + +func (e *EventLimitExceededError) Error() string { + return fmt.Sprintf( + "total event byte size (%d) exceeds limit (%d)", + e.TotalByteSize, + e.Limit, + ) +} + +// Code returns the error code for this error +func (e *EventLimitExceededError) Code() uint32 { + return errCodeEventLimitExceededError +} + // An InvalidHashAlgorithmError indicates that a given key has an invalid hash algorithm. type InvalidHashAlgorithmError struct { Address flow.Address diff --git a/fvm/fees.go b/fvm/fees.go index ea938bbc2e7..91e38bc900d 100644 --- a/fvm/fees.go +++ b/fvm/fees.go @@ -56,7 +56,7 @@ func deductAccountCreationFeeTransaction( SetScript([]byte(fmt.Sprintf(script, serviceAddress))). AddAuthorizer(accountAddress) - return Transaction(tx) + return Transaction(tx, 0) } func deductTransactionFeeTransaction(accountAddress, serviceAddress flow.Address) *TransactionProcedure { @@ -64,5 +64,6 @@ func deductTransactionFeeTransaction(accountAddress, serviceAddress flow.Address flow.NewTransactionBody(). SetScript([]byte(fmt.Sprintf(deductTransactionFeeTransactionTemplate, serviceAddress))). AddAuthorizer(accountAddress), + 0, ) } diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index f0622b06ab2..1670935bd0d 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -2,6 +2,7 @@ package fvm_test import ( "crypto/rand" + "encoding/hex" "fmt" "strconv" "testing" @@ -71,7 +72,7 @@ func TestBlockContext_ExecuteTransaction(t *testing.T) { cache, err := fvm.NewLRUASTCache(CacheSize) require.NoError(t, err) - ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache)) + ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache), fvm.WithCadenceLogging(true)) t.Run("Success", func(t *testing.T) { txBody := flow.NewTransactionBody(). @@ -87,7 +88,7 @@ func TestBlockContext_ExecuteTransaction(t *testing.T) { ledger := testutil.RootBootstrappedLedger(vm, ctx) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -120,7 +121,7 @@ func TestBlockContext_ExecuteTransaction(t *testing.T) { ledger := testutil.RootBootstrappedLedger(vm, ctx) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -144,7 +145,7 @@ func TestBlockContext_ExecuteTransaction(t *testing.T) { ledger := testutil.RootBootstrappedLedger(vm, ctx) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -170,7 +171,7 @@ func TestBlockContext_ExecuteTransaction(t *testing.T) { ledger := testutil.RootBootstrappedLedger(vm, ctx) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -178,7 +179,7 @@ func TestBlockContext_ExecuteTransaction(t *testing.T) { assert.NoError(t, tx.Err) require.Len(t, tx.Events, 1) - assert.EqualValues(t, flow.EventAccountCreated, tx.Events[0].EventType.ID()) + assert.EqualValues(t, flow.EventAccountCreated, tx.Events[0].Type) }) } @@ -192,7 +193,7 @@ func TestBlockContext_DeployContract(t *testing.T) { cache, err := fvm.NewLRUASTCache(CacheSize) require.NoError(t, err) - ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache)) + ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache), fvm.WithCadenceLogging(true)) t.Run("account update with set code succeeds as service account", func(t *testing.T) { ledger := testutil.RootBootstrappedLedger(vm, ctx) @@ -216,7 +217,7 @@ func TestBlockContext_DeployContract(t *testing.T) { err = testutil.SignEnvelope(txBody, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey) require.NoError(t, err) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -240,7 +241,7 @@ func TestBlockContext_DeployContract(t *testing.T) { err = testutil.SignTransaction(txBody, accounts[0], privateKeys[0], 0) require.NoError(t, err) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -269,7 +270,7 @@ func TestBlockContext_DeployContract(t *testing.T) { err = testutil.SignTransaction(txBody, accounts[0], privateKeys[0], 0) require.NoError(t, err) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -293,7 +294,7 @@ func TestBlockContext_ExecuteTransaction_WithArguments(t *testing.T) { cache, err := fvm.NewLRUASTCache(CacheSize) require.NoError(t, err) - ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache)) + ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache), fvm.WithCadenceLogging(true)) arg1, _ := jsoncdc.Encode(cadence.NewInt(42)) arg2, _ := jsoncdc.Encode(cadence.NewString("foo")) @@ -365,7 +366,7 @@ func TestBlockContext_ExecuteTransaction_WithArguments(t *testing.T) { err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) require.NoError(t, err) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -399,7 +400,7 @@ func TestBlockContext_ExecuteTransaction_GasLimit(t *testing.T) { cache, err := fvm.NewLRUASTCache(CacheSize) require.NoError(t, err) - ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache)) + ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache), fvm.WithCadenceLogging(true)) var tests = []struct { label string @@ -446,7 +447,7 @@ func TestBlockContext_ExecuteTransaction_GasLimit(t *testing.T) { err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) require.NoError(t, err) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -474,7 +475,7 @@ func TestBlockContext_ExecuteScript(t *testing.T) { cache, err := fvm.NewLRUASTCache(CacheSize) require.NoError(t, err) - ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache)) + ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache), fvm.WithCadenceLogging(true)) t.Run("script success", func(t *testing.T) { code := []byte(` @@ -544,7 +545,7 @@ func TestBlockContext_GetBlockInfo(t *testing.T) { cache, err := fvm.NewLRUASTCache(CacheSize) require.NoError(t, err) - ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache)) + ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache), fvm.WithCadenceLogging(true)) blocks := new(fvmmock.Blocks) @@ -580,7 +581,7 @@ func TestBlockContext_GetBlockInfo(t *testing.T) { ledger := testutil.RootBootstrappedLedger(vm, ctx) require.NoError(t, err) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(blockCtx, tx, ledger) assert.NoError(t, err) @@ -641,7 +642,7 @@ func TestBlockContext_GetBlockInfo(t *testing.T) { assert.PanicsWithValue(t, interpreter.ExternalError{ Recovered: logPanic{}, }, func() { - _ = vm.Run(blockCtx, fvm.Transaction(tx), ledger) + _ = vm.Run(blockCtx, fvm.Transaction(tx, 0), ledger) }) }) @@ -675,7 +676,7 @@ func TestBlockContext_GetAccount(t *testing.T) { cache, err := fvm.NewLRUASTCache(CacheSize) require.NoError(t, err) - ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache)) + ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache), fvm.WithCadenceLogging(true)) sequenceNumber := uint64(0) @@ -700,7 +701,7 @@ func TestBlockContext_GetAccount(t *testing.T) { require.NoError(t, err) // execute the transaction - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) require.NoError(t, err) @@ -708,10 +709,12 @@ func TestBlockContext_GetAccount(t *testing.T) { assert.NoError(t, tx.Err) assert.Len(t, tx.Events, 2) - assert.EqualValues(t, flow.EventAccountCreated, tx.Events[0].EventType.ID()) + assert.EqualValues(t, flow.EventAccountCreated, tx.Events[0].Type) // read the address of the account created (e.g. "0x01" and convert it to flow.address) - address := flow.BytesToAddress(tx.Events[0].Fields[0].(cadence.Address).Bytes()) + data, err := jsoncdc.Decode(tx.Events[0].Payload) + require.NoError(t, err) + address := flow.Address(data.(cadence.Event).Fields[0].(cadence.Address)) return address, privateKey.PublicKey(fvm.AccountKeyWeightThreshold).PublicKey } @@ -771,7 +774,7 @@ func TestBlockContext_UnsafeRandom(t *testing.T) { header := flow.Header{Height: 42} - ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache), fvm.WithBlockHeader(&header)) + ctx := fvm.NewContext(zerolog.Nop(), fvm.WithChain(chain), fvm.WithASTCache(cache), fvm.WithBlockHeader(&header), fvm.WithCadenceLogging(true)) t.Run("works as transaction", func(t *testing.T) { txBody := flow.NewTransactionBody(). @@ -790,7 +793,7 @@ func TestBlockContext_UnsafeRandom(t *testing.T) { ledger := testutil.RootBootstrappedLedger(vm, ctx) require.NoError(t, err) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) assert.NoError(t, err) @@ -826,7 +829,7 @@ func TestBlockContext_ExecuteTransaction_CreateAccount_WithMonotonicAddresses(t err = testutil.SignTransactionAsServiceAccount(txBody, 0, chain) require.NoError(t, err) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctx, tx, ledger) assert.NoError(t, err) @@ -834,8 +837,13 @@ func TestBlockContext_ExecuteTransaction_CreateAccount_WithMonotonicAddresses(t assert.NoError(t, tx.Err) require.Len(t, tx.Events, 1) - require.Equal(t, string(flow.EventAccountCreated), tx.Events[0].EventType.TypeID) - assert.Equal(t, flow.HexToAddress("05"), flow.Address(tx.Events[0].Fields[0].(cadence.Address))) + require.Equal(t, flow.EventAccountCreated, tx.Events[0].Type) + + data, err := jsoncdc.Decode(tx.Events[0].Payload) + require.NoError(t, err) + address := flow.Address(data.(cadence.Event).Fields[0].(cadence.Address)) + + assert.Equal(t, flow.HexToAddress("05"), address) } func TestSignatureVerification(t *testing.T) { @@ -1118,7 +1126,7 @@ func TestWithServiceAccount(t *testing.T) { AddAuthorizer(chain.ServiceAddress()) t.Run("With service account enabled", func(t *testing.T) { - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctxA, tx, ledger) require.NoError(t, err) @@ -1130,7 +1138,7 @@ func TestWithServiceAccount(t *testing.T) { t.Run("With service account disabled", func(t *testing.T) { ctxB := fvm.NewContextFromParent(ctxA, fvm.WithServiceAccount(false)) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, 0) err = vm.Run(ctxB, tx, ledger) require.NoError(t, err) @@ -1139,3 +1147,95 @@ func TestWithServiceAccount(t *testing.T) { assert.NoError(t, tx.Err) }) } + +func TestEventLimits(t *testing.T) { + rt := runtime.NewInterpreterRuntime() + chain := flow.Mainnet.Chain() + vm := fvm.New(rt) + + ctx := fvm.NewContext( + zerolog.Nop(), + fvm.WithChain(chain), + fvm.WithTransactionProcessors( + fvm.NewTransactionInvocator(zerolog.Nop()), + ), + ) + + ledger := testutil.RootBootstrappedLedger(vm, ctx) + + testContract := ` + access(all) contract TestContract { + access(all) event LargeEvent(value: Int256, str: String, list: [UInt256], dic: {String: String}) + access(all) fun EmitEvent() { + var s: Int256 = 1024102410241024 + var i = 0 + + while i < 20 { + emit LargeEvent(value: s, str: s.toString(), list:[], dic:{s.toString():s.toString()}) + i = i + 1 + } + } + } + ` + + deployingContractScriptTemplate := ` + transaction { + prepare(signer: AuthAccount) { + let code = "%s".decodeHex() + signer.contracts.add( + name: "TestContract", + code: code + ) + } + } + ` + + ctx = fvm.NewContext( + zerolog.Nop(), + fvm.WithChain(chain), + fvm.WithEventCollectionSizeLimit(2), + fvm.WithTransactionProcessors( + fvm.NewTransactionInvocator(zerolog.Nop()), + ), + ) + + txBody := flow.NewTransactionBody(). + SetScript([]byte(fmt.Sprintf(deployingContractScriptTemplate, hex.EncodeToString([]byte(testContract))))). + SetPayer(chain.ServiceAddress()). + AddAuthorizer(chain.ServiceAddress()) + + tx := fvm.Transaction(txBody, 0) + err := vm.Run(ctx, tx, ledger) + require.NoError(t, err) + + txBody = flow.NewTransactionBody(). + SetScript([]byte(fmt.Sprintf(` + import TestContract from 0x%s + transaction { + prepare(acct: AuthAccount) {} + execute { + TestContract.EmitEvent() + } + }`, chain.ServiceAddress()))). + AddAuthorizer(chain.ServiceAddress()) + + t.Run("With limits", func(t *testing.T) { + txBody.Payer = unittest.RandomAddressFixture() + tx := fvm.Transaction(txBody, 0) + err := vm.Run(ctx, tx, ledger) + require.NoError(t, err) + + // transaction should fail due to event size limit + assert.Error(t, tx.Err) + }) + + t.Run("With service account as payer", func(t *testing.T) { + txBody.Payer = chain.ServiceAddress() + tx := fvm.Transaction(txBody, 0) + err := vm.Run(ctx, tx, ledger) + require.NoError(t, err) + + // transaction should not fail due to event size limit + assert.NoError(t, tx.Err) + }) +} diff --git a/fvm/script.go b/fvm/script.go index fb324b2ef83..7690b452a13 100644 --- a/fvm/script.go +++ b/fvm/script.go @@ -24,7 +24,7 @@ type ScriptProcedure struct { Arguments [][]byte Value cadence.Value Logs []string - Events []cadence.Event + Events []flow.Event // TODO: report gas consumption: https://github.com/dapperlabs/flow-go/issues/4139 GasUsed uint64 Err Error @@ -85,7 +85,7 @@ func (i ScriptInvocator) Process( proc.Value = value proc.Logs = env.getLogs() - proc.Events = env.getEvents() + proc.Events = env.Events() return nil } diff --git a/fvm/transaction.go b/fvm/transaction.go index 486fc9908ab..925823a697d 100644 --- a/fvm/transaction.go +++ b/fvm/transaction.go @@ -5,8 +5,6 @@ import ( "strings" "github.com/davecgh/go-spew/spew" - "github.com/onflow/cadence" - jsoncdc "github.com/onflow/cadence/encoding/json" "github.com/onflow/cadence/runtime" "github.com/rs/zerolog" @@ -16,18 +14,20 @@ import ( const TopShotContractAddress = "0b2a3299cc857e29" -func Transaction(tx *flow.TransactionBody) *TransactionProcedure { +func Transaction(tx *flow.TransactionBody, txIndex uint32) *TransactionProcedure { return &TransactionProcedure{ ID: tx.ID(), Transaction: tx, + TxIndex: txIndex, } } type TransactionProcedure struct { ID flow.Identifier Transaction *flow.TransactionBody + TxIndex uint32 Logs []string - Events []cadence.Event + Events []flow.Event // TODO: report gas consumption: https://github.com/dapperlabs/flow-go/issues/4139 GasUsed uint64 Err Error @@ -54,27 +54,6 @@ func (proc *TransactionProcedure) Run(vm *VirtualMachine, ctx Context, ledger st return nil } -func (proc *TransactionProcedure) ConvertEvents(txIndex uint32) ([]flow.Event, error) { - flowEvents := make([]flow.Event, len(proc.Events)) - - for i, event := range proc.Events { - payload, err := jsoncdc.Encode(event) - if err != nil { - return nil, fmt.Errorf("failed to encode event: %w", err) - } - - flowEvents[i] = flow.Event{ - Type: flow.EventType(event.EventType.ID()), - TransactionID: proc.ID, - TransactionIndex: txIndex, - EventIndex: uint32(i), - Payload: payload, - } - } - - return flowEvents, nil -} - type TransactionInvocator struct { logger zerolog.Logger } @@ -95,7 +74,7 @@ func (i *TransactionInvocator) Process( if err != nil { return err } - env.setTransaction(vm, proc.Transaction) + env.setTransaction(vm, proc.Transaction, proc.TxIndex) location := runtime.TransactionLocation(proc.ID[:]) diff --git a/fvm/transaction_test.go b/fvm/transaction_test.go index 9eb44d2c589..9618bba9974 100644 --- a/fvm/transaction_test.go +++ b/fvm/transaction_test.go @@ -48,7 +48,7 @@ func prepare(executeTxResult error) (error, *bytes.Buffer) { runtime := mockRuntime{executeTxResult: executeTxResult} vm := New(runtime) - proc := Transaction(&flow.TransactionBody{}) + proc := Transaction(&flow.TransactionBody{}, 0) ledger := state.NewMapLedger() @@ -174,7 +174,7 @@ func TestTopShotSafety(t *testing.T) { Authorizers: nil, PayloadSignatures: nil, EnvelopeSignatures: nil, - }) + }, 0) topShotContractAddress := flow.HexToAddress(TopShotContractAddress) diff --git a/fvm/uuids.go b/fvm/uuids.go index 135d5384384..15608fbdfaf 100644 --- a/fvm/uuids.go +++ b/fvm/uuids.go @@ -18,7 +18,6 @@ func NewUUIDGenerator(uuids *state.UUIDs) *UUIDGenerator { func (u *UUIDGenerator) GenerateUUID() (uint64, error) { uuid, err := u.uuids.GetUUID() if err != nil { - // TODO - Return error once Cadence interface accommodates it return 0, fmt.Errorf("cannot get UUID: %w", err) } diff --git a/go.mod b/go.mod index 5eda85cd6f5..adeda3e1f14 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/libp2p/go-libp2p-transport-upgrader v0.3.0 github.com/libp2p/go-tcp-transport v0.2.1 github.com/multiformats/go-multiaddr v0.3.1 - github.com/onflow/cadence v0.10.3 + github.com/onflow/cadence v0.10.4 github.com/onflow/flow-core-contracts/lib/go/contracts v0.3.1-0.20201122012505-4061d358b8db github.com/onflow/flow-go-sdk v0.12.2 github.com/onflow/flow-go/crypto v0.12.0 diff --git a/go.sum b/go.sum index 9afa68374fd..8d9c82b73de 100644 --- a/go.sum +++ b/go.sum @@ -704,8 +704,8 @@ github.com/onflow/cadence v0.4.0-beta1/go.mod h1:gaPtSctdMzT5NAoJgzsRuwUkdgRswVH github.com/onflow/cadence v0.4.0/go.mod h1:gaPtSctdMzT5NAoJgzsRuwUkdgRswVHsRXFNNmCTn3I= github.com/onflow/cadence v0.10.2 h1:uBFhdlp0blYCddZTrnCjbLEVl/aYq1/9iP949KxzfbI= github.com/onflow/cadence v0.10.2/go.mod h1:ORAnWydDsrefAUazeD1g+l7vjNwEuJAcZ7bMz1KnSbg= -github.com/onflow/cadence v0.10.3 h1:X8FJJGTteuobXjWgIwZfx9ov1TLC/JZOrzYRJDderjM= -github.com/onflow/cadence v0.10.3/go.mod h1:ORAnWydDsrefAUazeD1g+l7vjNwEuJAcZ7bMz1KnSbg= +github.com/onflow/cadence v0.10.4 h1:0td4jBvB9+2wZ+qx294HmUuuQYfo9HYivD4OHdmjDhY= +github.com/onflow/cadence v0.10.4/go.mod h1:ORAnWydDsrefAUazeD1g+l7vjNwEuJAcZ7bMz1KnSbg= github.com/onflow/flow-core-contracts/lib/go/contracts v0.3.1-0.20201122012505-4061d358b8db h1:iMuIiGtc9EIE8RVSXHH+qFf/yITMT1yXQtXjFK19OW4= github.com/onflow/flow-core-contracts/lib/go/contracts v0.3.1-0.20201122012505-4061d358b8db/go.mod h1:yuFiT2+dZm42smG7XZQlMgZyb31hn5dvLrIDq0/PVc8= github.com/onflow/flow-ft/contracts v0.1.3/go.mod h1:IKe3yEurEKpg/J15q5WBlHkuMmt1iRECSHgnIa1gvRw= diff --git a/integration/go.mod b/integration/go.mod index 90359bacf3f..38399f038b5 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -9,7 +9,7 @@ require ( github.com/docker/go-connections v0.4.0 github.com/go-openapi/strfmt v0.19.5 // indirect github.com/jedib0t/go-pretty v4.3.0+incompatible - github.com/onflow/cadence v0.10.3 + github.com/onflow/cadence v0.10.4 github.com/onflow/flow-go v0.11.1 // replaced by version on-disk github.com/onflow/flow-go-sdk v0.12.2 github.com/onflow/flow-go/crypto v0.12.0 // replaced by version on-disk diff --git a/integration/go.sum b/integration/go.sum index dd9a70d3c0b..dff10fd9ae2 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -783,8 +783,8 @@ github.com/onflow/cadence v0.4.0-beta1/go.mod h1:gaPtSctdMzT5NAoJgzsRuwUkdgRswVH github.com/onflow/cadence v0.4.0/go.mod h1:gaPtSctdMzT5NAoJgzsRuwUkdgRswVHsRXFNNmCTn3I= github.com/onflow/cadence v0.10.2 h1:uBFhdlp0blYCddZTrnCjbLEVl/aYq1/9iP949KxzfbI= github.com/onflow/cadence v0.10.2/go.mod h1:ORAnWydDsrefAUazeD1g+l7vjNwEuJAcZ7bMz1KnSbg= -github.com/onflow/cadence v0.10.3 h1:X8FJJGTteuobXjWgIwZfx9ov1TLC/JZOrzYRJDderjM= -github.com/onflow/cadence v0.10.3/go.mod h1:ORAnWydDsrefAUazeD1g+l7vjNwEuJAcZ7bMz1KnSbg= +github.com/onflow/cadence v0.10.4 h1:0td4jBvB9+2wZ+qx294HmUuuQYfo9HYivD4OHdmjDhY= +github.com/onflow/cadence v0.10.4/go.mod h1:ORAnWydDsrefAUazeD1g+l7vjNwEuJAcZ7bMz1KnSbg= github.com/onflow/flow-core-contracts/lib/go/contracts v0.3.1-0.20201122012505-4061d358b8db h1:iMuIiGtc9EIE8RVSXHH+qFf/yITMT1yXQtXjFK19OW4= github.com/onflow/flow-core-contracts/lib/go/contracts v0.3.1-0.20201122012505-4061d358b8db/go.mod h1:yuFiT2+dZm42smG7XZQlMgZyb31hn5dvLrIDq0/PVc8= github.com/onflow/flow-ft/contracts v0.1.3/go.mod h1:IKe3yEurEKpg/J15q5WBlHkuMmt1iRECSHgnIa1gvRw= diff --git a/module/chunks/chunkVerifier.go b/module/chunks/chunkVerifier.go index 89cb6518b9b..55ff47a5df1 100644 --- a/module/chunks/chunkVerifier.go +++ b/module/chunks/chunkVerifier.go @@ -46,8 +46,8 @@ func (fcv *ChunkVerifier) Verify(vc *verification.VerifiableChunkData) ([]byte, } transactions := make([]*fvm.TransactionProcedure, 0) - for _, txBody := range vc.Collection.Transactions { - tx := fvm.Transaction(txBody) + for i, txBody := range vc.Collection.Transactions { + tx := fvm.Transaction(txBody, uint32(i)) transactions = append(transactions, tx) } @@ -66,7 +66,7 @@ func (fcv *ChunkVerifier) SystemChunkVerify(vc *verification.VerifiableChunkData // transaction body of system chunk txBody := fvm.SystemChunkTransaction(fcv.vmCtx.Chain.ServiceAddress()) - tx := fvm.Transaction(txBody) + tx := fvm.Transaction(txBody, uint32(0)) transactions := []*fvm.TransactionProcedure{tx} return fcv.verifyTransactions(vc.Chunk, vc.ChunkDataPack, vc.Result, vc.Header, transactions, vc.EndState) @@ -139,7 +139,7 @@ func (fcv *ChunkVerifier) verifyTransactions(chunk *flow.Chunk, for i, tx := range transactions { txView := chunkView.NewChild() - // tx := fvm.Transaction(txBody) + // tx := fvm.Transaction(txBody, uint32(i)) err := fcv.vm.Run(blockCtx, tx, txView) if err != nil {