This is a step-by-step guide on how to get started with your own raftified Cosmos validator. The main purpose of this guide is to point out the code changes needed to run Raftify and where to make them.
-
Pick a Cosmos SDK version of your choice.
-
Check out the
go.modfile in the Cosmos SDK to find out which Tendermint version your chosen Cosmos SDK version uses. For example:github.com/tendermint/tendermint v0.33.7
Raftify needs to hook into Tendermint to directly intercept the signing process such that inside of the raftify cluster only one single node is allowed to talk to the rest of the blockchain network at all times. This ultimately serves as double-signing prevention.
-
Go to Tendermint GitHub.
-
Switch to your desired branch/tag and clone it.
-
Open the cloned repository in a code editor
-
Open the
go.modfile in the repository's root directory and add Raftify as a dependency:require ( github.com/BlockscapeLab/raftify v0.2.0 )
-
Navigate to
privval/file.goand add the following code directly under the imports:var rpv *raftify.Node func Shutdown() { rpv.Shutdown() }
-
Modify the
LoadOrGenFilePVfunction as follows:func LoadOrGenFilePV(keyFilePath, stateFilePath string) *FilePV { // Add this... homeDir := filepath.Dir(keyFilePath) logger := log.New(os.Stderr, "", 0) var err error if rpv, err = raftify.InitNode(logger, homeDir); err != nil { logger.Printf("[ERR] raftify: %v\n", err.Error()) } // ...until here var pv *FilePV if tmos.FileExists(keyFilePath) { pv = LoadFilePV(keyFilePath, stateFilePath) } else { pv = GenFilePV(keyFilePath, stateFilePath) pv.Save() } return pv }
-
Modify the
SignVotefunction as follows:func (pv *FilePV) SignVote(chainID string, vote *tmproto.Vote) error { // Add this... if rpv.GetState() != raftify.Leader { return fmt.Errorf("%v is not a leader", rpv.GetID()) } // ...until here if err := pv.signVote(chainID, vote); err != nil { return fmt.Errorf("error signing vote: %v", err) } return nil }
-
Modify the
SignProposalfunction as follows:func (pv *FilePV) SignProposal(chainID string, proposal *tmproto.Proposal) error { // Add this... if rpv.GetState() != raftify.Leader { return fmt.Errorf("%v is not a leader", rpv.GetID()) } // ...until here if err := pv.signProposal(chainID, proposal); err != nil { return fmt.Errorf("error signing proposal: %v", err) } return nil }
-
Save all changes and proceed with the next step.
The Cosmos SDK only needs to make use of the modified Tendermint privval implementation we created in step 1.
-
Go to Cosmos SDK Github
-
Switch to your desired branch/tag and clone it
-
Open the cloned repository in a code editor and navigate to
server/start.go -
Scroll down to the anonymous function
TrapSignaland modify the code as follows:if tmNode.IsRunning() { _ = tmNode.Stop() pvm.Shutdown() // Add this }
-
In the repository's root directory, open the
go.modfile and replace Tendermint's privval implementation with your own local one. To do this, add...replace "github.com/tendermint/tendermint/privval" => "/<your-local-path-to>/tendermint/privval"
to the very bottom of the file.
-
Build the Cosmos SDK.
Finally, go to ~/.gaiad/config/ and create a raftify.json config file. Use the README for the configuration reference.