88 lines
2.8 KiB
Go
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
|
|
}
|