Skip to content

Commit

Permalink
update v2 to use newest onflow packages
Browse files Browse the repository at this point in the history
  • Loading branch information
bthaile committed Jun 5, 2024
1 parent 26cd9b2 commit e5aa3b8
Show file tree
Hide file tree
Showing 9 changed files with 2,518 additions and 375 deletions.
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Go Process",
"type": "go",
"request": "attach",
"mode": "local",
"processId": "${command:pickProcess}"
}
]
}
279 changes: 279 additions & 0 deletions blockchain/emulator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
/*
* Flow Playground
*
* Copyright 2019 Dapper Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package blockchain

import (
"fmt"

"github.com/getsentry/sentry-go"
"github.com/onflow/flow-emulator/convert"
emu "github.com/onflow/flow-emulator/emulator"
"github.com/onflow/flow-emulator/storage/memstore"
"github.com/onflow/flow-emulator/types"
flowsdk "github.com/onflow/flow-go-sdk"
"github.com/onflow/flow-go-sdk/crypto"
"github.com/onflow/flow-go-sdk/templates"
"github.com/onflow/flow-go/model/flow"
"github.com/pkg/errors"
)

// blockchain interface defines an abstract API for communication with the blockchain. It hides complexity from the
// consumer and communicates using flow native types.
type blockchain2 interface {
// executeTransaction builds and executes a transaction and uses provided authorizers for signing.
executeTransaction(
script string,
arguments []string,
authorizers []flowsdk.Address,
) (*types.TransactionResult, *flowsdk.Transaction, error)

// executeScript executes a provided script with the arguments.
executeScript(
script string,
arguments []string,
) (*types.ScriptResult, error)

// createAccount creates a new account and returns it along with transaction and result.
createAccount() (*flowsdk.Account, *flowsdk.Transaction, *types.TransactionResult, error)

// getAccount gets an account by the address and also returns its storage.
getAccount(address flowsdk.Address) (*flowsdk.Account, error)

// deployContract deploys a contract on the provided address and returns transaction and result.
deployContract(address flowsdk.Address, script string) (*types.TransactionResult, *flowsdk.Transaction, error)

// getLatestBlock height from the network.
getLatestBlockHeight() (int, error)
}

var _ blockchain2 = &emulator{}

type emulator struct {
blockchain *emu.Blockchain
}

func newEmulator() (*emulator, error) {
blockchain, err := emu.New(
emu.WithStore(memstore.New()),
emu.WithTransactionValidationEnabled(false),
emu.WithSimpleAddresses(),
emu.WithStorageLimitEnabled(false),
emu.WithTransactionFeesEnabled(false),
)
if err != nil {
return nil, errors.Wrap(err, "failed to create a new emulator instance")
}

return &emulator{
blockchain: blockchain,
}, nil
}

func (e *emulator) executeTransaction(
script string,
arguments []string,
authorizers []flowsdk.Address,
) (*types.TransactionResult, *flowsdk.Transaction, error) {
tx := &flowsdk.Transaction{}
tx.Script = []byte(script)

args, err := parseArguments(arguments)
if err != nil {
return nil, nil, err
}
tx.Arguments = args

return e.sendTransaction(tx, authorizers)
}

func (e *emulator) executeScript(script string, arguments []string) (*types.ScriptResult, error) {
args, err := parseArguments(arguments)
if err != nil {
return nil, err
}

return e.blockchain.ExecuteScript([]byte(script), args)
}

func (e *emulator) createAccount() (*flowsdk.Account, *flowsdk.Transaction, *types.TransactionResult, error) {
payer := e.blockchain.ServiceKey().Address

key := flowsdk.NewAccountKey().
FromPrivateKey(e.blockchain.ServiceKey().PrivateKey).
SetHashAlgo(crypto.SHA3_256).
SetWeight(flowsdk.AccountKeyWeightThreshold)

tx, err := templates.CreateAccount([]*flowsdk.AccountKey{key}, nil, payer)
if err != nil {
return nil, nil, nil, err
}

result, tx, err := e.sendTransaction(tx, nil)
if err != nil {
return nil, nil, nil, err
}

account := &flowsdk.Account{
Address: parseEventAddress(result.Events),
}

return account, tx, result, nil
}

func (e *emulator) getAccount(address flowsdk.Address) (*flowsdk.Account, error) {
account, err := e.blockchain.GetAccount(flow.ConvertAddress(address))
if err != nil {
return nil, err
}
// todo: add back in storage
// storage, err := e.blockchain.GetAccountStorage(address)
return convert.FlowAccountToSDK(*account)
}

func (e *emulator) deployContract(
address flowsdk.Address,
script string,
) (*types.TransactionResult, *flowsdk.Transaction, error) {
contractName, err := parseContractName(script)
if err != nil {
return nil, nil, err
}

tx := templates.AddAccountContract(address, templates.Contract{
Name: contractName,
Source: script,
})

return e.sendTransaction(tx, nil)
}

func (e *emulator) sendTransaction(
tx *flowsdk.Transaction,
authorizers []flowsdk.Address,
) (*types.TransactionResult, *flowsdk.Transaction, error) {
signer, err := e.blockchain.ServiceKey().Signer()
if err != nil {
return nil, nil, errors.Wrap(err, "error getting service signer")
}

for _, auth := range authorizers {
tx.AddAuthorizer(auth)
}
tx.SetPayer(e.blockchain.ServiceKey().Address)

for _, auth := range authorizers {
if len(authorizers) == 1 && tx.Payer == authorizers[0] {
break // don't sign if we have same authorizer and payer, only sign envelope
}

err := tx.SignPayload(auth, 0, signer)
if err != nil {
return nil, nil, errors.Wrap(err, "error signing payload")
}
}

err = tx.SignEnvelope(e.blockchain.ServiceKey().Address, e.blockchain.ServiceKey().Index, signer)
if err != nil { // todo should we return as transaction result error
return nil, nil, errors.Wrap(err, "error signing the envelope")
}

t := convert.SDKTransactionToFlow(*tx)
err = e.blockchain.AddTransaction(*t)
if err != nil {
return &types.TransactionResult{
Error: err,
}, nil, nil
}

_, res, err := e.blockchain.ExecuteAndCommitBlock()
if err != nil {
return nil, nil, errors.Wrap(err, "error executing the block")
}

// there should always be just one transaction per block execution, if not the case fail
if len(res) != 1 {
sentry.CaptureMessage(fmt.Sprintf("%d transactions were executed: %v", len(res), res))
return nil, nil, fmt.Errorf("failure during transaction execution, multiple transactions executed")
}

return res[0], tx, nil
}

func (e *emulator) getLatestBlockHeight() (int, error) {
block, err := e.blockchain.GetLatestBlock()
if err != nil {
return 0, err
}
return int(block.Header.Height), nil
}

// parseEventAddress gets an address out of the account creation events payloads
func parseEventAddress(events []flowsdk.Event) flowsdk.Address {
// todo: get the address from the create account event
for _, event := range events {
if event.Type == flowsdk.EventAccountCreated {
accountCreatedEvent := flowsdk.AccountCreatedEvent(event)
return accountCreatedEvent.Address()
}
}
return flowsdk.EmptyAddress
}

/*
// parseArguments converts string arguments list in cadence-JSON format into a byte serialised list
func parseArguments(args []string) ([][]byte, error) {
encodedArgs := make([][]byte, len(args))
for i, arg := range args {
// decode and then encode again to ensure the value is valid
val, err := jsoncdc.Decode(nil, []byte(arg))
if err != nil {
return nil, errors.Wrap(err, "failed to decode argument")
}
enc, _ := jsoncdc.Encode(val)
encodedArgs[i] = enc
}
return encodedArgs, nil
}
// parseContractName extracts contract name from its source
func parseContractName(code string) (string, error) {
program, err := parser.ParseProgram(nil, []byte(code), parser.Config{})
if err != nil {
return "", err
}
if len(program.CompositeDeclarations())+len(program.InterfaceDeclarations()) != 1 {
return "", errors.New("the code must declare exactly one contract or contract interface")
}
for _, compositeDeclaration := range program.CompositeDeclarations() {
if compositeDeclaration.CompositeKind == common.CompositeKindContract {
return compositeDeclaration.Identifier.Identifier, nil
}
}
for _, interfaceDeclaration := range program.InterfaceDeclarations() {
if interfaceDeclaration.CompositeKind == common.CompositeKindContract {
return interfaceDeclaration.Identifier.Identifier, nil
}
}
return "", fmt.Errorf("unable to determine contract name")
}
*/
16 changes: 8 additions & 8 deletions blockchain/flowkit.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ import (
jsoncdc "github.com/onflow/cadence/encoding/json"
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/parser"
kit "github.com/onflow/flow-cli/flowkit"
"github.com/onflow/flow-cli/flowkit/accounts"
"github.com/onflow/flow-cli/flowkit/config"
"github.com/onflow/flow-cli/flowkit/gateway"
"github.com/onflow/flow-cli/flowkit/output"
"github.com/onflow/flow-cli/flowkit/transactions"
emu "github.com/onflow/flow-emulator/emulator"
"github.com/onflow/flow-emulator/storage/memstore"
"github.com/onflow/flow-go-sdk"
"github.com/onflow/flow-go-sdk/crypto"
kit "github.com/onflow/flowkit"
"github.com/onflow/flowkit/accounts"
"github.com/onflow/flowkit/config"
"github.com/onflow/flowkit/gateway"
"github.com/onflow/flowkit/output"
"github.com/onflow/flowkit/transactions"
"github.com/pkg/errors"
"github.com/rs/zerolog"
)
Expand Down Expand Up @@ -95,7 +95,7 @@ type flowKit struct {

func newFlowkit() (*flowKit, error) {
readerWriter := NewInternalReaderWriter()
state, err := kit.Init(readerWriter, crypto.ECDSA_P256, crypto.SHA3_256)
state, err := kit.Init(readerWriter)
if err != nil {
return nil, errors.Wrap(err, "failed to create flow-kit state")
}
Expand Down Expand Up @@ -534,7 +534,7 @@ func (fk *flowKit) sendTransaction(
}

func (fk *flowKit) getLatestBlockHeight() (int, error) {
block, err := fk.blockchain.Gateway().GetLatestBlock()
block, err := fk.blockchain.Gateway().GetLatestBlock(context.Background())
if err != nil {
return 0, err
}
Expand Down
2 changes: 1 addition & 1 deletion blockchain/flowkit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import (
"context"
"testing"

"github.com/onflow/flow-cli/flowkit/accounts"
"github.com/onflow/flow-go-sdk"
"github.com/onflow/flowkit/accounts"
"github.com/stretchr/testify/assert"
)

Expand Down
1 change: 1 addition & 0 deletions blockchain/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package blockchain

import (
"fmt"

"github.com/dapperlabs/flow-playground-api/model"
"github.com/dapperlabs/flow-playground-api/storage"
"github.com/getsentry/sentry-go"
Expand Down
9 changes: 7 additions & 2 deletions blockchain/readerwriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
package blockchain

import (
"io/fs"
"os"

kit "github.com/onflow/flow-cli/flowkit"
kit "github.com/onflow/flowkit"
)

type InternalReaderWriter struct {
Expand All @@ -45,4 +46,8 @@ func (rw *InternalReaderWriter) WriteFile(_ string, data []byte, _ os.FileMode)

func (rw *InternalReaderWriter) MkdirAll(_ string, _ os.FileMode) error {
return nil
}
}

func (rw *InternalReaderWriter) Stat(_ string) (fs.FileInfo, error) {
return nil, nil
}
Loading

0 comments on commit e5aa3b8

Please sign in to comment.