I have always struggled keeping go modules neatly separated and avoiding cyclic dependencies.
Right now I have code like this:
package chain
type Block struct{
Content []byte
Number int
}
var Chain []Block = make([]Block, 10)
func AddBlockToChain(block Block){
// do some checks
//...
// add to chain
Chain[block.Number] = block
}
func GetBlock(number int) Block {
// do some checks
// ...
// get from chain
return Chain[number]
}
To achieve consensus I need some extra info on the block. The info may be different depending on which consensus algorithm I use. I want the consensus algorithm to be interchangeable (e.g. by flag on start up).
It would be convenient to store the info as fields in the block, so that when I call GetBlock(3) or get a block from anywhere else I can not only access the block but also the info on what state of consensus this block is in at the moment.
On the other hand I wanted to keep data that is only relevant for a certain consensus algorithm separate from basic block data - maybe even in a separate package.
How should I design my program? Where should I keep data relevant for a certain consensus algorithm?
Some background info:
I am trying to build a block chain application from scratch. I have a "chain" module responsible for storing blocks of data and a "consensus" module responsible for creating consensus between multiple peers running an instance of my program on what that data is.
consensus.Block
and chain.Block
are two separate types but since you are embeddding chain.Block
in consensus.Block
, you can access one from another.
Just pass consensus.Block
in AddBlockToChain()
:
`func AddBlockToChain(block consensus.Block)`
and then access it by: block.Block
Example:
package main
import "fmt"
type ChainBlock struct {
id int
}
type ConsensusBlock struct {
ChainBlock
idCons int
}
func printChainBlockId(consBlock ConsensusBlock) {
fmt.Println(consBlock.ChainBlock.id)
}
func main() {
test := ConsensusBlock{
ChainBlock: ChainBlock{
id: 42,
},
idCons: 44,
}
printChainBlockId(admin)
}
For completeness I want to add a solution that keeps the code and data of the consensus algorithm in a different package than the block and chain code:
Add an extra field to the block like this:
Extra []byte
Then keep the extra data needed for a consensus algorithm in that algorithms package in a struct and encode it to json or some other format to store it in the extra field.
This solution keeps separate concerns separate. The chain would not make sense without a consensus algorithm, but they can look very different and might need different data. This way different algorithms can store different data.
I did not go with this solution however, because it just seemed simpler to keep all the code in one package as @syntagma suggested.