mirror of
https://github.com/luxfi/coreth.git
synced 2026-10-30 17:00:51 +00:00
- Update import from vm/rpcchainvm to vm/rpc/chain - Update vm dependency to v1.0.18 - node dependency now test-only (platformvm.NewClient for warp tests)
696 lines
21 KiB
Go
696 lines
21 KiB
Go
//go:build skip
|
|
|
|
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package warp
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
consensusctx "github.com/luxfi/consensus/context"
|
|
"github.com/luxfi/consensus/engine/chain/block"
|
|
consensustest "github.com/luxfi/consensus/test/helpers"
|
|
validators "github.com/luxfi/consensus/validator"
|
|
"github.com/luxfi/consensus/validator/validatorstest"
|
|
"github.com/luxfi/constants"
|
|
"github.com/luxfi/coreth/precompile/precompileconfig"
|
|
"github.com/luxfi/coreth/precompile/precompiletest"
|
|
"github.com/luxfi/coreth/predicate"
|
|
"github.com/luxfi/coreth/utils"
|
|
"github.com/luxfi/crypto/bls"
|
|
"github.com/luxfi/crypto/bls/signer/localsigner"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/math/set"
|
|
agoUtils "github.com/luxfi/utils"
|
|
"github.com/luxfi/warp"
|
|
"github.com/luxfi/warp/payload"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const pChainHeight uint64 = 1337
|
|
|
|
var (
|
|
_ agoUtils.Sortable[*testValidator] = (*testValidator)(nil)
|
|
|
|
errTest = errors.New("non-nil error")
|
|
sourceChainID = ids.GenerateTestID()
|
|
sourceChainID = ids.GenerateTestID()
|
|
|
|
// valid unsigned warp message used throughout testing
|
|
unsignedMsg *warp.UnsignedMessage
|
|
// valid addressed payload
|
|
addressedPayload *payload.AddressedCall
|
|
addressedPayloadBytes []byte
|
|
// blsSignatures of [unsignedMsg] from each of [testVdrs]
|
|
blsSignatures []*bls.Signature
|
|
|
|
numTestVdrs = 10_000
|
|
testVdrs []*testValidator
|
|
vdrs map[ids.NodeID]*validators.GetValidatorOutput
|
|
)
|
|
|
|
func init() {
|
|
testVdrs = make([]*testValidator, 0, numTestVdrs)
|
|
for i := 0; i < numTestVdrs; i++ {
|
|
testVdrs = append(testVdrs, newTestValidator())
|
|
}
|
|
agoUtils.Sort(testVdrs)
|
|
|
|
vdrs = map[ids.NodeID]*validators.GetValidatorOutput{
|
|
testVdrs[0].nodeID: {
|
|
NodeID: testVdrs[0].nodeID,
|
|
PublicKey: testVdrs[0].vdr.PublicKeyBytes,
|
|
Weight: testVdrs[0].vdr.Weight,
|
|
},
|
|
testVdrs[1].nodeID: {
|
|
NodeID: testVdrs[1].nodeID,
|
|
PublicKey: testVdrs[1].vdr.PublicKeyBytes,
|
|
Weight: testVdrs[1].vdr.Weight,
|
|
},
|
|
testVdrs[2].nodeID: {
|
|
NodeID: testVdrs[2].nodeID,
|
|
PublicKey: testVdrs[2].vdr.PublicKeyBytes,
|
|
Weight: testVdrs[2].vdr.Weight,
|
|
},
|
|
}
|
|
|
|
var err error
|
|
addr := ids.GenerateTestShortID()
|
|
addressedPayload, err = payload.NewAddressedCall(
|
|
addr[:],
|
|
[]byte{1, 2, 3},
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
addressedPayloadBytes = addressedPayload.Bytes()
|
|
unsignedMsg, err = warp.NewUnsignedMessage(constants.UnitTestID, sourceChainID, addressedPayload.Bytes())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
for _, testVdr := range testVdrs {
|
|
blsSignature, err := testVdr.sk.Sign(unsignedMsg.Bytes())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
blsSignatures = append(blsSignatures, blsSignature)
|
|
}
|
|
}
|
|
|
|
type testValidator struct {
|
|
nodeID ids.NodeID
|
|
sk bls.Signer
|
|
vdr *warp.Validator
|
|
}
|
|
|
|
func (v *testValidator) Compare(o *testValidator) int {
|
|
return bytes.Compare(v.vdr.PublicKeyBytes, o.vdr.PublicKeyBytes)
|
|
}
|
|
|
|
func newTestValidator() *testValidator {
|
|
sk, err := localsigner.New()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
nodeID := ids.GenerateTestNodeID()
|
|
pk := sk.PublicKey()
|
|
return &testValidator{
|
|
nodeID: nodeID,
|
|
sk: sk,
|
|
vdr: &warp.Validator{
|
|
PublicKey: pk,
|
|
PublicKeyBytes: bls.PublicKeyToCompressedBytes(pk),
|
|
Weight: 3,
|
|
NodeID: nodeID,
|
|
},
|
|
}
|
|
}
|
|
|
|
// createWarpMessage constructs a signed warp message using the global variable [unsignedMsg]
|
|
// and the first [numKeys] signatures from [blsSignatures]
|
|
func createWarpMessage(numKeys int) *warp.Message {
|
|
aggregateSignature, err := bls.AggregateSignatures(blsSignatures[0:numKeys])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
bitSet := set.NewBits()
|
|
for i := 0; i < numKeys; i++ {
|
|
bitSet.Add(i)
|
|
}
|
|
warpSignature := &warp.BitSetSignature{
|
|
Signers: bitSet.Bytes(),
|
|
}
|
|
copy(warpSignature.Signature[:], bls.SignatureToBytes(aggregateSignature))
|
|
warpMsg, err := warp.NewMessage(unsignedMsg, warpSignature)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return warpMsg
|
|
}
|
|
|
|
// createPredicate constructs a warp message using createWarpMessage with numKeys signers
|
|
// and packs it into predicate encoding.
|
|
func createPredicate(numKeys int) []byte {
|
|
warpMsg := createWarpMessage(numKeys)
|
|
predicateBytes := predicate.PackPredicate(warpMsg.Bytes())
|
|
return predicateBytes
|
|
}
|
|
|
|
// validatorRange specifies a range of validators to include from [start, end), a staking weight
|
|
// to specify for each validator in that range, and whether or not to include the public key.
|
|
type validatorRange struct {
|
|
start int
|
|
end int
|
|
weight uint64
|
|
publicKey bool
|
|
}
|
|
|
|
// createConsensusCtx creates a consensusctx.Context instance with a validator state specified by the given validatorRanges
|
|
func createConsensusCtx(tb testing.TB, validatorRanges []validatorRange) *consensusctx.Context {
|
|
getValidatorsOutput := make(map[ids.NodeID]*validators.GetValidatorOutput)
|
|
|
|
for _, validatorRange := range validatorRanges {
|
|
for i := validatorRange.start; i < validatorRange.end; i++ {
|
|
validatorOutput := &validators.GetValidatorOutput{
|
|
NodeID: testVdrs[i].nodeID,
|
|
Weight: validatorRange.weight,
|
|
}
|
|
if validatorRange.publicKey {
|
|
validatorOutput.PublicKey = testVdrs[i].vdr.PublicKeyBytes
|
|
}
|
|
getValidatorsOutput[testVdrs[i].nodeID] = validatorOutput
|
|
}
|
|
}
|
|
|
|
quasarCtx := consensustest.Context(tb, consensustest.CChainID)
|
|
state := &validatorstest.State{
|
|
GetChainIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) {
|
|
return sourceChainID, nil
|
|
},
|
|
GetValidatorSetF: func(ctx context.Context, height uint64, chainID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
|
|
return getValidatorsOutput, nil
|
|
},
|
|
}
|
|
quasarCtx.ValidatorState = state
|
|
return quasarCtx
|
|
}
|
|
|
|
func createValidPredicateTest(quasarCtx *consensusctx.Context, numKeys uint64, predicateBytes []byte) precompiletest.PredicateTest {
|
|
return precompiletest.PredicateTest{
|
|
Config: NewDefaultConfig(utils.NewUint64(0)),
|
|
PredicateContext: &precompileconfig.PredicateContext{
|
|
ConsensusCtx: quasarCtx,
|
|
ProposerVMBlockCtx: &block.Context{
|
|
PChainHeight: 1,
|
|
},
|
|
},
|
|
PredicateBytes: predicateBytes,
|
|
Gas: GasCostPerSignatureVerification + uint64(len(predicateBytes))*GasCostPerWarpMessageBytes + numKeys*GasCostPerWarpSigner,
|
|
GasErr: nil,
|
|
ExpectedErr: nil,
|
|
}
|
|
}
|
|
|
|
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()
|
|
addressedCall, err := payload.NewAddressedCall(agoUtils.RandomBytes(20), agoUtils.RandomBytes(100))
|
|
require.NoError(err)
|
|
unsignedMsg, err := warp.NewUnsignedMessage(constants.UnitTestID, cChainID, addressedCall.Bytes())
|
|
require.NoError(err)
|
|
|
|
getValidatorsOutput := make(map[ids.NodeID]*validators.GetValidatorOutput)
|
|
blsSignatures := make([]*bls.Signature, 0, numKeys)
|
|
for i := 0; i < numKeys; i++ {
|
|
sig, err := testVdrs[i].sk.Sign(unsignedMsg.Bytes())
|
|
require.NoError(err)
|
|
|
|
validatorOutput := &validators.GetValidatorOutput{
|
|
NodeID: testVdrs[i].nodeID,
|
|
Weight: 20,
|
|
PublicKey: testVdrs[i].vdr.PublicKeyBytes,
|
|
}
|
|
getValidatorsOutput[testVdrs[i].nodeID] = validatorOutput
|
|
blsSignatures = append(blsSignatures, sig)
|
|
}
|
|
aggregateSignature, err := bls.AggregateSignatures(blsSignatures)
|
|
require.NoError(err)
|
|
bitSet := set.NewBits()
|
|
for i := 0; i < numKeys; i++ {
|
|
bitSet.Add(i)
|
|
}
|
|
warpSignature := &warp.BitSetSignature{
|
|
Signers: bitSet.Bytes(),
|
|
}
|
|
copy(warpSignature.Signature[:], bls.SignatureToBytes(aggregateSignature))
|
|
warpMsg, err := warp.NewMessage(unsignedMsg, warpSignature)
|
|
require.NoError(err)
|
|
|
|
predicateBytes := predicate.PackPredicate(warpMsg.Bytes())
|
|
|
|
quasarCtx := consensustest.Context(t, ids.GenerateTestID())
|
|
quasarCtx.ChainID = ids.GenerateTestID()
|
|
quasarCtx.CChainID = cChainID
|
|
quasarCtx.ValidatorState = &validatorstest.State{
|
|
GetChainIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) {
|
|
require.Equal(chainID, cChainID)
|
|
return constants.PrimaryNetworkID, nil // Return Primary Network ChainID
|
|
},
|
|
GetValidatorSetF: func(ctx context.Context, height uint64, chainID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
|
|
expectedChainID := quasarCtx.ChainID
|
|
if requirePrimaryNetworkSigners {
|
|
expectedChainID = constants.PrimaryNetworkID
|
|
}
|
|
require.Equal(expectedChainID, chainID)
|
|
return getValidatorsOutput, nil
|
|
},
|
|
}
|
|
|
|
test := precompiletest.PredicateTest{
|
|
Config: NewConfig(utils.NewUint64(0), 0, requirePrimaryNetworkSigners),
|
|
PredicateContext: &precompileconfig.PredicateContext{
|
|
ConsensusCtx: quasarCtx,
|
|
ProposerVMBlockCtx: &block.Context{
|
|
PChainHeight: 1,
|
|
},
|
|
},
|
|
PredicateBytes: predicateBytes,
|
|
Gas: GasCostPerSignatureVerification + uint64(len(predicateBytes))*GasCostPerWarpMessageBytes + uint64(numKeys)*GasCostPerWarpSigner,
|
|
GasErr: nil,
|
|
ExpectedErr: nil,
|
|
}
|
|
|
|
test.Run(t)
|
|
}
|
|
|
|
func TestInvalidPredicatePacking(t *testing.T) {
|
|
numKeys := 1
|
|
quasarCtx := createConsensusCtx(t, []validatorRange{
|
|
{
|
|
start: 0,
|
|
end: numKeys,
|
|
weight: 20,
|
|
publicKey: true,
|
|
},
|
|
})
|
|
predicateBytes := createPredicate(numKeys)
|
|
predicateBytes = append(predicateBytes, byte(0x01)) // Invalidate the predicate byte packing
|
|
|
|
test := precompiletest.PredicateTest{
|
|
Config: NewDefaultConfig(utils.NewUint64(0)),
|
|
PredicateContext: &precompileconfig.PredicateContext{
|
|
ConsensusCtx: quasarCtx,
|
|
ProposerVMBlockCtx: &block.Context{
|
|
PChainHeight: 1,
|
|
},
|
|
},
|
|
PredicateBytes: predicateBytes,
|
|
Gas: GasCostPerSignatureVerification + uint64(len(predicateBytes))*GasCostPerWarpMessageBytes + uint64(numKeys)*GasCostPerWarpSigner,
|
|
GasErr: errInvalidPredicateBytes,
|
|
}
|
|
|
|
test.Run(t)
|
|
}
|
|
|
|
func TestInvalidWarpMessage(t *testing.T) {
|
|
numKeys := 1
|
|
quasarCtx := createConsensusCtx(t, []validatorRange{
|
|
{
|
|
start: 0,
|
|
end: numKeys,
|
|
weight: 20,
|
|
publicKey: true,
|
|
},
|
|
})
|
|
warpMsg := createWarpMessage(1)
|
|
warpMsgBytes := warpMsg.Bytes()
|
|
warpMsgBytes = append(warpMsgBytes, byte(0x01)) // Invalidate warp message packing
|
|
predicateBytes := predicate.PackPredicate(warpMsgBytes)
|
|
|
|
test := precompiletest.PredicateTest{
|
|
Config: NewDefaultConfig(utils.NewUint64(0)),
|
|
PredicateContext: &precompileconfig.PredicateContext{
|
|
ConsensusCtx: quasarCtx,
|
|
ProposerVMBlockCtx: &block.Context{
|
|
PChainHeight: 1,
|
|
},
|
|
},
|
|
PredicateBytes: predicateBytes,
|
|
Gas: GasCostPerSignatureVerification + uint64(len(predicateBytes))*GasCostPerWarpMessageBytes + uint64(numKeys)*GasCostPerWarpSigner,
|
|
GasErr: errInvalidWarpMsg,
|
|
}
|
|
|
|
test.Run(t)
|
|
}
|
|
|
|
func TestInvalidAddressedPayload(t *testing.T) {
|
|
numKeys := 1
|
|
quasarCtx := createConsensusCtx(t, []validatorRange{
|
|
{
|
|
start: 0,
|
|
end: numKeys,
|
|
weight: 20,
|
|
publicKey: true,
|
|
},
|
|
})
|
|
aggregateSignature, err := bls.AggregateSignatures(blsSignatures[0:numKeys])
|
|
require.NoError(t, err)
|
|
bitSet := set.NewBits()
|
|
for i := 0; i < numKeys; i++ {
|
|
bitSet.Add(i)
|
|
}
|
|
warpSignature := &warp.BitSetSignature{
|
|
Signers: bitSet.Bytes(),
|
|
}
|
|
copy(warpSignature.Signature[:], bls.SignatureToBytes(aggregateSignature))
|
|
// Create an unsigned message with an invalid addressed payload
|
|
unsignedMsg, err := warp.NewUnsignedMessage(constants.UnitTestID, sourceChainID, []byte{1, 2, 3})
|
|
require.NoError(t, err)
|
|
warpMsg, err := warp.NewMessage(unsignedMsg, warpSignature)
|
|
require.NoError(t, err)
|
|
warpMsgBytes := warpMsg.Bytes()
|
|
predicateBytes := predicate.PackPredicate(warpMsgBytes)
|
|
|
|
test := precompiletest.PredicateTest{
|
|
Config: NewDefaultConfig(utils.NewUint64(0)),
|
|
PredicateContext: &precompileconfig.PredicateContext{
|
|
ConsensusCtx: quasarCtx,
|
|
ProposerVMBlockCtx: &block.Context{
|
|
PChainHeight: 1,
|
|
},
|
|
},
|
|
PredicateBytes: predicateBytes,
|
|
Gas: GasCostPerSignatureVerification + uint64(len(predicateBytes))*GasCostPerWarpMessageBytes + uint64(numKeys)*GasCostPerWarpSigner,
|
|
GasErr: errInvalidWarpMsgPayload,
|
|
}
|
|
|
|
test.Run(t)
|
|
}
|
|
|
|
func TestInvalidBitSet(t *testing.T) {
|
|
addressedCall, err := payload.NewAddressedCall(agoUtils.RandomBytes(20), agoUtils.RandomBytes(100))
|
|
require.NoError(t, err)
|
|
unsignedMsg, err := warp.NewUnsignedMessage(
|
|
constants.UnitTestID,
|
|
sourceChainID,
|
|
addressedCall.Bytes(),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
msg, err := warp.NewMessage(
|
|
unsignedMsg,
|
|
&warp.BitSetSignature{
|
|
Signers: make([]byte, 1),
|
|
Signature: [bls.SignatureLen]byte{},
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
numKeys := 1
|
|
quasarCtx := createConsensusCtx(t, []validatorRange{
|
|
{
|
|
start: 0,
|
|
end: numKeys,
|
|
weight: 20,
|
|
publicKey: true,
|
|
},
|
|
})
|
|
predicateBytes := predicate.PackPredicate(msg.Bytes())
|
|
test := precompiletest.PredicateTest{
|
|
Config: NewDefaultConfig(utils.NewUint64(0)),
|
|
PredicateContext: &precompileconfig.PredicateContext{
|
|
ConsensusCtx: quasarCtx,
|
|
ProposerVMBlockCtx: &block.Context{
|
|
PChainHeight: 1,
|
|
},
|
|
},
|
|
PredicateBytes: predicateBytes,
|
|
Gas: GasCostPerSignatureVerification + uint64(len(predicateBytes))*GasCostPerWarpMessageBytes + uint64(numKeys)*GasCostPerWarpSigner,
|
|
GasErr: errCannotGetNumSigners,
|
|
}
|
|
|
|
test.Run(t)
|
|
}
|
|
|
|
func TestWarpSignatureWeightsDefaultQuorumNumerator(t *testing.T) {
|
|
quasarCtx := createConsensusCtx(t, []validatorRange{
|
|
{
|
|
start: 0,
|
|
end: 100,
|
|
weight: 20,
|
|
publicKey: true,
|
|
},
|
|
})
|
|
|
|
tests := make(map[string]precompiletest.PredicateTest)
|
|
for _, numSigners := range []int{
|
|
1,
|
|
int(WarpDefaultQuorumNumerator) - 1,
|
|
int(WarpDefaultQuorumNumerator),
|
|
int(WarpDefaultQuorumNumerator) + 1,
|
|
int(WarpQuorumDenominator) - 1,
|
|
int(WarpQuorumDenominator),
|
|
int(WarpQuorumDenominator) + 1,
|
|
} {
|
|
predicateBytes := createPredicate(numSigners)
|
|
// The predicate is valid iff the number of signers is >= the required numerator and does not exceed the denominator.
|
|
var expectedErr error
|
|
if numSigners >= int(WarpDefaultQuorumNumerator) && numSigners <= int(WarpQuorumDenominator) {
|
|
expectedErr = nil
|
|
} else {
|
|
expectedErr = errFailedVerification
|
|
}
|
|
|
|
tests[fmt.Sprintf("default quorum %d signature(s)", numSigners)] = precompiletest.PredicateTest{
|
|
Config: NewDefaultConfig(utils.NewUint64(0)),
|
|
PredicateContext: &precompileconfig.PredicateContext{
|
|
ConsensusCtx: quasarCtx,
|
|
ProposerVMBlockCtx: &block.Context{
|
|
PChainHeight: 1,
|
|
},
|
|
},
|
|
PredicateBytes: predicateBytes,
|
|
Gas: GasCostPerSignatureVerification + uint64(len(predicateBytes))*GasCostPerWarpMessageBytes + uint64(numSigners)*GasCostPerWarpSigner,
|
|
GasErr: nil,
|
|
ExpectedErr: expectedErr,
|
|
}
|
|
}
|
|
precompiletest.RunPredicateTests(t, tests)
|
|
}
|
|
|
|
// multiple messages all correct, multiple messages all incorrect, mixed bag
|
|
func TestWarpMultiplePredicates(t *testing.T) {
|
|
quasarCtx := createConsensusCtx(t, []validatorRange{
|
|
{
|
|
start: 0,
|
|
end: 100,
|
|
weight: 20,
|
|
publicKey: true,
|
|
},
|
|
})
|
|
|
|
tests := make(map[string]precompiletest.PredicateTest)
|
|
for _, validMessageIndices := range [][]bool{
|
|
{},
|
|
{true, false},
|
|
{false, true},
|
|
{false, false},
|
|
{true, true},
|
|
} {
|
|
var (
|
|
numSigners = int(WarpQuorumDenominator)
|
|
invalidPredicateBytes = createPredicate(1)
|
|
validPredicateBytes = createPredicate(numSigners)
|
|
)
|
|
|
|
for _, valid := range validMessageIndices {
|
|
var (
|
|
predicate []byte
|
|
expectedGas uint64
|
|
expectedErr error
|
|
)
|
|
if valid {
|
|
predicate = validPredicateBytes
|
|
expectedGas = GasCostPerSignatureVerification + uint64(len(validPredicateBytes))*GasCostPerWarpMessageBytes + uint64(numSigners)*GasCostPerWarpSigner
|
|
expectedErr = nil
|
|
} else {
|
|
expectedGas = GasCostPerSignatureVerification + uint64(len(invalidPredicateBytes))*GasCostPerWarpMessageBytes + uint64(1)*GasCostPerWarpSigner
|
|
predicate = invalidPredicateBytes
|
|
expectedErr = errFailedVerification
|
|
}
|
|
|
|
tests[fmt.Sprintf("multiple predicates %v", validMessageIndices)] = precompiletest.PredicateTest{
|
|
Config: NewDefaultConfig(utils.NewUint64(0)),
|
|
PredicateContext: &precompileconfig.PredicateContext{
|
|
ConsensusCtx: quasarCtx,
|
|
ProposerVMBlockCtx: &block.Context{
|
|
PChainHeight: 1,
|
|
},
|
|
},
|
|
PredicateBytes: predicate,
|
|
Gas: expectedGas,
|
|
GasErr: nil,
|
|
ExpectedErr: expectedErr,
|
|
}
|
|
}
|
|
}
|
|
precompiletest.RunPredicateTests(t, tests)
|
|
}
|
|
|
|
func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) {
|
|
quasarCtx := createConsensusCtx(t, []validatorRange{
|
|
{
|
|
start: 0,
|
|
end: 100,
|
|
weight: 20,
|
|
publicKey: true,
|
|
},
|
|
})
|
|
|
|
tests := make(map[string]precompiletest.PredicateTest)
|
|
nonDefaultQuorumNumerator := 50
|
|
// Ensure this test fails if the DefaultQuroumNumerator is changed to an unexpected value during development
|
|
require.NotEqual(t, nonDefaultQuorumNumerator, int(WarpDefaultQuorumNumerator))
|
|
// Add cases with default quorum
|
|
for _, numSigners := range []int{nonDefaultQuorumNumerator, nonDefaultQuorumNumerator + 1, 99, 100, 101} {
|
|
predicateBytes := createPredicate(numSigners)
|
|
// The predicate is valid iff the number of signers is >= the required numerator and does not exceed the denominator.
|
|
var expectedErr error
|
|
if numSigners >= nonDefaultQuorumNumerator && numSigners <= int(WarpQuorumDenominator) {
|
|
expectedErr = nil
|
|
} else {
|
|
expectedErr = errFailedVerification
|
|
}
|
|
|
|
name := fmt.Sprintf("non-default quorum %d signature(s)", numSigners)
|
|
tests[name] = precompiletest.PredicateTest{
|
|
Config: NewConfig(utils.NewUint64(0), uint64(nonDefaultQuorumNumerator), false),
|
|
PredicateContext: &precompileconfig.PredicateContext{
|
|
ConsensusCtx: quasarCtx,
|
|
ProposerVMBlockCtx: &block.Context{
|
|
PChainHeight: 1,
|
|
},
|
|
},
|
|
PredicateBytes: predicateBytes,
|
|
Gas: GasCostPerSignatureVerification + uint64(len(predicateBytes))*GasCostPerWarpMessageBytes + uint64(numSigners)*GasCostPerWarpSigner,
|
|
GasErr: nil,
|
|
ExpectedErr: expectedErr,
|
|
}
|
|
}
|
|
|
|
precompiletest.RunPredicateTests(t, tests)
|
|
}
|
|
|
|
func makeWarpPredicateTests(tb testing.TB) map[string]precompiletest.PredicateTest {
|
|
predicateTests := make(map[string]precompiletest.PredicateTest)
|
|
for _, totalNodes := range []int{10, 100, 1_000, 10_000} {
|
|
testName := fmt.Sprintf("%d signers/%d validators", totalNodes, totalNodes)
|
|
|
|
predicateBytes := createPredicate(totalNodes)
|
|
quasarCtx := createConsensusCtx(tb, []validatorRange{
|
|
{
|
|
start: 0,
|
|
end: totalNodes,
|
|
weight: 20,
|
|
publicKey: true,
|
|
},
|
|
})
|
|
predicateTests[testName] = createValidPredicateTest(quasarCtx, uint64(totalNodes), predicateBytes)
|
|
}
|
|
|
|
numSigners := 10
|
|
for _, totalNodes := range []int{100, 1_000, 10_000} {
|
|
testName := fmt.Sprintf("%d signers (heavily weighted)/%d validators", numSigners, totalNodes)
|
|
|
|
predicateBytes := createPredicate(numSigners)
|
|
quasarCtx := createConsensusCtx(tb, []validatorRange{
|
|
{
|
|
start: 0,
|
|
end: numSigners,
|
|
weight: 10_000_000,
|
|
publicKey: true,
|
|
},
|
|
{
|
|
start: numSigners,
|
|
end: totalNodes,
|
|
weight: 20,
|
|
publicKey: true,
|
|
},
|
|
})
|
|
predicateTests[testName] = createValidPredicateTest(quasarCtx, uint64(numSigners), predicateBytes)
|
|
}
|
|
|
|
for _, totalNodes := range []int{100, 1_000, 10_000} {
|
|
testName := fmt.Sprintf("%d signers (heavily weighted)/%d validators (non-signers without registered PublicKey)", numSigners, totalNodes)
|
|
|
|
predicateBytes := createPredicate(numSigners)
|
|
quasarCtx := createConsensusCtx(tb, []validatorRange{
|
|
{
|
|
start: 0,
|
|
end: numSigners,
|
|
weight: 10_000_000,
|
|
publicKey: true,
|
|
},
|
|
{
|
|
start: numSigners,
|
|
end: totalNodes,
|
|
weight: 20,
|
|
publicKey: false,
|
|
},
|
|
})
|
|
predicateTests[testName] = createValidPredicateTest(quasarCtx, uint64(numSigners), predicateBytes)
|
|
}
|
|
|
|
for _, totalNodes := range []int{100, 1_000, 10_000} {
|
|
testName := fmt.Sprintf("%d validators w/ %d signers/repeated PublicKeys", totalNodes, numSigners)
|
|
|
|
predicateBytes := createPredicate(numSigners)
|
|
getValidatorsOutput := make(map[ids.NodeID]*validators.GetValidatorOutput, totalNodes)
|
|
for i := 0; i < totalNodes; i++ {
|
|
getValidatorsOutput[testVdrs[i].nodeID] = &validators.GetValidatorOutput{
|
|
NodeID: testVdrs[i].nodeID,
|
|
Weight: 20,
|
|
PublicKey: testVdrs[i%numSigners].vdr.PublicKeyBytes,
|
|
}
|
|
}
|
|
|
|
quasarCtx := consensustest.Context(tb, consensustest.CChainID)
|
|
state := &validatorstest.State{
|
|
GetChainIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) {
|
|
return sourceChainID, nil
|
|
},
|
|
GetValidatorSetF: func(ctx context.Context, height uint64, chainID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
|
|
return getValidatorsOutput, nil
|
|
},
|
|
}
|
|
quasarCtx.ValidatorState = state
|
|
|
|
predicateTests[testName] = createValidPredicateTest(quasarCtx, uint64(numSigners), predicateBytes)
|
|
}
|
|
return predicateTests
|
|
}
|
|
|
|
func TestWarpPredicate(t *testing.T) {
|
|
predicateTests := makeWarpPredicateTests(t)
|
|
precompiletest.RunPredicateTests(t, predicateTests)
|
|
}
|
|
|
|
func BenchmarkWarpPredicate(b *testing.B) {
|
|
predicateTests := makeWarpPredicateTests(b)
|
|
precompiletest.RunPredicateBenchmarks(b, predicateTests)
|
|
}
|