micw/server/core/blocks_creator.go
2024-11-24 15:04:52 +01:00

88 lines
2.8 KiB
Go

package core
import (
"context"
"errors"
"github.com/matchsystems/werr"
"mic-wallet/common"
"time"
)
var ErrOutOfDateBlockId = errors.New("")
var ErrBlockIdDoesntExist = errors.New("")
type IBlocksCreator interface {
Run(ctx context.Context) error
WriteTx(opts *common.NewTxOpts) error
// GetTx Searches for tx in every block
GetTx(block int64, hash string) *common.NewTxOpts
GetTxs() []*common.NewTxOpts
}
// BlocksCreator creates and rotates new Blocks and pushed them to the commiter
type BlocksCreator struct {
blocks map[int64]*Block
txsLeftOver map[string]*common.NewTxOpts
commiter IBlocksCommiter
prevBlockHash []byte
}
func NewBlocksCreator(commiter IBlocksCommiter) *BlocksCreator {
return &BlocksCreator{
blocks: make(map[int64]*Block),
txsLeftOver: make(map[string]*common.NewTxOpts),
commiter: commiter,
}
}
func (bc *BlocksCreator) Run(ctx context.Context) (err error) {
var blockID int64
var prevLeftOver map[string]*common.NewTxOpts
for {
blockID = common.GetBlockId(time.Now())
if oldBlock, ok := bc.blocks[blockID-common.BlockSecsDiff]; ok { // Commit and delete old block
// we need to know previous hash for new block.
// new transactions while oldBlock.Finish is executing will
// end up in leftOver so they will be parts of the next block
if bc.prevBlockHash, err = oldBlock.Finish(); err != nil {
return nil
}
go bc.commiter.CommitBlock(ctx, oldBlock) // may take a while
delete(bc.blocks, blockID-common.BlockSecsDiff)
}
// prevLeftOver becomes new block's transactions
// so there is no "lost" transactions
prevLeftOver = bc.txsLeftOver
// and also now we need to make new leftOver
bc.txsLeftOver = make(map[string]*common.NewTxOpts)
bc.blocks[blockID] = NewBlock(blockID, bc.prevBlockHash, prevLeftOver, bc.txsLeftOver)
// Waiting for the next block
time.Sleep(time.Until(time.Unix(blockID+common.BlockSecsDiff, 0)))
}
}
// WriteTx validates transaction and writes it into the current block
// if transaction is valid
func (bc *BlocksCreator) WriteTx(opts *common.NewTxOpts) error {
// validating may take some time, so to don't write into
if err := opts.Validate(); err != nil {
return werr.Wrapf(err,
"error validating transaction %s", opts.HashString())
}
currentBlockID := common.GetBlockId(time.Now())
if currentBlockID < opts.BlockId {
return werr.Wrapf(common.ErrEntityNotFound, "block id %d does not exist", currentBlockID)
} else if currentBlockID > opts.BlockId {
return werr.Wrapf(common.ErrEntityOutdated, "block id %d is out of date", currentBlockID)
}
block, ok := bc.blocks[currentBlockID]
if !ok {
return werr.Wrapf(common.ErrEntityNotFound, "block id %d does not exist", currentBlockID)
}
if err := block.AddTx(opts); err != nil {
return werr.Wrapf(err,
"error adding transaction to block %d", currentBlockID)
}
return nil
}