---
# System prepended metadata

title: Consensus Interface

---

This is a proposed interface for a _Consensus_ module to be used in a simplified version of the Lotus Filecoin node software.

## Consensus Interface

```go=+
package consensus

// Instance represents a single instance of a consensus protocol.
type Instance interface {
    
    // Start starts the execution of this instance of consensus.
    // The function returns a channel on which the caller will receive a 
    // Result value once the execution of the instance finishes. The Result 
    // contains either the decision value or an error.
    Start() (<-chan Result)
}

// Factory creates consensus instances.
type Factory interface {
    
    // NewInstance returns a consensus instance.
    // The returned consensus instance is not started yet. The execution
    // starts only after the Start() method is called on the instance.
    NewInstance(        
        // instanceID is a unique ID identifying this instance of consensus.
        // For example, if used for blockchain consensus,
        // the instanceID might correspond to the block height
        // for which a block is being agreed upon.
        instanceID []byte,
        
        // membership describes all nodes that participate in the created
        // consensus instance. Each NodeDescriptor's ID field
        // must be the same as the key under which it is stored here.
        // At least one node in the membership must have a non-zero weight.
        membership map[NodeID]*NodeDescriptor,
        
        // propose is a function that returns the local node's
        // consensus proposal as a byte slice.
        // The consensus implementation may call this function any time.
        // If propose returns a non-nil error, the proposal may be undefined
        // and the consensus implementation may stop with a non-nil error.
        propose func() ([]byte, error),
        
        // validate is a function for validating a proposal
        // (potentially received from another node).
        // It returns nil if the given proposal is valid,
        // a non-nil error otherwise.
        validate func([]byte) error,

        // params are arbitrary additional parameters
        // to the created consensus instance represented as key-value pairs.
        // They may be instance- and implementation-specific,
        // e.g., timeouts, quorum sizes, etc...
        params map[string]interface{},
    ) Consensus
}
```

## Data Types

```go=+

type NodeID []byte

// NodeDescriptor describes a node's identity.
type NodeDescriptor struct {
    
    // ID is a unique identifier of the node throughout the whole system.
    ID NodeID
    
    // Addr is the network address of the node.
    Addr string
    
    // Key is the public key of the node.
    Key []byte
    
    // Weight represents a relative (compared to other nodes)
    // "voting" power of the node, such as in proof-of-stake.
    Weight uint64
}

// Result encapsulates the output of a consensus instance. It either
// contains a decision value or an error.
type Result struct {
    
    // Decision holds the decided value.
    // If the consensus instance terminates successfully,
    // Decision is guaranteed to be the same across all nodes.
    Decision []byte
    
    // Error is set to a non-nil value if the local execution of the
    // consensus instance encounters an error. This does not imply that the 
    // other nodes have not decided. It just means that the local instance 
    // did not terminate correctly. If this value is not nil, Decision is
    // undefined and must not be used.
    Error error
}
```

## Example Implementation (Granite)

```go=+

// First, we define our factory of Granite consensus instances.
type GraniteFactory struct {
    // Potentially other Granite-specific fields.
    // The following are just examples.
    
    // The ID of the local node.
    myID NodeID
    
    // Implementation of cryptographic primitives used by Granite.
    crypto Crypto
    
    // Implementatino of a broadcast communication primitive
    // that Granite nodes use to communicate.
    broadcast Broadcast    
}

// NewGraniteFactory creates a new instance of the Granite consensus factory.
// The given crypto and braodcast modules will be used
// by all created Granite consensus instances.
func NewGraniteFactory() *GraniteFactory(crypto Crypto, broadcast Broadcast) {
    return &GraniteFactory{crypto, broadcast}
}

// We now define the Granite consensus instance.
// It contains all the information a single instance
// of Granite consensus needs to have.
// The ones listed here are just examples.
type GraniteConsensus struct {
    instanceID []byte
    membership map[NodeID]*NodeDescriptor
    myID       NodeID    
    propose    func() ([]byte, error)
    validate   func([]byte) error
    timeout    time.Duration
    crypto     Crypto
    broadcast  Broadcast
}

func (gc *GraniteConsensus) Start() <-chan Result {
    // The implementation of Granite consensus, e.g.,
    proposal, err := gc.propose()
    if err != nil {
        //...
    }
    signedProposal := gc.Crypto.Sign(proposal)
    gc.broadcast.Broadcast(signedProposal)
    
    // ...implementation continues...
    
}

// Finally, this method combines the parameters of the GraniteFactory
// with the instance-specific ones (received as arguments)
// and returns a new instance of Granite consensus.
func (gf *GraniteFactory) NewInstance(
    instanceID []byte,
    membership map[NodeID]*NodeDescriptor,
    propose    func() ([]byte, error),
    validate   func([]byte) error,
    params     map[string]interface{},
) *GraniteConsensus {
    return &GraniteConsensus{
        instanceID,
        membership,
        gf.myID,
        propose,
        validate,
        params["timeout"],
        gf.crypto,
        gf.broadcast,
    }
}

```

## Example Usage

```go=+
factory := NewGraniteFactory(crypto, broadcast)

instance := factory.NewInstance(
    nextHeight,
    membership,
    myID,
    params
    propose,
    validate
)

resultPromise := instance.Start()

// ... potentially more code ...

// When the decision is needed, wait for the consensus to terminate.
result := <-resultPromise
if result.Error != nil {
    // Deal with error.
} else {
    // Process result.Decision.
}

```

## Notes

- The interface still does not cover proofs of consensus, i.e., 