From 4e3d452bce324f7ecc241d61f0c123e1124c0ac7 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 9 Jul 2024 11:43:46 -0700 Subject: [PATCH 01/12] warp: add "requirePrimaryNetworkSigners" precompile config --- plugin/evm/vm.go | 20 +++++++++++++++++++- precompile/contracts/warp/config.go | 18 ++++++++++++++++-- precompile/contracts/warp/predicate_test.go | 14 ++++++++++++-- warp/validators/state.go | 16 +++++++++------- warp/validators/state_test.go | 2 +- 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 1c480b497b..2c33c10f2f 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -37,6 +37,7 @@ import ( "github.com/ava-labs/subnet-evm/plugin/evm/message" "github.com/ava-labs/subnet-evm/trie/triedb/hashdb" + warpcontract "github.com/ava-labs/subnet-evm/precompile/contracts/warp" "github.com/ava-labs/subnet-evm/rpc" statesyncclient "github.com/ava-labs/subnet-evm/sync/client" "github.com/ava-labs/subnet-evm/sync/client/stats" @@ -998,7 +999,18 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) { } if vm.config.WarpAPIEnabled { - validatorsState := warpValidators.NewState(vm.ctx) + requirePrimaryNetworkSigners := func() bool { + warpCfgIntf, ok := vm.currentRules().ActivePrecompiles[warpcontract.ContractAddress] + if !ok { + return false + } + warpCfg, ok := warpCfgIntf.(*warpcontract.Config) + if !ok { + return false + } + return warpCfg.RequirePrimaryNetworkSigners + } + validatorsState := warpValidators.NewState(vm.ctx, requirePrimaryNetworkSigners) if err := handler.RegisterName("warp", warp.NewAPI(vm.ctx.NetworkID, vm.ctx.SubnetID, vm.ctx.ChainID, validatorsState, vm.warpBackend, vm.client)); err != nil { return nil, err } @@ -1046,6 +1058,12 @@ func (vm *VM) GetCurrentNonce(address common.Address) (uint64, error) { return state.GetNonce(address), nil } +// currentRules returns the chain rules for the current block. +func (vm *VM) currentRules() params.Rules { + header := vm.eth.APIBackend.CurrentHeader() + return vm.chainConfig.Rules(header.Number, header.Time) +} + func (vm *VM) startContinuousProfiler() { // If the profiler directory is empty, return immediately // without creating or starting a continuous profiler. diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index dde04a8695..af74dfa55b 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -47,7 +47,8 @@ var ( // adds specific configuration for Warp. type Config struct { precompileconfig.Upgrade - QuorumNumerator uint64 `json:"quorumNumerator"` + QuorumNumerator uint64 `json:"quorumNumerator"` + RequirePrimaryNetworkSigners bool `json:"requirePrimaryNetworkSigners"` } // NewConfig returns a config for a network upgrade at [blockTimestamp] that enables @@ -59,6 +60,11 @@ func NewConfig(blockTimestamp *uint64, quorumNumerator uint64) *Config { } } +func (c *Config) WithRequirePrimaryNetworkSigners(requirePrimaryNetworkSigners bool) *Config { + c.RequirePrimaryNetworkSigners = requirePrimaryNetworkSigners + return c +} + // NewDefaultConfig returns a config for a network upgrade at [blockTimestamp] that enables // Warp with the default quorum numerator (0 denotes using the default). func NewDefaultConfig(blockTimestamp *uint64) *Config { @@ -202,7 +208,7 @@ func (c *Config) VerifyPredicate(predicateContext *precompileconfig.PredicateCon context.Background(), &warpMsg.UnsignedMessage, predicateContext.SnowCtx.NetworkID, - warpValidators.NewState(predicateContext.SnowCtx), // Wrap validators.State on the chain snow context to special case the Primary Network + warpValidators.NewState(predicateContext.SnowCtx, func() bool { return c.RequirePrimaryNetworkSigners }), // Wrap validators.State on the chain snow context to special case the Primary Network predicateContext.ProposerVMBlockCtx.PChainHeight, quorumNumerator, WarpQuorumDenominator, @@ -215,3 +221,11 @@ func (c *Config) VerifyPredicate(predicateContext *precompileconfig.PredicateCon return nil } + +func RequirePrimaryNetworkSigners(cfg precompileconfig.Config) func() bool { + warpCfg, ok := cfg.(*Config) + if !ok { + return func() bool { return false } + } + return func() bool { return warpCfg.RequirePrimaryNetworkSigners } +} diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index 2b293b97a9..ca2f1f8bce 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -227,6 +227,12 @@ func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicateBy } func TestWarpMessageFromPrimaryNetwork(t *testing.T) { + for _, requirePrimaryNetworkSigners := range []bool{true, false} { + testWarpMessageFromPrimaryNetwork(t, requirePrimaryNetworkSigners) + } +} + +func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigners bool) { require := require.New(t) numKeys := 10 cChainID := ids.GenerateTestID() @@ -272,13 +278,17 @@ func TestWarpMessageFromPrimaryNetwork(t *testing.T) { return constants.PrimaryNetworkID, nil // Return Primary Network SubnetID }, GetValidatorSetF: func(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - require.Equal(snowCtx.SubnetID, subnetID) + expectedSubnetID := snowCtx.SubnetID + if requirePrimaryNetworkSigners { + expectedSubnetID = constants.PrimaryNetworkID + } + require.Equal(expectedSubnetID, subnetID) return getValidatorsOutput, nil }, } test := testutils.PredicateTest{ - Config: NewDefaultConfig(utils.NewUint64(0)), + Config: NewDefaultConfig(utils.NewUint64(0)).WithRequirePrimaryNetworkSigners(requirePrimaryNetworkSigners), PredicateContext: &precompileconfig.PredicateContext{ SnowCtx: snowCtx, ProposerVMBlockCtx: &block.Context{ diff --git a/warp/validators/state.go b/warp/validators/state.go index c2929a5807..1c3079d66b 100644 --- a/warp/validators/state.go +++ b/warp/validators/state.go @@ -19,18 +19,20 @@ var _ validators.State = (*State)(nil) // signatures from a threshold of the RECEIVING subnet validator set rather than the full Primary Network // since the receiving subnet already relies on a majority of its validators being correct. type State struct { - chainContext *snow.Context validators.State + chainContext *snow.Context + requirePrimaryNetworkSigners func() bool } // NewState returns a wrapper of [validators.State] which special cases the handling of the Primary Network. // // The wrapped state will return the chainContext's Subnet validator set instead of the Primary Network when // the Primary Network SubnetID is passed in. -func NewState(chainContext *snow.Context) *State { +func NewState(chainContext *snow.Context, requirePrimaryNetworkSigners func() bool) *State { return &State{ - chainContext: chainContext, - State: chainContext.ValidatorState, + State: chainContext.ValidatorState, + chainContext: chainContext, + requirePrimaryNetworkSigners: requirePrimaryNetworkSigners, } } @@ -39,9 +41,9 @@ func (s *State) GetValidatorSet( height uint64, subnetID ids.ID, ) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - // If the subnetID is anything other than the Primary Network, this is a direct - // passthrough - if subnetID != constants.PrimaryNetworkID { + // If the subnetID is anything other than the Primary Network, or Primary + // Network signers are required, this is a direct passthrough. + if s.requirePrimaryNetworkSigners() || subnetID != constants.PrimaryNetworkID { return s.State.GetValidatorSet(ctx, height, subnetID) } diff --git a/warp/validators/state_test.go b/warp/validators/state_test.go index 4d642c3e2e..fdb1e0d613 100644 --- a/warp/validators/state_test.go +++ b/warp/validators/state_test.go @@ -26,7 +26,7 @@ func TestGetValidatorSetPrimaryNetwork(t *testing.T) { snowCtx := utils.TestSnowContext() snowCtx.SubnetID = mySubnetID snowCtx.ValidatorState = mockState - state := NewState(snowCtx) + state := NewState(snowCtx, func() bool { return false }) // Expect that requesting my validator set returns my validator set mockState.EXPECT().GetValidatorSet(gomock.Any(), gomock.Any(), mySubnetID).Return(make(map[ids.NodeID]*validators.GetValidatorOutput), nil) output, err := state.GetValidatorSet(context.Background(), 10, mySubnetID) From 75bc0b0f7017e895086fe6f1029cce7eb884ff29 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 9 Jul 2024 12:01:34 -0700 Subject: [PATCH 02/12] unused --- precompile/contracts/warp/config.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index af74dfa55b..6fffb89d21 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -221,11 +221,3 @@ func (c *Config) VerifyPredicate(predicateContext *precompileconfig.PredicateCon return nil } - -func RequirePrimaryNetworkSigners(cfg precompileconfig.Config) func() bool { - warpCfg, ok := cfg.(*Config) - if !ok { - return func() bool { return false } - } - return func() bool { return warpCfg.RequirePrimaryNetworkSigners } -} From efedf45d2704183461a1887a37aae3f52ce29678 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Mon, 19 Aug 2024 17:46:17 -0700 Subject: [PATCH 03/12] passing ut for re-enable precompile --- plugin/evm/vm_warp_test.go | 158 ++++++++++++++++++++++++++++--------- 1 file changed, 122 insertions(+), 36 deletions(-) diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index 2b4109bae2..17fbab7b99 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -13,11 +13,13 @@ import ( _ "embed" "github.com/ava-labs/avalanchego/ids" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/snow/validators/validatorstest" "github.com/ava-labs/avalanchego/upgrade" avagoUtils "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/components/chain" @@ -408,43 +410,107 @@ func TestReceiveWarpMessage(t *testing.T) { } genesisJSON, err := genesis.MarshalJSON() require.NoError(err) - issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), "", "") + + // disable warp so we can re-enable it with RequirePrimaryNetworkSigners + disableTime := upgrade.InitiallyActiveTime.Add(10 * time.Second) + disableConfig := warp.NewDisableConfig(utils.TimeToNewUint64(disableTime)) + + // re-enable warp with RequirePrimaryNetworkSigners + reEnableTime := disableTime.Add(10 * time.Second) + reEnableConfig := warp.NewDefaultConfig(utils.TimeToNewUint64(reEnableTime)).WithRequirePrimaryNetworkSigners(true) + + upgradeConfig := params.UpgradeConfig{ + PrecompileUpgrades: []params.PrecompileUpgrade{ + {Config: disableConfig}, + {Config: reEnableConfig}, + }, + } + upgradeBytes, err := json.Marshal(upgradeConfig) + require.NoError(err) + + issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), "", string(upgradeBytes)) defer func() { require.NoError(vm.Shutdown(context.Background())) }() - acceptedLogsChan := make(chan []*types.Log, 10) - logsSub := vm.eth.APIBackend.SubscribeAcceptedLogsEvent(acceptedLogsChan) - defer logsSub.Unsubscribe() + // Subnet messages should use subnet signers + testReceiveWarpMessage(t, issuer, vm, false, false, upgrade.InitiallyActiveTime) - payloadData := avagoUtils.RandomBytes(100) + /// At genesis, primary network messages should use subnet signers + blockTime := upgrade.InitiallyActiveTime.Add(2 * time.Second) // for fees + require.True(blockTime.Before(disableTime)) + testReceiveWarpMessage(t, issuer, vm, true, false, blockTime) + + // After re-enabled, primary network messages should use primary signers + testReceiveWarpMessage(t, issuer, vm, true, true, reEnableTime) +} +func testReceiveWarpMessage( + t *testing.T, issuer chan commonEng.Message, vm *VM, + fromPrimary, usePrimarySigners bool, + blockTime time.Time, +) { + require := require.New(t) + payloadData := avagoUtils.RandomBytes(100) addressedPayload, err := payload.NewAddressedCall( testEthAddrs[0].Bytes(), payloadData, ) require.NoError(err) + + vm.ctx.SubnetID = ids.GenerateTestID() + vm.ctx.NetworkID = testNetworkID + sourceChainID := vm.ctx.ChainID + if fromPrimary { + sourceChainID = testCChainID + } unsignedMessage, err := avalancheWarp.NewUnsignedMessage( vm.ctx.NetworkID, - vm.ctx.ChainID, + sourceChainID, addressedPayload.Bytes(), ) require.NoError(err) - nodeID1 := ids.GenerateTestNodeID() - blsSecretKey1, err := bls.NewSecretKey() - require.NoError(err) - blsPublicKey1 := bls.PublicFromSecretKey(blsSecretKey1) - blsSignature1 := bls.Sign(blsSecretKey1, unsignedMessage.Bytes()) + type signer struct { + networkID ids.ID + nodeID ids.NodeID + secret *bls.SecretKey + signature *bls.Signature + weight uint64 + } + newSigner := func(networkID ids.ID, weight uint64) signer { + secret, err := bls.NewSecretKey() + require.NoError(err) + return signer{ + networkID: networkID, + nodeID: ids.GenerateTestNodeID(), + secret: secret, + signature: bls.Sign(secret, unsignedMessage.Bytes()), + weight: weight, + } + } - nodeID2 := ids.GenerateTestNodeID() - blsSecretKey2, err := bls.NewSecretKey() - require.NoError(err) - blsPublicKey2 := bls.PublicFromSecretKey(blsSecretKey2) - blsSignature2 := bls.Sign(blsSecretKey2, unsignedMessage.Bytes()) + var ( + primarySigners = []signer{ + newSigner(constants.PrimaryNetworkID, 50), + newSigner(constants.PrimaryNetworkID, 50), + } + subnetSigners = []signer{ + newSigner(vm.ctx.SubnetID, 50), + newSigner(vm.ctx.SubnetID, 50), + } + ) + signers := subnetSigners + if usePrimarySigners { + signers = primarySigners + } - blsAggregatedSignature, err := bls.AggregateSignatures([]*bls.Signature{blsSignature1, blsSignature2}) + blsSignatures := make([]*bls.Signature, len(signers)) + for i := range signers { + blsSignatures[i] = signers[i].signature + } + blsAggregatedSignature, err := bls.AggregateSignatures(blsSignatures) require.NoError(err) minimumValidPChainHeight := uint64(10) @@ -452,30 +518,38 @@ func TestReceiveWarpMessage(t *testing.T) { vm.ctx.ValidatorState = &validatorstest.State{ GetSubnetIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) { - return ids.Empty, nil + if sourceChainID == testCChainID { + return constants.PrimaryNetworkID, nil + } + return vm.ctx.SubnetID, nil }, GetValidatorSetF: func(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { if height < minimumValidPChainHeight { return nil, getValidatorSetTestErr } - return map[ids.NodeID]*validators.GetValidatorOutput{ - nodeID1: { - NodeID: nodeID1, - PublicKey: blsPublicKey1, - Weight: 50, - }, - nodeID2: { - NodeID: nodeID2, - PublicKey: blsPublicKey2, - Weight: 50, - }, - }, nil + toValidatorOutput := func(signers []signer) map[ids.NodeID]*validators.GetValidatorOutput { + vdrOutput := make(map[ids.NodeID]*validators.GetValidatorOutput) + for _, s := range signers { + vdrOutput[s.nodeID] = &validators.GetValidatorOutput{ + NodeID: s.nodeID, + PublicKey: bls.PublicFromSecretKey(s.secret), + Weight: s.weight, + } + } + return vdrOutput + } + validators := subnetSigners + if subnetID == constants.PrimaryNetworkID { + validators = primarySigners + } + return toValidatorOutput(validators), nil }, } signersBitSet := set.NewBits() - signersBitSet.Add(0) - signersBitSet.Add(1) + for i := range signers { + signersBitSet.Add(i) + } warpSignature := &avalancheWarp.BitSetSignature{ Signers: signersBitSet.Bytes(), @@ -495,7 +569,7 @@ func TestReceiveWarpMessage(t *testing.T) { getVerifiedWarpMessageTx, err := types.SignTx( predicate.NewPredicateTx( vm.chainConfig.ChainID, - 0, + vm.txPool.Nonce(testEthAddrs[0]), &warp.Module.Address, 1_000_000, big.NewInt(225*params.GWei), @@ -519,11 +593,25 @@ func TestReceiveWarpMessage(t *testing.T) { validProposerCtx := &block.Context{ PChainHeight: minimumValidPChainHeight, } - vm.clock.Set(vm.clock.Time().Add(2 * time.Second)) + vm.clock.Set(blockTime) <-issuer block2, err := vm.BuildBlockWithContext(context.Background(), validProposerCtx) require.NoError(err) + require.NoError(vm.SetPreference(context.Background(), block2.ID())) + + // Require the block was built with a successful predicate result + ethBlock := block2.(*chain.BlockWrapper).Block.(*Block).ethBlock + headerPredicateResultsBytes, ok := predicate.GetPredicateResultBytes(ethBlock.Extra()) + require.True(ok) + results, err := predicate.ParseResults(headerPredicateResultsBytes) + require.NoError(err) + txResultsBytes := results.GetResults( + getVerifiedWarpMessageTx.Hash(), + warp.ContractAddress, + ) + bitset := set.BitsFromBytes(txResultsBytes) + require.Zero(bitset.Len()) // Empty bitset indicates success block2VerifyWithCtx, ok := block2.(block.WithVerifyContext) require.True(ok) @@ -531,7 +619,6 @@ func TestReceiveWarpMessage(t *testing.T) { require.NoError(err) require.True(shouldVerifyWithCtx) require.NoError(block2VerifyWithCtx.VerifyWithContext(context.Background(), validProposerCtx)) - require.NoError(vm.SetPreference(context.Background(), block2.ID())) // Verify the block with another valid context with identical predicate results require.NoError(block2VerifyWithCtx.VerifyWithContext(context.Background(), &block.Context{ @@ -548,7 +635,6 @@ func TestReceiveWarpMessage(t *testing.T) { require.NoError(block2.Accept(context.Background())) vm.blockChain.DrainAcceptorQueue() - ethBlock := block2.(*chain.BlockWrapper).Block.(*Block).ethBlock verifiedMessageReceipts := vm.blockChain.GetReceiptsByHash(ethBlock.Hash()) require.Len(verifiedMessageReceipts, 1) verifiedMessageTxReceipt := verifiedMessageReceipts[0] From 4fea6d6d1bcf1987d3a5031672f8566ef365fdf5 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Mon, 19 Aug 2024 17:47:51 -0700 Subject: [PATCH 04/12] simplify --- plugin/evm/vm_warp_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index 17fbab7b99..116cf25038 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -461,13 +461,9 @@ func testReceiveWarpMessage( vm.ctx.SubnetID = ids.GenerateTestID() vm.ctx.NetworkID = testNetworkID - sourceChainID := vm.ctx.ChainID - if fromPrimary { - sourceChainID = testCChainID - } unsignedMessage, err := avalancheWarp.NewUnsignedMessage( vm.ctx.NetworkID, - sourceChainID, + vm.ctx.ChainID, addressedPayload.Bytes(), ) require.NoError(err) @@ -518,7 +514,7 @@ func testReceiveWarpMessage( vm.ctx.ValidatorState = &validatorstest.State{ GetSubnetIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) { - if sourceChainID == testCChainID { + if fromPrimary { return constants.PrimaryNetworkID, nil } return vm.ctx.SubnetID, nil From 769564fc1c487d09d9309ee261ee533ce9d2a4e7 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Mon, 19 Aug 2024 17:53:46 -0700 Subject: [PATCH 05/12] Update plugin/evm/vm_warp_test.go Signed-off-by: Darioush Jalali --- plugin/evm/vm_warp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index 116cf25038..f1b2ec6bf9 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -437,7 +437,7 @@ func TestReceiveWarpMessage(t *testing.T) { // Subnet messages should use subnet signers testReceiveWarpMessage(t, issuer, vm, false, false, upgrade.InitiallyActiveTime) - /// At genesis, primary network messages should use subnet signers + // At genesis, primary network messages should use subnet signers blockTime := upgrade.InitiallyActiveTime.Add(2 * time.Second) // for fees require.True(blockTime.Before(disableTime)) testReceiveWarpMessage(t, issuer, vm, true, false, blockTime) From 379f34d79e532a2fdc16fe4d2386f1322fa9ca46 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Mon, 19 Aug 2024 17:54:58 -0700 Subject: [PATCH 06/12] reduce diff --- plugin/evm/vm_warp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index 116cf25038..f7728f3584 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -594,7 +594,6 @@ func testReceiveWarpMessage( block2, err := vm.BuildBlockWithContext(context.Background(), validProposerCtx) require.NoError(err) - require.NoError(vm.SetPreference(context.Background(), block2.ID())) // Require the block was built with a successful predicate result ethBlock := block2.(*chain.BlockWrapper).Block.(*Block).ethBlock @@ -615,6 +614,7 @@ func testReceiveWarpMessage( require.NoError(err) require.True(shouldVerifyWithCtx) require.NoError(block2VerifyWithCtx.VerifyWithContext(context.Background(), validProposerCtx)) + require.NoError(vm.SetPreference(context.Background(), block2.ID())) // Verify the block with another valid context with identical predicate results require.NoError(block2VerifyWithCtx.VerifyWithContext(context.Background(), &block.Context{ From 3f9340c916f7cabf802c6fbc8c99c3e3fccc238e Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 20 Aug 2024 15:37:01 -0700 Subject: [PATCH 07/12] review comments --- plugin/evm/vm.go | 25 +++--- plugin/evm/vm_warp_test.go | 91 +++++++++++++-------- precompile/contracts/warp/config.go | 14 ++-- precompile/contracts/warp/config_test.go | 18 ++-- precompile/contracts/warp/predicate_test.go | 4 +- 5 files changed, 85 insertions(+), 67 deletions(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index cd3b748778..c5d5ca7353 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -1020,18 +1020,7 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) { } if vm.config.WarpAPIEnabled { - requirePrimaryNetworkSigners := func() bool { - warpCfgIntf, ok := vm.currentRules().ActivePrecompiles[warpcontract.ContractAddress] - if !ok { - return false - } - warpCfg, ok := warpCfgIntf.(*warpcontract.Config) - if !ok { - return false - } - return warpCfg.RequirePrimaryNetworkSigners - } - validatorsState := warpValidators.NewState(vm.ctx, requirePrimaryNetworkSigners) + validatorsState := warpValidators.NewState(vm.ctx, vm.requirePrimaryNetworkSigners) if err := handler.RegisterName("warp", warp.NewAPI(vm.ctx.NetworkID, vm.ctx.SubnetID, vm.ctx.ChainID, validatorsState, vm.warpBackend, vm.client)); err != nil { return nil, err } @@ -1085,6 +1074,18 @@ func (vm *VM) currentRules() params.Rules { return vm.chainConfig.Rules(header.Number, header.Time) } +// requirePrimaryNetworkSigners returns true if warp messages from the primary +// network must be signed by the primary network validators. +// This is necessary when the subnet is not validating the primary network. +func (vm *VM) requirePrimaryNetworkSigners() bool { + switch c := vm.currentRules().ActivePrecompiles[warpcontract.ContractAddress].(type) { + case *warpcontract.Config: + return c.RequirePrimaryNetworkSigners + default: // includes nil due to non-presence + return false + } +} + func (vm *VM) startContinuousProfiler() { // If the profiler directory is empty, return immediately // without creating or starting a continuous profiler. diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index 4ebc94a15f..d7cb2e3aaa 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -47,6 +47,20 @@ var ( exampleWarpABI string ) +type warpMsgFrom int + +const ( + fromSubnet warpMsgFrom = iota + fromPrimary +) + +type useWarpMsgSigners int + +const ( + signersSubnet useWarpMsgSigners = iota + signersPrimary +) + func TestSendWarpMessage(t *testing.T) { require := require.New(t) genesis := &core.Genesis{} @@ -406,6 +420,8 @@ func TestReceiveWarpMessage(t *testing.T) { genesis := &core.Genesis{} require.NoError(genesis.UnmarshalJSON([]byte(genesisJSONDurango))) genesis.Config.GenesisPrecompiles = params.Precompiles{ + // Note that warp is enabled without RequirePrimaryNetworkSigners + // by default in the genesis configuration. warp.ConfigKey: warp.NewDefaultConfig(utils.TimeToNewUint64(upgrade.InitiallyActiveTime)), } genesisJSON, err := genesis.MarshalJSON() @@ -417,7 +433,11 @@ func TestReceiveWarpMessage(t *testing.T) { // re-enable warp with RequirePrimaryNetworkSigners reEnableTime := disableTime.Add(10 * time.Second) - reEnableConfig := warp.NewDefaultConfig(utils.TimeToNewUint64(reEnableTime)).WithRequirePrimaryNetworkSigners(true) + reEnableConfig := warp.NewConfig( + utils.TimeToNewUint64(reEnableTime), + 0, // QuorumNumerator + true, // RequirePrimaryNetworkSigners + ) upgradeConfig := params.UpgradeConfig{ PrecompileUpgrades: []params.PrecompileUpgrade{ @@ -434,21 +454,23 @@ func TestReceiveWarpMessage(t *testing.T) { require.NoError(vm.Shutdown(context.Background())) }() - // Subnet messages should use subnet signers - testReceiveWarpMessage(t, issuer, vm, false, false, upgrade.InitiallyActiveTime) + testReceiveWarpMessage(t, issuer, vm, fromSubnet, signersSubnet, upgrade.InitiallyActiveTime) - // At genesis, primary network messages should use subnet signers - blockTime := upgrade.InitiallyActiveTime.Add(2 * time.Second) // for fees + // Build the next block with a 2 second delay. Fees will be high if the gap is too short. + blockTime := upgrade.InitiallyActiveTime.Add(2 * time.Second) require.True(blockTime.Before(disableTime)) - testReceiveWarpMessage(t, issuer, vm, true, false, blockTime) - // After re-enabled, primary network messages should use primary signers - testReceiveWarpMessage(t, issuer, vm, true, true, reEnableTime) + // At genesis, primary network messages should use subnet signers + testReceiveWarpMessage(t, issuer, vm, fromPrimary, signersSubnet, blockTime) + + // After re-enabled with the modified configuration, primary network messages + // should use primary signers. + testReceiveWarpMessage(t, issuer, vm, fromPrimary, signersPrimary, reEnableTime) } func testReceiveWarpMessage( t *testing.T, issuer chan commonEng.Message, vm *VM, - fromPrimary, usePrimarySigners bool, + msgFrom warpMsgFrom, useSigners useWarpMsgSigners, blockTime time.Time, ) { require := require.New(t) @@ -487,18 +509,16 @@ func testReceiveWarpMessage( } } - var ( - primarySigners = []signer{ - newSigner(constants.PrimaryNetworkID, 50), - newSigner(constants.PrimaryNetworkID, 50), - } - subnetSigners = []signer{ - newSigner(vm.ctx.SubnetID, 50), - newSigner(vm.ctx.SubnetID, 50), - } - ) + primarySigners := []signer{ + newSigner(constants.PrimaryNetworkID, 50), + newSigner(constants.PrimaryNetworkID, 50), + } + subnetSigners := []signer{ + newSigner(vm.ctx.SubnetID, 50), + newSigner(vm.ctx.SubnetID, 50), + } signers := subnetSigners - if usePrimarySigners { + if useSigners == signersPrimary { signers = primarySigners } @@ -514,7 +534,7 @@ func testReceiveWarpMessage( vm.ctx.ValidatorState = &validatorstest.State{ GetSubnetIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) { - if fromPrimary { + if msgFrom == fromPrimary { return constants.PrimaryNetworkID, nil } return vm.ctx.SubnetID, nil @@ -523,22 +543,20 @@ func testReceiveWarpMessage( if height < minimumValidPChainHeight { return nil, getValidatorSetTestErr } - toValidatorOutput := func(signers []signer) map[ids.NodeID]*validators.GetValidatorOutput { - vdrOutput := make(map[ids.NodeID]*validators.GetValidatorOutput) - for _, s := range signers { - vdrOutput[s.nodeID] = &validators.GetValidatorOutput{ - NodeID: s.nodeID, - PublicKey: bls.PublicFromSecretKey(s.secret), - Weight: s.weight, - } - } - return vdrOutput - } - validators := subnetSigners + signers := subnetSigners if subnetID == constants.PrimaryNetworkID { - validators = primarySigners + signers = primarySigners } - return toValidatorOutput(validators), nil + + vdrOutput := make(map[ids.NodeID]*validators.GetValidatorOutput) + for _, s := range signers { + vdrOutput[s.nodeID] = &validators.GetValidatorOutput{ + NodeID: s.nodeID, + PublicKey: bls.PublicFromSecretKey(s.secret), + Weight: s.weight, + } + } + return vdrOutput, nil }, } @@ -601,6 +619,9 @@ func testReceiveWarpMessage( require.True(ok) results, err := predicate.ParseResults(headerPredicateResultsBytes) require.NoError(err) + + // Predicate results encode the index of invalid warp messages in a bitset. + // An empty bitset indicates success. txResultsBytes := results.GetResults( getVerifiedWarpMessageTx.Hash(), warp.ContractAddress, diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index 6fffb89d21..0c6dba8aaa 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -53,22 +53,18 @@ type Config struct { // NewConfig returns a config for a network upgrade at [blockTimestamp] that enables // Warp with the given quorum numerator. -func NewConfig(blockTimestamp *uint64, quorumNumerator uint64) *Config { +func NewConfig(blockTimestamp *uint64, quorumNumerator uint64, requirePrimaryNetworkSigners bool) *Config { return &Config{ - Upgrade: precompileconfig.Upgrade{BlockTimestamp: blockTimestamp}, - QuorumNumerator: quorumNumerator, + Upgrade: precompileconfig.Upgrade{BlockTimestamp: blockTimestamp}, + QuorumNumerator: quorumNumerator, + RequirePrimaryNetworkSigners: requirePrimaryNetworkSigners, } } -func (c *Config) WithRequirePrimaryNetworkSigners(requirePrimaryNetworkSigners bool) *Config { - c.RequirePrimaryNetworkSigners = requirePrimaryNetworkSigners - return c -} - // NewDefaultConfig returns a config for a network upgrade at [blockTimestamp] that enables // Warp with the default quorum numerator (0 denotes using the default). func NewDefaultConfig(blockTimestamp *uint64) *Config { - return NewConfig(blockTimestamp, 0) + return NewConfig(blockTimestamp, 0, false) } // NewDisableConfig returns config for a network upgrade at [blockTimestamp] diff --git a/precompile/contracts/warp/config_test.go b/precompile/contracts/warp/config_test.go index dc52f3a5df..a29a9f48ed 100644 --- a/precompile/contracts/warp/config_test.go +++ b/precompile/contracts/warp/config_test.go @@ -16,24 +16,24 @@ import ( func TestVerify(t *testing.T) { tests := map[string]testutils.ConfigVerifyTest{ "quorum numerator less than minimum": { - Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum-1), + Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum-1, false), ExpectedError: fmt.Sprintf("cannot specify quorum numerator (%d) < min quorum numerator (%d)", WarpQuorumNumeratorMinimum-1, WarpQuorumNumeratorMinimum), }, "quorum numerator greater than quorum denominator": { - Config: NewConfig(utils.NewUint64(3), WarpQuorumDenominator+1), + Config: NewConfig(utils.NewUint64(3), WarpQuorumDenominator+1, false), ExpectedError: fmt.Sprintf("cannot specify quorum numerator (%d) > quorum denominator (%d)", WarpQuorumDenominator+1, WarpQuorumDenominator), }, "default quorum numerator": { Config: NewDefaultConfig(utils.NewUint64(3)), }, "valid quorum numerator 1 less than denominator": { - Config: NewConfig(utils.NewUint64(3), WarpQuorumDenominator-1), + Config: NewConfig(utils.NewUint64(3), WarpQuorumDenominator-1, false), }, "valid quorum numerator 1 more than minimum": { - Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+1), + Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+1, false), }, "invalid cannot activated before Durango activation": { - Config: NewConfig(utils.NewUint64(3), 0), + Config: NewConfig(utils.NewUint64(3), 0, false), ChainConfig: func() precompileconfig.ChainConfig { config := precompileconfig.NewMockChainConfig(gomock.NewController(t)) config.EXPECT().IsDurango(gomock.Any()).Return(false) @@ -66,8 +66,8 @@ func TestEqualWarpConfig(t *testing.T) { }, "different quorum numerator": { - Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+1), - Other: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+2), + Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+1, false), + Other: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+2, false), Expected: false, }, @@ -78,8 +78,8 @@ func TestEqualWarpConfig(t *testing.T) { }, "same non-default config": { - Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+5), - Other: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+5), + Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+5, false), + Other: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+5, false), Expected: true, }, } diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index 411595914a..23b82644a5 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -289,7 +289,7 @@ func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigner } test := testutils.PredicateTest{ - Config: NewDefaultConfig(utils.NewUint64(0)).WithRequirePrimaryNetworkSigners(requirePrimaryNetworkSigners), + Config: NewConfig(utils.NewUint64(0), 0, requirePrimaryNetworkSigners), PredicateContext: &precompileconfig.PredicateContext{ SnowCtx: snowCtx, ProposerVMBlockCtx: &block.Context{ @@ -586,7 +586,7 @@ func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) { name := fmt.Sprintf("non-default quorum %d signature(s)", numSigners) tests[name] = testutils.PredicateTest{ - Config: NewConfig(utils.NewUint64(0), uint64(nonDefaultQuorumNumerator)), + Config: NewConfig(utils.NewUint64(0), uint64(nonDefaultQuorumNumerator), false), PredicateContext: &precompileconfig.PredicateContext{ SnowCtx: snowCtx, ProposerVMBlockCtx: &block.Context{ From bd0f59f23c62e3634e89ca8b268c98af1794dc3d Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 20 Aug 2024 16:14:33 -0700 Subject: [PATCH 08/12] refactor --- plugin/evm/vm.go | 4 +--- precompile/contracts/warp/config.go | 10 +++++++++- warp/service.go | 24 ++++++++++++++---------- warp/validators/state.go | 27 ++++++++++++++++++--------- warp/validators/state_test.go | 2 +- 5 files changed, 43 insertions(+), 24 deletions(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index c5d5ca7353..3e49bcc94d 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -44,7 +44,6 @@ import ( "github.com/ava-labs/subnet-evm/trie" "github.com/ava-labs/subnet-evm/warp" "github.com/ava-labs/subnet-evm/warp/handlers" - warpValidators "github.com/ava-labs/subnet-evm/warp/validators" // Force-load tracer engine to trigger registration // @@ -1020,8 +1019,7 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) { } if vm.config.WarpAPIEnabled { - validatorsState := warpValidators.NewState(vm.ctx, vm.requirePrimaryNetworkSigners) - if err := handler.RegisterName("warp", warp.NewAPI(vm.ctx.NetworkID, vm.ctx.SubnetID, vm.ctx.ChainID, validatorsState, vm.warpBackend, vm.client)); err != nil { + if err := handler.RegisterName("warp", warp.NewAPI(vm.ctx.NetworkID, vm.ctx.SubnetID, vm.ctx.ChainID, vm.ctx.ValidatorState, vm.warpBackend, vm.client, vm.requirePrimaryNetworkSigners)); err != nil { return nil, err } enabledAPIs = append(enabledAPIs, "warp") diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index 0c6dba8aaa..feede8eb44 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -200,11 +200,19 @@ func (c *Config) VerifyPredicate(predicateContext *precompileconfig.PredicateCon } log.Debug("verifying warp message", "warpMsg", warpMsg, "quorumNum", quorumNumerator, "quorumDenom", WarpQuorumDenominator) + + // Wrap validators.State on the chain snow context to special case the Primary Network + state := warpValidators.NewState( + predicateContext.SnowCtx.ValidatorState, + predicateContext.SnowCtx.SubnetID, + warpMsg.SourceChainID, + c.RequirePrimaryNetworkSigners, + ) err = warpMsg.Signature.Verify( context.Background(), &warpMsg.UnsignedMessage, predicateContext.SnowCtx.NetworkID, - warpValidators.NewState(predicateContext.SnowCtx, func() bool { return c.RequirePrimaryNetworkSigners }), // Wrap validators.State on the chain snow context to special case the Primary Network + state, predicateContext.ProposerVMBlockCtx.PChainHeight, quorumNumerator, WarpQuorumDenominator, diff --git a/warp/service.go b/warp/service.go index 2bd310f38d..4afe93a168 100644 --- a/warp/service.go +++ b/warp/service.go @@ -9,11 +9,12 @@ import ( "fmt" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/subnet-evm/peer" "github.com/ava-labs/subnet-evm/warp/aggregator" - "github.com/ava-labs/subnet-evm/warp/validators" + warpValidators "github.com/ava-labs/subnet-evm/warp/validators" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" ) @@ -25,18 +26,20 @@ type API struct { networkID uint32 sourceSubnetID, sourceChainID ids.ID backend Backend - state *validators.State + state validators.State client peer.NetworkClient + requirePrimaryNetworkSigners func() bool } -func NewAPI(networkID uint32, sourceSubnetID ids.ID, sourceChainID ids.ID, state *validators.State, backend Backend, client peer.NetworkClient) *API { +func NewAPI(networkID uint32, sourceSubnetID ids.ID, sourceChainID ids.ID, state validators.State, backend Backend, client peer.NetworkClient, requirePrimaryNetworkSigners func() bool) *API { return &API{ - networkID: networkID, - sourceSubnetID: sourceSubnetID, - sourceChainID: sourceChainID, - backend: backend, - state: state, - client: client, + networkID: networkID, + sourceSubnetID: sourceSubnetID, + sourceChainID: sourceChainID, + backend: backend, + state: state, + client: client, + requirePrimaryNetworkSigners: requirePrimaryNetworkSigners, } } @@ -104,7 +107,8 @@ func (a *API) aggregateSignatures(ctx context.Context, unsignedMessage *warp.Uns return nil, err } - validators, totalWeight, err := warp.GetCanonicalValidatorSet(ctx, a.state, pChainHeight, subnetID) + state := warpValidators.NewState(a.state, a.sourceSubnetID, a.sourceChainID, a.requirePrimaryNetworkSigners()) + validators, totalWeight, err := warp.GetCanonicalValidatorSet(ctx, state, pChainHeight, subnetID) if err != nil { return nil, fmt.Errorf("failed to get validator set: %w", err) } diff --git a/warp/validators/state.go b/warp/validators/state.go index 1c3079d66b..9c30daa306 100644 --- a/warp/validators/state.go +++ b/warp/validators/state.go @@ -7,7 +7,6 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/constants" ) @@ -20,18 +19,20 @@ var _ validators.State = (*State)(nil) // since the receiving subnet already relies on a majority of its validators being correct. type State struct { validators.State - chainContext *snow.Context - requirePrimaryNetworkSigners func() bool + mySubnetID ids.ID + sourceChainID ids.ID + requirePrimaryNetworkSigners bool } // NewState returns a wrapper of [validators.State] which special cases the handling of the Primary Network. // -// The wrapped state will return the chainContext's Subnet validator set instead of the Primary Network when +// The wrapped state will return the [mySubnetID's] validator set instead of the Primary Network when // the Primary Network SubnetID is passed in. -func NewState(chainContext *snow.Context, requirePrimaryNetworkSigners func() bool) *State { +func NewState(state validators.State, mySubnetID ids.ID, sourceChainID ids.ID, requirePrimaryNetworkSigners bool) *State { return &State{ - State: chainContext.ValidatorState, - chainContext: chainContext, + State: state, + mySubnetID: mySubnetID, + sourceChainID: sourceChainID, requirePrimaryNetworkSigners: requirePrimaryNetworkSigners, } } @@ -41,13 +42,21 @@ func (s *State) GetValidatorSet( height uint64, subnetID ids.ID, ) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + requirePrimaryNetworkSigners := s.requirePrimaryNetworkSigners + if s.sourceChainID == constants.PlatformChainID { + // Since subnets still process the P-Chain, their validators should + // be used in verifying messages from the P-Chain. + // TODO: uncomment + // requirePrimaryNetworkSigners = false + } + // If the subnetID is anything other than the Primary Network, or Primary // Network signers are required, this is a direct passthrough. - if s.requirePrimaryNetworkSigners() || subnetID != constants.PrimaryNetworkID { + if requirePrimaryNetworkSigners || subnetID != constants.PrimaryNetworkID { return s.State.GetValidatorSet(ctx, height, subnetID) } // If the requested subnet is the primary network, then we return the validator // set for the Subnet that is receiving the message instead. - return s.State.GetValidatorSet(ctx, height, s.chainContext.SubnetID) + return s.State.GetValidatorSet(ctx, height, s.mySubnetID) } diff --git a/warp/validators/state_test.go b/warp/validators/state_test.go index fdb1e0d613..b3dc67ac51 100644 --- a/warp/validators/state_test.go +++ b/warp/validators/state_test.go @@ -26,7 +26,7 @@ func TestGetValidatorSetPrimaryNetwork(t *testing.T) { snowCtx := utils.TestSnowContext() snowCtx.SubnetID = mySubnetID snowCtx.ValidatorState = mockState - state := NewState(snowCtx, func() bool { return false }) + state := NewState(snowCtx.ValidatorState, snowCtx.SubnetID, snowCtx.ChainID, false) // Expect that requesting my validator set returns my validator set mockState.EXPECT().GetValidatorSet(gomock.Any(), gomock.Any(), mySubnetID).Return(make(map[ids.NodeID]*validators.GetValidatorOutput), nil) output, err := state.GetValidatorSet(context.Background(), 10, mySubnetID) From 588f5cdf5450565950deeca6f9b45291da9de075 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 20 Aug 2024 17:12:12 -0700 Subject: [PATCH 09/12] verified in ut --- plugin/evm/vm_warp_test.go | 77 +++++++++++++++++++++++++++++++------- warp/validators/state.go | 10 +---- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index d7cb2e3aaa..eb235379ee 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -454,22 +454,73 @@ func TestReceiveWarpMessage(t *testing.T) { require.NoError(vm.Shutdown(context.Background())) }() - testReceiveWarpMessage(t, issuer, vm, fromSubnet, signersSubnet, upgrade.InitiallyActiveTime) - - // Build the next block with a 2 second delay. Fees will be high if the gap is too short. - blockTime := upgrade.InitiallyActiveTime.Add(2 * time.Second) - require.True(blockTime.Before(disableTime)) - - // At genesis, primary network messages should use subnet signers - testReceiveWarpMessage(t, issuer, vm, fromPrimary, signersSubnet, blockTime) + type test struct { + name string + sourceChainID ids.ID + msgFrom warpMsgFrom + useSigners useWarpMsgSigners + blockTime time.Time + } - // After re-enabled with the modified configuration, primary network messages - // should use primary signers. - testReceiveWarpMessage(t, issuer, vm, fromPrimary, signersPrimary, reEnableTime) + blockGap := 2 * time.Second // Build blocks with a gap. Blocks built too quickly will have high fees. + tests := []test{ + { + name: "subnet message should be signed by subnet without RequirePrimaryNetworkSigners", + sourceChainID: vm.ctx.ChainID, + msgFrom: fromSubnet, + useSigners: signersSubnet, + blockTime: upgrade.InitiallyActiveTime, + }, + { + name: "P-Chain message should be signed by subnet without RequirePrimaryNetworkSigners", + sourceChainID: constants.PlatformChainID, + msgFrom: fromPrimary, + useSigners: signersSubnet, + blockTime: upgrade.InitiallyActiveTime.Add(blockGap), + }, + { + name: "C-Chain message should be signed by subnet without RequirePrimaryNetworkSigners", + sourceChainID: testCChainID, + msgFrom: fromPrimary, + useSigners: signersSubnet, + blockTime: upgrade.InitiallyActiveTime.Add(2 * blockGap), + }, + // Note here we disable warp and re-enable it with RequirePrimaryNetworkSigners + // by using reEnableTime. + { + name: "subnet message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)", + sourceChainID: vm.ctx.ChainID, + msgFrom: fromSubnet, + useSigners: signersSubnet, + blockTime: reEnableTime, + }, + { + name: "P-Chain message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)", + sourceChainID: constants.PlatformChainID, + msgFrom: fromPrimary, + useSigners: signersSubnet, + blockTime: reEnableTime.Add(blockGap), + }, + { + name: "C-Chain message should be signed by primary with RequirePrimaryNetworkSigners (impacted)", + sourceChainID: testCChainID, + msgFrom: fromPrimary, + useSigners: signersPrimary, + blockTime: reEnableTime.Add(2 * blockGap), + }, + } + // Note each test corresponds to a block, the tests must be ordered by block + // time and cannot, eg be run in parallel or a separate golang test. + for _, test := range tests { + testReceiveWarpMessage( + t, issuer, vm, test.sourceChainID, test.msgFrom, test.useSigners, test.blockTime, + ) + } } func testReceiveWarpMessage( t *testing.T, issuer chan commonEng.Message, vm *VM, + sourceChainID ids.ID, msgFrom warpMsgFrom, useSigners useWarpMsgSigners, blockTime time.Time, ) { @@ -485,7 +536,7 @@ func testReceiveWarpMessage( vm.ctx.NetworkID = testNetworkID unsignedMessage, err := avalancheWarp.NewUnsignedMessage( vm.ctx.NetworkID, - vm.ctx.ChainID, + sourceChainID, addressedPayload.Bytes(), ) require.NoError(err) @@ -659,7 +710,7 @@ func testReceiveWarpMessage( expectedOutput, err := warp.PackGetVerifiedWarpMessageOutput(warp.GetVerifiedWarpMessageOutput{ Message: warp.WarpMessage{ - SourceChainID: common.Hash(vm.ctx.ChainID), + SourceChainID: common.Hash(sourceChainID), OriginSenderAddress: testEthAddrs[0], Payload: payloadData, }, diff --git a/warp/validators/state.go b/warp/validators/state.go index 9c30daa306..2b30cb0499 100644 --- a/warp/validators/state.go +++ b/warp/validators/state.go @@ -42,17 +42,9 @@ func (s *State) GetValidatorSet( height uint64, subnetID ids.ID, ) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - requirePrimaryNetworkSigners := s.requirePrimaryNetworkSigners - if s.sourceChainID == constants.PlatformChainID { - // Since subnets still process the P-Chain, their validators should - // be used in verifying messages from the P-Chain. - // TODO: uncomment - // requirePrimaryNetworkSigners = false - } - // If the subnetID is anything other than the Primary Network, or Primary // Network signers are required, this is a direct passthrough. - if requirePrimaryNetworkSigners || subnetID != constants.PrimaryNetworkID { + if s.requirePrimaryNetworkSigners && s.sourceChainID != constants.PlatformChainID || subnetID != constants.PrimaryNetworkID { return s.State.GetValidatorSet(ctx, height, subnetID) } From ace0680f86e7a6c9500e21f752b158b6e93f50df Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 20 Aug 2024 17:20:05 -0700 Subject: [PATCH 10/12] minor refactor --- warp/validators/state.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/warp/validators/state.go b/warp/validators/state.go index 2b30cb0499..61e6e14cfe 100644 --- a/warp/validators/state.go +++ b/warp/validators/state.go @@ -43,8 +43,9 @@ func (s *State) GetValidatorSet( subnetID ids.ID, ) (map[ids.NodeID]*validators.GetValidatorOutput, error) { // If the subnetID is anything other than the Primary Network, or Primary - // Network signers are required, this is a direct passthrough. - if s.requirePrimaryNetworkSigners && s.sourceChainID != constants.PlatformChainID || subnetID != constants.PrimaryNetworkID { + // Network signers are required (except P-Chain), this is a direct passthrough. + usePrimary := s.requirePrimaryNetworkSigners && s.sourceChainID != constants.PlatformChainID + if usePrimary || subnetID != constants.PrimaryNetworkID { return s.State.GetValidatorSet(ctx, height, subnetID) } From 74d4c70e7808e3a4aad724a68b30a839313dbca8 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 20 Aug 2024 18:16:18 -0700 Subject: [PATCH 11/12] simplify --- plugin/evm/vm_warp_test.go | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index eb235379ee..8d4e82f9dd 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -47,13 +47,6 @@ var ( exampleWarpABI string ) -type warpMsgFrom int - -const ( - fromSubnet warpMsgFrom = iota - fromPrimary -) - type useWarpMsgSigners int const ( @@ -457,7 +450,6 @@ func TestReceiveWarpMessage(t *testing.T) { type test struct { name string sourceChainID ids.ID - msgFrom warpMsgFrom useSigners useWarpMsgSigners blockTime time.Time } @@ -467,21 +459,18 @@ func TestReceiveWarpMessage(t *testing.T) { { name: "subnet message should be signed by subnet without RequirePrimaryNetworkSigners", sourceChainID: vm.ctx.ChainID, - msgFrom: fromSubnet, useSigners: signersSubnet, blockTime: upgrade.InitiallyActiveTime, }, { name: "P-Chain message should be signed by subnet without RequirePrimaryNetworkSigners", sourceChainID: constants.PlatformChainID, - msgFrom: fromPrimary, useSigners: signersSubnet, blockTime: upgrade.InitiallyActiveTime.Add(blockGap), }, { name: "C-Chain message should be signed by subnet without RequirePrimaryNetworkSigners", sourceChainID: testCChainID, - msgFrom: fromPrimary, useSigners: signersSubnet, blockTime: upgrade.InitiallyActiveTime.Add(2 * blockGap), }, @@ -490,21 +479,18 @@ func TestReceiveWarpMessage(t *testing.T) { { name: "subnet message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)", sourceChainID: vm.ctx.ChainID, - msgFrom: fromSubnet, useSigners: signersSubnet, blockTime: reEnableTime, }, { name: "P-Chain message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)", sourceChainID: constants.PlatformChainID, - msgFrom: fromPrimary, useSigners: signersSubnet, blockTime: reEnableTime.Add(blockGap), }, { name: "C-Chain message should be signed by primary with RequirePrimaryNetworkSigners (impacted)", sourceChainID: testCChainID, - msgFrom: fromPrimary, useSigners: signersPrimary, blockTime: reEnableTime.Add(2 * blockGap), }, @@ -513,16 +499,14 @@ func TestReceiveWarpMessage(t *testing.T) { // time and cannot, eg be run in parallel or a separate golang test. for _, test := range tests { testReceiveWarpMessage( - t, issuer, vm, test.sourceChainID, test.msgFrom, test.useSigners, test.blockTime, + t, issuer, vm, test.sourceChainID, test.useSigners, test.blockTime, ) } } func testReceiveWarpMessage( t *testing.T, issuer chan commonEng.Message, vm *VM, - sourceChainID ids.ID, - msgFrom warpMsgFrom, useSigners useWarpMsgSigners, - blockTime time.Time, + sourceChainID ids.ID, useSigners useWarpMsgSigners, blockTime time.Time, ) { require := require.New(t) payloadData := avagoUtils.RandomBytes(100) @@ -585,10 +569,7 @@ func testReceiveWarpMessage( vm.ctx.ValidatorState = &validatorstest.State{ GetSubnetIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) { - if msgFrom == fromPrimary { - return constants.PrimaryNetworkID, nil - } - return vm.ctx.SubnetID, nil + return sourceChainID, nil }, GetValidatorSetF: func(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { if height < minimumValidPChainHeight { From 036a518c0a7a254467816396b56e4f8f08f13423 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 20 Aug 2024 18:28:52 -0700 Subject: [PATCH 12/12] Revert "simplify" This reverts commit 74d4c70e7808e3a4aad724a68b30a839313dbca8. --- plugin/evm/vm_warp_test.go | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index 8d4e82f9dd..eb235379ee 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -47,6 +47,13 @@ var ( exampleWarpABI string ) +type warpMsgFrom int + +const ( + fromSubnet warpMsgFrom = iota + fromPrimary +) + type useWarpMsgSigners int const ( @@ -450,6 +457,7 @@ func TestReceiveWarpMessage(t *testing.T) { type test struct { name string sourceChainID ids.ID + msgFrom warpMsgFrom useSigners useWarpMsgSigners blockTime time.Time } @@ -459,18 +467,21 @@ func TestReceiveWarpMessage(t *testing.T) { { name: "subnet message should be signed by subnet without RequirePrimaryNetworkSigners", sourceChainID: vm.ctx.ChainID, + msgFrom: fromSubnet, useSigners: signersSubnet, blockTime: upgrade.InitiallyActiveTime, }, { name: "P-Chain message should be signed by subnet without RequirePrimaryNetworkSigners", sourceChainID: constants.PlatformChainID, + msgFrom: fromPrimary, useSigners: signersSubnet, blockTime: upgrade.InitiallyActiveTime.Add(blockGap), }, { name: "C-Chain message should be signed by subnet without RequirePrimaryNetworkSigners", sourceChainID: testCChainID, + msgFrom: fromPrimary, useSigners: signersSubnet, blockTime: upgrade.InitiallyActiveTime.Add(2 * blockGap), }, @@ -479,18 +490,21 @@ func TestReceiveWarpMessage(t *testing.T) { { name: "subnet message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)", sourceChainID: vm.ctx.ChainID, + msgFrom: fromSubnet, useSigners: signersSubnet, blockTime: reEnableTime, }, { name: "P-Chain message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)", sourceChainID: constants.PlatformChainID, + msgFrom: fromPrimary, useSigners: signersSubnet, blockTime: reEnableTime.Add(blockGap), }, { name: "C-Chain message should be signed by primary with RequirePrimaryNetworkSigners (impacted)", sourceChainID: testCChainID, + msgFrom: fromPrimary, useSigners: signersPrimary, blockTime: reEnableTime.Add(2 * blockGap), }, @@ -499,14 +513,16 @@ func TestReceiveWarpMessage(t *testing.T) { // time and cannot, eg be run in parallel or a separate golang test. for _, test := range tests { testReceiveWarpMessage( - t, issuer, vm, test.sourceChainID, test.useSigners, test.blockTime, + t, issuer, vm, test.sourceChainID, test.msgFrom, test.useSigners, test.blockTime, ) } } func testReceiveWarpMessage( t *testing.T, issuer chan commonEng.Message, vm *VM, - sourceChainID ids.ID, useSigners useWarpMsgSigners, blockTime time.Time, + sourceChainID ids.ID, + msgFrom warpMsgFrom, useSigners useWarpMsgSigners, + blockTime time.Time, ) { require := require.New(t) payloadData := avagoUtils.RandomBytes(100) @@ -569,7 +585,10 @@ func testReceiveWarpMessage( vm.ctx.ValidatorState = &validatorstest.State{ GetSubnetIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) { - return sourceChainID, nil + if msgFrom == fromPrimary { + return constants.PrimaryNetworkID, nil + } + return vm.ctx.SubnetID, nil }, GetValidatorSetF: func(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { if height < minimumValidPChainHeight {