blockchain

package
v0.0.0-...-68fb9df Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 16, 2021 License: GPL-3.0 Imports: 18 Imported by: 0

README

The blockchain

This is a simple blockchain.

Each block can have an arbitrary payload up to 2^16 bytes.

A SHA256 hash of the payload is stored with it as that is used in the header.

The ID of a block uses the ID of the previous block and the payload hash and the timestamp. So the ID is tied to the previous block and its payload.

The Proof of Work is simple HashCash style algorithm over the 2 IDs so can be calculated independently of the Block IDs.

The Genesis block has a previous ID set to all ZEROs.

We store the blocks in sqlite.

The network services should maintain speculative blocks and have a way to remove blocks from a chain to replace the chain with the new "longer" version.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrBlockMissing = errors.New("Block Not Found")

ErrBlockMissing should be returned from Storage.Get for missing blocks

Functions

func MaybeProgress

func MaybeProgress(n int) *maybeProgress

Types

type Block

type Block struct {
	Header  *BlockHeader
	Payload []byte
}

Block in the blockchain

type BlockHeader

type BlockHeader struct {
	ID           BlockID
	PrevID       BlockID
	EpochSeconds uint32
	PayloadHash  [sha256.Size]byte
	PayloadHint  uint8
	Proof        uint32
	Depth        uint64
}

BlockHeader is the detail of a block without the payload. We only ever keep the headers in memory. We read the payload on demand, or when validating/iterating the chain.

func (*BlockHeader) CalculateBlockID

func (bh *BlockHeader) CalculateBlockID() (id BlockID)

CalculateBlockID works out the hash given the current state of the header and puts it in the slice given. This can be `nil` and a new slice will be allocated and returned.

func (*BlockHeader) CalculateProofOfWork

func (bh *BlockHeader) CalculateProofOfWork(ctx context.Context, n int) (nonce uint32, found bool)

CalculateProofOfWork tries to calculate a proof of work with the current block if the context if cancelled it will return early. if it cannot it will return false as the bool

func (*BlockHeader) GetTime

func (bh *BlockHeader) GetTime() time.Time

func (*BlockHeader) IsGenesis

func (bh *BlockHeader) IsGenesis() bool

IsGenesis checks if this is the initial block

func (*BlockHeader) Validate

func (bh *BlockHeader) Validate(workLevel int) error

Validate a BlockHeader, assuming the PrevId and PayloadHash are good. This means you will need to validate the PayloadHash matches the payload seperately. It _will_ validate the proof of work though as that is not included in the hash

func (*BlockHeader) VerifyProofOfWork

func (bh *BlockHeader) VerifyProofOfWork(n int) bool

VerifyProofOfWork checks the current Nonce is correct

type BlockID

type BlockID [sha256.Size]byte
var ZeroId BlockID

Go zero's buffers on allocation

func (*BlockID) FromString

func (id *BlockID) FromString(s string) error

func (BlockID) String

func (id BlockID) String() string

type BlockValidator

type BlockValidator interface {
	Validate(*Block) error
	WorkLevel() int
}

BlockValidator is a "stateful" system, it should always be expecting the "next" block. But we should have a way to clone that state at a point in time (hopefully cheaply) so we can speculatively validate without commiting. That is what the Clone method is for

type Chain

type Chain interface {
	ID() BlockID                 // get the chain ID of this chain (the ID of the genesis block)
	Head() (*BlockHeader, error) // get the current head of the chain

	// GetHeader retrieves a block from the current chain
	// with the given hash
	Header(hash BlockID) (*BlockHeader, error)
	// GetPayload retrieves the payload for a block with the given hash
	Payload(hash BlockID) ([]byte, error)

	// given a certain depth, find the ID of the block at that depth, or not.
	AtDepth(d uint64) (BlockID, bool)

	// give a blockID, find the "next" block, or not if there isn't one
	Next(prev BlockID) (BlockID, bool)

	// Add a block to the chain
	// the block should be fully populated and implementations should validate that
	// This is now an "official" block and so we add to the current state by "validating" it.
	Add(*Block) error
	// mint a new block with work level w and timestamp ts, then add it to the chain
	Mint(b *Block, w int, ts uint32) error
}

Chain is actually a convenience for the concept of a chain. We keep a fixed number of blocks and speculate on blocks we recieve. The consensus mechanism says that the network may broadcast a new chain with a higher depth than ours. At this point we need to verify that the new chain is indeed valid and longer than ours.

func Create

func Create(dir string, genesisBlock *Block, initialWorkLevel int, validator BlockValidator) (Chain, error)

func Open

func Open(dir string, chainId BlockID, initialWorkLevel int, validator BlockValidator) (Chain, error)

type PayloadHashValidator

type PayloadHashValidator int

this validator does nothing but validate that the payload matches the hash. all other validators should do that. as well

func (PayloadHashValidator) Validate

func (p PayloadHashValidator) Validate(b *Block) error

stateless hash validation

func (PayloadHashValidator) WorkLevel

func (p PayloadHashValidator) WorkLevel() int

stateful work level

type SQLiteStorage

type SQLiteStorage struct {
	// contains filtered or unexported fields
}

SQLiteStorage is backed by SQLite

func NewSQLiteStorage

func NewSQLiteStorage(path string) (*SQLiteStorage, error)

NewSQLiteStorage does what is says on the tin

func (*SQLiteStorage) Head

func (s *SQLiteStorage) Head() (BlockID, error)

will return nil if the chain is empty.

func (*SQLiteStorage) Header

func (s *SQLiteStorage) Header(hash BlockID, blk *BlockHeader) (*BlockHeader, error)

GetHeader fetches a block by it's hash. find and populate the block, or error validate the hash over this single block

func (*SQLiteStorage) Payload

func (s *SQLiteStorage) Payload(hash BlockID, b []byte) ([]byte, error)

GetPayload fetches the block payload by the block id.

func (*SQLiteStorage) Write

func (s *SQLiteStorage) Write(blk *Block) error

type Storage

type Storage interface {
	Head() (BlockID, error)                                   // find the current HEAD
	Header(id BlockID, hd *BlockHeader) (*BlockHeader, error) // get Header for block into hd
	Payload(id BlockID, b []byte) ([]byte, error)             // get Payload for block into b
	Write(*Block) error                                       // write new block
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL