Skip to content

Commit b0abb3b

Browse files
author
Darioush Jalali
authored
warp: add "requirePrimaryNetworkSigners" precompile config (#1232)
* warp: add "requirePrimaryNetworkSigners" precompile config * unused * passing ut for re-enable precompile * simplify * Update plugin/evm/vm_warp_test.go Signed-off-by: Darioush Jalali <[email protected]> * reduce diff * review comments * refactor * verified in ut * minor refactor * simplify * Revert "simplify" This reverts commit 74d4c70. --------- Signed-off-by: Darioush Jalali <[email protected]>
1 parent 1d688de commit b0abb3b

File tree

8 files changed

+277
-78
lines changed

8 files changed

+277
-78
lines changed

plugin/evm/vm.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ import (
3737
"github.com/ava-labs/subnet-evm/plugin/evm/message"
3838
"github.com/ava-labs/subnet-evm/trie/triedb/hashdb"
3939

40+
warpcontract "github.com/ava-labs/subnet-evm/precompile/contracts/warp"
4041
"github.com/ava-labs/subnet-evm/rpc"
4142
statesyncclient "github.com/ava-labs/subnet-evm/sync/client"
4243
"github.com/ava-labs/subnet-evm/sync/client/stats"
4344
"github.com/ava-labs/subnet-evm/trie"
4445
"github.com/ava-labs/subnet-evm/warp"
4546
"github.com/ava-labs/subnet-evm/warp/handlers"
46-
warpValidators "github.com/ava-labs/subnet-evm/warp/validators"
4747

4848
// Force-load tracer engine to trigger registration
4949
//
@@ -1019,8 +1019,7 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) {
10191019
}
10201020

10211021
if vm.config.WarpAPIEnabled {
1022-
validatorsState := warpValidators.NewState(vm.ctx)
1023-
if err := handler.RegisterName("warp", warp.NewAPI(vm.ctx.NetworkID, vm.ctx.SubnetID, vm.ctx.ChainID, validatorsState, vm.warpBackend, vm.client)); err != nil {
1022+
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 {
10241023
return nil, err
10251024
}
10261025
enabledAPIs = append(enabledAPIs, "warp")
@@ -1067,6 +1066,24 @@ func (vm *VM) GetCurrentNonce(address common.Address) (uint64, error) {
10671066
return state.GetNonce(address), nil
10681067
}
10691068

1069+
// currentRules returns the chain rules for the current block.
1070+
func (vm *VM) currentRules() params.Rules {
1071+
header := vm.eth.APIBackend.CurrentHeader()
1072+
return vm.chainConfig.Rules(header.Number, header.Time)
1073+
}
1074+
1075+
// requirePrimaryNetworkSigners returns true if warp messages from the primary
1076+
// network must be signed by the primary network validators.
1077+
// This is necessary when the subnet is not validating the primary network.
1078+
func (vm *VM) requirePrimaryNetworkSigners() bool {
1079+
switch c := vm.currentRules().ActivePrecompiles[warpcontract.ContractAddress].(type) {
1080+
case *warpcontract.Config:
1081+
return c.RequirePrimaryNetworkSigners
1082+
default: // includes nil due to non-presence
1083+
return false
1084+
}
1085+
}
1086+
10701087
func (vm *VM) startContinuousProfiler() {
10711088
// If the profiler directory is empty, return immediately
10721089
// without creating or starting a continuous profiler.

plugin/evm/vm_warp_test.go

Lines changed: 190 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ import (
1313
_ "embed"
1414

1515
"github.com/ava-labs/avalanchego/ids"
16+
commonEng "github.com/ava-labs/avalanchego/snow/engine/common"
1617
"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
1718
"github.com/ava-labs/avalanchego/snow/validators"
1819
"github.com/ava-labs/avalanchego/snow/validators/validatorstest"
1920
"github.com/ava-labs/avalanchego/upgrade"
2021
avagoUtils "github.com/ava-labs/avalanchego/utils"
22+
"github.com/ava-labs/avalanchego/utils/constants"
2123
"github.com/ava-labs/avalanchego/utils/crypto/bls"
2224
"github.com/ava-labs/avalanchego/utils/set"
2325
"github.com/ava-labs/avalanchego/vms/components/chain"
@@ -45,6 +47,20 @@ var (
4547
exampleWarpABI string
4648
)
4749

50+
type warpMsgFrom int
51+
52+
const (
53+
fromSubnet warpMsgFrom = iota
54+
fromPrimary
55+
)
56+
57+
type useWarpMsgSigners int
58+
59+
const (
60+
signersSubnet useWarpMsgSigners = iota
61+
signersPrimary
62+
)
63+
4864
func TestSendWarpMessage(t *testing.T) {
4965
require := require.New(t)
5066
genesis := &core.Genesis{}
@@ -404,78 +420,201 @@ func TestReceiveWarpMessage(t *testing.T) {
404420
genesis := &core.Genesis{}
405421
require.NoError(genesis.UnmarshalJSON([]byte(genesisJSONDurango)))
406422
genesis.Config.GenesisPrecompiles = params.Precompiles{
423+
// Note that warp is enabled without RequirePrimaryNetworkSigners
424+
// by default in the genesis configuration.
407425
warp.ConfigKey: warp.NewDefaultConfig(utils.TimeToNewUint64(upgrade.InitiallyActiveTime)),
408426
}
409427
genesisJSON, err := genesis.MarshalJSON()
410428
require.NoError(err)
411-
issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), "", "")
429+
430+
// disable warp so we can re-enable it with RequirePrimaryNetworkSigners
431+
disableTime := upgrade.InitiallyActiveTime.Add(10 * time.Second)
432+
disableConfig := warp.NewDisableConfig(utils.TimeToNewUint64(disableTime))
433+
434+
// re-enable warp with RequirePrimaryNetworkSigners
435+
reEnableTime := disableTime.Add(10 * time.Second)
436+
reEnableConfig := warp.NewConfig(
437+
utils.TimeToNewUint64(reEnableTime),
438+
0, // QuorumNumerator
439+
true, // RequirePrimaryNetworkSigners
440+
)
441+
442+
upgradeConfig := params.UpgradeConfig{
443+
PrecompileUpgrades: []params.PrecompileUpgrade{
444+
{Config: disableConfig},
445+
{Config: reEnableConfig},
446+
},
447+
}
448+
upgradeBytes, err := json.Marshal(upgradeConfig)
449+
require.NoError(err)
450+
451+
issuer, vm, _, _ := GenesisVM(t, true, string(genesisJSON), "", string(upgradeBytes))
412452

413453
defer func() {
414454
require.NoError(vm.Shutdown(context.Background()))
415455
}()
416456

417-
acceptedLogsChan := make(chan []*types.Log, 10)
418-
logsSub := vm.eth.APIBackend.SubscribeAcceptedLogsEvent(acceptedLogsChan)
419-
defer logsSub.Unsubscribe()
457+
type test struct {
458+
name string
459+
sourceChainID ids.ID
460+
msgFrom warpMsgFrom
461+
useSigners useWarpMsgSigners
462+
blockTime time.Time
463+
}
420464

421-
payloadData := avagoUtils.RandomBytes(100)
465+
blockGap := 2 * time.Second // Build blocks with a gap. Blocks built too quickly will have high fees.
466+
tests := []test{
467+
{
468+
name: "subnet message should be signed by subnet without RequirePrimaryNetworkSigners",
469+
sourceChainID: vm.ctx.ChainID,
470+
msgFrom: fromSubnet,
471+
useSigners: signersSubnet,
472+
blockTime: upgrade.InitiallyActiveTime,
473+
},
474+
{
475+
name: "P-Chain message should be signed by subnet without RequirePrimaryNetworkSigners",
476+
sourceChainID: constants.PlatformChainID,
477+
msgFrom: fromPrimary,
478+
useSigners: signersSubnet,
479+
blockTime: upgrade.InitiallyActiveTime.Add(blockGap),
480+
},
481+
{
482+
name: "C-Chain message should be signed by subnet without RequirePrimaryNetworkSigners",
483+
sourceChainID: testCChainID,
484+
msgFrom: fromPrimary,
485+
useSigners: signersSubnet,
486+
blockTime: upgrade.InitiallyActiveTime.Add(2 * blockGap),
487+
},
488+
// Note here we disable warp and re-enable it with RequirePrimaryNetworkSigners
489+
// by using reEnableTime.
490+
{
491+
name: "subnet message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)",
492+
sourceChainID: vm.ctx.ChainID,
493+
msgFrom: fromSubnet,
494+
useSigners: signersSubnet,
495+
blockTime: reEnableTime,
496+
},
497+
{
498+
name: "P-Chain message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)",
499+
sourceChainID: constants.PlatformChainID,
500+
msgFrom: fromPrimary,
501+
useSigners: signersSubnet,
502+
blockTime: reEnableTime.Add(blockGap),
503+
},
504+
{
505+
name: "C-Chain message should be signed by primary with RequirePrimaryNetworkSigners (impacted)",
506+
sourceChainID: testCChainID,
507+
msgFrom: fromPrimary,
508+
useSigners: signersPrimary,
509+
blockTime: reEnableTime.Add(2 * blockGap),
510+
},
511+
}
512+
// Note each test corresponds to a block, the tests must be ordered by block
513+
// time and cannot, eg be run in parallel or a separate golang test.
514+
for _, test := range tests {
515+
testReceiveWarpMessage(
516+
t, issuer, vm, test.sourceChainID, test.msgFrom, test.useSigners, test.blockTime,
517+
)
518+
}
519+
}
422520

521+
func testReceiveWarpMessage(
522+
t *testing.T, issuer chan commonEng.Message, vm *VM,
523+
sourceChainID ids.ID,
524+
msgFrom warpMsgFrom, useSigners useWarpMsgSigners,
525+
blockTime time.Time,
526+
) {
527+
require := require.New(t)
528+
payloadData := avagoUtils.RandomBytes(100)
423529
addressedPayload, err := payload.NewAddressedCall(
424530
testEthAddrs[0].Bytes(),
425531
payloadData,
426532
)
427533
require.NoError(err)
534+
535+
vm.ctx.SubnetID = ids.GenerateTestID()
536+
vm.ctx.NetworkID = testNetworkID
428537
unsignedMessage, err := avalancheWarp.NewUnsignedMessage(
429538
vm.ctx.NetworkID,
430-
vm.ctx.ChainID,
539+
sourceChainID,
431540
addressedPayload.Bytes(),
432541
)
433542
require.NoError(err)
434543

435-
nodeID1 := ids.GenerateTestNodeID()
436-
blsSecretKey1, err := bls.NewSecretKey()
437-
require.NoError(err)
438-
blsPublicKey1 := bls.PublicFromSecretKey(blsSecretKey1)
439-
blsSignature1 := bls.Sign(blsSecretKey1, unsignedMessage.Bytes())
544+
type signer struct {
545+
networkID ids.ID
546+
nodeID ids.NodeID
547+
secret *bls.SecretKey
548+
signature *bls.Signature
549+
weight uint64
550+
}
551+
newSigner := func(networkID ids.ID, weight uint64) signer {
552+
secret, err := bls.NewSecretKey()
553+
require.NoError(err)
554+
return signer{
555+
networkID: networkID,
556+
nodeID: ids.GenerateTestNodeID(),
557+
secret: secret,
558+
signature: bls.Sign(secret, unsignedMessage.Bytes()),
559+
weight: weight,
560+
}
561+
}
440562

441-
nodeID2 := ids.GenerateTestNodeID()
442-
blsSecretKey2, err := bls.NewSecretKey()
443-
require.NoError(err)
444-
blsPublicKey2 := bls.PublicFromSecretKey(blsSecretKey2)
445-
blsSignature2 := bls.Sign(blsSecretKey2, unsignedMessage.Bytes())
563+
primarySigners := []signer{
564+
newSigner(constants.PrimaryNetworkID, 50),
565+
newSigner(constants.PrimaryNetworkID, 50),
566+
}
567+
subnetSigners := []signer{
568+
newSigner(vm.ctx.SubnetID, 50),
569+
newSigner(vm.ctx.SubnetID, 50),
570+
}
571+
signers := subnetSigners
572+
if useSigners == signersPrimary {
573+
signers = primarySigners
574+
}
446575

447-
blsAggregatedSignature, err := bls.AggregateSignatures([]*bls.Signature{blsSignature1, blsSignature2})
576+
blsSignatures := make([]*bls.Signature, len(signers))
577+
for i := range signers {
578+
blsSignatures[i] = signers[i].signature
579+
}
580+
blsAggregatedSignature, err := bls.AggregateSignatures(blsSignatures)
448581
require.NoError(err)
449582

450583
minimumValidPChainHeight := uint64(10)
451584
getValidatorSetTestErr := errors.New("can't get validator set test error")
452585

453586
vm.ctx.ValidatorState = &validatorstest.State{
454587
GetSubnetIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) {
455-
return ids.Empty, nil
588+
if msgFrom == fromPrimary {
589+
return constants.PrimaryNetworkID, nil
590+
}
591+
return vm.ctx.SubnetID, nil
456592
},
457593
GetValidatorSetF: func(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
458594
if height < minimumValidPChainHeight {
459595
return nil, getValidatorSetTestErr
460596
}
461-
return map[ids.NodeID]*validators.GetValidatorOutput{
462-
nodeID1: {
463-
NodeID: nodeID1,
464-
PublicKey: blsPublicKey1,
465-
Weight: 50,
466-
},
467-
nodeID2: {
468-
NodeID: nodeID2,
469-
PublicKey: blsPublicKey2,
470-
Weight: 50,
471-
},
472-
}, nil
597+
signers := subnetSigners
598+
if subnetID == constants.PrimaryNetworkID {
599+
signers = primarySigners
600+
}
601+
602+
vdrOutput := make(map[ids.NodeID]*validators.GetValidatorOutput)
603+
for _, s := range signers {
604+
vdrOutput[s.nodeID] = &validators.GetValidatorOutput{
605+
NodeID: s.nodeID,
606+
PublicKey: bls.PublicFromSecretKey(s.secret),
607+
Weight: s.weight,
608+
}
609+
}
610+
return vdrOutput, nil
473611
},
474612
}
475613

476614
signersBitSet := set.NewBits()
477-
signersBitSet.Add(0)
478-
signersBitSet.Add(1)
615+
for i := range signers {
616+
signersBitSet.Add(i)
617+
}
479618

480619
warpSignature := &avalancheWarp.BitSetSignature{
481620
Signers: signersBitSet.Bytes(),
@@ -495,7 +634,7 @@ func TestReceiveWarpMessage(t *testing.T) {
495634
getVerifiedWarpMessageTx, err := types.SignTx(
496635
predicate.NewPredicateTx(
497636
vm.chainConfig.ChainID,
498-
0,
637+
vm.txPool.Nonce(testEthAddrs[0]),
499638
&warp.Module.Address,
500639
1_000_000,
501640
big.NewInt(225*params.GWei),
@@ -519,12 +658,28 @@ func TestReceiveWarpMessage(t *testing.T) {
519658
validProposerCtx := &block.Context{
520659
PChainHeight: minimumValidPChainHeight,
521660
}
522-
vm.clock.Set(vm.clock.Time().Add(2 * time.Second))
661+
vm.clock.Set(blockTime)
523662
<-issuer
524663

525664
block2, err := vm.BuildBlockWithContext(context.Background(), validProposerCtx)
526665
require.NoError(err)
527666

667+
// Require the block was built with a successful predicate result
668+
ethBlock := block2.(*chain.BlockWrapper).Block.(*Block).ethBlock
669+
headerPredicateResultsBytes, ok := predicate.GetPredicateResultBytes(ethBlock.Extra())
670+
require.True(ok)
671+
results, err := predicate.ParseResults(headerPredicateResultsBytes)
672+
require.NoError(err)
673+
674+
// Predicate results encode the index of invalid warp messages in a bitset.
675+
// An empty bitset indicates success.
676+
txResultsBytes := results.GetResults(
677+
getVerifiedWarpMessageTx.Hash(),
678+
warp.ContractAddress,
679+
)
680+
bitset := set.BitsFromBytes(txResultsBytes)
681+
require.Zero(bitset.Len()) // Empty bitset indicates success
682+
528683
block2VerifyWithCtx, ok := block2.(block.WithVerifyContext)
529684
require.True(ok)
530685
shouldVerifyWithCtx, err := block2VerifyWithCtx.ShouldVerifyWithContext(context.Background())
@@ -548,15 +703,14 @@ func TestReceiveWarpMessage(t *testing.T) {
548703
require.NoError(block2.Accept(context.Background()))
549704
vm.blockChain.DrainAcceptorQueue()
550705

551-
ethBlock := block2.(*chain.BlockWrapper).Block.(*Block).ethBlock
552706
verifiedMessageReceipts := vm.blockChain.GetReceiptsByHash(ethBlock.Hash())
553707
require.Len(verifiedMessageReceipts, 1)
554708
verifiedMessageTxReceipt := verifiedMessageReceipts[0]
555709
require.Equal(types.ReceiptStatusSuccessful, verifiedMessageTxReceipt.Status)
556710

557711
expectedOutput, err := warp.PackGetVerifiedWarpMessageOutput(warp.GetVerifiedWarpMessageOutput{
558712
Message: warp.WarpMessage{
559-
SourceChainID: common.Hash(vm.ctx.ChainID),
713+
SourceChainID: common.Hash(sourceChainID),
560714
OriginSenderAddress: testEthAddrs[0],
561715
Payload: payloadData,
562716
},

0 commit comments

Comments
 (0)