### Install Protobuf Plugins Source: https://github.com/relab/hotstuff/blob/main/README.md Commands to manually install the required gRPC and gorums plugins for protobuf compilation. ```bash go install github.com/relab/gorums/cmd/protoc-gen-gorums go install google.golang.org/protobuf/cmd/protoc-gen-go ``` -------------------------------- ### Implement HotStuff Client in Go Source: https://context7.com/relab/hotstuff/llms.txt Example of initializing a client, connecting to replicas, and registering latency measurement handlers. ```go package main import ( "context" "fmt" "io" "strings" "time" "github.com/relab/hotstuff" "github.com/relab/hotstuff/client" "github.com/relab/hotstuff/core/eventloop" "github.com/relab/hotstuff/core/logging" ) func main() { logger := logging.NewNopLogger() el := eventloop.New(logger, 100) // Client configuration config := client.Config{ TLS: false, MaxConcurrent: 100, // Max in-flight commands PayloadSize: 256, // Bytes per command Input: io.NopCloser(strings.NewReader("data")), // Command data source RateLimit: 1000.0, // Commands per second RateStep: 100.0, // Rate increase per interval RateStepInterval: 5 * time.Second, Timeout: 10 * time.Second, } // Create client c := client.New(el, logger, client.ID(1), config) // Connect to replicas replicas := []hotstuff.ReplicaInfo{ {ID: 1, Address: "localhost:8001"}, {ID: 2, Address: "localhost:8002"}, {ID: 3, Address: "localhost:8003"}, {ID: 4, Address: "localhost:8004"}, } err := c.Connect(replicas) if err != nil { fmt.Printf("Connection error: %v\n", err) return } // Run client (sends commands until context canceled or input exhausted) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // Register latency measurement handler eventloop.Register(el, func(event client.LatencyMeasurementEvent) { fmt.Printf("Command latency: %v\n", event.Latency) }) // Start client c.Run(ctx) } ``` -------------------------------- ### Implement Module Initialization Source: https://github.com/relab/hotstuff/blob/main/docs/modules.md Example of a module using InitModule to retrieve dependencies from the core. ```go type A1 struct{ b B } func (a *A1) InitModule(mods *modules.Core) { mods.Get(&a.b) } ``` -------------------------------- ### SSH Configuration Example Source: https://github.com/relab/hotstuff/blob/main/docs/experimentation.md Example of an SSH configuration file used by the CLI to establish connections to remote hosts. Ensure host entries match those used with the --hosts flag. ```text Host 127.0.0.1 localhost User root Port 2020 IdentityFile ./scripts/id UserKnownHostsFile ./scripts/known_hosts ``` -------------------------------- ### Run Experiments with HotStuff CLI Source: https://context7.com/relab/hotstuff/llms.txt Provides command-line examples for configuring consensus protocols, cryptographic implementations, and performance parameters. ```bash # Build the CLI make # Run a local experiment with 4 replicas and 1 client for 10 seconds ./hotstuff run --replicas=4 --clients=1 --duration=10s # Run with specific consensus protocol ./hotstuff run --consensus=chainedhotstuff --replicas=4 --duration=30s ./hotstuff run --consensus=fasthotstuff --replicas=4 --duration=30s ./hotstuff run --consensus=simplehotstuff --replicas=4 --duration=30s # Configure cryptographic implementation ./hotstuff run --crypto=ecdsa --replicas=4 ./hotstuff run --crypto=bls12 --replicas=7 # Leader rotation schemes ./hotstuff run --leader-rotation=round-robin --replicas=4 ./hotstuff run --leader-rotation=fixed --replicas=4 # Client configuration ./hotstuff run \ --clients=2 \ --payload-size=256 \ --max-concurrent=100 \ --rate-limit=1000 \ --rate-step=100 \ --rate-step-interval=5s # Replica timeouts and batching ./hotstuff run \ --batch-size=100 \ --view-timeout=1s \ --max-timeout=30s \ --timeout-multiplier=2.0 # Enable metrics collection ./hotstuff run \ --metrics=throughput,latency \ --output=./results \ --measurement-interval=1s ``` -------------------------------- ### Create and Verify Partial Certificates with Cert Authority Source: https://context7.com/relab/hotstuff/llms.txt Demonstrates setting up a certificate authority, creating a partial certificate for a block, and verifying its validity. Requires initial setup of replicas and cryptographic keys. ```go package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "fmt" "github.com/relab/hotstuff" "github.com/relab/hotstuff/core" "github.com/relab/hotstuff/core/eventloop" "github.com/relab/hotstuff/core/logging" "github.com/relab/hotstuff/security/blockchain" "github.com/relab/hotstuff/security/cert" "github.com/relab/hotstuff/security/crypto" ) func main() { logger := logging.NewNopLogger() el := eventloop.New(logger, 100) // Setup 4 replicas with keys keys := make([]*ecdsa.PrivateKey, 4) for i := range keys { keys[i], _ = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } config := core.NewRuntimeConfig(hotstuff.ID(1), keys[0]) for i, key := range keys { config.AddReplica(&hotstuff.ReplicaInfo{ ID: hotstuff.ID(i + 1), PubKey: &key.PublicKey, }) } bc := blockchain.New(el, logger, nil) ecdsaBase := crypto.NewECDSA(config) // Create certificate authority auth := cert.NewAuthority(config, bc, ecdsaBase) // Create a block and sign it (partial certificate) genesis := hotstuff.GetGenesis() block := hotstuff.NewBlock( genesis.Hash(), hotstuff.NewQuorumCert(nil, 0, genesis.Hash()), nil, hotstuff.View(1), hotstuff.ID(1), ) bc.Store(block) partialCert, err := auth.CreatePartialCert(block) if err != nil { panic(err) } fmt.Printf("Partial cert for block: %s\n", partialCert.BlockHash().SmallString()) fmt.Printf("Signed by replica: %d\n", partialCert.Signer()) // Verify partial certificate err = auth.VerifyPartialCert(partialCert) fmt.Printf("Partial cert valid: %v\n", err == nil) // Collect partial certs from quorum (3 of 4 replicas) // In practice, each replica creates and sends their partial cert partialCerts := []hotstuff.PartialCert{partialCert} // Create quorum certificate from partial certs (when quorum collected) // Note: In real usage, you'd collect from multiple replicas qc, err := auth.CreateQuorumCert(block, partialCerts) if err != nil { fmt.Printf("QC creation (expected to need more sigs): %v\n", err) } else { fmt.Printf("QC created for view %d\n", qc.View()) } } ``` -------------------------------- ### Remote Host Configuration Example Source: https://github.com/relab/hotstuff/blob/main/docs/experimentation.md TOML configuration snippet for specifying internal network addresses for remote hosts. This is used by replicas for communication when the controller uses a different address. ```toml [[hosts-config]] name = "hotstuff-worker-1" internal-address = "192.168.10.2" ``` ```toml [[hosts-config]] name = "hotstuff-worker-1" internal-address = "192.168.10.3" ``` -------------------------------- ### Naive Module Composition Implementation Source: https://github.com/relab/hotstuff/blob/main/docs/modules.md Demonstrates a basic conditional approach to selecting and initializing module implementations based on configuration. ```go func compose(choiceA string) { var ( a A b B ) b = NewB1() if choiceA == "A1" { a = NewA1(b) } else { a = NewA2() } } ``` -------------------------------- ### Manage Quorum Certificates and SyncInfo Source: https://context7.com/relab/hotstuff/llms.txt Demonstrates creating QuorumCert and TimeoutCert structures and bundling them into a SyncInfo object for view synchronization. ```go package main import ( "fmt" "github.com/relab/hotstuff" ) func main() { genesis := hotstuff.GetGenesis() // Create a QuorumCert (normally created from collected signatures) qc := hotstuff.NewQuorumCert( nil, // QuorumSignature (aggregated from partial certs) hotstuff.View(5), // View in which QC was created genesis.Hash(), // Hash of the certified block ) fmt.Printf("QC view: %d, block: %s\n", qc.View(), qc.BlockHash().SmallString()) // Create a TimeoutCert (created when view times out) tc := hotstuff.NewTimeoutCert( nil, // QuorumSignature from timeout messages hotstuff.View(6), // View that timed out ) fmt.Printf("TC view: %d\n", tc.View()) // SyncInfo carries highest known QC/TC for view synchronization syncInfo := hotstuff.NewSyncInfo() syncInfo.SetQC(qc) syncInfo.SetTC(tc) // Or create directly with a value syncInfo2 := hotstuff.NewSyncInfoWith(qc) syncInfo2.SetTC(tc) // Extract QC/TC from SyncInfo if highQC, ok := syncInfo.QC(); ok { fmt.Printf("SyncInfo HighQC view: %d\n", highQC.View()) } if highTC, ok := syncInfo.TC(); ok { fmt.Printf("SyncInfo HighTC view: %d\n", highTC.View()) } } ``` -------------------------------- ### Configure Leader Rotation Schemes Source: https://context7.com/relab/hotstuff/llms.txt Demonstrates how to initialize round-robin and fixed leader rotation strategies within a HotStuff runtime configuration. ```go package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "fmt" "github.com/relab/hotstuff" "github.com/relab/hotstuff/core" "github.com/relab/hotstuff/protocol/leaderrotation" ) func main() { // Create config with 4 replicas key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) config := core.NewRuntimeConfig(hotstuff.ID(1), key) for i := 1; i <= 4; i++ { k, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) config.AddReplica(&hotstuff.ReplicaInfo{ ID: hotstuff.ID(i), PubKey: &k.PublicKey, }) } // Round-Robin rotation rr := leaderrotation.NewRoundRobin(config) fmt.Println("Round-Robin Leaders:") for view := hotstuff.View(1); view <= 8; view++ { leader := rr.GetLeader(view) fmt.Printf(" View %d -> Leader %d\n", view, leader) } // Output: 1,2,3,4,1,2,3,4... // Fixed leader (useful for testing) fixed := leaderrotation.NewFixed(hotstuff.ID(1)) fmt.Println("\nFixed Leader:") for view := hotstuff.View(1); view <= 4; view++ { leader := fixed.GetLeader(view) fmt.Printf(" View %d -> Leader %d\n", view, leader) } // Output: always 1 // Helper function for round-robin calculation leader := leaderrotation.ChooseRoundRobin(hotstuff.View(7), 4) fmt.Printf("\nView 7 with 4 replicas: Leader %d\n", leader) } ``` -------------------------------- ### Manage Blockchain Storage and Blocks Source: https://context7.com/relab/hotstuff/llms.txt Illustrates how to initialize the blockchain, store new blocks, retrieve them by hash, check block extension, and prune old blocks. Genesis block is automatically included. ```go package main import ( "fmt" "github.com/relab/hotstuff" "github.com/relab/hotstuff/core/eventloop" "github.com/relab/hotstuff/core/logging" "github.com/relab/hotstuff/security/blockchain" ) func main() { logger := logging.NewNopLogger() el := eventloop.New(logger, 100) // Create blockchain (automatically includes genesis) bc := blockchain.New(el, logger, nil) // Genesis is always available genesis := hotstuff.GetGenesis() block, ok := bc.Get(genesis.Hash()) fmt.Printf("Genesis found: %v, view: %d\n", ok, block.View()) // Store new blocks qc := hotstuff.NewQuorumCert(nil, 0, genesis.Hash()) block1 := hotstuff.NewBlock(genesis.Hash(), qc, nil, hotstuff.View(1), hotstuff.ID(1)) bc.Store(block1) block2 := hotstuff.NewBlock(block1.Hash(), hotstuff.NewQuorumCert(nil, 1, block1.Hash()), nil, hotstuff.View(2), hotstuff.ID(2)) bc.Store(block2) // Retrieve blocks retrieved, ok := bc.Get(block1.Hash()) fmt.Printf("Block 1 found: %v, view: %d\n", ok, retrieved.View()) // LocalGet only checks local cache (no network fetch) local, ok := bc.LocalGet(block2.Hash()) fmt.Printf("Block 2 local: %v, view: %d\n", ok, local.View()) // Check if one block extends another extends := bc.Extends(block2, genesis) fmt.Printf("Block 2 extends genesis: %v\n", extends) // Prune old blocks (after commit) forkedBlocks := bc.PruneToHeight(block1.View(), block2.View()) fmt.Printf("Forked blocks pruned: %d\n", len(forkedBlocks)) fmt.Printf("Prune height: %d\n", bc.PruneHeight()) } ``` -------------------------------- ### Configure Replica Runtime Settings Source: https://context7.com/relab/hotstuff/llms.txt Initializes a replica's runtime configuration, including identity, cryptographic keys, and network peer information. ```go package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "fmt" "github.com/relab/hotstuff" "github.com/relab/hotstuff/core" ) func main() { // Generate a key pair for the replica privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // Create runtime config with options config := core.NewRuntimeConfig( hotstuff.ID(1), // Replica ID (must be > 0) privateKey, core.WithAggregateQC(false), // Enable for Fast-HotStuff core.WithCacheSize(100), // Crypto verification cache ) fmt.Printf("Replica ID: %d\n", config.ID()) fmt.Printf("Has AggregateQC: %v\n", config.HasAggregateQC()) fmt.Printf("Cache size: %d\n", config.CacheSize()) // Add other replicas to the configuration for i := hotstuff.ID(1); i <= 4; i++ { pk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) config.AddReplica(&hotstuff.ReplicaInfo{ ID: i, Address: fmt.Sprintf("localhost:%d", 8000+int(i)), PubKey: &pk.PublicKey, Location: "local", }) } fmt.Printf("Replica count: %d\n", config.ReplicaCount()) fmt.Printf("Quorum size: %d\n", config.QuorumSize()) // Get specific replica info if replica, ok := config.ReplicaInfo(hotstuff.ID(2)); ok { fmt.Printf("Replica 2 address: %s\n", replica.Address) } } ``` -------------------------------- ### HotStuff Block Creation and Management Source: https://context7.com/relab/hotstuff/llms.txt Illustrates how to create and manage blocks within the HotStuff framework. This includes obtaining the genesis block, creating command batches, and constructing new blocks with parent links, quorum certificates, and proposer information. ```go package main import ( "fmt" "github.com/relab/hotstuff" "github.com/relab/hotstuff/internal/proto/clientpb" ) func main() { // Get the genesis block (view 0, always exists) genesis := hotstuff.GetGenesis() fmt.Printf("Genesis hash: %s\n", genesis.Hash().SmallString()) // Create a command batch (client commands to be executed) batch := &clientpb.Batch{ Commands: []*clientpb.Command{ {ClientID: 1, SequenceNumber: 1, Data: []byte("transfer A->B")}, {ClientID: 2, SequenceNumber: 1, Data: []byte("transfer C->D")}, }, } // Create a new block linking to genesis parentHash := genesis.Hash() qc := hotstuff.NewQuorumCert(nil, 0, genesis.Hash()) // QC for genesis proposerID := hotstuff.ID(1) view := hotstuff.View(1) block := hotstuff.NewBlock(parentHash, qc, batch, view, proposerID) fmt.Printf("Block view: %d\n", block.View()) fmt.Printf("Block proposer: %d\n", block.Proposer()) fmt.Printf("Block parent: %s\n", block.Parent().SmallString()) fmt.Printf("Block hash: %s\n", block.Hash().SmallString()) fmt.Printf("Block commands: %d\n", len(block.Commands().GetCommands())) } ``` -------------------------------- ### Compose Modules with Registry Source: https://github.com/relab/hotstuff/blob/main/docs/modules.md Using the module registry to simplify composition by looking up modules by name. ```go func compose(choiceA string) { builder := modules.NewBuilder() builder.Add( NewB1(), modules.New(choiceA), ) mods := builder.Build() var ( a A b B ) mods.GetAll(&a, &b) } ``` -------------------------------- ### Generate Partition Scenarios (Go) Source: https://github.com/relab/hotstuff/blob/main/docs/twins.md Initiates the recursive generation of partition scenarios. Requires the total number of nodes and the desired number of partitions. ```go func partitionScenarios(n, k uint8) { state := make([]uint8, k) genPartitionScenarios(0, n, k, state) } ``` -------------------------------- ### Configure and Execute FastHotStuff Consensus Source: https://context7.com/relab/hotstuff/llms.txt Initializes the FastHotStuff protocol with AggregateQC enabled and demonstrates the usage of VoteRule and CommitRule. ```go package main import ( "fmt" "github.com/relab/hotstuff" "github.com/relab/hotstuff/core" "github.com/relab/hotstuff/core/logging" "github.com/relab/hotstuff/protocol/rules" "github.com/relab/hotstuff/security/blockchain" "github.com/relab/hotstuff/core/eventloop" ) func main() { logger := logging.NewNopLogger() el := eventloop.New(logger, 100) // FastHotStuff requires AggregateQC enabled config := core.NewRuntimeConfig( hotstuff.ID(1), nil, core.WithAggregateQC(true), // Required for FastHotStuff ) bc := blockchain.New(el, logger, nil) // Create FastHotStuff rules fhs := rules.NewFastHotStuff(logger, config, bc) fmt.Printf("Protocol: %s\n", rules.NameFastHotStuff) // "fasthotstuff" fmt.Printf("Chain length: %d blocks\n", fhs.ChainLength()) // 2 blocks (faster!) // FastHotStuff VoteRule handles both regular QC and AggregateQC proposals genesis := hotstuff.GetGenesis() qc := hotstuff.NewQuorumCert(nil, 0, genesis.Hash()) proposal := hotstuff.ProposeMsg{ ID: hotstuff.ID(1), Block: hotstuff.NewBlock(genesis.Hash(), qc, nil, hotstuff.View(1), hotstuff.ID(1)), AggregateQC: nil, // Would contain collected QCs from timeout } canVote := fhs.VoteRule(hotstuff.View(1), proposal) fmt.Printf("Can vote: %v\n", canVote) // Commit rule: commit grandparent with 2 consecutive blocks toCommit := fhs.CommitRule(proposal.Block) fmt.Printf("Block to commit: %v\n", toCommit) } ``` -------------------------------- ### Register Module Source: https://github.com/relab/hotstuff/blob/main/docs/modules.md Registering a module constructor with the global registry for dynamic instantiation. ```go func init() { modules.RegisterModule("A1", NewA1) } ``` -------------------------------- ### Recursive Partition Scenario Generation (Go) Source: https://github.com/relab/hotstuff/blob/main/docs/twins.md Recursively generates partition scenarios by assigning nodes to partitions. It ensures that partition sizes are non-increasing and handles the distribution of remaining nodes. ```go func genPartitionScenarios(i, n, k uint8, state []uint8) { // i is the index of the partition to modify // n is the total number of nodes that we can assign // make a copy of the state state = append([]uint8(nil), state...) // first, attempt to assign all available nodes to the partition at index i. state[i] = n if i == 0 || state[i-1] >= n { // found a valid partition fmt.Println(state); } // find the next valid size for partition at index i var m = n - 1; if i > 0 { m = min(m, state[i-1]) } // try all smaller sizes of partition at index i while handing the remaining nodes to the next partitions. if i + 1 < k { for ; m > 0; m-- { state[i] = m genPartitionScenarios(i+1, n-m, k, state) } } } ``` -------------------------------- ### Manage Protocol State with ViewStates Source: https://context7.com/relab/hotstuff/llms.txt Shows how to track views, update HighQC, and manage committed blocks using the ViewStates component. ```go package main import ( "fmt" "github.com/relab/hotstuff" "github.com/relab/hotstuff/core/eventloop" "github.com/relab/hotstuff/core/logging" "github.com/relab/hotstuff/protocol" "github.com/relab/hotstuff/security/blockchain" "github.com/relab/hotstuff/security/cert" "github.com/relab/hotstuff/security/crypto" "github.com/relab/hotstuff/core" ) func main() { logger := logging.NewNopLogger() el := eventloop.New(logger, 100) config := core.NewRuntimeConfig(hotstuff.ID(1), nil) bc := blockchain.New(el, logger, nil) // Create a minimal auth (normally with real crypto) auth := cert.NewAuthority(config, bc, &crypto.ECDSA{}) // Create view states states, err := protocol.NewViewStates(bc, auth) if err != nil { panic(err) } // Initial state fmt.Printf("Initial view: %d\n", states.View()) fmt.Printf("Initial committed block view: %d\n", states.CommittedBlock().View()) fmt.Printf("Initial HighQC view: %d\n", states.HighQC().View()) // Advance view newView := states.NextView() fmt.Printf("After NextView: %d\n", newView) fmt.Printf("Current view: %d\n", states.View()) // Update HighQC (normally after receiving votes) genesis := hotstuff.GetGenesis() block := hotstuff.NewBlock(genesis.Hash(), hotstuff.NewQuorumCert(nil, 0, genesis.Hash()), nil, hotstuff.View(1), hotstuff.ID(1)) bc.Store(block) newQC := hotstuff.NewQuorumCert(nil, block.View(), block.Hash()) updated, err := states.UpdateHighQC(newQC) fmt.Printf("HighQC updated: %v (err: %v)\n", updated, err) // Get SyncInfo for view synchronization messages syncInfo := states.SyncInfo() if qc, ok := syncInfo.QC(); ok { fmt.Printf("SyncInfo QC view: %d\n", qc.View()) } // Update committed block states.UpdateCommittedBlock(block) fmt.Printf("Committed block view: %d\n", states.CommittedBlock().View()) } ``` -------------------------------- ### Implement ECDSA Cryptography for Replicas Source: https://context7.com/relab/hotstuff/llms.txt Demonstrates key generation, message signing, verification, and signature combination using the ECDSA implementation. ```go package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "fmt" "github.com/relab/hotstuff" "github.com/relab/hotstuff/core" "github.com/relab/hotstuff/security/crypto" ) func main() { // Generate keys for 4 replicas keys := make([]*ecdsa.PrivateKey, 4) for i := range keys { keys[i], _ = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } // Create config with replica info config := core.NewRuntimeConfig(hotstuff.ID(1), keys[0]) for i, key := range keys { config.AddReplica(&hotstuff.ReplicaInfo{ ID: hotstuff.ID(i + 1), PubKey: &key.PublicKey, }) } // Create ECDSA crypto implementation ec := crypto.NewECDSA(config) // Sign a message message := []byte("block data to sign") sig, err := ec.Sign(message) if err != nil { panic(err) } fmt.Printf("Signature created by replica %d\n", config.ID()) fmt.Printf("Participants: %d\n", sig.Participants().Len()) // Verify the signature err = ec.Verify(sig, message) if err != nil { fmt.Printf("Verification failed: %v\n", err) } else { fmt.Println("Signature verified successfully") } // Combine multiple signatures (for quorum cert) // Each replica would sign and send their signature config2 := core.NewRuntimeConfig(hotstuff.ID(2), keys[1]) for i, key := range keys { config2.AddReplica(&hotstuff.ReplicaInfo{ ID: hotstuff.ID(i + 1), PubKey: &key.PublicKey, }) } ec2 := crypto.NewECDSA(config2) sig2, _ := ec2.Sign(message) // Combine signatures combined, err := ec.Combine(sig, sig2) if err != nil { panic(err) } fmt.Printf("Combined signature participants: %d\n", combined.Participants().Len()) // Verify combined signature err = ec.Verify(combined, message) fmt.Printf("Combined signature valid: %v\n", err == nil) } ``` -------------------------------- ### Experiment Configuration File Source: https://context7.com/relab/hotstuff/llms.txt TOML configuration for defining consensus settings, network topology, and host-specific parameters. ```toml # experiment.toml - Example configuration file # Basic settings replicas = 8 clients = 2 duration = "60s" log-level = "info" # Consensus settings consensus = "chainedhotstuff" crypto = "ecdsa" leader-rotation = "round-robin" # Client settings payload-size = 256 max-concurrent = 100 rate-limit = 5000.0 rate-step = 500.0 rate-step-interval = "10s" # Replica settings batch-size = 100 view-timeout = "1s" max-timeout = "30s" timeout-multiplier = 2.0 duration-samples = 10 # Metrics metrics = ["throughput", "latency"] output = "./experiment-results" measurement-interval = "1s" # Remote hosts (for distributed experiments) hosts = [ "hotstuff-worker-1", "hotstuff-worker-2", "hotstuff-worker-3", "hotstuff-worker-4", ] # Per-host configuration [[hosts-config]] name = "hotstuff-worker-1" internal-address = "192.168.10.2" clients = 2 replicas = 0 [[hosts-config]] name = "hotstuff-worker-2" internal-address = "192.168.10.3" replicas = 2 [[hosts-config]] name = "hotstuff-worker-3" internal-address = "192.168.10.4" replicas = 3 [[hosts-config]] name = "hotstuff-worker-4" internal-address = "192.168.10.5" replicas = 3 ``` -------------------------------- ### Run HotStuff Experiments via CLI Source: https://context7.com/relab/hotstuff/llms.txt Commands for profiling, remote execution, and plotting experimental results. ```bash ./hotstuff run \ --cpu-profile \ --mem-profile \ --trace \ --output=./profiles ``` ```bash ./hotstuff run \ --hosts=server1,server2,server3,server4 \ --ssh-config=~/.ssh/config \ --replicas=4 \ --clients=2 \ --duration=60s ``` ```bash ./hotstuff run --config=experiment.toml ``` ```bash ./plot --input=./results --output=./graphs --format=pdf ``` -------------------------------- ### Initialize ChainedHotStuff Implementation in Go Source: https://context7.com/relab/hotstuff/llms.txt Use the built-in ChainedHotStuff ruleset to handle three-phase pipelined consensus. Requires a logger, runtime configuration, and blockchain storage. ```go package main import ( "fmt" "github.com/relab/hotstuff" "github.com/relab/hotstuff/core" "github.com/relab/hotstuff/core/logging" "github.com/relab/hotstuff/protocol/rules" "github.com/relab/hotstuff/security/blockchain" "github.com/relab/hotstuff/core/eventloop" ) func main() { logger := logging.NewNopLogger() el := eventloop.New(logger, 100) // Create mock sender for blockchain config := createMockConfig() bc := blockchain.New(el, logger, nil) // sender would be set in real usage // Create ChainedHotStuff consensus rules hs := rules.NewChainedHotStuff(logger, config, bc) fmt.Printf("Protocol: %s\n", rules.NameChainedHotStuff) // "chainedhotstuff" fmt.Printf("Chain length: %d blocks\n", hs.ChainLength()) // 3 blocks // The commit rule checks for 3 consecutive blocks: // block -> parent -> grandparent -> great-grandparent (committed) genesis := hotstuff.GetGenesis() // VoteRule checks liveness (QC view > locked view) or safety (extends locked block) proposal := hotstuff.ProposeMsg{ ID: hotstuff.ID(1), Block: hotstuff.NewBlock( genesis.Hash(), hotstuff.NewQuorumCert(nil, 0, genesis.Hash()), nil, hotstuff.View(1), hotstuff.ID(1), ), } canVote := hs.VoteRule(hotstuff.View(1), proposal) fmt.Printf("Can vote for proposal: %v\n", canVote) // CommitRule returns the block to commit (or nil) toCommit := hs.CommitRule(proposal.Block) fmt.Printf("Block to commit: %v\n", toCommit) // nil initially } func createMockConfig() *core.RuntimeConfig { // Simplified config creation return core.NewRuntimeConfig(hotstuff.ID(1), nil) } ``` -------------------------------- ### Implement Asynchronous Event Loop Source: https://context7.com/relab/hotstuff/llms.txt Sets up an event loop to handle consensus events asynchronously using typed handlers and a thread-safe queue. ```go package main import ( "context" "fmt" "time" "github.com/relab/hotstuff" "github.com/relab/hotstuff/core/eventloop" "github.com/relab/hotstuff/core/logging" ) // Custom event types type ProposalReceived struct { View hotstuff.View Leader hotstuff.ID } func main() { logger := logging.NewNopLogger() // or logging.NewLogger("debug") // Create event loop with buffer size el := eventloop.New(logger, 100) // Register typed event handlers unregister := eventloop.Register(el, func(event ProposalReceived) { fmt.Printf("Received proposal for view %d from leader %d\n", event.View, event.Leader) }) defer unregister() // Unregister when done // Register handler for view changes eventloop.Register(el, func(event hotstuff.ViewChangeEvent) { fmt.Printf("View changed to %d (timeout: %v)\n", event.View, event.Timeout) }) // Register handler for commit events eventloop.Register(el, func(event hotstuff.CommitEvent) { fmt.Printf("Block committed: view %d, hash %s\n", event.Block.View(), event.Block.Hash().SmallString()) }) // Add events to the queue el.AddEvent(ProposalReceived{View: 1, Leader: 1}) el.AddEvent(hotstuff.ViewChangeEvent{View: 2, Timeout: false}) // Run event loop with timeout ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() go el.Run(ctx) <-ctx.Done() } ``` -------------------------------- ### Deferred Initialization for Mutually Dependent Modules Source: https://github.com/relab/hotstuff/blob/main/docs/modules.md Uses a two-step construction and initialization process to resolve circular dependencies between modules. ```go type A struct { b *B } func NewA() *A { return &A{} } func (a *A) Init(*b B) { a.b = b } type B struct { a *A } func NewB() *B { return &B{} } func (b *B) Init(*a A) { b.a = a } func main() { a, b := NewA(), NewB() a.Init(b) b.Init(a) } ``` -------------------------------- ### Compose Modules with Builder Source: https://github.com/relab/hotstuff/blob/main/docs/modules.md Using the module builder to assemble dependencies and resolve circular references. ```go func compose(choiceA string) { builder := modules.NewBuilder() builder.Add(NewB1()) if choiceA == "A1" { builder.Add(NewA1()) } else { builder.Add(NewA2()) } mods := builder.Build() var ( a A b B ) mods.GetAll(&a, &b) } ``` -------------------------------- ### Initialize Throughput Metric Module in Go Source: https://github.com/relab/hotstuff/blob/main/docs/experimentation.md Implement the modules.Module interface for a custom metric. Register handlers for relevant events like consensus.CommitEvent and observers for types.TickEvent to periodically log measurements. ```go // InitModule implements the modules.Module interface func (t *Throughput) InitModule(mods *modules.Core) { var ( eventLoop *eventloop.EventLoop logger logging.Logger ) mods.Get(&eventLoop, &logger) // register handlers/observers for relevant events eventLoop.RegisterHandler(consensus.CommitEvent{}, func(event interface{}) { // The event loop will only call the handler with events of the specified type, so this assertion is safe. commitEvent := event.(consensus.CommitEvent) t.recordCommit(commitEvent.Commands) }) // register observer for tick events eventLoop.RegisterObserver(types.TickEvent{}, func(event interface{}) { t.tick(event.(types.TickEvent)) }) logger.Info("Throughput metric enabled") } ``` -------------------------------- ### Manual Client and Replica Assignment Source: https://github.com/relab/hotstuff/blob/main/docs/experimentation.md TOML configuration for manually assigning the number of clients and replicas to specific hosts. This overrides the default even distribution. ```toml clients = 2 replicas = 8 hosts = [ "hotstuff-worker-1", "hotstuff-worker-2", "hotstuff-worker-3", "hotstuff-worker-4", ] # specific assignments for some hosts [[hosts-config]] name = "hotstuff-worker-1" clients = 2 replicas = 0 ``` -------------------------------- ### Implement Provider with Generics Source: https://github.com/relab/hotstuff/blob/main/docs/modules.md Helper struct to simplify Provider implementation using Go generics. ```go type Implements[T any] struct{} func (Implements[T]) ModuleType() any { return new(T) } ``` -------------------------------- ### Assign Single Pair of Twins (Pseudocode) Source: https://github.com/relab/hotstuff/blob/main/docs/twins.md Illustrates the nested loop structure to find all possible assignments for a single pair of twins across partitions. The actual implementation involves computing the Cartesian product for multiple twin pairs. ```pseudocode for i := 0; i < k; i++ { for j := i; j < k; k++ { fmt.Printf("(%d, %d)\n", i, j) } } ``` -------------------------------- ### HotStuff Core Types and Quorum Calculations Source: https://context7.com/relab/hotstuff/llms.txt Demonstrates the usage of replica IDs, view numbers, and quorum calculations for different cluster sizes in the HotStuff framework. Replica IDs are uint32, view numbers track rounds, and quorum size is calculated based on the number of replicas and faulty nodes. ```go package main import ( "fmt" "github.com/relab/hotstuff" ) func main() { // Replica IDs are uint32 values starting from 1 replicaID := hotstuff.ID(1) idBytes := replicaID.ToBytes() // Convert to []byte for hashing // View numbers track consensus rounds view := hotstuff.View(42) viewBytes := view.ToBytes() // 8-byte little-endian encoding // Calculate quorum requirements for different cluster sizes for n := 4; n <= 10; n++ { f := hotstuff.NumFaulty(n) // Maximum faulty replicas: (n-1)/3 q := hotstuff.QuorumSize(n) // Minimum quorum: ceil((n+f+1)/2) fmt.Printf("n=%d: f=%d, quorum=%d\n", n, f, q) } // Output: // n=4: f=1, quorum=3 // n=7: f=2, quorum=5 // n=10: f=3, quorum=7 } ``` -------------------------------- ### Define Module Interface Source: https://github.com/relab/hotstuff/blob/main/docs/modules.md The base interface for modules requiring deferred initialization via the module system. ```go type Module interface { InitModule(mods *modules.Core) } ``` -------------------------------- ### Register Replica Metric in Go Source: https://github.com/relab/hotstuff/blob/main/docs/experimentation.md Register a custom replica metric, such as 'throughput', in the metrics registry. This involves providing a name and a constructor function for the metric. Ensure the metric's package is imported by the CLI. ```go func init() { RegisterReplicaMetric("throughput", func() interface{} { return &Throughput{} }) } ``` -------------------------------- ### Define Provider Interface Source: https://github.com/relab/hotstuff/blob/main/docs/modules.md Interface for modules to explicitly declare the type they provide to the system. ```go type Provider interface { ModuleType() any } ``` -------------------------------- ### Implement Custom Consensus Ruleset in Go Source: https://context7.com/relab/hotstuff/llms.txt Define a custom ruleset by implementing the VoteRule, CommitRule, ProposeRule, and ChainLength methods. Ensure the struct satisfies the consensus.Ruleset interface. ```go package main import ( "fmt" "github.com/relab/hotstuff" "github.com/relab/hotstuff/internal/proto/clientpb" "github.com/relab/hotstuff/protocol/consensus" ) // CustomRuleset implements a simplified consensus protocol type CustomRuleset struct { lockedView hotstuff.View } // VoteRule decides whether to vote for a proposal func (r *CustomRuleset) VoteRule(view hotstuff.View, proposal hotstuff.ProposeMsg) bool { block := proposal.Block // Safety: only vote if QC view >= locked view (liveness condition) // or if block extends the locked block (safety condition) qcView := block.QuorumCert().View() return qcView >= r.lockedView } // CommitRule decides which ancestor block can be committed func (r *CustomRuleset) CommitRule(block *hotstuff.Block) *hotstuff.Block { // In chained HotStuff: commit great-grandparent when 3 consecutive blocks exist // This is a simplified example - return nil if no block ready to commit return nil } // ProposeRule creates a new proposal func (r *CustomRuleset) ProposeRule(view hotstuff.View, cert hotstuff.SyncInfo, cmd *clientpb.Batch) (hotstuff.ProposeMsg, bool) { qc, ok := cert.QC() if !ok { return hotstuff.ProposeMsg{}, false } proposerID := hotstuff.ID(1) // Would come from config return hotstuff.NewProposeMsg(proposerID, view, qc, cmd), true } // ChainLength returns blocks needed to commit (3 for chained HotStuff) func (r *CustomRuleset) ChainLength() int { return 3 } // Verify interface compliance var _ consensus.Ruleset = (*CustomRuleset)(nil) func main() { ruleset := &CustomRuleset{lockedView: 0} fmt.Printf("Chain length: %d\n", ruleset.ChainLength()) } ``` -------------------------------- ### Emit Commit Event in Go Source: https://github.com/relab/hotstuff/blob/main/docs/experimentation.md Emit a CommitEvent from the Executor module to track command counts for throughput metrics. This event should be added to the event loop when relevant. ```go eventLoop.AddEvent(consensus.CommitEvent{Commands: len(batch.GetCommands())}) ``` -------------------------------- ### Define Latency Measurement Protobuf Message Source: https://github.com/relab/hotstuff/blob/main/docs/experimentation.md Define a Protobuf message for latency measurements, including event details, latency, variance, and count. Ensure it includes an 'Event' field of type 'types.Event' for compatibility with the plotting package. ```protobuf message LatencyMeasurement { Event Event = 1; double Latency = 2; double Variance = 3; uint64 Count = 4; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.