Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: init pre-funded local authFactories and URIs usage #1814

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
8 changes: 8 additions & 0 deletions auth/ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ func (d *ED25519Factory) Address() codec.Address {
return NewED25519Address(d.priv.PublicKey())
}

func GenerateED25519AuthFactory() (chain.AuthFactory, error) {
privateKey, err := ed25519.GeneratePrivateKey()
if err != nil {
return nil, err
}
return NewED25519Factory(privateKey), nil
}

type ED25519AuthEngine struct{}

func (*ED25519AuthEngine) GetBatchVerifier(cores int, count int) chain.AuthBatchVerifier {
Expand Down
39 changes: 26 additions & 13 deletions docs/tutorials/morpheusvm/5_testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ import (
// ref https://onsi.github.io/ginkgo/#mental-model-how-ginkgo-traverses-the-spec-hierarchy
var TestsRegistry = &registry.Registry{}

var _ = registry.Register(TestsRegistry, "Transfer Transaction", func(t ginkgo.FullGinkgoTInterface, tn tworkload.TestNetwork) {
var _ = registry.Register(TestsRegistry, "Transfer Transaction", func(t ginkgo.FullGinkgoTInterface, tn tworkload.TestNetwork, authFactories []chain.AuthFactory) {

})
```
Expand All @@ -365,51 +365,58 @@ registry of all the tests that we want to run against our VM.
Afterwards, we have the following snippet:

```go
registry.Register(TestsRegistry, "Transfer Transaction", func(t ginkgo.FullGinkgoTInterface, tn tworkload.TestNetwork) {
registry.Register(TestsRegistry, "Transfer Transaction", func(t ginkgo.FullGinkgoTInterface, tn tworkload.TestNetwork, authFactories ...chain.AuthFactory) {

})
}, 1000000, 1000000)
```

Here, we are adding a test to `TestRegistry`. However, we're
Here, we are adding a test to `TestRegistry`, requesting an authFactory to be funded with 1_000_000 tokens. However, we're
missing the test itself. In short, here's what we want to do in
our testing logic:

- Setup necessary values
- Setup necessary values & Check the funds of the requested authFactories
- Create our test TX
- Send our TX
- Require that our TX is sent and that the outputs are as expected
- Check the receiver has received the funds

Focusing on the first step, we can write the following inside the anonymous
function:

```go
require := require.New(t)
other, err := ed25519.GeneratePrivateKey()
require.NoError(err)
toAddress := auth.NewED25519Address(other.PublicKey())
ctx := context.Background()
sourceAuthFactory, targetAuthFactory := authFactories[0], authFactories[1]

authFactory := tn.Configuration().AuthFactories()[0]
client := jsonrpc.NewJSONRPCClient(tn.URIs()[0])
sourceBalance, err := client.GetBalance(ctx, sourceAuthFactory.Address())
require.NoError(err)
require.Equal(uint64(1000000), sourceBalance)
targetBalance, err := client.GetBalance(ctx, targetAuthFactory.Address())
require.NoError(err)
require.Equal(uint64(1000000), targetBalance)
```

Next, we'll create our test transaction. In short, we'll want to send a value of
`1` to `To`. Therefore, we have:

```go
tx, err := tn.GenerateTx(context.Background(), []chain.Action{&actions.Transfer{
To: toAddress,
To: targetAuthFactory.Address(,
Value: 1,
}},
authFactory,
sourceAuthFactory,
)
require.NoError(err)
```

Finally, we'll want to send our TX and do the checks mentioned in the last step.
Finally, we'll want to send our TX, check that the Tx has been executed and receiver has received the amount of token.
This step will consist of the following:

- Creating a context with a deadline of 2 seconds
- If the test takes longer than 2 seconds, it will fail
- Calling `ConfirmTxs` with our TX being passed in
- Requesting the balance

The function `ConfirmTXs` is useful as it checks that our TX was
sent and that, if finalized, our transaction has the expected outputs. We have
Expand All @@ -418,8 +425,14 @@ the following:
```go
timeoutCtx, timeoutCtxFnc := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second))
defer timeoutCtxFnc()

require.NoError(tn.ConfirmTxs(timeoutCtx, []*chain.Transaction{tx}))

sourceBalance, err = client.GetBalance(ctx, sourceAuthFactory.Address())
require.NoError(err)
require.True(uint64(1000000) > sourceBalance)
targetBalance, err = client.GetBalance(ctx, targetAuthFactory.Address())
require.NoError(err)
require.Equal(uint64(1000001), targetBalance)
```

## Registering our Tests
Expand Down
7 changes: 4 additions & 3 deletions examples/morpheusvm/tests/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import (
"github.com/ava-labs/avalanchego/tests/fixture/e2e"
"github.com/stretchr/testify/require"

_ "github.com/ava-labs/hypersdk/examples/morpheusvm/tests" // include the tests that are shared between the integration and e2e

"github.com/ava-labs/hypersdk/abi"
"github.com/ava-labs/hypersdk/auth"
"github.com/ava-labs/hypersdk/examples/morpheusvm/consts"
"github.com/ava-labs/hypersdk/examples/morpheusvm/tests" // include the tests that are shared between the integration and e2e
"github.com/ava-labs/hypersdk/examples/morpheusvm/tests/workload"
"github.com/ava-labs/hypersdk/examples/morpheusvm/throughput"
"github.com/ava-labs/hypersdk/examples/morpheusvm/vm"
Expand All @@ -40,7 +39,9 @@ func init() {
var _ = ginkgo.SynchronizedBeforeSuite(func() []byte {
require := require.New(ginkgo.GinkgoT())

testingNetworkConfig, err := workload.NewTestNetworkConfig(100 * time.Millisecond)
customAllocs, err := tests.TestsRegistry.GenerateCustomAllocations(auth.GenerateED25519AuthFactory)
require.NoError(err)
testingNetworkConfig, err := workload.NewTestNetworkConfig(100*time.Millisecond, customAllocs)
require.NoError(err)

expectedABI, err := abi.NewABI(vm.ActionParser.GetRegisteredTypes(), vm.OutputParser.GetRegisteredTypes())
Expand Down
7 changes: 4 additions & 3 deletions examples/morpheusvm/tests/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import (

"github.com/stretchr/testify/require"

_ "github.com/ava-labs/hypersdk/examples/morpheusvm/tests" // include the tests that are shared between the integration and e2e

"github.com/ava-labs/hypersdk/auth"
"github.com/ava-labs/hypersdk/crypto/ed25519"
"github.com/ava-labs/hypersdk/examples/morpheusvm/tests" // include the tests that are shared between the integration and e2e
"github.com/ava-labs/hypersdk/examples/morpheusvm/tests/workload"
"github.com/ava-labs/hypersdk/examples/morpheusvm/vm"
"github.com/ava-labs/hypersdk/tests/integration"
Expand All @@ -27,7 +26,9 @@ func TestIntegration(t *testing.T) {
var _ = ginkgo.BeforeSuite(func() {
require := require.New(ginkgo.GinkgoT())

testingNetworkConfig, err := workload.NewTestNetworkConfig(0)
customAllocs, err := tests.TestsRegistry.GenerateCustomAllocations(auth.GenerateED25519AuthFactory)
require.NoError(err)
testingNetworkConfig, err := workload.NewTestNetworkConfig(0, customAllocs)
require.NoError(err)

randomEd25519Priv, err := ed25519.GeneratePrivateKey()
Expand Down
56 changes: 34 additions & 22 deletions examples/morpheusvm/tests/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import (

"github.com/stretchr/testify/require"

"github.com/ava-labs/hypersdk/auth"
"github.com/ava-labs/hypersdk/api/jsonrpc"
"github.com/ava-labs/hypersdk/chain"
"github.com/ava-labs/hypersdk/crypto/ed25519"
"github.com/ava-labs/hypersdk/examples/morpheusvm/actions"
"github.com/ava-labs/hypersdk/tests/registry"

Expand All @@ -24,23 +23,36 @@ import (
// ref https://onsi.github.io/ginkgo/#mental-model-how-ginkgo-traverses-the-spec-hierarchy
var TestsRegistry = &registry.Registry{}

var _ = registry.Register(TestsRegistry, "Transfer Transaction", func(t ginkgo.FullGinkgoTInterface, tn tworkload.TestNetwork) {
require := require.New(t)
other, err := ed25519.GeneratePrivateKey()
require.NoError(err)
toAddress := auth.NewED25519Address(other.PublicKey())

authFactory := tn.Configuration().AuthFactories()[0]
tx, err := tn.GenerateTx(context.Background(), []chain.Action{&actions.Transfer{
To: toAddress,
Value: 1,
}},
authFactory,
)
require.NoError(err)

timeoutCtx, timeoutCtxFnc := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second))
defer timeoutCtxFnc()

require.NoError(tn.ConfirmTxs(timeoutCtx, []*chain.Transaction{tx}))
})
var _ = registry.Register(TestsRegistry, "Transfer Transaction",
func(t ginkgo.FullGinkgoTInterface, tn tworkload.TestNetwork, authFactories []chain.AuthFactory) {
require := require.New(t)
ctx := context.Background()
sourceAuthFactory, targetAuthFactory := authFactories[0], authFactories[1]

client := jsonrpc.NewJSONRPCClient(tn.URIs()[0])
sourceBalance, err := client.GetBalance(ctx, sourceAuthFactory.Address())
require.NoError(err)
require.Equal(uint64(1000000), sourceBalance)
targetBalance, err := client.GetBalance(ctx, targetAuthFactory.Address())
require.NoError(err)
require.Equal(uint64(1000000), targetBalance)

tx, err := tn.GenerateTx(ctx, []chain.Action{&actions.Transfer{
To: targetAuthFactory.Address(),
Value: 1,
}},
sourceAuthFactory,
)
require.NoError(err)

timeoutCtx, timeoutCtxFnc := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second))
defer timeoutCtxFnc()
require.NoError(tn.ConfirmTxs(timeoutCtx, []*chain.Transaction{tx}))

sourceBalance, err = client.GetBalance(ctx, sourceAuthFactory.Address())
require.NoError(err)
require.True(uint64(1000000) > sourceBalance)
targetBalance, err = client.GetBalance(ctx, targetAuthFactory.Address())
require.NoError(err)
require.Equal(uint64(1000001), targetBalance)
}, 1000000, 1000000)
7 changes: 4 additions & 3 deletions examples/morpheusvm/tests/workload/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ var ed25519HexKeys = []string{
"8a7be2e0c9a2d09ac2861c34326d6fe5a461d920ba9c2b345ae28e603d517df148735063f8d5d8ba79ea4668358943e5c80bc09e9b2b9a15b5b15db6c1862e88", //nolint:lll
}

func newGenesis(authFactories []chain.AuthFactory, minBlockGap time.Duration) *genesis.DefaultGenesis {
func newGenesis(authFactories []chain.AuthFactory, testsLocalAllocations []*genesis.CustomAllocation, minBlockGap time.Duration) *genesis.DefaultGenesis {
// allocate the initial balance to the addresses
customAllocs := make([]*genesis.CustomAllocation, 0, len(authFactories))
for _, authFactory := range authFactories {
Expand All @@ -41,6 +41,7 @@ func newGenesis(authFactories []chain.AuthFactory, minBlockGap time.Duration) *g
Balance: InitialBalance,
})
}
customAllocs = append(customAllocs, testsLocalAllocations...)

genesis := genesis.NewDefaultGenesis(customAllocs)

Expand Down Expand Up @@ -70,9 +71,9 @@ func newDefaultAuthFactories() []chain.AuthFactory {
return authFactories
}

func NewTestNetworkConfig(minBlockGap time.Duration) (workload.DefaultTestNetworkConfiguration, error) {
func NewTestNetworkConfig(minBlockGap time.Duration, customAllocs []*genesis.CustomAllocation) (workload.DefaultTestNetworkConfiguration, error) {
keys := newDefaultAuthFactories()
genesis := newGenesis(keys, minBlockGap)
genesis := newGenesis(keys, customAllocs, minBlockGap)
genesisBytes, err := json.Marshal(genesis)
if err != nil {
return workload.DefaultTestNetworkConfiguration{}, err
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ var _ = ginkgo.Describe("[Custom VM Tests]", ginkgo.Serial, func() {
for _, test := range testRegistry.List() {
ginkgo.It(test.Name, func() {
testNetwork := NewNetwork(tc)
test.Fnc(ginkgo.GinkgoT(), testNetwork)
test.Fnc(ginkgo.GinkgoT(), testNetwork, test.AuthFactories)
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ var _ = ginkgo.Describe("[Tx Processing]", ginkgo.Serial, func() {
for _, test := range testRegistry.List() {
ginkgo.It(fmt.Sprintf("Custom VM Test '%s'", test.Name), func() {
require.NoError(testNetwork.SynchronizeNetwork(context.Background()))
test.Fnc(ginkgo.GinkgoT(), testNetwork)
test.Fnc(ginkgo.GinkgoT(), testNetwork, test.AuthFactories)
})
}
}
Expand Down
55 changes: 45 additions & 10 deletions tests/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,77 @@ package registry

import (
"github.com/onsi/ginkgo/v2"
"github.com/stretchr/testify/require"

"github.com/ava-labs/hypersdk/chain"
"github.com/ava-labs/hypersdk/genesis"
"github.com/ava-labs/hypersdk/tests/workload"
)

type TestFunc func(t ginkgo.FullGinkgoTInterface, tn workload.TestNetwork)
type TestFunc func(t ginkgo.FullGinkgoTInterface, tn workload.TestNetwork, authFactories []chain.AuthFactory)

type namedTest struct {
Fnc TestFunc
Name string
Fnc TestFunc
Name string
AuthFactories []chain.AuthFactory
requestedBalances []uint64
}
type Registry struct {
tests []namedTest
tests []*namedTest
}

func (r *Registry) Add(name string, f TestFunc) {
r.tests = append(r.tests, namedTest{Fnc: f, Name: name})
func (r *Registry) Add(name string, f TestFunc, requestedBalances ...uint64) {
r.tests = append(r.tests, &namedTest{Fnc: f, Name: name, requestedBalances: requestedBalances})
}

func (r *Registry) List() []namedTest {
func (r *Registry) List() []*namedTest {
if r == nil {
return []namedTest{}
return []*namedTest{}
}
return r.tests
}

func (r *Registry) GenerateCustomAllocations(generateAuthFactory func() (chain.AuthFactory, error)) ([]*genesis.CustomAllocation, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The authFactories creation is delayed because when registering the tests, we don't know which private key type to use.

requestedAllocations := make([]*genesis.CustomAllocation, 0)
for index, test := range r.tests {
for _, requestedBalance := range test.requestedBalances {
authFactory, err := generateAuthFactory()
if err != nil {
return nil, err
}
test.AuthFactories = append(test.AuthFactories, authFactory)
requestedAllocations = append(
requestedAllocations,
&genesis.CustomAllocation{
Address: authFactory.Address(),
Balance: requestedBalance,
},
)
}
r.tests[index] = test
}
return requestedAllocations, nil
}

// we need to pre-register all the test registries that are created externally in order to comply with the ginko execution order.
// i.e. the global `var _ = ginkgo.Describe` used in the integration/e2e tests need to have this field populated before the iteration
// over the top level nodes.
var testRegistries = map[*Registry]bool{}

func Register(registry *Registry, name string, f TestFunc) bool {
registry.Add(name, f)
func Register(registry *Registry, name string, f TestFunc, requestedBalances ...uint64) bool {
registry.Add(name, withRequiredPrefundedAuthFactories(f, len(requestedBalances)), requestedBalances...)
testRegistries[registry] = true
return true
}

func GetTestsRegistries() map[*Registry]bool {
return testRegistries
}

// withRequiredPrefundedAuthFactories wraps the TestFunc in a new TestFunc adding length validation over the provided authFactories
func withRequiredPrefundedAuthFactories(f TestFunc, requiredLength int) TestFunc {
return func(t ginkgo.FullGinkgoTInterface, tn workload.TestNetwork, authFactories []chain.AuthFactory) {
require.Len(t, authFactories, requiredLength, "required pre-funded authFactories have not been initialized")
f(t, tn, authFactories)
}
}