Prysm's Golang Principles

Prysmatic Labs sticks to the Effective Go guidelines written on the official website of the Go project here. All code committed to master in Prysm goes through extensive lint tools that check correctness in formatting and potential security flaws from the code itself. We require all code to adhere to correct commentary conventions in order for any new package to have proper Godocs which can then be accessible to anyone publicly.

Good:

// ExecuteStateTransition transforms the current state of a beacon node by applying the
// transformations specified in the official Ethereum Serenity specification.
func ExecuteStateTransition(state *pb.BeaconState, block *pb.BeaconBlock) (*pb.BeaconState, error) {
...
}

Bad:

// This function executes a state transition.
func ExecuteStateTransition(state *pb.BeaconState, block *pb.BeaconBlock) (*pb.BeaconState, error) {
...
}

Ensure your naming is clear, concise, and makes no assumptions about your code without explaining the surrounding context with good commentary. Additionally, ensure all variables are namespaced properly and have a clear purpose to the reader of your code.

Good:

// justificationUpperLimit defines a ceiling after which a block cannot pass processing conditions
// as defined in the Ethereum Serenity specification.
justificationUpperLimit := params.BeaconConfig().SlotDuration*params.BeaconConfig().JustificationBoundary
If block.Slot() > justificationUpperLimit {
...
}

Bad (uses magic numbers with no explanation or context):

If block.Slot() > 12032*(23 + 45 % 15) {
}

With respect to error handling, always handle fatal situations gracefully through informative error messaging or logging. Error handling is critical in code paths where critical state mutations occur which affect data persisted to disk. In particular, not handling an error gracefully a few lines above where data is written to disk can cause the system to remain in an inconsistent state (one of the most critical types of failures).

For example:

func UpdateBeaconState(currentState *pb.BeaconState, blockCh chan<- *types.Block) {
For {
select {
Case block :=<-blockCh:
If err != processBlock(block); err != nil {
log.Errorf(“block failed processing conditions: %v”, err)
}
newState := executeStateTransition(currentState, block)
// BAD: This can lead to a critical, inconsistent state! The line below will save to DB
// DB even if block failed processing conditions!
db.SaveState(newState)
}
}
}

Instead, ensure to handle all errors in critical code paths appropriately. The error above can easily be fixed by a continue line added after the log.Errorf call to continue the loop and not store a bad state to disk.

func UpdateBeaconState(currentState *pb.BeaconState, blockCh chan<- *types.Block) {
For {
select {
Case block :=<-blockCh:
If err != processBlock(block); err != nil {
log.Errorf(“block failed processing conditions: %v”, err)
continue
}
newState := executeStateTransition(currentState, block)
// BAD: This can lead to a critical, inconsistent state! The line below will save to DB
// DB even if block failed processing conditions!
db.SaveState(newState)
}
}
}

For a recommended book on the Go language itself, see The Go Programming Language by Alan A. A. Donovan and Brian Kernighan available for purchase here on Amazon.