Skip to content

Commit 7ef2fce

Browse files
devbuggingjribbink
andauthored
Update scheduled transaction usage (#873)
* remove uneeded methods * remove duplicated test * update usage of blueprints --------- Co-authored-by: Jordan Ribbink <[email protected]>
1 parent 8fc5805 commit 7ef2fce

File tree

3 files changed

+47
-410
lines changed

3 files changed

+47
-410
lines changed

emulator/blockchain.go

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,14 @@ import (
5252
"github.com/onflow/flow-go/access/validator"
5353
"github.com/onflow/flow-go/engine"
5454
"github.com/onflow/flow-go/fvm"
55+
"github.com/onflow/flow-go/fvm/blueprints"
5556
fvmcrypto "github.com/onflow/flow-go/fvm/crypto"
5657
"github.com/onflow/flow-go/fvm/environment"
5758
fvmerrors "github.com/onflow/flow-go/fvm/errors"
5859
"github.com/onflow/flow-go/fvm/meter"
5960
reusableRuntime "github.com/onflow/flow-go/fvm/runtime"
6061
"github.com/onflow/flow-go/fvm/storage/snapshot"
62+
"github.com/onflow/flow-go/fvm/systemcontracts"
6163
accessmodel "github.com/onflow/flow-go/model/access"
6264
flowgo "github.com/onflow/flow-go/model/flow"
6365
"github.com/onflow/flow-go/module/metrics"
@@ -1908,57 +1910,60 @@ func (b *Blockchain) executeSystemChunkTransaction() error {
19081910
func (b *Blockchain) executeScheduledTransactions(blockContext fvm.Context) ([]*types.TransactionResult, error) {
19091911
var results []*types.TransactionResult
19101912

1911-
serviceAddress := b.GetChain().ServiceAddress()
1912-
parentID := b.pendingBlock.parentID
19131913
// disable checks for signatures and keys since we are executing a system transaction
19141914
ctx := fvm.NewContextFromParent(
19151915
blockContext,
19161916
fvm.WithAuthorizationChecksEnabled(false),
19171917
fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
1918+
fvm.WithTransactionFeesEnabled(false),
19181919
)
19191920

19201921
// process scheduled transactions out-of-band (do not alter collections)
1921-
processTx := processScheduledTransaction(serviceAddress, parentID)
1922+
processTx, err := blueprints.ProcessCallbacksTransaction(b.GetChain())
1923+
if err != nil {
1924+
return nil, err
1925+
}
1926+
19221927
executionSnapshot, output, err := b.vm.Run(
19231928
ctx,
1924-
fvm.Transaction(&processTx, uint32(len(b.pendingBlock.Transactions()))),
1929+
fvm.Transaction(processTx, uint32(len(b.pendingBlock.Transactions()))),
19251930
b.pendingBlock.ledgerState,
19261931
)
19271932
if err != nil {
1928-
return results, err
1933+
return nil, err
19291934
}
19301935
// record events and state changes
19311936
b.pendingBlock.events = append(b.pendingBlock.events, output.Events...)
19321937
if err := b.pendingBlock.ledgerState.Merge(executionSnapshot); err != nil {
1933-
return results, err
1938+
return nil, err
19341939
}
19351940

1936-
// filter to only process PendingExecution events
1937-
sdkEvents, err := convert.FlowEventsToSDK(output.Events)
1941+
executeTxs, err := blueprints.ExecuteCallbacksTransactions(b.GetChain(), output.Events)
19381942
if err != nil {
1939-
return results, err
1943+
return nil, err
19401944
}
1941-
pendingExecutionEvents := filterPendingExecutionEvents(sdkEvents, serviceAddress)
1942-
executeTxs, scheduledIDs, err := executeScheduledTransactions(pendingExecutionEvents, serviceAddress, parentID)
1945+
1946+
env := systemcontracts.SystemContractsForChain(b.GetChain().ChainID()).AsTemplateEnv()
1947+
scheduledIDs, err := parseScheduledIDs(env, output.Events)
19431948
if err != nil {
1944-
return results, err
1949+
return nil, err
19451950
}
19461951

19471952
// execute scheduled transactions out-of-band
19481953
for idx, tx := range executeTxs {
19491954
execSnapshot, execOutput, err := b.vm.Run(
19501955
ctx,
1951-
fvm.Transaction(&tx, uint32(len(b.pendingBlock.Transactions()))),
1956+
fvm.Transaction(tx, uint32(len(b.pendingBlock.Transactions()))),
19521957
b.pendingBlock.ledgerState,
19531958
)
19541959
if err != nil {
1955-
return results, err
1960+
return nil, err
19561961
}
19571962

19581963
// Print scheduled transaction result (labeled), including app-level scheduled tx id
19591964
schedResult, err := convert.VMTransactionResultToEmulator(tx.ID(), execOutput)
19601965
if err != nil {
1961-
return results, err
1966+
return nil, err
19621967
}
19631968
appScheduledID := ""
19641969
if idx < len(scheduledIDs) {
@@ -1968,7 +1973,7 @@ func (b *Blockchain) executeScheduledTransactions(blockContext fvm.Context) ([]*
19681973

19691974
b.pendingBlock.events = append(b.pendingBlock.events, execOutput.Events...)
19701975
if err := b.pendingBlock.ledgerState.Merge(execSnapshot); err != nil {
1971-
return results, err
1976+
return nil, err
19721977
}
19731978
}
19741979

emulator/scheduled_transactions.go

Lines changed: 26 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -23,157 +23,48 @@ import (
2323
"fmt"
2424

2525
"github.com/onflow/cadence"
26-
"github.com/onflow/cadence/common"
27-
jsoncdc "github.com/onflow/cadence/encoding/json"
26+
"github.com/onflow/cadence/encoding/ccf"
2827
"github.com/onflow/flow-core-contracts/lib/go/templates"
29-
flowsdk "github.com/onflow/flow-go-sdk"
28+
"github.com/onflow/flow-go/fvm/blueprints"
3029
flowgo "github.com/onflow/flow-go/model/flow"
3130
)
3231

33-
const (
34-
contractName = "FlowTransactionScheduler"
35-
pendingExecutionEventName = "PendingExecution"
36-
)
37-
38-
// filterPendingExecutionEvents filters events to only include PendingExecution events
39-
func filterPendingExecutionEvents(events []flowsdk.Event, serviceAddress flowgo.Address) []flowsdk.Event {
40-
var filteredEvents []flowsdk.Event
32+
// parseScheduledIDs parses ID of the scheduled transactions from the events
33+
func parseScheduledIDs(env templates.Environment, events flowgo.EventsList) ([]string, error) {
34+
const (
35+
processedCallbackIDFieldName = "id"
36+
processedCallbackEffortFieldName = "executionEffort"
37+
)
4138

42-
contractLocation := common.AddressLocation{
43-
Address: common.Address(serviceAddress),
44-
Name: contractName,
45-
}
46-
expectedEventType := string(contractLocation.TypeID(nil, fmt.Sprintf("%s.%s", contractName, pendingExecutionEventName)))
39+
ids := make([]string, 0)
4740

4841
for _, event := range events {
49-
if event.Type == expectedEventType {
50-
filteredEvents = append(filteredEvents, event)
42+
if blueprints.PendingExecutionEventType(env) != event.Type {
43+
continue
5144
}
52-
}
53-
54-
return filteredEvents
55-
}
56-
57-
// todo: replace all the functions bellow with flow-go implementation once it's done
58-
// issue: https://github.com/onflow/flow-emulator/issues/829
59-
60-
func processScheduledTransaction(
61-
serviceAddress flowgo.Address,
62-
parentID flowgo.Identifier,
63-
) flowgo.TransactionBody {
64-
env := templates.Environment{
65-
FlowTransactionSchedulerAddress: serviceAddress.HexWithPrefix(),
66-
}
67-
68-
script := templates.GenerateProcessTransactionScript(env)
69-
70-
txBuilder := flowgo.NewTransactionBodyBuilder().
71-
SetScript(script).
72-
SetComputeLimit(defaultTransactionMaxGasLimit).
73-
SetPayer(serviceAddress).
74-
AddAuthorizer(serviceAddress).
75-
SetReferenceBlockID(parentID)
76-
77-
tx, err := txBuilder.Build()
78-
if err != nil {
79-
panic(err)
80-
}
81-
82-
return *tx
83-
}
84-
85-
func executeScheduledTransactions(
86-
pendingExecutionEvents []flowsdk.Event,
87-
serviceAddress flowgo.Address,
88-
parentID flowgo.Identifier,
89-
) (transactions []flowgo.TransactionBody, scheduledIDs []string, err error) {
90-
transactions = make([]flowgo.TransactionBody, 0)
91-
scheduledIDs = make([]string, 0)
92-
93-
env := templates.Environment{
94-
FlowTransactionSchedulerAddress: serviceAddress.HexWithPrefix(),
95-
}
96-
97-
script := templates.GenerateExecuteTransactionScript(env)
9845

99-
for _, e := range pendingExecutionEvents {
100-
id, _, limit, _, err := parseSchedulerPendingExecutionEvent(e, serviceAddress)
46+
eventData, err := ccf.Decode(nil, event.Payload)
10147
if err != nil {
102-
return nil, nil, err
48+
return nil, fmt.Errorf("failed to decode event: %w", err)
10349
}
10450

105-
txBuilder := flowgo.NewTransactionBodyBuilder().
106-
SetScript(script).
107-
AddArgument(id).
108-
SetPayer(serviceAddress).
109-
AddAuthorizer(serviceAddress).
110-
SetReferenceBlockID(parentID).
111-
SetComputeLimit(limit)
112-
113-
tx, err := txBuilder.Build()
114-
if err != nil {
115-
return nil, nil, err
51+
cadenceEvent, ok := eventData.(cadence.Event)
52+
if !ok {
53+
return nil, fmt.Errorf("event data is not a cadence event")
11654
}
11755

118-
transactions = append(transactions, *tx)
119-
scheduledIDs = append(scheduledIDs, string(id))
120-
}
121-
122-
return transactions, scheduledIDs, nil
123-
}
124-
125-
// parseSchedulerPendingExecutionEvent parses flow event that is emitted during scheduler
126-
// marking the transaction as pending execution.
127-
// Returns:
128-
// - ID of the transaction encoded as bytes
129-
// - The priority of the transaction
130-
// - execution effort
131-
// - The address of the account that owns the transaction
132-
// - error in case the event type is not correct
133-
func parseSchedulerPendingExecutionEvent(event flowsdk.Event, serviceAddress flowgo.Address) ([]byte, uint8, uint64, cadence.Address, error) {
134-
contractLocation := common.AddressLocation{
135-
Address: common.Address(serviceAddress),
136-
Name: contractName,
137-
}
138-
transactionPendingExecutionEvent := contractLocation.TypeID(nil, fmt.Sprintf("%s.%s", contractName, pendingExecutionEventName))
139-
140-
const (
141-
IDField = "id"
142-
priorityField = "priority"
143-
executionField = "executionEffort"
144-
ownerField = "transactionHandlerOwner"
145-
)
146-
147-
if event.Type != string(transactionPendingExecutionEvent) {
148-
return nil, 0, 0, cadence.BytesToAddress([]byte{}), fmt.Errorf("invalid event type, got: %s, expected: %s", event.Type, transactionPendingExecutionEvent)
149-
}
150-
151-
id, ok := event.Value.SearchFieldByName(IDField).(cadence.UInt64)
152-
if !ok {
153-
return nil, 0, 0, cadence.BytesToAddress([]byte{}), fmt.Errorf("invalid ID value type: %v", id)
154-
}
155-
156-
encodedID, err := jsoncdc.Encode(id)
157-
if err != nil {
158-
return nil, 0, 0, cadence.BytesToAddress([]byte{}), err
159-
}
160-
161-
priorityRaw, ok := event.Value.SearchFieldByName(priorityField).(cadence.UInt8)
162-
if !ok {
163-
return nil, 0, 0, cadence.BytesToAddress([]byte{}), fmt.Errorf("invalid priority value type: %v", priorityRaw)
164-
}
165-
priority := uint8(priorityRaw)
56+
idValue := cadence.SearchFieldByName(
57+
cadenceEvent,
58+
processedCallbackIDFieldName,
59+
)
16660

167-
effortRaw, ok := event.Value.SearchFieldByName(executionField).(cadence.UInt64)
168-
if !ok {
169-
return nil, 0, 0, cadence.BytesToAddress([]byte{}), fmt.Errorf("invalid effort value type: %v", effortRaw)
170-
}
171-
executionEffort := uint64(effortRaw)
61+
id, ok := idValue.(cadence.UInt64)
62+
if !ok {
63+
return nil, fmt.Errorf("id is not uint64")
64+
}
17265

173-
owner, ok := event.Value.SearchFieldByName(ownerField).(cadence.Address)
174-
if !ok {
175-
return nil, 0, 0, cadence.BytesToAddress([]byte{}), fmt.Errorf("invalid owner value type: %v", owner)
66+
ids = append(ids, fmt.Sprintf("%d", id))
17667
}
17768

178-
return encodedID, priority, executionEffort, owner, nil
69+
return ids, nil
17970
}

0 commit comments

Comments
 (0)