core: implement basic policying support, fix #370

Implement mempool and consensus block creation policies, almost the same as
SimplePolicy plugin for C# node provides with two caveats:
 * HighPriorityTxType is not configured and hardcoded to ClaimType
 * BlockedAccounts are not supported

Other than that it allows us to run successfuly as testnet CN, previously our
proposals were rejected because we were proposing blocks with oversized
transactions (that are rejected by PoolTx() now).

Mainnet and testnet configuration files are updated accordingly, but privnet
is left as is with no limits.

Configuration is currently attached to the Blockchain and so is the code that
does policying, it may be moved somewhere in the future, but it works for
now.
This commit is contained in:
Roman Khimov 2020-02-18 20:16:38 +03:00
parent 22f5667530
commit eb11e5fb11
8 changed files with 75 additions and 4 deletions

View file

@ -49,6 +49,9 @@ var (
// ErrOOM is returned when adding transaction to the memory pool because
// it reached its full capacity.
ErrOOM = errors.New("no space left in the memory pool")
// ErrPolicy is returned on attempt to add transaction that doesn't
// comply with node's configured policy into the mempool.
ErrPolicy = errors.New("not allowed by policy")
)
var (
genAmount = []int{8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
@ -125,6 +128,22 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L
cfg.MemPoolSize = defaultMemPoolSize
log.Info("mempool size is not set or wrong, setting default value", zap.Int("MemPoolSize", cfg.MemPoolSize))
}
if cfg.MaxTransactionsPerBlock <= 0 {
cfg.MaxTransactionsPerBlock = 0
log.Info("MaxTransactionsPerBlock is not set or wrong, setting default value (unlimited)", zap.Int("MaxTransactionsPerBlock", cfg.MaxTransactionsPerBlock))
}
if cfg.MaxFreeTransactionsPerBlock <= 0 {
cfg.MaxFreeTransactionsPerBlock = 0
log.Info("MaxFreeTransactionsPerBlock is not set or wrong, setting default value (unlimited)", zap.Int("MaxFreeTransactionsPerBlock", cfg.MaxFreeTransactionsPerBlock))
}
if cfg.MaxFreeTransactionSize <= 0 {
cfg.MaxFreeTransactionSize = 0
log.Info("MaxFreeTransactionSize is not set or wrong, setting default value (unlimited)", zap.Int("MaxFreeTransactionSize", cfg.MaxFreeTransactionSize))
}
if cfg.FeePerExtraByte <= 0 {
cfg.FeePerExtraByte = 0
log.Info("FeePerExtraByte is not set or wrong, setting default value", zap.Float64("FeePerExtraByte", cfg.FeePerExtraByte))
}
bc := &Blockchain{
config: cfg,
dao: newDao(s),
@ -1030,6 +1049,24 @@ func (bc *Blockchain) GetMemPool() *mempool.Pool {
return &bc.memPool
}
// ApplyPolicyToTxSet applies configured policies to given transaction set. It
// expects slice to be ordered by fee and returns a subslice of it.
func (bc *Blockchain) ApplyPolicyToTxSet(txes []mempool.TxWithFee) []mempool.TxWithFee {
if bc.config.MaxTransactionsPerBlock != 0 && len(txes) > bc.config.MaxTransactionsPerBlock {
txes = txes[:bc.config.MaxTransactionsPerBlock]
}
maxFree := bc.config.MaxFreeTransactionsPerBlock
if maxFree != 0 {
lowStart := sort.Search(len(txes), func(i int) bool {
return bc.IsLowPriority(txes[i].Fee)
})
if lowStart+maxFree < len(txes) {
txes = txes[:lowStart+maxFree]
}
}
return txes
}
// VerifyBlock verifies block against its current state.
func (bc *Blockchain) VerifyBlock(block *block.Block) error {
prevHeader, err := bc.GetHeader(block.PrevHash)
@ -1127,6 +1164,18 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
if err := bc.verifyTx(t, nil); err != nil {
return err
}
// Policying.
if t.Type != transaction.ClaimType {
txSize := io.GetVarSize(t)
maxFree := bc.config.MaxFreeTransactionSize
if maxFree != 0 && txSize > maxFree {
netFee := bc.NetworkFee(t)
if bc.IsLowPriority(netFee) ||
netFee < util.Fixed8FromFloat(bc.config.FeePerExtraByte)*util.Fixed8(txSize-maxFree) {
return ErrPolicy
}
}
}
if err := bc.memPool.Add(t, bc); err != nil {
switch err {
case mempool.ErrOOM:

View file

@ -15,6 +15,7 @@ import (
// Blockchainer is an interface that abstract the implementation
// of the blockchain.
type Blockchainer interface {
ApplyPolicyToTxSet([]mempool.TxWithFee) []mempool.TxWithFee
GetConfig() config.ProtocolConfiguration
AddHeaders(...*block.Header) error
AddBlock(*block.Block) error