Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions engine/execution/state/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func TestBootstrapLedger(t *testing.T) {

func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) {
expectedStateCommitmentBytes, _ := hex.DecodeString(
"072ff5128df34db4483879f1b617338cd51def0e55cae25927eb5f3b404f3ef9",
"506f0c7c9c12fb9b37deb015e3f148dd11ed139030fa0a6eeb6b162c73e4fb14",
)
expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes)
require.NoError(t, err)
Expand Down Expand Up @@ -107,7 +107,7 @@ func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) {
// This tests that the state commitment has not changed for the bookkeeping parts of the transaction.
func TestBootstrapLedger_EmptyTransaction(t *testing.T) {
expectedStateCommitmentBytes, _ := hex.DecodeString(
"61acd22132b90efd23bb1c4413ae5071dc9c0e123c288371a755e4f041791a20",
"a16a6bf392c9dcffda1baeea9e70f1f20c504aeb912a1ed5cfadca65feb962ce",
)
expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes)
require.NoError(t, err)
Expand Down
1 change: 1 addition & 0 deletions fvm/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ func Bootstrap(
ServiceAccountPublicKeys: []flow.AccountPublicKey{serviceAccountPublicKey},
FungibleTokenAccountPublicKeys: []flow.AccountPublicKey{serviceAccountPublicKey},
FlowTokenAccountPublicKeys: []flow.AccountPublicKey{serviceAccountPublicKey},
FlowFeesAccountPublicKeys: []flow.AccountPublicKey{serviceAccountPublicKey},
NodeAccountPublicKeys: []flow.AccountPublicKey{serviceAccountPublicKey},
},
transactionFees: BootstrapProcedureFeeParameters{0, 0, 0},
Expand Down
80 changes: 80 additions & 0 deletions fvm/fvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/hex"
"fmt"
"math"
"strconv"
"strings"
"testing"

Expand Down Expand Up @@ -4277,3 +4278,82 @@ func Test_BlockHashListShouldWriteOnPush(t *testing.T) {
require.Equal(t, expectedBlockHashListBucket, newBlockHashListBucket)
}))
}

func TestTransactionIndexCall(t *testing.T) {
t.Parallel()

t.Run("in transactions",
newVMTest().
run(
func(
t *testing.T,
vm fvm.VM,
chain flow.Chain,
ctx fvm.Context,
snapshotTree snapshot.SnapshotTree,
) {
txBodyBuilder := flow.NewTransactionBodyBuilder().
SetScript([]byte(`
transaction {
prepare() {
let idx = getTransactionIndex()
log(idx)
}
}
`)).
SetProposalKey(chain.ServiceAddress(), 0, 0).
SetPayer(chain.ServiceAddress())

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

txBody, err := txBodyBuilder.Build()
require.NoError(t, err)

ctx = fvm.NewContextFromParent(ctx, fvm.WithCadenceLogging(true))

txIndex := uint32(3)

_, output, err := vm.Run(
ctx,
fvm.Transaction(txBody, txIndex),
snapshotTree)
require.NoError(t, err)
require.NoError(t, output.Err)
require.Len(t, output.Logs, 1)

idx, err := strconv.Atoi(output.Logs[0])
require.NoError(t, err)
require.Equal(t, txIndex, uint32(idx))
},
),
)

t.Run("in scripts",
newVMTest().
run(
func(
t *testing.T,
vm fvm.VM,
chain flow.Chain,
ctx fvm.Context,
snapshotTree snapshot.SnapshotTree,
) {
script := fvm.Script(
[]byte(`
access(all) fun main(): UInt32 {
return getTransactionIndex()
}`))

_, output, err := vm.Run(
ctx,
script,
snapshotTree)
require.NoError(t, err)
require.NoError(t, output.Err)

require.Equal(t, cadence.UInt32(0), output.Value)
},
),
)
}
82 changes: 82 additions & 0 deletions fvm/runtime/cadence_function_declarations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package runtime

import (
"github.com/onflow/cadence/common"
"github.com/onflow/cadence/interpreter"
"github.com/onflow/cadence/sema"
"github.com/onflow/cadence/stdlib"

"github.com/onflow/flow-go/fvm/errors"
)

// randomSourceFunctionType is the type of the `randomSource` function.
// This defines the signature as `func(): [UInt8]`
var randomSourceFunctionType = &sema.FunctionType{
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.ByteArrayType),
}

func blockRandomSourceDeclaration(renv *ReusableCadenceRuntime) stdlib.StandardLibraryValue {
// Declare the `randomSourceHistory` function. This function is **only** used by the
// System transaction, to fill the `RandomBeaconHistory` contract via the heartbeat
// resource. This allows the `RandomBeaconHistory` contract to be a standard contract,
// without any special parts.
// Since the `randomSourceHistory` function is only used by the System transaction,
// it is not part of the cadence standard library, and can just be injected from here.
// It also doesn't need user documentation, since it is not (and should not)
// be called by the user. If it is called by the user it will panic.
return stdlib.StandardLibraryValue{
Name: "randomSourceHistory",
Type: randomSourceFunctionType,
Kind: common.DeclarationKindFunction,
Value: interpreter.NewUnmeteredStaticHostFunctionValue(
randomSourceFunctionType,
func(invocation interpreter.Invocation) interpreter.Value {
var err error
var source []byte
env := renv.fvmEnv
if env != nil {
source, err = env.RandomSourceHistory()
} else {
err = errors.NewOperationNotSupportedError("randomSourceHistory")
}

if err != nil {
panic(err)
}

return interpreter.ByteSliceToByteArrayValue(
invocation.InvocationContext,
source)
},
),
}
}

// transactionIndexFunctionType is the type of the `getTransactionIndex` function.
// This defines the signature as `func(): UInt32`
var transactionIndexFunctionType = &sema.FunctionType{
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.UInt32Type),
}

func transactionIndexDeclaration(renv *ReusableCadenceRuntime) stdlib.StandardLibraryValue {
return stdlib.StandardLibraryValue{
Name: "getTransactionIndex",
DocString: `Returns the transaction index in the current block, i.e. first transaction in a block has index of 0, second has index of 1...`,
Type: transactionIndexFunctionType,
Kind: common.DeclarationKindFunction,
Value: interpreter.NewUnmeteredStaticHostFunctionValue(
transactionIndexFunctionType,
func(invocation interpreter.Invocation) interpreter.Value {
return interpreter.NewUInt32Value(
invocation.InvocationContext,
func() uint32 {
env := renv.fvmEnv
if env == nil {
panic(errors.NewOperationNotSupportedError("transactionIndex"))
}
return env.TxIndex()
})
},
),
}
}
69 changes: 7 additions & 62 deletions fvm/runtime/reusable_cadence_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@ package runtime
import (
"github.com/onflow/cadence"
"github.com/onflow/cadence/common"
"github.com/onflow/cadence/interpreter"
"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/sema"
"github.com/onflow/cadence/stdlib"

"github.com/onflow/flow-go/fvm/errors"
)

// Note: this is a subset of environment.Environment, redeclared to handle
Expand All @@ -18,12 +14,7 @@ type Environment interface {
common.Gauge

RandomSourceHistory() ([]byte, error)
}

// randomSourceFunctionType is the type of the `randomSource` function.
// This defines the signature as `func(): [UInt8]`
var randomSourceFunctionType = &sema.FunctionType{
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.ByteArrayType),
TxIndex() uint32
}

type ReusableCadenceRuntime struct {
Expand All @@ -44,63 +35,17 @@ func NewReusableCadenceRuntime(
ScriptRuntimeEnv: runtime.NewScriptInterpreterEnvironment(config),
}

reusable.declareRandomSourceHistory()
reusable.declareStandardLibraryFunctions()

return reusable
}

func (reusable *ReusableCadenceRuntime) declareRandomSourceHistory() {

// Declare the `randomSourceHistory` function. This function is **only** used by the
// System transaction, to fill the `RandomBeaconHistory` contract via the heartbeat
// resource. This allows the `RandomBeaconHistory` contract to be a standard contract,
// without any special parts.
// Since the `randomSourceHistory` function is only used by the System transaction,
// it is not part of the cadence standard library, and can just be injected from here.
// It also doesnt need user documentation, since it is not (and should not)
// be called by the user. If it is called by the user it will panic.
functionType := randomSourceFunctionType

blockRandomSource := stdlib.StandardLibraryValue{
Name: "randomSourceHistory",
Type: functionType,
Kind: common.DeclarationKindFunction,
Value: interpreter.NewUnmeteredStaticHostFunctionValue(
functionType,
func(invocation interpreter.Invocation) interpreter.Value {

actualArgumentCount := len(invocation.Arguments)
expectedArgumentCount := len(functionType.Parameters)

if actualArgumentCount != expectedArgumentCount {
panic(errors.NewInvalidArgumentErrorf(
"incorrect number of arguments: got %d, expected %d",
actualArgumentCount,
expectedArgumentCount,
))
}

var err error
var source []byte
fvmEnv := reusable.fvmEnv
if fvmEnv != nil {
source, err = fvmEnv.RandomSourceHistory()
} else {
err = errors.NewOperationNotSupportedError("randomSourceHistory")
}

if err != nil {
panic(err)
}

return interpreter.ByteSliceToByteArrayValue(
invocation.InvocationContext,
source)
},
),
}
func (reusable *ReusableCadenceRuntime) declareStandardLibraryFunctions() {
reusable.TxRuntimeEnv.DeclareValue(blockRandomSourceDeclaration(reusable), nil)

reusable.TxRuntimeEnv.DeclareValue(blockRandomSource, nil)
declaration := transactionIndexDeclaration(reusable)
reusable.TxRuntimeEnv.DeclareValue(declaration, nil)
reusable.ScriptRuntimeEnv.DeclareValue(declaration, nil)
}

func (reusable *ReusableCadenceRuntime) SetFvmEnvironment(fvmEnv Environment) {
Expand Down
6 changes: 3 additions & 3 deletions utils/unittest/execution_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const ServiceAccountPrivateKeySignAlgo = crypto.ECDSAP256
const ServiceAccountPrivateKeyHashAlgo = hash.SHA2_256

// Pre-calculated state commitment with root account with the above private key
const GenesisStateCommitmentHex = "dad6fda8f9745ec0e1cf9d4d2429060a9460cef513f29272a6deb973820af6aa"
const GenesisStateCommitmentHex = "e53b39d66b2689882f21d0a0605de5693df8090c01279a14718c81746daf804b"

var GenesisStateCommitment flow.StateCommitment

Expand Down Expand Up @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string {
return GenesisStateCommitmentHex
}
if chainID == flow.Testnet {
return "d758711a98ebb0cb16023f8b14dfd18ab6b4292af6634f115f47b2065b839f4b"
return "838452ac649d949992e5fb0da61fdba9cfeb09530e27e259b3b24300d3a7687f"
}
if chainID == flow.Sandboxnet {
return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1"
}
return "a3af0430b178103671b02564a1cc3477ab25acf459523449640a2b6198bd83c8"
return "5164cee75a6f5795a77f52bdf6eb05f47162ddd2a54af97ac30dd3068e6c2b5c"
}
Loading