-
Notifications
You must be signed in to change notification settings - Fork 4
Fuzz Network Test Framework #326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1e1fdc1
fe7ce40
9c31826
3d87fd2
c45f2df
cf27dea
6cc4278
334cfe0
216f5fa
4f51c7a
dd57de0
dc5f059
b2f99b1
be450f1
e906dc2
07d22ce
0826a28
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package simplex_test | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import ( | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/ava-labs/simplex/testutil/random_network" | ||
| ) | ||
|
|
||
| func TestNetworkSimpleFuzz(t *testing.T) { | ||
| for i := 0; i < 10; i++ { | ||
| t.Run("", func(t *testing.T) { | ||
| config := random_network.DefaultFuzzConfig() | ||
| config.RandomSeed = time.Now().UnixMilli() | ||
| network := random_network.NewNetwork(config, t) | ||
| network.Run() | ||
| network.PrintStatus() | ||
| }) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
|
|
||
| package random_network | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import ( | ||
| "context" | ||
| "crypto/sha256" | ||
| "encoding/asn1" | ||
| "fmt" | ||
|
|
||
| "github.com/ava-labs/simplex" | ||
| ) | ||
|
|
||
| var _ simplex.Block = (*Block)(nil) | ||
|
|
||
| type Block struct { | ||
| blacklist simplex.Blacklist | ||
|
|
||
| // contents | ||
| txs []*TX | ||
|
|
||
| // protocol metadata | ||
| metadata simplex.ProtocolMetadata | ||
| digest simplex.Digest | ||
|
|
||
| // mempool access | ||
| mempool *Mempool | ||
| } | ||
|
|
||
| func NewBlock(metadata simplex.ProtocolMetadata, blacklist simplex.Blacklist, mempool *Mempool, txs []*TX) *Block { | ||
| b := &Block{ | ||
| mempool: mempool, | ||
| txs: txs, | ||
| metadata: metadata, | ||
| blacklist: blacklist, | ||
| } | ||
|
|
||
| b.ComputeAndSetDigest() | ||
| return b | ||
| } | ||
|
|
||
| func (b *Block) Verify(ctx context.Context) (simplex.VerifiedBlock, error) { | ||
| return b, b.mempool.VerifyBlock(ctx, b) | ||
| } | ||
|
|
||
| func (b *Block) Blacklist() simplex.Blacklist { | ||
| return b.blacklist | ||
| } | ||
|
|
||
| func (b *Block) BlockHeader() simplex.BlockHeader { | ||
| return simplex.BlockHeader{ | ||
| ProtocolMetadata: b.metadata, | ||
| Digest: b.digest, | ||
| } | ||
| } | ||
|
|
||
| type encodedBlock struct { | ||
| ProtocolMetadata []byte | ||
| TXs []asn1TX | ||
| Blacklist []byte | ||
| } | ||
|
|
||
| func (b *Block) Bytes() ([]byte, error) { | ||
| mdBytes := b.metadata.Bytes() | ||
|
|
||
| var asn1TXs []asn1TX | ||
| for _, tx := range b.txs { | ||
| asn1TXs = append(asn1TXs, asn1TX{ID: tx.ID[:], ShouldFailVerification: tx.shouldFailVerification}) | ||
| } | ||
|
|
||
| blacklistBytes := b.blacklist.Bytes() | ||
|
|
||
| encodedB := encodedBlock{ | ||
| ProtocolMetadata: mdBytes, | ||
| TXs: asn1TXs, | ||
| Blacklist: blacklistBytes, | ||
| } | ||
|
|
||
| return asn1.Marshal(encodedB) | ||
| } | ||
|
|
||
| func (b *Block) containsTX(txID txID) bool { | ||
| for _, tx := range b.txs { | ||
| if tx.ID == txID { | ||
| return true | ||
| } | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| func (b *Block) ComputeAndSetDigest() { | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| tbBytes, err := b.Bytes() | ||
| if err != nil { | ||
| panic(fmt.Sprintf("failed to serialize test block: %v", err)) | ||
| } | ||
|
|
||
| b.digest = sha256.Sum256(tbBytes) | ||
| } | ||
|
|
||
| type BlockDeserializer struct { | ||
| mempool *Mempool | ||
| } | ||
|
|
||
| var _ simplex.BlockDeserializer = (*BlockDeserializer)(nil) | ||
|
|
||
| func (bd *BlockDeserializer) DeserializeBlock(ctx context.Context, buff []byte) (simplex.Block, error) { | ||
| var encodedBlock encodedBlock | ||
| _, err := asn1.Unmarshal(buff, &encodedBlock) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| md, err := simplex.ProtocolMetadataFromBytes(encodedBlock.ProtocolMetadata) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| var blacklist simplex.Blacklist | ||
| if err := blacklist.FromBytes(encodedBlock.Blacklist); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| txs := make([]*TX, len(encodedBlock.TXs)) | ||
| for i, asn1Tx := range encodedBlock.TXs { | ||
| tx := asn1Tx.toTX() | ||
| txs[i] = tx | ||
| } | ||
|
|
||
| b := &Block{ | ||
| metadata: *md, | ||
| txs: txs, | ||
| blacklist: blacklist, | ||
| mempool: bd.mempool, | ||
| } | ||
|
|
||
| b.ComputeAndSetDigest() | ||
|
|
||
| return b, nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
|
|
||
| package random_network | ||
samliok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import ( | ||
| "time" | ||
|
|
||
| "github.com/ava-labs/simplex" | ||
| ) | ||
|
|
||
| type FuzzConfig struct { | ||
| // The minimum and maximum number of nodes in the network. | ||
| MinNodes int // Default is 3. | ||
| MaxNodes int // Default is 10. | ||
|
|
||
| // The minimum and maximum number of transactions to be issued at a block. Default is between 5 and 20. | ||
| MinTxsPerIssue int | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the benefit of having a random number of transactions issued? Doesn't this make the test even more non-deterministic? Why not just use 1 (issue a single transaction) all the time?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i was hoping that we would occasionally issue more than a blocks worth of transactions at a time. this way we when we are waiting for txs to be accepted we need to wait multiple rounds. Potentially even more rounds than 2 if nodes are blacklisted. This way we hit more code paths and hopefully more edge cases. |
||
| MaxTxsPerIssue int | ||
|
|
||
| // Number of transactions per block. Default is 15. | ||
| TxsPerBlock int | ||
|
|
||
| // The number of blocks that must be finalized before ending the fuzz test. Default is 100. | ||
| NumFinalizedBlocks int | ||
|
|
||
| RandomSeed int64 | ||
|
|
||
| // Probability that a node will be randomly crashed. Default is .1 (10%). | ||
| NodeCrashProbability float64 | ||
|
|
||
| // Probability that a crashed node will be restarted. Default is .5 (50%). | ||
| NodeRecoverProbability float64 | ||
|
|
||
| // Amount to advance the time by. Default is simplex.DefaultMaxProposalWaitTime / 5. | ||
| AdvanceTimeTickAmount time.Duration | ||
|
|
||
| // Creates main.log for network logs and {nodeID-short}.log for each node. | ||
| // NodeID is represented as a 16-character hex string (first 8 bytes). | ||
| // Default directory is "tmp". | ||
| // If empty, logging to files is disabled and logs will only be printed to console. | ||
| LogDirectory string | ||
| } | ||
|
|
||
| func DefaultFuzzConfig() *FuzzConfig { | ||
| return &FuzzConfig{ | ||
| MinNodes: 3, | ||
| MaxNodes: 10, | ||
| MinTxsPerIssue: 5, | ||
| MaxTxsPerIssue: 20, | ||
| TxsPerBlock: 15, | ||
| NumFinalizedBlocks: 100, | ||
| RandomSeed: time.Now().UnixMilli(), | ||
| NodeCrashProbability: 0.1, | ||
| NodeRecoverProbability: 0.5, | ||
| AdvanceTimeTickAmount: simplex.DefaultMaxProposalWaitTime / 5, | ||
| LogDirectory: "tmp", | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.