mirror of
https://github.com/luxfi/evmgpu.git
synced 2026-10-30 16:57:11 +00:00
1083 lines
36 KiB
Plaintext
1083 lines
36 KiB
Plaintext
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package evm
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"encoding/json"
|
|
"errors"
|
|
"math/big"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
_ "embed"
|
|
|
|
commonEng "github.com/luxfi/consensus/core"
|
|
consensuscontext "github.com/luxfi/consensus/context"
|
|
"github.com/luxfi/consensus/engine/chain/block"
|
|
"github.com/luxfi/consensus/validator"
|
|
"github.com/luxfi/consensus/validator/validatorstest"
|
|
"github.com/luxfi/crypto"
|
|
"github.com/luxfi/crypto/bls"
|
|
"github.com/luxfi/crypto/bls/signer/localsigner"
|
|
"github.com/luxfi/evm/consensus"
|
|
"github.com/luxfi/evm/core"
|
|
"github.com/luxfi/evm/eth/tracers"
|
|
"github.com/luxfi/evm/params"
|
|
"github.com/luxfi/evm/params/extras"
|
|
customheader "github.com/luxfi/evm/plugin/evm/header"
|
|
"github.com/luxfi/evm/precompile/contract"
|
|
warpcontract "github.com/luxfi/evm/precompile/contracts/warp"
|
|
"github.com/luxfi/evm/predicate"
|
|
"github.com/luxfi/evm/utils"
|
|
"github.com/luxfi/evm/utils/utilstest"
|
|
"github.com/luxfi/evm/warp"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/geth/core/rawdb"
|
|
"github.com/luxfi/geth/core/types"
|
|
luxfiDatabase "github.com/luxfi/database"
|
|
"github.com/luxfi/database/memdb"
|
|
"github.com/luxfi/database/prefixdb"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/math/set"
|
|
"github.com/luxfi/p2p"
|
|
"github.com/luxfi/p2p/lp118"
|
|
"github.com/luxfi/vm/proto/pb/sdk"
|
|
luxdUtils "github.com/luxfi/utils"
|
|
"github.com/luxfi/constants"
|
|
luxWarp "github.com/luxfi/warp"
|
|
"github.com/luxfi/warp/payload"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
var (
|
|
//go:embed ExampleWarp.bin
|
|
exampleWarpBin string
|
|
//go:embed ExampleWarp.abi
|
|
exampleWarpABI string
|
|
)
|
|
|
|
type warpMsgFrom int
|
|
|
|
const (
|
|
fromSubnet warpMsgFrom = iota
|
|
fromPrimary
|
|
)
|
|
|
|
type useWarpMsgSigners int
|
|
|
|
const (
|
|
signersSubnet useWarpMsgSigners = iota
|
|
signersPrimary
|
|
)
|
|
|
|
func TestSendWarpMessage(t *testing.T) {
|
|
for _, scheme := range schemes {
|
|
t.Run(scheme, func(t *testing.T) {
|
|
testSendWarpMessage(t, scheme)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testSendWarpMessage(t *testing.T, scheme string) {
|
|
require := require.New(t)
|
|
genesis := &core.Genesis{}
|
|
|
|
require.NoError(genesis.UnmarshalJSON([]byte(toGenesisJSON(forkToChainConfig["Latest"]))))
|
|
// Copy network upgrades from source config and add warp precompile
|
|
sourceExtra := params.GetExtra(forkToChainConfig["Latest"])
|
|
extra := params.GetExtra(genesis.Config)
|
|
extra.NetworkUpgrades = sourceExtra.NetworkUpgrades
|
|
extra.GenesisPrecompiles = extras.Precompiles{
|
|
warpcontract.ConfigKey: warpcontract.NewDefaultConfig(utils.NewUint64(0)), // Enable at genesis
|
|
}
|
|
genesisJSON := marshalGenesisWithExtras(genesis)
|
|
tvm := newVM(t, testVMConfig{
|
|
genesisJSON: genesisJSON,
|
|
configJSON: getConfig(scheme, ""),
|
|
})
|
|
|
|
defer func() {
|
|
require.NoError(tvm.vm.Shutdown(context.Background()))
|
|
}()
|
|
|
|
acceptedLogsChan := make(chan []*types.Log, 10)
|
|
logsSub := tvm.vm.eth.APIBackend.SubscribeAcceptedLogsEvent(acceptedLogsChan)
|
|
defer logsSub.Unsubscribe()
|
|
|
|
payloadData := luxdUtils.RandomBytes(100)
|
|
|
|
warpSendMessageInput, err := warpcontract.PackSendWarpMessage(payloadData)
|
|
require.NoError(err)
|
|
addressedPayload, err := payload.NewAddressedCall(
|
|
testEthAddrs[0].Bytes(),
|
|
payloadData,
|
|
)
|
|
require.NoError(err)
|
|
// Use consensuscontext to match what the warp precompile uses
|
|
chainID := consensuscontext.GetChainID(tvm.vm.ctx)
|
|
expectedUnsignedMessage, err := luxWarp.NewUnsignedMessage(
|
|
consensuscontext.GetNetworkID(tvm.vm.ctx),
|
|
chainID[:],
|
|
addressedPayload.Bytes(),
|
|
)
|
|
require.NoError(err)
|
|
|
|
// Submit a transaction to trigger sending a warp message
|
|
tx0 := types.NewTransaction(uint64(0), warpcontract.ContractAddress, big.NewInt(1), 100_000, big.NewInt(testMinGasPrice), warpSendMessageInput)
|
|
key0ECDSA, err := crypto.ToECDSA(testKeys[0].Bytes())
|
|
require.NoError(err)
|
|
signedTx0, err := types.SignTx(tx0, types.LatestSignerForChainID(tvm.vm.chainConfig.ChainID), key0ECDSA)
|
|
require.NoError(err)
|
|
|
|
errs := tvm.vm.txPool.AddRemotesSync([]*types.Transaction{signedTx0})
|
|
require.NoError(errs[0])
|
|
|
|
msg, err := tvm.vm.WaitForEvent(context.Background())
|
|
require.NoError(err)
|
|
require.Equal(commonEng.PendingTxs, msg)
|
|
|
|
blk, err := tvm.vm.BuildBlock(context.Background())
|
|
require.NoError(err)
|
|
|
|
require.NoError(blk.Verify(context.Background()))
|
|
|
|
// Verify that the constructed block contains the expected log with an unsigned warp message in the log data
|
|
// Get internal block safely using unwrapBlock helper
|
|
internalBlock := unwrapBlock(t, blk)
|
|
ethBlock1 := internalBlock.ethBlock
|
|
require.Len(ethBlock1.Transactions(), 1)
|
|
receipts := rawdb.ReadReceipts(tvm.vm.chaindb, ethBlock1.Hash(), ethBlock1.NumberU64(), ethBlock1.Time(), tvm.vm.chainConfig)
|
|
require.Len(receipts, 1)
|
|
|
|
require.Len(receipts[0].Logs, 1)
|
|
expectedTopics := []common.Hash{
|
|
warpcontract.WarpABI.Events["SendWarpMessage"].ID,
|
|
common.BytesToHash(testEthAddrs[0].Bytes()),
|
|
common.Hash(expectedUnsignedMessage.ID()),
|
|
}
|
|
require.Equal(expectedTopics, receipts[0].Logs[0].Topics)
|
|
logData := receipts[0].Logs[0].Data
|
|
unsignedMessage, err := warpcontract.UnpackSendWarpEventDataToMessage(logData)
|
|
require.NoError(err)
|
|
|
|
// Verify the signature cannot be fetched before the block is accepted
|
|
_, err = tvm.vm.warpBackend.GetMessageSignature(context.Background(), unsignedMessage)
|
|
require.Error(err)
|
|
|
|
require.NoError(tvm.vm.SetPreference(context.Background(), blk.ID()))
|
|
require.NoError(blk.Accept(context.Background()))
|
|
tvm.vm.blockChain.DrainAcceptorQueue()
|
|
|
|
// Verify the message signature after accepting the block.
|
|
rawSignatureBytes, err := tvm.vm.warpBackend.GetMessageSignature(context.Background(), unsignedMessage)
|
|
require.NoError(err)
|
|
blsSignature, err := bls.SignatureFromBytes(rawSignatureBytes)
|
|
require.NoError(err)
|
|
|
|
select {
|
|
case acceptedLogs := <-acceptedLogsChan:
|
|
require.Len(acceptedLogs, 1, "unexpected length of accepted logs")
|
|
require.Equal(acceptedLogs[0], receipts[0].Logs[0])
|
|
case <-time.After(time.Second):
|
|
require.Fail("Failed to read accepted logs from subscription")
|
|
}
|
|
|
|
// Verify the produced message signature is valid
|
|
// Use the test warp holder to access the underlying BLS signer for verification
|
|
testHolder := getTestWarpHolder()
|
|
blsSigner := testHolder.GetBLSSigner()
|
|
publicKey := blsSigner.PublicKey()
|
|
require.True(bls.Verify(publicKey, blsSignature, unsignedMessage.Bytes()))
|
|
|
|
// Verify the blockID will now be signed by the backend and produces a valid signature.
|
|
blockID := blk.ID()
|
|
rawBlockSignatureBytes, err := tvm.vm.warpBackend.GetBlockSignature(context.Background(), blockID)
|
|
require.NoError(err)
|
|
blsSignature, err = bls.SignatureFromBytes(rawBlockSignatureBytes)
|
|
require.NoError(err)
|
|
|
|
blockHashPayload, err := payload.NewHash(blockID)
|
|
require.NoError(err)
|
|
chainID = consensuscontext.GetChainID(tvm.vm.ctx)
|
|
blockUnsignedMessage, err := luxWarp.NewUnsignedMessage(consensuscontext.GetNetworkID(tvm.vm.ctx), chainID[:], blockHashPayload.Bytes())
|
|
require.NoError(err)
|
|
|
|
// Verify the produced message signature is valid
|
|
publicKey = blsSigner.PublicKey()
|
|
require.True(bls.Verify(publicKey, blsSignature, blockUnsignedMessage.Bytes()))
|
|
}
|
|
|
|
func TestValidateWarpMessage(t *testing.T) {
|
|
for _, scheme := range schemes {
|
|
t.Run(scheme, func(t *testing.T) {
|
|
testValidateWarpMessage(t, scheme)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testValidateWarpMessage(t *testing.T, scheme string) {
|
|
require := require.New(t)
|
|
sourceChainID := ids.GenerateTestID()
|
|
sourceAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")
|
|
payloadData := []byte{1, 2, 3}
|
|
addressedPayload, err := payload.NewAddressedCall(
|
|
sourceAddress.Bytes(),
|
|
payloadData,
|
|
)
|
|
require.NoError(err)
|
|
unsignedMessage, err := luxWarp.NewUnsignedMessage(testNetworkID, sourceChainID[:], addressedPayload.Bytes())
|
|
require.NoError(err)
|
|
|
|
exampleWarpABI := contract.ParseABI(exampleWarpABI)
|
|
exampleWarpPayload, err := exampleWarpABI.Pack(
|
|
"validateWarpMessage",
|
|
uint32(0),
|
|
sourceChainID,
|
|
sourceAddress,
|
|
payloadData,
|
|
)
|
|
require.NoError(err)
|
|
|
|
testWarpVMTransaction(t, scheme, unsignedMessage, true, exampleWarpPayload)
|
|
}
|
|
|
|
func TestValidateInvalidWarpMessage(t *testing.T) {
|
|
for _, scheme := range schemes {
|
|
t.Run(scheme, func(t *testing.T) {
|
|
testValidateInvalidWarpMessage(t, scheme)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testValidateInvalidWarpMessage(t *testing.T, scheme string) {
|
|
require := require.New(t)
|
|
sourceChainID := ids.GenerateTestID()
|
|
sourceAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")
|
|
payloadData := []byte{1, 2, 3}
|
|
addressedPayload, err := payload.NewAddressedCall(
|
|
sourceAddress.Bytes(),
|
|
payloadData,
|
|
)
|
|
require.NoError(err)
|
|
unsignedMessage, err := luxWarp.NewUnsignedMessage(testNetworkID, sourceChainID[:], addressedPayload.Bytes())
|
|
require.NoError(err)
|
|
|
|
exampleWarpABI := contract.ParseABI(exampleWarpABI)
|
|
exampleWarpPayload, err := exampleWarpABI.Pack(
|
|
"validateInvalidWarpMessage",
|
|
uint32(0),
|
|
)
|
|
require.NoError(err)
|
|
|
|
testWarpVMTransaction(t, scheme, unsignedMessage, false, exampleWarpPayload)
|
|
}
|
|
|
|
func TestValidateWarpBlockHash(t *testing.T) {
|
|
for _, scheme := range schemes {
|
|
t.Run(scheme, func(t *testing.T) {
|
|
testValidateWarpBlockHash(t, scheme)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testValidateWarpBlockHash(t *testing.T, scheme string) {
|
|
require := require.New(t)
|
|
sourceChainID := ids.GenerateTestID()
|
|
blockHash := ids.GenerateTestID()
|
|
blockHashPayload, err := payload.NewHash(blockHash)
|
|
require.NoError(err)
|
|
unsignedMessage, err := luxWarp.NewUnsignedMessage(testNetworkID, sourceChainID[:], blockHashPayload.Bytes())
|
|
require.NoError(err)
|
|
|
|
exampleWarpABI := contract.ParseABI(exampleWarpABI)
|
|
exampleWarpPayload, err := exampleWarpABI.Pack(
|
|
"validateWarpBlockHash",
|
|
uint32(0),
|
|
sourceChainID,
|
|
blockHash,
|
|
)
|
|
require.NoError(err)
|
|
|
|
testWarpVMTransaction(t, scheme, unsignedMessage, true, exampleWarpPayload)
|
|
}
|
|
|
|
func TestValidateInvalidWarpBlockHash(t *testing.T) {
|
|
for _, scheme := range schemes {
|
|
t.Run(scheme, func(t *testing.T) {
|
|
testValidateInvalidWarpBlockHash(t, scheme)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testValidateInvalidWarpBlockHash(t *testing.T, scheme string) {
|
|
require := require.New(t)
|
|
sourceChainID := ids.GenerateTestID()
|
|
blockHash := ids.GenerateTestID()
|
|
blockHashPayload, err := payload.NewHash(blockHash)
|
|
require.NoError(err)
|
|
unsignedMessage, err := luxWarp.NewUnsignedMessage(testNetworkID, sourceChainID[:], blockHashPayload.Bytes())
|
|
require.NoError(err)
|
|
|
|
exampleWarpABI := contract.ParseABI(exampleWarpABI)
|
|
exampleWarpPayload, err := exampleWarpABI.Pack(
|
|
"validateInvalidWarpBlockHash",
|
|
uint32(0),
|
|
)
|
|
require.NoError(err)
|
|
|
|
testWarpVMTransaction(t, scheme, unsignedMessage, false, exampleWarpPayload)
|
|
}
|
|
|
|
func testWarpVMTransaction(t *testing.T, scheme string, unsignedMessage *luxWarp.UnsignedMessage, validSignature bool, txPayload []byte) {
|
|
require := require.New(t)
|
|
genesis := &core.Genesis{}
|
|
require.NoError(genesis.UnmarshalJSON([]byte(toGenesisJSON(forkToChainConfig["Latest"]))))
|
|
// Copy network upgrades from source config and add warp precompile
|
|
sourceExtra := params.GetExtra(forkToChainConfig["Latest"])
|
|
extra := params.GetExtra(genesis.Config)
|
|
extra.NetworkUpgrades = sourceExtra.NetworkUpgrades
|
|
extra.GenesisPrecompiles = extras.Precompiles{
|
|
warpcontract.ConfigKey: warpcontract.NewDefaultConfig(utils.NewUint64(0)), // Enable at genesis
|
|
}
|
|
genesisJSON := marshalGenesisWithExtras(genesis)
|
|
tvm := newVM(t, testVMConfig{
|
|
genesisJSON: genesisJSON,
|
|
configJSON: getConfig(scheme, ""),
|
|
})
|
|
|
|
defer func() {
|
|
require.NoError(tvm.vm.Shutdown(context.Background()))
|
|
}()
|
|
|
|
acceptedLogsChan := make(chan []*types.Log, 10)
|
|
logsSub := tvm.vm.eth.APIBackend.SubscribeAcceptedLogsEvent(acceptedLogsChan)
|
|
defer logsSub.Unsubscribe()
|
|
|
|
nodeID1 := ids.GenerateTestNodeID()
|
|
blsSecretKey1, err := localsigner.New()
|
|
require.NoError(err)
|
|
blsPublicKey1 := blsSecretKey1.PublicKey()
|
|
blsSignature1, err := blsSecretKey1.Sign(unsignedMessage.Bytes())
|
|
require.NoError(err)
|
|
|
|
nodeID2 := ids.GenerateTestNodeID()
|
|
blsSecretKey2, err := localsigner.New()
|
|
require.NoError(err)
|
|
blsPublicKey2 := blsSecretKey2.PublicKey()
|
|
blsSignature2, err := blsSecretKey2.Sign(unsignedMessage.Bytes())
|
|
require.NoError(err)
|
|
|
|
blsAggregatedSignature, err := bls.AggregateSignatures([]*bls.Signature{blsSignature1, blsSignature2})
|
|
require.NoError(err)
|
|
|
|
minimumValidPChainHeight := uint64(10)
|
|
getValidatorSetTestErr := errors.New("can't get validator set test error")
|
|
|
|
testValidatorState := &validatorstest.State{
|
|
// GetSubnetIDF removed - not available in current interface
|
|
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: bls.PublicKeyToCompressedBytes(blsPublicKey1),
|
|
Weight: 50,
|
|
},
|
|
nodeID2: {
|
|
NodeID: nodeID2,
|
|
PublicKey: bls.PublicKeyToCompressedBytes(blsPublicKey2),
|
|
Weight: 50,
|
|
},
|
|
}, nil
|
|
},
|
|
}
|
|
wrappedValidatorState := utilstest.NewTestValidatorStateFromBase(testValidatorState)
|
|
tvm.vm.ctx = consensuscontext.WithValidatorState(tvm.vm.ctx, wrappedValidatorState)
|
|
|
|
signersBitSet := set.NewBits()
|
|
signersBitSet.Add(0)
|
|
signersBitSet.Add(1)
|
|
|
|
warpSignature := &luxWarp.BitSetSignature{
|
|
Signers: signersBitSet.Bytes(),
|
|
}
|
|
|
|
blsAggregatedSignatureBytes := bls.SignatureToBytes(blsAggregatedSignature)
|
|
copy(warpSignature.Signature[:], blsAggregatedSignatureBytes)
|
|
|
|
signedMessage, err := luxWarp.NewMessage(
|
|
unsignedMessage,
|
|
warpSignature,
|
|
)
|
|
require.NoError(err)
|
|
|
|
createTx, err := types.SignTx(
|
|
types.NewContractCreation(0, common.Big0, 7_000_000, big.NewInt(225*utils.GWei), common.Hex2Bytes(exampleWarpBin)),
|
|
types.LatestSignerForChainID(tvm.vm.chainConfig.ChainID),
|
|
func() *ecdsa.PrivateKey {
|
|
key, err := crypto.ToECDSA(testKeys[0].Bytes())
|
|
require.NoError(err)
|
|
return key
|
|
}(),
|
|
)
|
|
require.NoError(err)
|
|
cryptoAddr := crypto.Address(testEthAddrs[0])
|
|
exampleWarpAddress := common.Address(crypto.CreateAddress(cryptoAddr, 0))
|
|
|
|
tx, err := types.SignTx(
|
|
predicate.NewPredicateTx(
|
|
tvm.vm.chainConfig.ChainID,
|
|
1,
|
|
&exampleWarpAddress,
|
|
1_000_000,
|
|
big.NewInt(225*utils.GWei),
|
|
big.NewInt(200*utils.GWei), // Increased from 1 GWei to cover block gas cost
|
|
common.Big0,
|
|
txPayload,
|
|
types.AccessList{},
|
|
warpcontract.ContractAddress,
|
|
signedMessage.Bytes(),
|
|
),
|
|
types.LatestSignerForChainID(tvm.vm.chainConfig.ChainID),
|
|
func() *ecdsa.PrivateKey {
|
|
key, err := crypto.ToECDSA(testKeys[0].Bytes())
|
|
require.NoError(err)
|
|
return key
|
|
}(),
|
|
)
|
|
require.NoError(err)
|
|
errs := tvm.vm.txPool.AddRemotesSync([]*types.Transaction{createTx, tx})
|
|
for i, err := range errs {
|
|
require.NoError(err, "failed to add tx at index %d", i)
|
|
}
|
|
|
|
// If [validSignature] set the signature to be considered valid at the verified height.
|
|
blockCtx := &block.Context{
|
|
PChainHeight: minimumValidPChainHeight - 1,
|
|
}
|
|
if validSignature {
|
|
blockCtx.PChainHeight = minimumValidPChainHeight
|
|
}
|
|
tvm.vm.clock.Set(tvm.vm.clock.Time().Add(2 * time.Second))
|
|
|
|
msg, err := tvm.vm.WaitForEvent(context.Background())
|
|
require.NoError(err)
|
|
require.Equal(commonEng.PendingTxs, msg)
|
|
|
|
warpBlock, err := tvm.vm.BuildBlockWithContext(context.Background(), blockCtx)
|
|
require.NoError(err)
|
|
|
|
warpBlockVerifyWithCtx, ok := warpBlock.(block.WithVerifyContext)
|
|
require.True(ok)
|
|
shouldVerifyWithCtx, err := warpBlockVerifyWithCtx.ShouldVerifyWithContext(context.Background())
|
|
require.NoError(err)
|
|
require.True(shouldVerifyWithCtx)
|
|
require.NoError(warpBlockVerifyWithCtx.VerifyWithContext(context.Background(), blockCtx))
|
|
require.NoError(tvm.vm.SetPreference(context.Background(), warpBlock.ID()))
|
|
require.NoError(warpBlock.Accept(context.Background()))
|
|
tvm.vm.blockChain.DrainAcceptorQueue()
|
|
|
|
// Get internal block safely using unwrapBlock helper
|
|
warpInternalBlock := unwrapBlock(t, warpBlock)
|
|
ethBlock := warpInternalBlock.ethBlock
|
|
verifiedMessageReceipts := tvm.vm.blockChain.GetReceiptsByHash(ethBlock.Hash())
|
|
require.Len(verifiedMessageReceipts, 2)
|
|
for i, receipt := range verifiedMessageReceipts {
|
|
require.Equal(types.ReceiptStatusSuccessful, receipt.Status, "index: %d", i)
|
|
}
|
|
|
|
tracerAPI := tracers.NewAPI(tvm.vm.eth.APIBackend)
|
|
txTraceResults, err := tracerAPI.TraceBlockByHash(context.Background(), ethBlock.Hash(), nil)
|
|
require.NoError(err)
|
|
require.Len(txTraceResults, 2)
|
|
blockTxTraceResultBytes, err := json.Marshal(txTraceResults[1].Result)
|
|
require.NoError(err)
|
|
unmarshalResults := make(map[string]interface{})
|
|
require.NoError(json.Unmarshal(blockTxTraceResultBytes, &unmarshalResults))
|
|
// The tracer returns "0x" for empty return data (hex encoding of empty bytes)
|
|
require.Equal("0x", unmarshalResults["returnValue"])
|
|
|
|
txTraceResult, err := tracerAPI.TraceTransaction(context.Background(), tx.Hash(), nil)
|
|
require.NoError(err)
|
|
txTraceResultBytes, err := json.Marshal(txTraceResult)
|
|
require.NoError(err)
|
|
require.JSONEq(string(txTraceResultBytes), string(blockTxTraceResultBytes))
|
|
}
|
|
|
|
func TestReceiveWarpMessage(t *testing.T) {
|
|
for _, scheme := range schemes {
|
|
t.Run(scheme, func(t *testing.T) {
|
|
testReceiveWarpMessageWithScheme(t, scheme)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testReceiveWarpMessageWithScheme(t *testing.T, scheme string) {
|
|
require := require.New(t)
|
|
fork := "Latest"
|
|
tvm := newVM(t, testVMConfig{
|
|
fork: &fork,
|
|
configJSON: getConfig(scheme, ""),
|
|
})
|
|
defer func() {
|
|
require.NoError(tvm.vm.Shutdown(context.Background()))
|
|
}()
|
|
|
|
// enable warp at the default genesis time
|
|
enableTime := time.Unix(0, 0)
|
|
enableConfig := warpcontract.NewDefaultConfig(utils.TimeToNewUint64(enableTime))
|
|
|
|
// disable warp so we can re-enable it with RequirePrimaryNetworkSigners
|
|
disableTime := time.Unix(0, 0).Add(10 * time.Second)
|
|
disableConfig := warpcontract.NewDisableConfig(utils.TimeToNewUint64(disableTime))
|
|
|
|
// re-enable warp with RequirePrimaryNetworkSigners
|
|
reEnableTime := disableTime.Add(10 * time.Second)
|
|
reEnableConfig := warpcontract.NewConfig(
|
|
utils.TimeToNewUint64(reEnableTime),
|
|
0, // QuorumNumerator
|
|
true, // RequirePrimaryNetworkSigners
|
|
)
|
|
|
|
tvm.vm.chainConfigExtra().UpgradeConfig = extras.UpgradeConfig{
|
|
PrecompileUpgrades: []extras.PrecompileUpgrade{
|
|
{Config: enableConfig},
|
|
{Config: disableConfig},
|
|
{Config: reEnableConfig},
|
|
},
|
|
}
|
|
|
|
type test struct {
|
|
name string
|
|
sourceChainID ids.ID
|
|
msgFrom warpMsgFrom
|
|
useSigners useWarpMsgSigners
|
|
blockTime time.Time
|
|
}
|
|
|
|
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: consensuscontext.GetChainID(tvm.vm.ctx),
|
|
msgFrom: fromSubnet,
|
|
useSigners: signersSubnet,
|
|
blockTime: time.Unix(0, 0), // Genesis time
|
|
},
|
|
{
|
|
name: "P-Chain message should be signed by subnet without RequirePrimaryNetworkSigners",
|
|
sourceChainID: constants.PlatformChainID,
|
|
msgFrom: fromPrimary,
|
|
useSigners: signersSubnet,
|
|
blockTime: time.Unix(0, 0).Add(blockGap), // Genesis + blockGap
|
|
},
|
|
{
|
|
name: "C-Chain message should be signed by subnet without RequirePrimaryNetworkSigners",
|
|
sourceChainID: consensuscontext.GetChainID(tvm.vm.ctx),
|
|
msgFrom: fromPrimary,
|
|
useSigners: signersSubnet,
|
|
blockTime: time.Unix(0, 0).Add(2 * blockGap), // Genesis + 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: consensuscontext.GetChainID(tvm.vm.ctx),
|
|
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: consensuscontext.GetChainID(tvm.vm.ctx),
|
|
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 {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
testReceiveWarpMessage(
|
|
t, tvm.vm, test.sourceChainID, test.msgFrom, test.useSigners, test.blockTime,
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testReceiveWarpMessage(
|
|
t *testing.T, vm *VM,
|
|
sourceChainID ids.ID,
|
|
msgFrom warpMsgFrom, useSigners useWarpMsgSigners,
|
|
blockTime time.Time,
|
|
) {
|
|
require := require.New(t)
|
|
payloadData := luxdUtils.RandomBytes(100)
|
|
addressedPayload, err := payload.NewAddressedCall(
|
|
testEthAddrs[0].Bytes(),
|
|
payloadData,
|
|
)
|
|
require.NoError(err)
|
|
|
|
subnetID := ids.GenerateTestID()
|
|
vm.ctx = consensus.WithSubnetID(vm.ctx, subnetID)
|
|
vm.ctx = consensus.WithNetworkID(vm.ctx, testNetworkID)
|
|
// Set the chain-to-subnet mapping so GetSubnetID returns the correct subnet
|
|
// For subnet sources, map to the generated subnet
|
|
// For primary network sources (except P-Chain), map to Empty (primary network)
|
|
// P-Chain doesn't need a mapping as it's handled specially
|
|
if msgFrom == fromSubnet {
|
|
utilstest.SetChainSubnetMapping(sourceChainID, subnetID)
|
|
} else if sourceChainID != constants.PlatformChainID {
|
|
// Primary network source (e.g., "C-Chain" in test) - map to Empty
|
|
utilstest.SetChainSubnetMapping(sourceChainID, ids.Empty)
|
|
}
|
|
defer utilstest.ClearChainSubnetMapping()
|
|
unsignedMessage, err := luxWarp.NewUnsignedMessage(
|
|
consensuscontext.GetNetworkID(vm.ctx),
|
|
sourceChainID[:],
|
|
addressedPayload.Bytes(),
|
|
)
|
|
require.NoError(err)
|
|
|
|
type signer struct {
|
|
nodeID ids.NodeID
|
|
secret bls.Signer
|
|
signature *bls.Signature
|
|
weight uint64
|
|
}
|
|
newSigner := func(weight uint64) signer {
|
|
secret, err := localsigner.New()
|
|
require.NoError(err)
|
|
sig, err := secret.Sign(unsignedMessage.Bytes())
|
|
require.NoError(err)
|
|
|
|
return signer{
|
|
nodeID: ids.GenerateTestNodeID(),
|
|
secret: secret,
|
|
signature: sig,
|
|
weight: weight,
|
|
}
|
|
}
|
|
|
|
primarySigners := []signer{
|
|
newSigner(50),
|
|
newSigner(50),
|
|
}
|
|
subnetSigners := []signer{
|
|
newSigner(50),
|
|
newSigner(50),
|
|
}
|
|
signers := subnetSigners
|
|
if useSigners == signersPrimary {
|
|
signers = primarySigners
|
|
}
|
|
|
|
// Sort signers by public key for canonical ordering - this must match
|
|
// the order used during verification (CanonicalValidatorSet sorts by pubkey)
|
|
sort.Slice(signers, func(i, j int) bool {
|
|
return bytes.Compare(
|
|
bls.PublicKeyToCompressedBytes(signers[i].secret.PublicKey()),
|
|
bls.PublicKeyToCompressedBytes(signers[j].secret.PublicKey()),
|
|
) < 0
|
|
})
|
|
|
|
// Debug: print sorted public keys
|
|
for i, s := range signers {
|
|
pkBytes := bls.PublicKeyToCompressedBytes(s.secret.PublicKey())
|
|
t.Logf("DEBUG TEST: signer[%d] pubkey=%x", i, pkBytes)
|
|
}
|
|
|
|
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)
|
|
getValidatorSetTestErr := errors.New("can't get validator set test error")
|
|
|
|
testValidatorState2 := &validatorstest.State{
|
|
// GetSubnetIDF removed - not available in current interface
|
|
GetValidatorSetF: func(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
|
|
if height < minimumValidPChainHeight {
|
|
return nil, getValidatorSetTestErr
|
|
}
|
|
signers := subnetSigners
|
|
if subnetID == constants.PrimaryNetworkID {
|
|
signers = primarySigners
|
|
}
|
|
|
|
vdrOutput := make(map[ids.NodeID]*validators.GetValidatorOutput)
|
|
for _, s := range signers {
|
|
vdrOutput[s.nodeID] = &validators.GetValidatorOutput{
|
|
NodeID: s.nodeID,
|
|
PublicKey: bls.PublicKeyToCompressedBytes(s.secret.PublicKey()),
|
|
Weight: s.weight,
|
|
}
|
|
}
|
|
return vdrOutput, nil
|
|
},
|
|
}
|
|
wrappedValidatorState2 := utilstest.NewTestValidatorStateFromBase(testValidatorState2)
|
|
vm.ctx = consensus.WithValidatorState(vm.ctx, wrappedValidatorState2)
|
|
// Propagate the updated context to the blockchain so the miner can access the validator state
|
|
vm.blockChain.SetConsensusContext(vm.ctx)
|
|
|
|
signersBitSet := set.NewBits()
|
|
for i := range signers {
|
|
signersBitSet.Add(i)
|
|
}
|
|
|
|
warpSignature := &luxWarp.BitSetSignature{
|
|
Signers: signersBitSet.Bytes(),
|
|
}
|
|
|
|
blsAggregatedSignatureBytes := bls.SignatureToBytes(blsAggregatedSignature)
|
|
copy(warpSignature.Signature[:], blsAggregatedSignatureBytes)
|
|
|
|
signedMessage, err := luxWarp.NewMessage(
|
|
unsignedMessage,
|
|
warpSignature,
|
|
)
|
|
require.NoError(err)
|
|
|
|
getWarpMsgInput, err := warpcontract.PackGetVerifiedWarpMessage(0)
|
|
require.NoError(err)
|
|
// Use a higher gasTipCap (200 GWei) to ensure the transaction pays enough
|
|
// block fee to cover the requiredBlockGasCost. The block fee verification
|
|
// checks: (tip * gasUsed) / baseFee >= requiredBlockGasCost
|
|
pendingNonce := vm.txPool.Nonce(testEthAddrs[0])
|
|
t.Logf("DEBUG TEST: pendingNonce=%d, blockTime=%v", pendingNonce, blockTime)
|
|
getVerifiedWarpMessageTx, err := types.SignTx(
|
|
predicate.NewPredicateTx(
|
|
vm.chainConfig.ChainID,
|
|
pendingNonce,
|
|
&warpcontract.Module.Address,
|
|
1_000_000,
|
|
big.NewInt(225*utils.GWei),
|
|
big.NewInt(200*utils.GWei), // Increased from 1 GWei to cover block gas cost
|
|
common.Big0,
|
|
getWarpMsgInput,
|
|
types.AccessList{},
|
|
warpcontract.ContractAddress,
|
|
signedMessage.Bytes(),
|
|
),
|
|
types.LatestSignerForChainID(vm.chainConfig.ChainID),
|
|
func() *ecdsa.PrivateKey {
|
|
key, err := crypto.ToECDSA(testKeys[0].Bytes())
|
|
require.NoError(err)
|
|
return key
|
|
}(),
|
|
)
|
|
require.NoError(err)
|
|
errs := vm.txPool.AddRemotesSync([]*types.Transaction{getVerifiedWarpMessageTx})
|
|
for i, err := range errs {
|
|
require.NoError(err, "failed to add tx at index %d", i)
|
|
}
|
|
|
|
// Build, verify, and accept block with valid proposer context.
|
|
validProposerCtx := &block.Context{
|
|
PChainHeight: minimumValidPChainHeight,
|
|
}
|
|
vm.clock.Set(blockTime)
|
|
|
|
msg, err := vm.WaitForEvent(context.Background())
|
|
require.NoError(err)
|
|
require.Equal(commonEng.PendingTxs, msg)
|
|
|
|
block2, err := vm.BuildBlockWithContext(context.Background(), validProposerCtx)
|
|
require.NoError(err)
|
|
|
|
// Require the block was built with a successful predicate result
|
|
// Get internal block safely using unwrapBlock helper
|
|
block2Internal := unwrapBlock(t, block2)
|
|
ethBlock := block2Internal.ethBlock
|
|
t.Logf("DEBUG TEST: block built at time=%d, numTxs=%d", ethBlock.Time(), len(ethBlock.Transactions()))
|
|
headerPredicateResultsBytes := customheader.PredicateBytesFromExtra(ethBlock.Extra())
|
|
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(),
|
|
warpcontract.ContractAddress,
|
|
)
|
|
bitset := set.BitsFromBytes(txResultsBytes)
|
|
require.Zero(bitset.Len()) // Empty bitset indicates success
|
|
|
|
block2VerifyWithCtx, ok := block2.(block.WithVerifyContext)
|
|
require.True(ok)
|
|
shouldVerifyWithCtx, err := block2VerifyWithCtx.ShouldVerifyWithContext(context.Background())
|
|
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{
|
|
PChainHeight: minimumValidPChainHeight + 1,
|
|
}))
|
|
|
|
// Verify the block in a different context causing the warp message to fail verification changing
|
|
// the expected header predicate results.
|
|
require.ErrorIs(block2VerifyWithCtx.VerifyWithContext(context.Background(), &block.Context{
|
|
PChainHeight: minimumValidPChainHeight - 1,
|
|
}), errInvalidHeaderPredicateResults)
|
|
|
|
// Accept the block after performing multiple VerifyWithContext operations
|
|
require.NoError(block2.Accept(context.Background()))
|
|
vm.blockChain.DrainAcceptorQueue()
|
|
|
|
verifiedMessageReceipts := vm.blockChain.GetReceiptsByHash(ethBlock.Hash())
|
|
t.Logf("DEBUG TEST: Getting receipts for block %d (hash=%s), got %d receipts", ethBlock.Number().Uint64(), ethBlock.Hash().Hex(), len(verifiedMessageReceipts))
|
|
for i, r := range verifiedMessageReceipts {
|
|
t.Logf("DEBUG TEST: Receipt[%d]: TxHash=%s, Status=%d", i, r.TxHash.Hex(), r.Status)
|
|
}
|
|
require.Len(verifiedMessageReceipts, 1)
|
|
verifiedMessageTxReceipt := verifiedMessageReceipts[0]
|
|
require.Equal(types.ReceiptStatusSuccessful, verifiedMessageTxReceipt.Status)
|
|
|
|
expectedOutput, err := warpcontract.PackGetVerifiedWarpMessageOutput(warpcontract.GetVerifiedWarpMessageOutput{
|
|
Message: warpcontract.WarpMessage{
|
|
SourceChainID: common.Hash(sourceChainID),
|
|
OriginSenderAddress: testEthAddrs[0],
|
|
Payload: payloadData,
|
|
},
|
|
Valid: true,
|
|
})
|
|
require.NoError(err)
|
|
|
|
tracerAPI := tracers.NewAPI(vm.eth.APIBackend)
|
|
txTraceResults, err := tracerAPI.TraceBlockByHash(context.Background(), ethBlock.Hash(), nil)
|
|
require.NoError(err)
|
|
require.Len(txTraceResults, 1)
|
|
blockTxTraceResultBytes, err := json.Marshal(txTraceResults[0].Result)
|
|
require.NoError(err)
|
|
unmarshalResults := make(map[string]interface{})
|
|
require.NoError(json.Unmarshal(blockTxTraceResultBytes, &unmarshalResults))
|
|
require.Equal("0x"+common.Bytes2Hex(expectedOutput), unmarshalResults["returnValue"])
|
|
|
|
txTraceResult, err := tracerAPI.TraceTransaction(context.Background(), getVerifiedWarpMessageTx.Hash(), nil)
|
|
require.NoError(err)
|
|
txTraceResultBytes, err := json.Marshal(txTraceResult)
|
|
require.NoError(err)
|
|
require.JSONEq(string(txTraceResultBytes), string(blockTxTraceResultBytes))
|
|
}
|
|
|
|
func TestSignatureRequestsToVM(t *testing.T) {
|
|
for _, scheme := range schemes {
|
|
t.Run(scheme, func(t *testing.T) {
|
|
testSignatureRequestsToVM(t, scheme)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testSignatureRequestsToVM(t *testing.T, scheme string) {
|
|
fork := "Latest"
|
|
tvm := newVM(t, testVMConfig{
|
|
fork: &fork,
|
|
configJSON: getConfig(scheme, ""),
|
|
})
|
|
|
|
defer func() {
|
|
require.NoError(t, tvm.vm.Shutdown(context.Background()))
|
|
}()
|
|
|
|
// Setup known message
|
|
knownPayload, err := payload.NewAddressedCall([]byte{0, 0, 0}, []byte("test"))
|
|
require.NoError(t, err)
|
|
chainID := consensuscontext.GetChainID(tvm.vm.ctx)
|
|
knownWarpMessage, err := luxWarp.NewUnsignedMessage(consensuscontext.GetNetworkID(tvm.vm.ctx), chainID[:], knownPayload.Bytes())
|
|
require.NoError(t, err)
|
|
|
|
// Add the known message and get its signature to confirm
|
|
require.NoError(t, tvm.vm.warpBackend.AddMessage(knownWarpMessage))
|
|
knownMessageSignature, err := tvm.vm.warpBackend.GetMessageSignature(context.Background(), knownWarpMessage)
|
|
require.NoError(t, err)
|
|
|
|
// Setup known block
|
|
lastAcceptedID, err := tvm.vm.LastAccepted(context.Background())
|
|
require.NoError(t, err)
|
|
knownBlockSignature, err := tvm.vm.warpBackend.GetBlockSignature(context.Background(), lastAcceptedID)
|
|
require.NoError(t, err)
|
|
|
|
type testCase struct {
|
|
name string
|
|
message *luxWarp.UnsignedMessage
|
|
expectedResponse []byte
|
|
err *commonEng.AppError
|
|
}
|
|
|
|
tests := []testCase{
|
|
{
|
|
name: "known message",
|
|
message: knownWarpMessage,
|
|
expectedResponse: knownMessageSignature,
|
|
},
|
|
{
|
|
name: "unknown message",
|
|
message: func() *luxWarp.UnsignedMessage {
|
|
unknownPayload, err := payload.NewAddressedCall([]byte{1, 1, 1}, []byte("unknown"))
|
|
require.NoError(t, err)
|
|
chainID := consensuscontext.GetChainID(tvm.vm.ctx)
|
|
msg, err := luxWarp.NewUnsignedMessage(consensuscontext.GetNetworkID(tvm.vm.ctx), chainID[:], unknownPayload.Bytes())
|
|
require.NoError(t, err)
|
|
return msg
|
|
}(),
|
|
err: &commonEng.AppError{Code: warp.ParseErrCode},
|
|
},
|
|
{
|
|
name: "known block",
|
|
message: func() *luxWarp.UnsignedMessage {
|
|
hashPayload, err := payload.NewHash(lastAcceptedID)
|
|
require.NoError(t, err)
|
|
chainID := consensuscontext.GetChainID(tvm.vm.ctx)
|
|
msg, err := luxWarp.NewUnsignedMessage(consensuscontext.GetNetworkID(tvm.vm.ctx), chainID[:], hashPayload.Bytes())
|
|
require.NoError(t, err)
|
|
return msg
|
|
}(),
|
|
expectedResponse: knownBlockSignature,
|
|
},
|
|
{
|
|
name: "unknown block",
|
|
message: func() *luxWarp.UnsignedMessage {
|
|
testID := ids.GenerateTestID()
|
|
hashPayload, err := payload.NewHash(testID)
|
|
require.NoError(t, err)
|
|
chainID := consensuscontext.GetChainID(tvm.vm.ctx)
|
|
msg, err := luxWarp.NewUnsignedMessage(consensuscontext.GetNetworkID(tvm.vm.ctx), chainID[:], hashPayload.Bytes())
|
|
require.NoError(t, err)
|
|
return msg
|
|
}(),
|
|
err: &commonEng.AppError{Code: warp.VerifyErrCode},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
calledSendAppResponseFn := false
|
|
calledSendAppErrorFn := false
|
|
|
|
tvm.appSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, responseBytes []byte) error {
|
|
calledSendAppResponseFn = true
|
|
var response sdk.SignatureResponse
|
|
if err := proto.Unmarshal(responseBytes, &response); err != nil {
|
|
return err
|
|
}
|
|
require.Equal(t, test.expectedResponse, response.Signature)
|
|
return nil
|
|
}
|
|
|
|
tvm.appSender.SendAppErrorF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, errCode int32, errString string) error {
|
|
calledSendAppErrorFn = true
|
|
require.ErrorIs(t, test.err, test.err)
|
|
return nil
|
|
}
|
|
|
|
protoMsg := &sdk.SignatureRequest{Message: test.message.Bytes()}
|
|
requestBytes, err := proto.Marshal(protoMsg)
|
|
require.NoError(t, err)
|
|
msg := p2p.PrefixMessage(p2p.ProtocolPrefix(lp118.HandlerID), requestBytes)
|
|
|
|
// Send the app request and verify the response
|
|
deadline := time.Now().Add(60 * time.Second)
|
|
appErr := tvm.vm.Network.AppRequest(context.Background(), ids.GenerateTestNodeID(), 1, deadline, msg)
|
|
require.Nil(t, appErr)
|
|
if test.err != nil {
|
|
require.True(t, calledSendAppErrorFn)
|
|
} else {
|
|
require.True(t, calledSendAppResponseFn)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// noCloseDB wraps a database to ignore Close() calls, allowing reuse across VM restarts
|
|
type noCloseDB struct {
|
|
luxfiDatabase.Database
|
|
}
|
|
|
|
func (db *noCloseDB) Close() error {
|
|
return nil // Ignore close
|
|
}
|
|
|
|
func TestClearWarpDB(t *testing.T) {
|
|
// Create a shared baseDB that persists across VM restarts
|
|
// Wrap in noCloseDB to prevent the VM from closing it
|
|
baseDB := &noCloseDB{memdb.New()}
|
|
genesisJSON := toGenesisJSON(forkToChainConfig["Latest"])
|
|
genesisBytes := []byte(genesisJSON)
|
|
|
|
// First VM initialization
|
|
ctx := createTestConsensusContext(t)
|
|
db := prefixdb.New([]byte{1}, baseDB)
|
|
vm := &VM{}
|
|
require.NoError(t, vm.Initialize(context.Background(), ctx, db, genesisBytes, []byte{}, []byte{}, nil, nil, &TestSender{}))
|
|
|
|
// use multiple messages to test that all messages get cleared
|
|
payloads := [][]byte{[]byte("test1"), []byte("test2"), []byte("test3"), []byte("test4"), []byte("test5")}
|
|
messages := []*luxWarp.UnsignedMessage{}
|
|
|
|
// add all messages
|
|
for _, msgPayload := range payloads {
|
|
chainID := consensuscontext.GetChainID(vm.ctx)
|
|
unsignedMsg, err := luxWarp.NewUnsignedMessage(consensuscontext.GetNetworkID(vm.ctx), chainID[:], msgPayload)
|
|
require.NoError(t, err)
|
|
require.NoError(t, vm.warpBackend.AddMessage(unsignedMsg))
|
|
messages = append(messages, unsignedMsg)
|
|
}
|
|
|
|
require.NoError(t, vm.Shutdown(context.Background()))
|
|
|
|
// Restart VM with the same database default should not prune the warp db
|
|
vm = &VM{}
|
|
// we need new context since the previous one has registered metrics.
|
|
ctx = createTestConsensusContext(t)
|
|
db = prefixdb.New([]byte{1}, baseDB) // Create new prefixdb from same baseDB
|
|
require.NoError(t, vm.Initialize(context.Background(), ctx, db, genesisBytes, []byte{}, []byte{}, nil, nil, &TestSender{}))
|
|
|
|
// check messages are still present
|
|
for _, message := range messages {
|
|
_, err := vm.warpBackend.GetMessageSignature(context.Background(), message)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
require.NoError(t, vm.Shutdown(context.Background()))
|
|
|
|
// restart the VM with pruning enabled
|
|
vm = &VM{}
|
|
config := `{"prune-warp-db-enabled": true}`
|
|
ctx = createTestConsensusContext(t)
|
|
db = prefixdb.New([]byte{1}, baseDB) // Create new prefixdb from same baseDB
|
|
require.NoError(t, vm.Initialize(context.Background(), ctx, db, genesisBytes, []byte{}, []byte(config), nil, nil, &TestSender{}))
|
|
|
|
it := vm.warpDB.NewIterator()
|
|
require.False(t, it.Next())
|
|
it.Release()
|
|
|
|
// ensure all messages have been deleted
|
|
for _, message := range messages {
|
|
_, err := vm.warpBackend.GetMessageSignature(context.Background(), message)
|
|
require.Error(t, err)
|
|
}
|
|
}
|