Skip to content

Commit

Permalink
test(e2e): fix proposer selection test (#926)
Browse files Browse the repository at this point in the history
* test(e2e): fix proposer selection test

* test(e2e): support consensus version update testing

* test(e2e): consensus version updates

* fix(e2e): TestApp_TxTooBig takes too much time

* chore(e2e): self-review

* chore:typo

* chore: typo
  • Loading branch information
lklimek authored Sep 26, 2024
1 parent aebd750 commit 005af92
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 80 deletions.
2 changes: 2 additions & 0 deletions abci/example/kvstore/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ type Config struct {
ChainLockUpdates map[string]string `toml:"chainlock_updates"`
PrivValServerType string `toml:"privval_server_type"`
InitAppInitialCoreHeight uint32 `toml:"init_app_core_chain_locked_height"`
// ConsensusVersionUpdates is a map of heights to consensus version ; ONLY SUPPORTED BY e2e.Application
ConsensusVersionUpdates map[string]int32 `toml:"consensus_version_updates"`
}

func DefaultConfig(dir string) Config {
Expand Down
16 changes: 15 additions & 1 deletion test/e2e/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ func NewApplication(cfg kvstore.Config, opts ...kvstore.OptFunc) (*Application,
return nil, err
}

for h, ver := range cfg.ConsensusVersionUpdates {
height, err := strconv.Atoi(h)
if err != nil {
return nil, fmt.Errorf("consensus_version_updates: failed to parse height %s: %w", h, err)
}
params := types1.ConsensusParams{
Version: &types1.VersionParams{
ConsensusVersion: types1.VersionParams_ConsensusVersion(ver),
AppVersion: kvstore.ProtocolVersion,
},
}
app.AddConsensusParamsUpdate(params, int64(height))
}

return &app, nil
}

Expand Down Expand Up @@ -146,7 +160,7 @@ func (app *Application) VerifyVoteExtension(_ context.Context, req *abci.Request
}

if app.cfg.VoteExtensionDelayMS != 0 {
time.Sleep(time.Duration(app.cfg.VoteExtensionDelayMS) * time.Millisecond)
time.Sleep(time.Duration(app.cfg.VoteExtensionDelayMS) * time.Millisecond) //#nosec G115
}

app.logger.Info("verified vote extension value", "req", req, "nums", nums)
Expand Down
22 changes: 22 additions & 0 deletions test/e2e/networks/rotate.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,28 @@ validator04 = 100
validator05 = 100
validator11 = 100


[validator_update.1070]
validator01 = 100
validator02 = 100
validator03 = 100
validator04 = 100
validator05 = 100


[validator_update.1077]
validator01 = 100
validator07 = 100
validator08 = 100
validator10 = 100
validator11 = 100

[consensus_version_updates]

1070 = 1
1076 = 0
1079 = 1

[node.seed01]
mode = "seed"
perturb = ["restart"]
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/node/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Config struct {
QuorumHashUpdate map[string]string `toml:"quorum_hash_update"`
ChainLockUpdates map[string]string `toml:"chainlock_updates"`
PrivValServerType string `toml:"privval_server_type"`
ConsensusVersionUpdates map[string]int32 `toml:"consensus_version_updates"`
}

// App extracts out the application specific configuration parameters
Expand All @@ -46,6 +47,7 @@ func (cfg *Config) App() *kvstore.Config {
QuorumHashUpdate: cfg.QuorumHashUpdate,
ChainLockUpdates: cfg.ChainLockUpdates,
PrivValServerType: cfg.PrivValServerType,
ConsensusVersionUpdates: cfg.ConsensusVersionUpdates,
}
}

Expand Down
9 changes: 7 additions & 2 deletions test/e2e/pkg/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ type Manifest struct {
// not specified are not changed.
ValidatorUpdates map[string]map[string]int64 `toml:"validator_update"`

// ConsensusVersionUpdates is a map of heights to consensus versions, and
// will be sent by the ABCI application as a consensus params update.
// For example, the following sets the consensus version to 1 at height 1000:
//
// [consensus_version_updates]
// 1000 = 1
ConsensusVersionUpdates map[string]int32 `toml:"consensus_version_updates"`
// ChainLockUpdates is a map of heights at which a new chain lock should be proposed
// The first number is the tendermint height, and the second is the
//
Expand All @@ -64,8 +71,6 @@ type Manifest struct {
// 1004 = 3451
// 1020 = 3454
// 1040 = 3500
//

ChainLockUpdates map[string]int64 `toml:"chainlock_updates"`

// Nodes specifies the network nodes. At least one node must be given.
Expand Down
27 changes: 19 additions & 8 deletions test/e2e/pkg/testnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ type Testnet struct {
QuorumType btcjson.LLMQType
QuorumHash crypto.QuorumHash
QuorumHashUpdates map[int64]crypto.QuorumHash
// ConsensusVersionUpdates maps height to new consensus version (ConsensusParams.Version.ConsensusVersion)
ConsensusVersionUpdates map[int64]int32
}

// Node represents a Tenderdash node in a testnet.
Expand Down Expand Up @@ -204,18 +206,19 @@ func LoadTestnet(file string) (*Testnet, error) {
LogLevel: manifest.LogLevel,
TxSize: manifest.TxSize,
ABCIProtocol: Protocol(manifest.ABCIProtocol),
PrepareProposalDelayMS: int(manifest.PrepareProposalDelayMS),
ProcessProposalDelayMS: int(manifest.ProcessProposalDelayMS),
CheckTxDelayMS: int(manifest.CheckTxDelayMS),
VoteExtensionDelayMS: int(manifest.VoteExtensionDelayMS),
FinalizeBlockDelayMS: int(manifest.FinalizeBlockDelayMS),
MaxBlockSize: int64(manifest.MaxBlockSize),
MaxEvidenceSize: int64(manifest.MaxEvidenceSize),
PrepareProposalDelayMS: int(manifest.PrepareProposalDelayMS), //#nosec G115
ProcessProposalDelayMS: int(manifest.ProcessProposalDelayMS), //#nosec G115
CheckTxDelayMS: int(manifest.CheckTxDelayMS), //#nosec G115
VoteExtensionDelayMS: int(manifest.VoteExtensionDelayMS), //#nosec G115
FinalizeBlockDelayMS: int(manifest.FinalizeBlockDelayMS), //#nosec G115
MaxBlockSize: int64(manifest.MaxBlockSize), //#nosec G115
MaxEvidenceSize: int64(manifest.MaxEvidenceSize), //#nosec G115
ThresholdPublicKey: ld.ThresholdPubKey,
ThresholdPublicKeyUpdates: map[int64]crypto.PubKey{},
QuorumType: btcjson.LLMQType(quorumType),
QuorumHash: quorumHash,
QuorumHashUpdates: map[int64]crypto.QuorumHash{},
ConsensusVersionUpdates: map[int64]int32{},
}
if len(manifest.KeyType) != 0 {
testnet.KeyType = manifest.KeyType
Expand Down Expand Up @@ -439,14 +442,22 @@ func LoadTestnet(file string) (*Testnet, error) {

sort.Ints(chainLockSetHeights)

// Set up validator updates.
// Set up chainlock updates.
for _, height := range chainLockSetHeights {
heightStr := strconv.FormatInt(int64(height), 10)
chainLockHeight := manifest.ChainLockUpdates[heightStr]
testnet.ChainLockUpdates[int64(height)] = chainLockHeight
fmt.Printf("Set chainlock at height %d / core height is %d\n", height, chainLockHeight)
}

for heightStr, cpUpdate := range manifest.ConsensusVersionUpdates {
height, err := strconv.Atoi(heightStr)
if err != nil {
return nil, fmt.Errorf("invalid consensus version update height %q: %w", height, err)
}
testnet.ConsensusVersionUpdates[int64(height)] = cpUpdate
}

return testnet, testnet.Validate()
}

Expand Down
8 changes: 8 additions & 0 deletions test/e2e/runner/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,14 @@ func MakeAppConfig(node *e2e.Node) ([]byte, error) {
cfg["chainlock_updates"] = chainLockUpdates
}

if len(node.Testnet.ConsensusVersionUpdates) > 0 {
consensusVersionUpdates := map[string]int32{}
for height, version := range node.Testnet.ConsensusVersionUpdates {
consensusVersionUpdates[strconv.Itoa(int(height))] = version //#nosec:G115
}
cfg["consensus_version_updates"] = consensusVersionUpdates
}

var buf bytes.Buffer
err := toml.NewEncoder(&buf).Encode(cfg)
if err != nil {
Expand Down
90 changes: 46 additions & 44 deletions test/e2e/tests/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,68 +242,70 @@ func TestApp_TxTooBig(t *testing.T) {
outcome := make([]txPair, 0, len(nodes))

start := time.Now()
/// Send to each node more txs than we can fit into block
for _, node := range nodes {
ctx, cancel := context.WithTimeout(mainCtx, broadcastTimeout)
defer cancel()

if ctx.Err() != nil {
t.Fatalf("context canceled before broadcasting to all nodes")
}
node := *node
/// Send more txs than we can fit into block

if node.Stateless() {
continue
ctx, cancel := context.WithTimeout(mainCtx, broadcastTimeout)
defer cancel()

if ctx.Err() != nil {
t.Fatalf("context canceled before broadcasting to all nodes")
}
// find first non-stateless node
var node *e2e.Node
for _, node = range nodes {
if !node.Stateless() {
break
}
}

t.Logf("broadcasting to %s", node.Name)
t.Logf("broadcasting to %s", node.Name)

session := rand.Int63()
session := rand.Int63()

var err error
client, err = node.Client()
require.NoError(t, err)
var err error
client, err = node.Client()
require.NoError(t, err)

// FIXME: ConsensusParams is broken for last height, this is just workaround
status, err := client.Status(ctx)
assert.NoError(t, err)
cp, err := client.ConsensusParams(ctx, &status.SyncInfo.LatestBlockHeight)
assert.NoError(t, err)
// FIXME: ConsensusParams is broken for last height, this is just workaround
status, err := client.Status(ctx)
assert.NoError(t, err)
cp, err := client.ConsensusParams(ctx, &status.SyncInfo.LatestBlockHeight)
assert.NoError(t, err)

// ensure we have more txs than fits the block
TxPayloadSize := int(cp.ConsensusParams.Block.MaxBytes / 100) // 1% of block size
numTxs := 101
// ensure we have more txs than fits the block
TxPayloadSize := int(cp.ConsensusParams.Block.MaxBytes / 100) // 1% of block size
numTxs := 101

tx := make(types.Tx, TxPayloadSize) // first tx is just zeros
tx := make(types.Tx, TxPayloadSize) // first tx is just zeros

var firstTxHash []byte
var key string
var firstTxHash []byte
var key string

for i := 0; i < numTxs; i++ {
key = fmt.Sprintf("testapp-big-tx-%v-%08x-%d=", node.Name, session, i)
copy(tx, key)
for i := 0; i < numTxs; i++ {
key = fmt.Sprintf("testapp-big-tx-%v-%08x-%d=", node.Name, session, i)
copy(tx, key)

payloadOffset := len(tx) - 8 // where we put the `i` into the payload
assert.Greater(t, payloadOffset, len(key))
payloadOffset := len(tx) - 8 // where we put the `i` into the payload
assert.Greater(t, payloadOffset, len(key))

big.NewInt(int64(i)).FillBytes(tx[payloadOffset:])
assert.Len(t, tx, TxPayloadSize)
big.NewInt(int64(i)).FillBytes(tx[payloadOffset:])
assert.Len(t, tx, TxPayloadSize)

if i == 0 {
firstTxHash = tx.Hash()
}

_, err = client.BroadcastTxAsync(ctx, tx)

assert.NoError(t, err, "failed to broadcast tx %06x", i)
if i == 0 {
firstTxHash = tx.Hash()
}

outcome = append(outcome, txPair{
firstTxHash: firstTxHash,
lastTxHash: tx.Hash(),
})
_, err = client.BroadcastTxAsync(ctx, tx)

assert.NoError(t, err, "failed to broadcast tx %06x", i)
}

outcome = append(outcome, txPair{
firstTxHash: firstTxHash,
lastTxHash: tx.Hash(),
})

t.Logf("submitted txs in %s", time.Since(start).String())

successful := 0
Expand Down
8 changes: 8 additions & 0 deletions test/e2e/tests/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import (
"github.com/dashpay/tenderdash/types"
)

// maxBlocks is the maximum number of blocks to fetch from the archive node.
// Use to limit test run time.
const maxBlocks = 500

func init() {
// This can be used to manually specify a testnet manifest and/or node to
// run tests against. The testnet must have been started by the runner first.
Expand Down Expand Up @@ -122,6 +126,10 @@ func fetchBlockChain(ctx context.Context, t *testing.T) []*types.Block {

from := status.SyncInfo.EarliestBlockHeight
to := status.SyncInfo.LatestBlockHeight
// limit blocks to fetch to avoid long test times
if to-from > maxBlocks {
to = from + maxBlocks
}
blocks, ok := blocksCache[testnet.Name]
if !ok {
blocks = make([]*types.Block, 0, to-from+1)
Expand Down
Loading

0 comments on commit 005af92

Please sign in to comment.