From 1471214c440c289a1158f7740535dd52ff92db91 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Tue, 8 Oct 2024 16:04:50 -0400 Subject: [PATCH 1/4] reduce diff --- tests/e2e/p/permissionless_layer_one.go | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/e2e/p/permissionless_layer_one.go b/tests/e2e/p/permissionless_layer_one.go index 64ee1da7aa8f..30278b316ad2 100644 --- a/tests/e2e/p/permissionless_layer_one.go +++ b/tests/e2e/p/permissionless_layer_one.go @@ -27,6 +27,8 @@ import ( "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) +const genesisWeight = 100 + var _ = e2e.DescribePChain("[Permissionless L1]", func() { tc := e2e.NewTestContext() require := require.New(tc) @@ -108,19 +110,17 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { ) require.NoError(err) - tc.By("creating an ephemeral node") + tc.By("creating the genesis validator") subnetGenesisNode := e2e.AddEphemeralNode(tc, env.GetNetwork(), tmpnet.FlagsMap{ config.TrackSubnetsKey: subnetID.String(), }) - subnetGenesisNodeInfoAPI := info.NewClient(subnetGenesisNode.URI) - nodeID, nodePoP, err := subnetGenesisNodeInfoAPI.GetNodeID(tc.DefaultContext()) + genesisNodePoP, err := subnetGenesisNode.GetProofOfPossession() require.NoError(err) - nodePK, err := bls.PublicKeyFromCompressedBytes(nodePoP.PublicKey[:]) + genesisNodePK, err := bls.PublicKeyFromCompressedBytes(genesisNodePoP.PublicKey[:]) require.NoError(err) - const weight = 100 var ( chainID = chainTx.ID() address = []byte{} @@ -132,10 +132,10 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { address, []*txs.ConvertSubnetValidator{ { - NodeID: nodeID.Bytes(), - Weight: weight, + NodeID: subnetGenesisNode.NodeID.Bytes(), + Weight: genesisWeight, Balance: units.Avax, - Signer: *nodePoP, + Signer: *genesisNodePoP, }, }, tc.WithDefaultContext(), @@ -148,9 +148,9 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { ManagerAddress: address, Validators: []message.SubnetConversionValidatorData{ { - NodeID: nodeID.Bytes(), - BLSPublicKey: nodePoP.PublicKey, - Weight: weight, + NodeID: subnetGenesisNode.NodeID.Bytes(), + BLSPublicKey: genesisNodePoP.PublicKey, + Weight: genesisWeight, }, }, }) @@ -183,8 +183,8 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { map[ids.NodeID]*validators.GetValidatorOutput{ subnetGenesisNode.NodeID: { NodeID: subnetGenesisNode.NodeID, - PublicKey: nodePK, - Weight: weight, + PublicKey: genesisNodePK, + Weight: genesisWeight, }, }, subnetValidators, From 21d732ba80be144bfd1bb67d8256d7e853ac7a27 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Tue, 8 Oct 2024 16:36:50 -0400 Subject: [PATCH 2/4] backport tests --- tests/e2e/p/permissionless_layer_one.go | 182 +++++++++++++----------- 1 file changed, 98 insertions(+), 84 deletions(-) diff --git a/tests/e2e/p/permissionless_layer_one.go b/tests/e2e/p/permissionless_layer_one.go index 22c4b8f7154a..e7a9b64c61a3 100644 --- a/tests/e2e/p/permissionless_layer_one.go +++ b/tests/e2e/p/permissionless_layer_one.go @@ -5,6 +5,7 @@ package p import ( "context" + "errors" "math" "slices" "time" @@ -31,7 +32,6 @@ import ( "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/example/xsvm/genesis" - "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" @@ -41,6 +41,7 @@ import ( p2psdk "github.com/ava-labs/avalanchego/network/p2p" p2ppb "github.com/ava-labs/avalanchego/proto/pb/p2p" snowvalidators "github.com/ava-labs/avalanchego/snow/validators" + platformvmsdk "github.com/ava-labs/avalanchego/vms/platformvm" platformvmvalidators "github.com/ava-labs/avalanchego/vms/platformvm/validators" warpmessage "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" ) @@ -77,7 +78,7 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { keychain = env.NewKeychain() baseWallet = e2e.NewWallet(tc, keychain, nodeURI) pWallet = baseWallet.P() - pClient = platformvm.NewClient(nodeURI.URI) + pClient = platformvmsdk.NewClient(nodeURI.URI) owner = &secp256k1fx.OutputOwners{ Threshold: 1, Addrs: []ids.ShortID{ @@ -118,7 +119,7 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { subnet, err := pClient.GetSubnet(tc.DefaultContext(), subnetID) require.NoError(err) require.Equal( - platformvm.GetSubnetClientResponse{ + platformvmsdk.GetSubnetClientResponse{ IsPermissioned: true, ControlKeys: []ids.ShortID{ keychain.Keys[0].Address(), @@ -195,6 +196,14 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { require.NoError(err) }) + verifyValidatorSet := func(expectedValidators map[ids.NodeID]*snowvalidators.GetValidatorOutput) { + height, err := pClient.GetHeight(tc.DefaultContext()) + require.NoError(err) + + subnetValidators, err := pClient.GetValidatorsAt(tc.DefaultContext(), subnetID, height) + require.NoError(err) + require.Equal(expectedValidators, subnetValidators) + } tc.By("verifying the Permissioned Subnet was converted to a Permissionless L1", func() { expectedConversionID, err := warpmessage.SubnetConversionID(warpmessage.SubnetConversionData{ SubnetID: subnetID, @@ -210,38 +219,32 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { }) require.NoError(err) - subnet, err := pClient.GetSubnet(tc.DefaultContext(), subnetID) - require.NoError(err) - require.Equal( - platformvm.GetSubnetClientResponse{ - IsPermissioned: false, - ControlKeys: []ids.ShortID{ - keychain.Keys[0].Address(), + tc.By("verifying the subnet reports as being converted", func() { + subnet, err := pClient.GetSubnet(tc.DefaultContext(), subnetID) + require.NoError(err) + require.Equal( + platformvmsdk.GetSubnetClientResponse{ + IsPermissioned: false, + ControlKeys: []ids.ShortID{ + keychain.Keys[0].Address(), + }, + Threshold: 1, + ConversionID: expectedConversionID, + ManagerChainID: chainID, + ManagerAddress: address, }, - Threshold: 1, - ConversionID: expectedConversionID, - ManagerChainID: chainID, - ManagerAddress: address, - }, - subnet, - ) - }) - - verifyValidatorSet := func(expectedValidators map[ids.NodeID]*snowvalidators.GetValidatorOutput) { - height, err := pClient.GetHeight(tc.DefaultContext()) - require.NoError(err) + subnet, + ) + }) - subnetValidators, err := pClient.GetValidatorsAt(tc.DefaultContext(), subnetID, height) - require.NoError(err) - require.Equal(expectedValidators, subnetValidators) - } - tc.By("verifying the Permissionless L1 reports the correct validator set", func() { - verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ - subnetGenesisNode.NodeID: { - NodeID: subnetGenesisNode.NodeID, - PublicKey: genesisNodePK, - Weight: genesisWeight, - }, + tc.By("verifying the validator set was updated", func() { + verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ + subnetGenesisNode.NodeID: { + NodeID: subnetGenesisNode.NodeID, + PublicKey: genesisNodePK, + Weight: genesisWeight, + }, + }) }) }) @@ -301,7 +304,6 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { tc.By("sending the request to sign the warp message", func() { registerSubnetValidatorRequest, err := wrapWarpSignatureRequest( - chainID, unsignedRegisterSubnetValidator, nil, ) @@ -311,7 +313,8 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { }) tc.By("getting the signature response") - registerSubnetValidatorSignature, ok := findMessage(genesisPeerMessages, unwrapWarpSignature) + registerSubnetValidatorSignature, ok, err := findMessage(genesisPeerMessages, unwrapWarpSignature) + require.NoError(err) require.True(ok) tc.By("creating the signed warp message to register the validator") @@ -339,17 +342,19 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { }) }) - tc.By("verifying the Permissionless L1 reports the correct validator set", func() { - verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ - subnetGenesisNode.NodeID: { - NodeID: subnetGenesisNode.NodeID, - PublicKey: genesisNodePK, - Weight: genesisWeight, - }, - ids.EmptyNodeID: { // The validator is not active - NodeID: ids.EmptyNodeID, - Weight: registerWeight, - }, + tc.By("verifying the validator was registered", func() { + tc.By("verifying the validator set was updated", func() { + verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ + subnetGenesisNode.NodeID: { + NodeID: subnetGenesisNode.NodeID, + PublicKey: genesisNodePK, + Weight: genesisWeight, + }, + ids.EmptyNodeID: { // The validator is not active + NodeID: ids.EmptyNodeID, + Weight: registerWeight, + }, + }) }) }) @@ -371,7 +376,6 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { tc.By("sending the request to sign the warp message", func() { setSubnetValidatorWeightRequest, err := wrapWarpSignatureRequest( - chainID, unsignedSubnetValidatorWeight, nil, ) @@ -381,7 +385,8 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { }) tc.By("getting the signature response") - setSubnetValidatorWeightSignature, ok := findMessage(genesisPeerMessages, unwrapWarpSignature) + setSubnetValidatorWeightSignature, ok, err := findMessage(genesisPeerMessages, unwrapWarpSignature) + require.NoError(err) require.True(ok) tc.By("creating the signed warp message to increase the weight of the validator") @@ -413,17 +418,19 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { setWeight(registerValidationID, updatedWeight) }) - tc.By("verifying the Permissionless L1 reports the correct validator set", func() { - verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ - subnetGenesisNode.NodeID: { - NodeID: subnetGenesisNode.NodeID, - PublicKey: genesisNodePK, - Weight: genesisWeight, - }, - ids.EmptyNodeID: { // The validator is not active - NodeID: ids.EmptyNodeID, - Weight: updatedWeight, - }, + tc.By("verifying the validator weight was increased", func() { + tc.By("verifying the validator set was updated", func() { + verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ + subnetGenesisNode.NodeID: { + NodeID: subnetGenesisNode.NodeID, + PublicKey: genesisNodePK, + Weight: genesisWeight, + }, + ids.EmptyNodeID: { // The validator is not active + NodeID: ids.EmptyNodeID, + Weight: updatedWeight, + }, + }) }) }) @@ -433,20 +440,21 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { setWeight(registerValidationID, 0) }) - tc.By("verifying the Permissionless L1 reports the correct validator set", func() { - verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ - subnetGenesisNode.NodeID: { - NodeID: subnetGenesisNode.NodeID, - PublicKey: genesisNodePK, - Weight: genesisWeight, - }, + tc.By("verifying the validator was removed", func() { + tc.By("verifying the validator set was updated", func() { + verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ + subnetGenesisNode.NodeID: { + NodeID: subnetGenesisNode.NodeID, + PublicKey: genesisNodePK, + Weight: genesisWeight, + }, + }) }) }) }) }) func wrapWarpSignatureRequest( - chainID ids.ID, msg *warp.UnsignedMessage, justification []byte, ) (p2pmessage.OutboundMessage, error) { @@ -470,7 +478,7 @@ func wrapWarpSignatureRequest( } return p2pMessageFactory.AppRequest( - chainID, + msg.SourceChainID, 0, time.Hour, p2psdk.PrefixMessage( @@ -482,8 +490,8 @@ func wrapWarpSignatureRequest( func findMessage[T any]( q buffer.BlockingDeque[p2pmessage.InboundMessage], - parser func(p2pmessage.InboundMessage) (T, bool), -) (T, bool) { + parser func(p2pmessage.InboundMessage) (T, bool, error), +) (T, bool, error) { var messagesToReprocess []p2pmessage.InboundMessage defer func() { slices.Reverse(messagesToReprocess) @@ -495,35 +503,41 @@ func findMessage[T any]( for { msg, ok := q.PopLeft() if !ok { - return utils.Zero[T](), false + return utils.Zero[T](), false, nil } - parsed, ok := parser(msg) + parsed, ok, err := parser(msg) + if err != nil { + return utils.Zero[T](), false, err + } if ok { - return parsed, true + return parsed, true, nil } messagesToReprocess = append(messagesToReprocess, msg) } } -func unwrapWarpSignature(msg p2pmessage.InboundMessage) (*bls.Signature, bool) { - appResponse, ok := msg.Message().(*p2ppb.AppResponse) - if !ok { - return nil, false +// unwrapWarpSignature assumes the only type of AppResponses that will be +// received are ACP-118 compliant responses. +func unwrapWarpSignature(msg p2pmessage.InboundMessage) (*bls.Signature, bool, error) { + var appResponse *p2ppb.AppResponse + switch msg := msg.Message().(type) { + case *p2ppb.AppResponse: + appResponse = msg + case *p2ppb.AppError: + return nil, false, errors.New(msg.ErrorMessage) + default: + return nil, false, nil } var response sdk.SignatureResponse - err := proto.Unmarshal(appResponse.AppBytes, &response) - if err != nil { - return nil, false + if err := proto.Unmarshal(appResponse.AppBytes, &response); err != nil { + return nil, false, err } warpSignature, err := bls.SignatureFromBytes(response.Signature) - if err != nil { - return nil, false - } - return warpSignature, true + return warpSignature, true, err } func must[T any](t require.TestingT) func(T, error) T { From afeabb608f9b0bbb200bc036bbc55dd2b8636c29 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Tue, 8 Oct 2024 16:38:00 -0400 Subject: [PATCH 3/4] backport tests --- tests/e2e/p/permissionless_layer_one.go | 463 +++++++++++++----------- 1 file changed, 246 insertions(+), 217 deletions(-) diff --git a/tests/e2e/p/permissionless_layer_one.go b/tests/e2e/p/permissionless_layer_one.go index 7eccfdd6965e..a583789ecf01 100644 --- a/tests/e2e/p/permissionless_layer_one.go +++ b/tests/e2e/p/permissionless_layer_one.go @@ -5,6 +5,7 @@ package p import ( "context" + "errors" "math" "slices" "time" @@ -31,7 +32,6 @@ import ( "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/example/xsvm/genesis" - "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" @@ -41,44 +41,53 @@ import ( p2psdk "github.com/ava-labs/avalanchego/network/p2p" p2ppb "github.com/ava-labs/avalanchego/proto/pb/p2p" snowvalidators "github.com/ava-labs/avalanchego/snow/validators" + platformvmsdk "github.com/ava-labs/avalanchego/vms/platformvm" platformvmvalidators "github.com/ava-labs/avalanchego/vms/platformvm/validators" warpmessage "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" ) -const genesisWeight = 100 +const ( + genesisWeight = units.Schmeckle + genesisBalance = units.Avax + registerWeight = genesisWeight / 10 + updatedWeight = 2 * registerWeight + registerBalance = 0 +) var _ = e2e.DescribePChain("[Permissionless L1]", func() { tc := e2e.NewTestContext() require := require.New(tc) - ginkgo.It("creates a Permissionless L1", func() { + ginkgo.It("creates and updates Permissionless L1", func() { env := e2e.GetEnv(tc) nodeURI := env.GetRandomNodeURI() - infoClient := info.NewClient(nodeURI.URI) - - tc.By("fetching upgrade config") - upgrades, err := infoClient.Upgrades(tc.DefaultContext()) - require.NoError(err) - tc.By("verifying Etna is activated") - now := time.Now() - if !upgrades.IsEtnaActivated(now) { - ginkgo.Skip("Etna is not activated. Permissionless L1s are enabled post-Etna, skipping test.") - } - - keychain := env.NewKeychain() - baseWallet := e2e.NewWallet(tc, keychain, nodeURI) + tc.By("verifying Etna is activated", func() { + infoClient := info.NewClient(nodeURI.URI) + upgrades, err := infoClient.Upgrades(tc.DefaultContext()) + require.NoError(err) - pWallet := baseWallet.P() - pClient := platformvm.NewClient(nodeURI.URI) + now := time.Now() + if !upgrades.IsEtnaActivated(now) { + ginkgo.Skip("Etna is not activated. Permissionless L1s are enabled post-Etna, skipping test.") + } + }) - owner := &secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{ - keychain.Keys[0].Address(), - }, - } + tc.By("loading the wallet") + var ( + keychain = env.NewKeychain() + baseWallet = e2e.NewWallet(tc, keychain, nodeURI) + pWallet = baseWallet.P() + pClient = platformvmsdk.NewClient(nodeURI.URI) + owner = &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ + keychain.Keys[0].Address(), + }, + } + ) + tc.By("creating the chain genesis") genesisKey, err := secp256k1.NewPrivateKey() require.NoError(err) @@ -93,40 +102,48 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { }) require.NoError(err) - tc.By("issuing a CreateSubnetTx") - subnetTx, err := pWallet.IssueCreateSubnetTx( - owner, - tc.WithDefaultContext(), - ) - require.NoError(err) + var subnetID ids.ID + tc.By("issuing a CreateSubnetTx", func() { + subnetTx, err := pWallet.IssueCreateSubnetTx( + owner, + tc.WithDefaultContext(), + ) + require.NoError(err) - tc.By("verifying a Permissioned Subnet was successfully created") - subnetID := subnetTx.ID() - require.NotEqual(subnetID, constants.PrimaryNetworkID) + subnetID = subnetTx.ID() + }) - res, err := pClient.GetSubnet(tc.DefaultContext(), subnetID) - require.NoError(err) - require.Equal( - platformvm.GetSubnetClientResponse{ - IsPermissioned: true, - ControlKeys: []ids.ShortID{ - keychain.Keys[0].Address(), + tc.By("verifying a Permissioned Subnet was successfully created", func() { + require.NotEqual(constants.PrimaryNetworkID, subnetID) + + subnet, err := pClient.GetSubnet(tc.DefaultContext(), subnetID) + require.NoError(err) + require.Equal( + platformvmsdk.GetSubnetClientResponse{ + IsPermissioned: true, + ControlKeys: []ids.ShortID{ + keychain.Keys[0].Address(), + }, + Threshold: 1, }, - Threshold: 1, - }, - res, - ) + subnet, + ) + }) - tc.By("issuing a CreateChainTx") - chainTx, err := pWallet.IssueCreateChainTx( - subnetID, - genesisBytes, - constants.XSVMID, - nil, - "No Permissions", - tc.WithDefaultContext(), - ) - require.NoError(err) + var chainID ids.ID + tc.By("issuing a CreateChainTx", func() { + chainTx, err := pWallet.IssueCreateChainTx( + subnetID, + genesisBytes, + constants.XSVMID, + nil, + "No Permissions", + tc.WithDefaultContext(), + ) + require.NoError(err) + + chainID = chainTx.ID() + }) tc.By("creating the genesis validator") subnetGenesisNode := e2e.AddEphemeralNode(tc, env.GetNetwork(), tmpnet.FlagsMap{ @@ -155,90 +172,98 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { ) require.NoError(err) defer func() { + genesisPeerMessages.Close() genesisPeer.StartClose() require.NoError(genesisPeer.AwaitClosed(tc.DefaultContext())) }() - var ( - chainID = chainTx.ID() - address = []byte{} - ) - tc.By("issuing a ConvertSubnetTx") - _, err = pWallet.IssueConvertSubnetTx( - subnetID, - chainID, - address, - []*txs.ConvertSubnetValidator{ - { - NodeID: subnetGenesisNode.NodeID.Bytes(), - Weight: genesisWeight, - Balance: units.Avax, - Signer: *genesisNodePoP, - }, - }, - tc.WithDefaultContext(), - ) - require.NoError(err) - - expectedConversionID, err := warpmessage.SubnetConversionID(warpmessage.SubnetConversionData{ - SubnetID: subnetID, - ManagerChainID: chainID, - ManagerAddress: address, - Validators: []warpmessage.SubnetConversionValidatorData{ - { - NodeID: subnetGenesisNode.NodeID.Bytes(), - BLSPublicKey: genesisNodePoP.PublicKey, - Weight: genesisWeight, + address := []byte{} + tc.By("issuing a ConvertSubnetTx", func() { + _, err := pWallet.IssueConvertSubnetTx( + subnetID, + chainID, + address, + []*txs.ConvertSubnetValidator{ + { + NodeID: subnetGenesisNode.NodeID.Bytes(), + Weight: genesisWeight, + Balance: genesisBalance, + Signer: *genesisNodePoP, + }, }, - }, + tc.WithDefaultContext(), + ) + require.NoError(err) }) - require.NoError(err) - tc.By("waiting to update the proposervm P-chain height") - time.Sleep((5 * platformvmvalidators.RecentlyAcceptedWindowTTL) / 4) + verifyValidatorSet := func(expectedValidators map[ids.NodeID]*snowvalidators.GetValidatorOutput) { + height, err := pClient.GetHeight(tc.DefaultContext()) + require.NoError(err) - tc.By("issuing random transactions to update the proposervm P-chain height") - for range 2 { - _, err = pWallet.IssueCreateSubnetTx( - owner, - tc.WithDefaultContext(), - ) + subnetValidators, err := pClient.GetValidatorsAt(tc.DefaultContext(), subnetID, height) require.NoError(err) + require.Equal(expectedValidators, subnetValidators) } - - tc.By("verifying the Permissioned Subnet was converted to a Permissionless L1") - res, err = pClient.GetSubnet(tc.DefaultContext(), subnetID) - require.NoError(err) - require.Equal( - platformvm.GetSubnetClientResponse{ - IsPermissioned: false, - ControlKeys: []ids.ShortID{ - keychain.Keys[0].Address(), - }, - Threshold: 1, - ConversionID: expectedConversionID, + tc.By("verifying the Permissioned Subnet was converted to a Permissionless L1", func() { + expectedConversionID, err := warpmessage.SubnetConversionID(warpmessage.SubnetConversionData{ + SubnetID: subnetID, ManagerChainID: chainID, ManagerAddress: address, - }, - res, - ) + Validators: []warpmessage.SubnetConversionValidatorData{ + { + NodeID: subnetGenesisNode.NodeID.Bytes(), + BLSPublicKey: genesisNodePoP.PublicKey, + Weight: genesisWeight, + }, + }, + }) + require.NoError(err) - tc.By("verifying the Permissionless L1 reports the correct validator set") - height, err := pClient.GetHeight(tc.DefaultContext()) - require.NoError(err) + tc.By("verifying the subnet reports as being converted", func() { + subnet, err := pClient.GetSubnet(tc.DefaultContext(), subnetID) + require.NoError(err) + require.Equal( + platformvmsdk.GetSubnetClientResponse{ + IsPermissioned: false, + ControlKeys: []ids.ShortID{ + keychain.Keys[0].Address(), + }, + Threshold: 1, + ConversionID: expectedConversionID, + ManagerChainID: chainID, + ManagerAddress: address, + }, + subnet, + ) + }) + + tc.By("verifying the validator set was updated", func() { + verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ + subnetGenesisNode.NodeID: { + NodeID: subnetGenesisNode.NodeID, + PublicKey: genesisNodePK, + Weight: genesisWeight, + }, + }) + }) + }) - subnetValidators, err := pClient.GetValidatorsAt(tc.DefaultContext(), subnetID, height) - require.NoError(err) - require.Equal( - map[ids.NodeID]*snowvalidators.GetValidatorOutput{ - subnetGenesisNode.NodeID: { - NodeID: subnetGenesisNode.NodeID, - PublicKey: genesisNodePK, - Weight: genesisWeight, - }, - }, - subnetValidators, - ) + advanceProposerVMPChainHeight := func() { + // We first must wait at least [RecentlyAcceptedWindowTTL] to ensure + // the next block will evict the prior block from the windower. + time.Sleep((5 * platformvmvalidators.RecentlyAcceptedWindowTTL) / 4) + + // Now we must: + // 1. issue a block which should include the old P-chain height. + // 2. issue a block which should include the new P-chain height. + for range 2 { + _, err = pWallet.IssueBaseTx(nil, tc.WithDefaultContext()) + require.NoError(err) + } + // Now that a block has been issued with the new P-chain height, the + // next block will use that height for warp message verification. + } + tc.By("advancing the proposervm P-chain height", advanceProposerVMPChainHeight) tc.By("creating the validator to register") subnetRegisterNode := e2e.AddEphemeralNode(tc, env.GetNetwork(), tmpnet.FlagsMap{ @@ -248,95 +273,93 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { registerNodePoP, err := subnetRegisterNode.GetProofOfPossession() require.NoError(err) - registerNodePK, err := bls.PublicKeyFromCompressedBytes(registerNodePoP.PublicKey[:]) - require.NoError(err) - - tc.By("ensures the subnet nodes are healthy") - e2e.WaitForHealthy(tc, subnetGenesisNode) - e2e.WaitForHealthy(tc, subnetRegisterNode) - - const registerWeight = 1 - tc.By("create the unsigned warp message to register the validator") - unsignedRegisterSubnetValidator := must[*warp.UnsignedMessage](tc)(warp.NewUnsignedMessage( - networkID, - chainID, - must[*payload.AddressedCall](tc)(payload.NewAddressedCall( - address, - must[*warpmessage.RegisterSubnetValidator](tc)(warpmessage.NewRegisterSubnetValidator( - subnetID, - subnetRegisterNode.NodeID, - registerNodePoP.PublicKey, - uint64(time.Now().Add(5*time.Minute).Unix()), - warpmessage.PChainOwner{}, - warpmessage.PChainOwner{}, - registerWeight, // weight - )).Bytes(), - )).Bytes(), - )) + tc.By("ensuring the subnet nodes are healthy", func() { + e2e.WaitForHealthy(tc, subnetGenesisNode) + e2e.WaitForHealthy(tc, subnetRegisterNode) + }) - registerSubnetValidatorRequest, err := wrapWarpSignatureRequest( - chainID, - unsignedRegisterSubnetValidator, - nil, + tc.By("creating the RegisterSubnetValidatorMessage") + registerSubnetValidatorMessage, err := warpmessage.NewRegisterSubnetValidator( + subnetID, + subnetRegisterNode.NodeID, + registerNodePoP.PublicKey, + uint64(time.Now().Add(5*time.Minute).Unix()), + warpmessage.PChainOwner{}, + warpmessage.PChainOwner{}, + registerWeight, ) require.NoError(err) - tc.By("send the request to sign the warp message") - require.True(genesisPeer.Send(tc.DefaultContext(), registerSubnetValidatorRequest)) + tc.By("registering the validator", func() { + tc.By("creating the unsigned warp message") + unsignedRegisterSubnetValidator := must[*warp.UnsignedMessage](tc)(warp.NewUnsignedMessage( + networkID, + chainID, + must[*payload.AddressedCall](tc)(payload.NewAddressedCall( + address, + registerSubnetValidatorMessage.Bytes(), + )).Bytes(), + )) - tc.By("get the signature response") - registerSubnetValidatorSignature, ok := findMessage(genesisPeerMessages, unwrapWarpSignature) - require.True(ok) + tc.By("sending the request to sign the warp message", func() { + registerSubnetValidatorRequest, err := wrapWarpSignatureRequest( + unsignedRegisterSubnetValidator, + nil, + ) + require.NoError(err) - tc.By("create the signed warp message to register the validator") - signers := set.NewBits() - signers.Add(0) // [signers] has weight from the genesis peer + require.True(genesisPeer.Send(tc.DefaultContext(), registerSubnetValidatorRequest)) + }) - var sigBytes [bls.SignatureLen]byte - copy(sigBytes[:], bls.SignatureToBytes(registerSubnetValidatorSignature)) - registerSubnetValidator, err := warp.NewMessage( - unsignedRegisterSubnetValidator, - &warp.BitSetSignature{ - Signers: signers.Bytes(), - Signature: sigBytes, - }, - ) - require.NoError(err) - - tc.By("register the validator") - _, err = pWallet.IssueRegisterSubnetValidatorTx( - 1, - registerNodePoP.ProofOfPossession, - registerSubnetValidator.Bytes(), - ) - require.NoError(err) + tc.By("getting the signature response") + registerSubnetValidatorSignature, ok, err := findMessage(genesisPeerMessages, unwrapWarpSignature) + require.NoError(err) + require.True(ok) + + tc.By("creating the signed warp message to register the validator") + signers := set.NewBits() + signers.Add(0) // [signers] has weight from the genesis peer + + var sigBytes [bls.SignatureLen]byte + copy(sigBytes[:], bls.SignatureToBytes(registerSubnetValidatorSignature)) + registerSubnetValidator, err := warp.NewMessage( + unsignedRegisterSubnetValidator, + &warp.BitSetSignature{ + Signers: signers.Bytes(), + Signature: sigBytes, + }, + ) + require.NoError(err) - tc.By("verify that the validator was registered") - height, err = pClient.GetHeight(tc.DefaultContext()) - require.NoError(err) + tc.By("issuing a RegisterSubnetValidatorTx", func() { + _, err := pWallet.IssueRegisterSubnetValidatorTx( + registerBalance, + registerNodePoP.ProofOfPossession, + registerSubnetValidator.Bytes(), + ) + require.NoError(err) + }) + }) - subnetValidators, err = pClient.GetValidatorsAt(tc.DefaultContext(), subnetID, height) - require.NoError(err) - require.Equal( - map[ids.NodeID]*snowvalidators.GetValidatorOutput{ - subnetGenesisNode.NodeID: { - NodeID: subnetGenesisNode.NodeID, - PublicKey: genesisNodePK, - Weight: genesisWeight, - }, - subnetRegisterNode.NodeID: { - NodeID: subnetRegisterNode.NodeID, - PublicKey: registerNodePK, - Weight: registerWeight, - }, - }, - subnetValidators, - ) + tc.By("verifying the validator was registered", func() { + tc.By("verifying the validator set was updated", func() { + verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ + subnetGenesisNode.NodeID: { + NodeID: subnetGenesisNode.NodeID, + PublicKey: genesisNodePK, + Weight: genesisWeight, + }, + ids.EmptyNodeID: { // The validator is not active + NodeID: ids.EmptyNodeID, + Weight: registerWeight, + }, + }) + }) + }) }) }) func wrapWarpSignatureRequest( - chainID ids.ID, msg *warp.UnsignedMessage, justification []byte, ) (p2pmessage.OutboundMessage, error) { @@ -360,7 +383,7 @@ func wrapWarpSignatureRequest( } return p2pMessageFactory.AppRequest( - chainID, + msg.SourceChainID, 0, time.Hour, p2psdk.PrefixMessage( @@ -372,8 +395,8 @@ func wrapWarpSignatureRequest( func findMessage[T any]( q buffer.BlockingDeque[p2pmessage.InboundMessage], - parser func(p2pmessage.InboundMessage) (T, bool), -) (T, bool) { + parser func(p2pmessage.InboundMessage) (T, bool, error), +) (T, bool, error) { var messagesToReprocess []p2pmessage.InboundMessage defer func() { slices.Reverse(messagesToReprocess) @@ -385,35 +408,41 @@ func findMessage[T any]( for { msg, ok := q.PopLeft() if !ok { - return utils.Zero[T](), false + return utils.Zero[T](), false, nil } - parsed, ok := parser(msg) + parsed, ok, err := parser(msg) + if err != nil { + return utils.Zero[T](), false, err + } if ok { - return parsed, true + return parsed, true, nil } messagesToReprocess = append(messagesToReprocess, msg) } } -func unwrapWarpSignature(msg p2pmessage.InboundMessage) (*bls.Signature, bool) { - appResponse, ok := msg.Message().(*p2ppb.AppResponse) - if !ok { - return nil, false +// unwrapWarpSignature assumes the only type of AppResponses that will be +// received are ACP-118 compliant responses. +func unwrapWarpSignature(msg p2pmessage.InboundMessage) (*bls.Signature, bool, error) { + var appResponse *p2ppb.AppResponse + switch msg := msg.Message().(type) { + case *p2ppb.AppResponse: + appResponse = msg + case *p2ppb.AppError: + return nil, false, errors.New(msg.ErrorMessage) + default: + return nil, false, nil } var response sdk.SignatureResponse - err := proto.Unmarshal(appResponse.AppBytes, &response) - if err != nil { - return nil, false + if err := proto.Unmarshal(appResponse.AppBytes, &response); err != nil { + return nil, false, err } warpSignature, err := bls.SignatureFromBytes(response.Signature) - if err != nil { - return nil, false - } - return warpSignature, true + return warpSignature, true, err } func must[T any](t require.TestingT) func(T, error) T { From 39beefb827939de1e40d2931f66d7ec9f99221dd Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Tue, 8 Oct 2024 16:39:20 -0400 Subject: [PATCH 4/4] backport tests --- tests/e2e/p/permissionless_layer_one.go | 273 ++++++++++++++---------- 1 file changed, 159 insertions(+), 114 deletions(-) diff --git a/tests/e2e/p/permissionless_layer_one.go b/tests/e2e/p/permissionless_layer_one.go index 30278b316ad2..9489edd5271f 100644 --- a/tests/e2e/p/permissionless_layer_one.go +++ b/tests/e2e/p/permissionless_layer_one.go @@ -4,6 +4,7 @@ package p import ( + "context" "math" "time" @@ -13,54 +14,67 @@ import ( "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/config" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/network/peer" + "github.com/ava-labs/avalanchego/snow/networking/router" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" + "github.com/ava-labs/avalanchego/utils/buffer" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/example/xsvm/genesis" - "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/avalanchego/vms/platformvm/txs" - "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" "github.com/ava-labs/avalanchego/vms/secp256k1fx" + + p2pmessage "github.com/ava-labs/avalanchego/message" + snowvalidators "github.com/ava-labs/avalanchego/snow/validators" + platformvmsdk "github.com/ava-labs/avalanchego/vms/platformvm" + warpmessage "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" ) -const genesisWeight = 100 +const ( + genesisWeight = units.Schmeckle + genesisBalance = units.Avax + registerWeight = genesisWeight / 10 + updatedWeight = 2 * registerWeight + registerBalance = 0 +) var _ = e2e.DescribePChain("[Permissionless L1]", func() { tc := e2e.NewTestContext() require := require.New(tc) - ginkgo.It("creates a Permissionless L1", func() { + ginkgo.It("creates and updates Permissionless L1", func() { env := e2e.GetEnv(tc) nodeURI := env.GetRandomNodeURI() - infoClient := info.NewClient(nodeURI.URI) - tc.By("fetching upgrade config") - upgrades, err := infoClient.Upgrades(tc.DefaultContext()) - require.NoError(err) + tc.By("verifying Etna is activated", func() { + infoClient := info.NewClient(nodeURI.URI) + upgrades, err := infoClient.Upgrades(tc.DefaultContext()) + require.NoError(err) - tc.By("verifying Etna is activated") - now := time.Now() - if !upgrades.IsEtnaActivated(now) { - ginkgo.Skip("Etna is not activated. Permissionless L1s are enabled post-Etna, skipping test.") - } - - keychain := env.NewKeychain() - baseWallet := e2e.NewWallet(tc, keychain, nodeURI) - - pWallet := baseWallet.P() - pClient := platformvm.NewClient(nodeURI.URI) + now := time.Now() + if !upgrades.IsEtnaActivated(now) { + ginkgo.Skip("Etna is not activated. Permissionless L1s are enabled post-Etna, skipping test.") + } + }) - owner := &secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{ - keychain.Keys[0].Address(), - }, - } + tc.By("loading the wallet") + var ( + keychain = env.NewKeychain() + baseWallet = e2e.NewWallet(tc, keychain, nodeURI) + pWallet = baseWallet.P() + pClient = platformvmsdk.NewClient(nodeURI.URI) + owner = &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ + keychain.Keys[0].Address(), + }, + } + ) + tc.By("creating the chain genesis") genesisKey, err := secp256k1.NewPrivateKey() require.NoError(err) @@ -75,40 +89,48 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { }) require.NoError(err) - tc.By("issuing a CreateSubnetTx") - subnetTx, err := pWallet.IssueCreateSubnetTx( - owner, - tc.WithDefaultContext(), - ) - require.NoError(err) + var subnetID ids.ID + tc.By("issuing a CreateSubnetTx", func() { + subnetTx, err := pWallet.IssueCreateSubnetTx( + owner, + tc.WithDefaultContext(), + ) + require.NoError(err) - tc.By("verifying a Permissioned Subnet was successfully created") - subnetID := subnetTx.ID() - require.NotEqual(subnetID, constants.PrimaryNetworkID) + subnetID = subnetTx.ID() + }) - res, err := pClient.GetSubnet(tc.DefaultContext(), subnetID) - require.NoError(err) - require.Equal( - platformvm.GetSubnetClientResponse{ - IsPermissioned: true, - ControlKeys: []ids.ShortID{ - keychain.Keys[0].Address(), + tc.By("verifying a Permissioned Subnet was successfully created", func() { + require.NotEqual(constants.PrimaryNetworkID, subnetID) + + subnet, err := pClient.GetSubnet(tc.DefaultContext(), subnetID) + require.NoError(err) + require.Equal( + platformvmsdk.GetSubnetClientResponse{ + IsPermissioned: true, + ControlKeys: []ids.ShortID{ + keychain.Keys[0].Address(), + }, + Threshold: 1, }, - Threshold: 1, - }, - res, - ) + subnet, + ) + }) - tc.By("issuing a CreateChainTx") - chainTx, err := pWallet.IssueCreateChainTx( - subnetID, - genesisBytes, - constants.XSVMID, - nil, - "No Permissions", - tc.WithDefaultContext(), - ) - require.NoError(err) + var chainID ids.ID + tc.By("issuing a CreateChainTx", func() { + chainTx, err := pWallet.IssueCreateChainTx( + subnetID, + genesisBytes, + constants.XSVMID, + nil, + "No Permissions", + tc.WithDefaultContext(), + ) + require.NoError(err) + + chainID = chainTx.ID() + }) tc.By("creating the genesis validator") subnetGenesisNode := e2e.AddEphemeralNode(tc, env.GetNetwork(), tmpnet.FlagsMap{ @@ -121,73 +143,96 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() { genesisNodePK, err := bls.PublicKeyFromCompressedBytes(genesisNodePoP.PublicKey[:]) require.NoError(err) + tc.By("connecting to the genesis validator") var ( - chainID = chainTx.ID() - address = []byte{} + networkID = env.GetNetwork().GetNetworkID() + genesisPeerMessages = buffer.NewUnboundedBlockingDeque[p2pmessage.InboundMessage](1) ) - tc.By("issuing a ConvertSubnetTx") - _, err = pWallet.IssueConvertSubnetTx( - subnetID, - chainID, - address, - []*txs.ConvertSubnetValidator{ - { - NodeID: subnetGenesisNode.NodeID.Bytes(), - Weight: genesisWeight, - Balance: units.Avax, - Signer: *genesisNodePoP, - }, - }, - tc.WithDefaultContext(), + genesisPeer, err := peer.StartTestPeer( + tc.DefaultContext(), + subnetGenesisNode.StakingAddress, + networkID, + router.InboundHandlerFunc(func(_ context.Context, m p2pmessage.InboundMessage) { + tc.Outf("received %s %s from %s\n", m.Op(), m.Message(), m.NodeID()) + genesisPeerMessages.PushRight(m) + }), ) require.NoError(err) - - expectedConversionID, err := message.SubnetConversionID(message.SubnetConversionData{ - SubnetID: subnetID, - ManagerChainID: chainID, - ManagerAddress: address, - Validators: []message.SubnetConversionValidatorData{ - { - NodeID: subnetGenesisNode.NodeID.Bytes(), - BLSPublicKey: genesisNodePoP.PublicKey, - Weight: genesisWeight, + defer func() { + genesisPeerMessages.Close() + genesisPeer.StartClose() + require.NoError(genesisPeer.AwaitClosed(tc.DefaultContext())) + }() + + address := []byte{} + tc.By("issuing a ConvertSubnetTx", func() { + _, err := pWallet.IssueConvertSubnetTx( + subnetID, + chainID, + address, + []*txs.ConvertSubnetValidator{ + { + NodeID: subnetGenesisNode.NodeID.Bytes(), + Weight: genesisWeight, + Balance: genesisBalance, + Signer: *genesisNodePoP, + }, }, - }, + tc.WithDefaultContext(), + ) + require.NoError(err) }) - require.NoError(err) - tc.By("verifying the Permissioned Subnet was converted to a Permissionless L1") - res, err = pClient.GetSubnet(tc.DefaultContext(), subnetID) - require.NoError(err) - require.Equal( - platformvm.GetSubnetClientResponse{ - IsPermissioned: false, - ControlKeys: []ids.ShortID{ - keychain.Keys[0].Address(), - }, - Threshold: 1, - ConversionID: expectedConversionID, + verifyValidatorSet := func(expectedValidators map[ids.NodeID]*snowvalidators.GetValidatorOutput) { + height, err := pClient.GetHeight(tc.DefaultContext()) + require.NoError(err) + + subnetValidators, err := pClient.GetValidatorsAt(tc.DefaultContext(), subnetID, height) + require.NoError(err) + require.Equal(expectedValidators, subnetValidators) + } + tc.By("verifying the Permissioned Subnet was converted to a Permissionless L1", func() { + expectedConversionID, err := warpmessage.SubnetConversionID(warpmessage.SubnetConversionData{ + SubnetID: subnetID, ManagerChainID: chainID, ManagerAddress: address, - }, - res, - ) - - tc.By("verifying the Permissionless L1 reports the correct validator set") - height, err := pClient.GetHeight(tc.DefaultContext()) - require.NoError(err) - - subnetValidators, err := pClient.GetValidatorsAt(tc.DefaultContext(), subnetID, height) - require.NoError(err) - require.Equal( - map[ids.NodeID]*validators.GetValidatorOutput{ - subnetGenesisNode.NodeID: { - NodeID: subnetGenesisNode.NodeID, - PublicKey: genesisNodePK, - Weight: genesisWeight, + Validators: []warpmessage.SubnetConversionValidatorData{ + { + NodeID: subnetGenesisNode.NodeID.Bytes(), + BLSPublicKey: genesisNodePoP.PublicKey, + Weight: genesisWeight, + }, }, - }, - subnetValidators, - ) + }) + require.NoError(err) + + tc.By("verifying the subnet reports as being converted", func() { + subnet, err := pClient.GetSubnet(tc.DefaultContext(), subnetID) + require.NoError(err) + require.Equal( + platformvmsdk.GetSubnetClientResponse{ + IsPermissioned: false, + ControlKeys: []ids.ShortID{ + keychain.Keys[0].Address(), + }, + Threshold: 1, + ConversionID: expectedConversionID, + ManagerChainID: chainID, + ManagerAddress: address, + }, + subnet, + ) + }) + + tc.By("verifying the validator set was updated", func() { + verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{ + subnetGenesisNode.NodeID: { + NodeID: subnetGenesisNode.NodeID, + PublicKey: genesisNodePK, + Weight: genesisWeight, + }, + }) + }) + }) }) })