forked from TrueCloudLab/neoneo-go
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:
parent
22f5667530
commit
eb11e5fb11
8 changed files with 75 additions and 4 deletions
|
@ -46,7 +46,7 @@ type (
|
||||||
AddressVersion byte `yaml:"AddressVersion"`
|
AddressVersion byte `yaml:"AddressVersion"`
|
||||||
SecondsPerBlock int `yaml:"SecondsPerBlock"`
|
SecondsPerBlock int `yaml:"SecondsPerBlock"`
|
||||||
LowPriorityThreshold float64 `yaml:"LowPriorityThreshold"`
|
LowPriorityThreshold float64 `yaml:"LowPriorityThreshold"`
|
||||||
MaxTransactionsPerBlock int64 `yaml:"MaxTransactionsPerBlock"`
|
MaxTransactionsPerBlock int `yaml:"MaxTransactionsPerBlock"`
|
||||||
MemPoolSize int `yaml:"MemPoolSize"`
|
MemPoolSize int `yaml:"MemPoolSize"`
|
||||||
StandbyValidators []string `yaml:"StandbyValidators"`
|
StandbyValidators []string `yaml:"StandbyValidators"`
|
||||||
SeedList []string `yaml:"SeedList"`
|
SeedList []string `yaml:"SeedList"`
|
||||||
|
@ -59,6 +59,13 @@ type (
|
||||||
FreeGasLimit util.Fixed8 `yaml:"FreeGasLimit"`
|
FreeGasLimit util.Fixed8 `yaml:"FreeGasLimit"`
|
||||||
// SaveStorageBatch enables storage batch saving before every persist.
|
// SaveStorageBatch enables storage batch saving before every persist.
|
||||||
SaveStorageBatch bool `yaml:"SaveStorageBatch"`
|
SaveStorageBatch bool `yaml:"SaveStorageBatch"`
|
||||||
|
// Maximum number of low priority transactions accepted into block.
|
||||||
|
MaxFreeTransactionsPerBlock int `yaml:"MaxFreeTransactionsPerBlock"`
|
||||||
|
// Maximum size of low priority transaction in bytes.
|
||||||
|
MaxFreeTransactionSize int `yaml:"MaxFreeTransactionSize"`
|
||||||
|
// FeePerExtraByte sets the expected per-byte fee for
|
||||||
|
// transactions exceeding the MaxFreeTransactionSize.
|
||||||
|
FeePerExtraByte float64 `yaml:"FeePerExtraByte"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SystemFee fees related to system.
|
// SystemFee fees related to system.
|
||||||
|
|
|
@ -31,6 +31,10 @@ ProtocolConfiguration:
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
VerifyTransactions: false
|
VerifyTransactions: false
|
||||||
FreeGasLimit: 10.0
|
FreeGasLimit: 10.0
|
||||||
|
MaxTransactionsPerBlock: 500
|
||||||
|
MaxFreeTransactionsPerBlock: 20
|
||||||
|
MaxFreeTransactionSize: 1024
|
||||||
|
FeePerExtraByte: 0.00001
|
||||||
|
|
||||||
ApplicationConfiguration:
|
ApplicationConfiguration:
|
||||||
# LogPath could be set up in case you need stdout logs to some proper file.
|
# LogPath could be set up in case you need stdout logs to some proper file.
|
||||||
|
|
|
@ -31,6 +31,10 @@ ProtocolConfiguration:
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
VerifyTransactions: false
|
VerifyTransactions: false
|
||||||
FreeGasLimit: 10.0
|
FreeGasLimit: 10.0
|
||||||
|
MaxTransactionsPerBlock: 500
|
||||||
|
MaxFreeTransactionsPerBlock: 20
|
||||||
|
MaxFreeTransactionSize: 1024
|
||||||
|
FeePerExtraByte: 0.00001
|
||||||
|
|
||||||
ApplicationConfiguration:
|
ApplicationConfiguration:
|
||||||
# LogPath could be set up in case you need stdout logs to some proper file.
|
# LogPath could be set up in case you need stdout logs to some proper file.
|
||||||
|
|
|
@ -430,6 +430,10 @@ func (s *service) getVerifiedTx(count int) []block.Transaction {
|
||||||
txx = pool.GetVerifiedTransactions()
|
txx = pool.GetVerifiedTransactions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(txx) > 0 {
|
||||||
|
txx = s.Config.Chain.ApplyPolicyToTxSet(txx)
|
||||||
|
}
|
||||||
|
|
||||||
res := make([]block.Transaction, len(txx)+1)
|
res := make([]block.Transaction, len(txx)+1)
|
||||||
var netFee util.Fixed8
|
var netFee util.Fixed8
|
||||||
for i := range txx {
|
for i := range txx {
|
||||||
|
|
|
@ -49,6 +49,9 @@ var (
|
||||||
// ErrOOM is returned when adding transaction to the memory pool because
|
// ErrOOM is returned when adding transaction to the memory pool because
|
||||||
// it reached its full capacity.
|
// it reached its full capacity.
|
||||||
ErrOOM = errors.New("no space left in the memory pool")
|
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 (
|
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}
|
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
|
cfg.MemPoolSize = defaultMemPoolSize
|
||||||
log.Info("mempool size is not set or wrong, setting default value", zap.Int("MemPoolSize", cfg.MemPoolSize))
|
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{
|
bc := &Blockchain{
|
||||||
config: cfg,
|
config: cfg,
|
||||||
dao: newDao(s),
|
dao: newDao(s),
|
||||||
|
@ -1030,6 +1049,24 @@ func (bc *Blockchain) GetMemPool() *mempool.Pool {
|
||||||
return &bc.memPool
|
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.
|
// VerifyBlock verifies block against its current state.
|
||||||
func (bc *Blockchain) VerifyBlock(block *block.Block) error {
|
func (bc *Blockchain) VerifyBlock(block *block.Block) error {
|
||||||
prevHeader, err := bc.GetHeader(block.PrevHash)
|
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 {
|
if err := bc.verifyTx(t, nil); err != nil {
|
||||||
return err
|
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 {
|
if err := bc.memPool.Add(t, bc); err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case mempool.ErrOOM:
|
case mempool.ErrOOM:
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
// Blockchainer is an interface that abstract the implementation
|
// Blockchainer is an interface that abstract the implementation
|
||||||
// of the blockchain.
|
// of the blockchain.
|
||||||
type Blockchainer interface {
|
type Blockchainer interface {
|
||||||
|
ApplyPolicyToTxSet([]mempool.TxWithFee) []mempool.TxWithFee
|
||||||
GetConfig() config.ProtocolConfiguration
|
GetConfig() config.ProtocolConfiguration
|
||||||
AddHeaders(...*block.Header) error
|
AddHeaders(...*block.Header) error
|
||||||
AddBlock(*block.Block) error
|
AddBlock(*block.Block) error
|
||||||
|
|
|
@ -26,6 +26,9 @@ type testChain struct {
|
||||||
blockheight uint32
|
blockheight uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (chain testChain) ApplyPolicyToTxSet([]mempool.TxWithFee) []mempool.TxWithFee {
|
||||||
|
panic("TODO")
|
||||||
|
}
|
||||||
func (chain testChain) GetConfig() config.ProtocolConfiguration {
|
func (chain testChain) GetConfig() config.ProtocolConfiguration {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
|
@ -786,15 +786,14 @@ func (s *Server) verifyAndPoolTX(t *transaction.Transaction) RelayReason {
|
||||||
if t.Type == transaction.MinerType {
|
if t.Type == transaction.MinerType {
|
||||||
return RelayInvalid
|
return RelayInvalid
|
||||||
}
|
}
|
||||||
// TODO: Implement Plugin.CheckPolicy?
|
|
||||||
//if (!Plugin.CheckPolicy(transaction))
|
|
||||||
// return RelayResultReason.PolicyFail;
|
|
||||||
if err := s.chain.PoolTx(t); err != nil {
|
if err := s.chain.PoolTx(t); err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case core.ErrAlreadyExists:
|
case core.ErrAlreadyExists:
|
||||||
return RelayAlreadyExists
|
return RelayAlreadyExists
|
||||||
case core.ErrOOM:
|
case core.ErrOOM:
|
||||||
return RelayOutOfMemory
|
return RelayOutOfMemory
|
||||||
|
case core.ErrPolicy:
|
||||||
|
return RelayPolicyFail
|
||||||
default:
|
default:
|
||||||
return RelayInvalid
|
return RelayInvalid
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue