mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-14 00:59:33 +00:00
[DEV-311] Enforce gas limit in blocks (#172)
* [DEV-311] Moved subnetwork storage from directly in DAG to subnetworkStore * Added gas validation in CheckBlockSanity * [DEV-311] Add SubnetworkStore to last remaining call for CheckBlockSanity * [DEV-311] Added subnetworkID to config in TestcheckBlockSanity * [DEV-311] Moved CheckBlockSanity to be method of BlockDAG, and removed subnetworkStore argument * [DEV-311] Removed SubnetworkID as parameter to CheckBlockSanity * [DEV-311] Update gas usage before * [DEV-311] some chain=>DAG updates in comments * [DEV-311] Removed remaining dag-specific parameters from checkBlockSanity
This commit is contained in:
parent
6960e2469a
commit
9f93a1c50b
@ -147,6 +147,8 @@ type BlockDAG struct {
|
||||
notifications []NotificationCallback
|
||||
|
||||
lastFinalityPoint *blockNode
|
||||
|
||||
SubnetworkStore *subnetworkStore
|
||||
}
|
||||
|
||||
// HaveBlock returns whether or not the DAG instance has the block represented
|
||||
@ -1482,6 +1484,7 @@ func New(config *Config) (*BlockDAG, error) {
|
||||
warningCaches: newThresholdCaches(vbNumBits),
|
||||
deploymentCaches: newThresholdCaches(dagconfig.DefinedDeployments),
|
||||
blockCount: 1,
|
||||
SubnetworkStore: newSubnetworkStore(config.DB),
|
||||
subnetworkID: config.SubnetworkID,
|
||||
}
|
||||
|
||||
|
@ -164,7 +164,7 @@ func (dag *BlockDAG) ProcessBlock(block *util.Block, flags BehaviorFlags) (bool,
|
||||
}
|
||||
|
||||
// Perform preliminary sanity checks on the block and its transactions.
|
||||
err = checkBlockSanity(block, dag.dagParams.PowLimit, dag.timeSource, dag.subnetworkID, flags)
|
||||
err = dag.checkBlockSanity(block, flags)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/daglabs/btcd/util"
|
||||
|
||||
"github.com/daglabs/btcd/database"
|
||||
@ -11,6 +12,16 @@ import (
|
||||
"github.com/daglabs/btcd/wire"
|
||||
)
|
||||
|
||||
type subnetworkStore struct {
|
||||
db database.DB
|
||||
}
|
||||
|
||||
func newSubnetworkStore(db database.DB) *subnetworkStore {
|
||||
return &subnetworkStore{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// registerSubnetworks scans a list of accepted transactions, singles out
|
||||
// subnetwork registry transactions, validates them, and registers a new
|
||||
// subnetwork based on it.
|
||||
@ -77,10 +88,10 @@ func txToSubnetworkID(tx *wire.MsgTx) (*subnetworkid.SubnetworkID, error) {
|
||||
|
||||
// subnetwork returns a registered subnetwork. If the subnetwork does not exist
|
||||
// this method returns an error.
|
||||
func (dag *BlockDAG) subnetwork(subnetworkID *subnetworkid.SubnetworkID) (*subnetwork, error) {
|
||||
func (s *subnetworkStore) subnetwork(subnetworkID *subnetworkid.SubnetworkID) (*subnetwork, error) {
|
||||
var sNet *subnetwork
|
||||
var err error
|
||||
dbErr := dag.db.View(func(dbTx database.Tx) error {
|
||||
dbErr := s.db.View(func(dbTx database.Tx) error {
|
||||
sNet, err = dbGetSubnetwork(dbTx, subnetworkID)
|
||||
return nil
|
||||
})
|
||||
@ -96,8 +107,8 @@ func (dag *BlockDAG) subnetwork(subnetworkID *subnetworkid.SubnetworkID) (*subne
|
||||
|
||||
// GasLimit returns the gas limit of a registered subnetwork. If the subnetwork does not
|
||||
// exist this method returns an error.
|
||||
func (dag *BlockDAG) GasLimit(subnetworkID *subnetworkid.SubnetworkID) (uint64, error) {
|
||||
sNet, err := dag.subnetwork(subnetworkID)
|
||||
func (s *subnetworkStore) GasLimit(subnetworkID *subnetworkid.SubnetworkID) (uint64, error) {
|
||||
sNet, err := s.subnetwork(subnetworkID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ func TestSubnetworkRegistry(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("could not register network: %s", err)
|
||||
}
|
||||
limit, err := dag.GasLimit(subnetworkID)
|
||||
limit, err := dag.SubnetworkStore.GasLimit(subnetworkID)
|
||||
if err != nil {
|
||||
t.Fatalf("could not retrieve gas limit: %s", err)
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID
|
||||
// The flags modify the behavior of this function as follows:
|
||||
// - BFNoPoWCheck: The check to ensure the block hash is less than the target
|
||||
// difficulty is not performed.
|
||||
func checkProofOfWork(header *wire.BlockHeader, powLimit *big.Int, flags BehaviorFlags) error {
|
||||
func (dag *BlockDAG) checkProofOfWork(header *wire.BlockHeader, flags BehaviorFlags) error {
|
||||
// The target difficulty must be larger than zero.
|
||||
target := CompactToBig(header.Bits)
|
||||
if target.Sign() <= 0 {
|
||||
@ -293,9 +293,9 @@ func checkProofOfWork(header *wire.BlockHeader, powLimit *big.Int, flags Behavio
|
||||
}
|
||||
|
||||
// The target difficulty must be less than the maximum allowed.
|
||||
if target.Cmp(powLimit) > 0 {
|
||||
if target.Cmp(dag.dagParams.PowLimit) > 0 {
|
||||
str := fmt.Sprintf("block target difficulty of %064x is "+
|
||||
"higher than max of %064x", target, powLimit)
|
||||
"higher than max of %064x", target, dag.dagParams.PowLimit)
|
||||
return ruleError(ErrUnexpectedDifficulty, str)
|
||||
}
|
||||
|
||||
@ -399,11 +399,11 @@ func CountP2SHSigOps(tx *util.Tx, isCoinBaseTx bool, utxoSet UTXOSet) (int, erro
|
||||
//
|
||||
// The flags do not modify the behavior of this function directly, however they
|
||||
// are needed to pass along to checkProofOfWork.
|
||||
func checkBlockHeaderSanity(header *wire.BlockHeader, powLimit *big.Int, timeSource MedianTimeSource, flags BehaviorFlags) error {
|
||||
func (dag *BlockDAG) checkBlockHeaderSanity(header *wire.BlockHeader, flags BehaviorFlags) error {
|
||||
// Ensure the proof of work bits in the block header is in min/max range
|
||||
// and the block hash is less than the target value described by the
|
||||
// bits.
|
||||
err := checkProofOfWork(header, powLimit, flags)
|
||||
err := dag.checkProofOfWork(header, flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -425,7 +425,7 @@ func checkBlockHeaderSanity(header *wire.BlockHeader, powLimit *big.Int, timeSou
|
||||
}
|
||||
|
||||
// Ensure the block time is not too far in the future.
|
||||
maxTimestamp := timeSource.AdjustedTime().Add(time.Second *
|
||||
maxTimestamp := dag.timeSource.AdjustedTime().Add(time.Second *
|
||||
MaxTimeOffsetSeconds)
|
||||
if header.Timestamp.After(maxTimestamp) {
|
||||
str := fmt.Sprintf("block timestamp of %v is too far in the "+
|
||||
@ -456,11 +456,11 @@ func checkBlockParentsOrder(header *wire.BlockHeader) error {
|
||||
//
|
||||
// The flags do not modify the behavior of this function directly, however they
|
||||
// are needed to pass along to checkBlockHeaderSanity.
|
||||
func checkBlockSanity(block *util.Block, powLimit *big.Int, timeSource MedianTimeSource,
|
||||
subnetworkID *subnetworkid.SubnetworkID, flags BehaviorFlags) error {
|
||||
func (dag *BlockDAG) checkBlockSanity(block *util.Block, flags BehaviorFlags) error {
|
||||
|
||||
msgBlock := block.MsgBlock()
|
||||
header := &msgBlock.Header
|
||||
err := checkBlockHeaderSanity(header, powLimit, timeSource, flags)
|
||||
err := dag.checkBlockHeaderSanity(header, flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -512,7 +512,7 @@ func checkBlockSanity(block *util.Block, powLimit *big.Int, timeSource MedianTim
|
||||
// Do some preliminary checks on each transaction to ensure they are
|
||||
// sane before continuing.
|
||||
for _, tx := range transactions {
|
||||
err := CheckTransactionSanity(tx, subnetworkID)
|
||||
err := CheckTransactionSanity(tx, dag.subnetworkID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -572,13 +572,41 @@ func checkBlockSanity(block *util.Block, powLimit *big.Int, timeSource MedianTim
|
||||
}
|
||||
}
|
||||
|
||||
// Amount of gas consumed per sub-network shouldn't be more than the subnetwork's limit
|
||||
gasUsageInAllSubnetworks := map[subnetworkid.SubnetworkID]uint64{}
|
||||
for _, tx := range transactions {
|
||||
msgTx := tx.MsgTx()
|
||||
// In DAGCoin and Registry sub-networks all txs must have Gas = 0, and that is validated in checkTransactionSanity
|
||||
// Therefore - no need to check them here.
|
||||
if msgTx.SubnetworkID != wire.SubnetworkIDNative && msgTx.SubnetworkID != wire.SubnetworkIDRegistry {
|
||||
gasUsageInSubnetwork := gasUsageInAllSubnetworks[msgTx.SubnetworkID]
|
||||
gasUsageInSubnetwork += msgTx.Gas
|
||||
if gasUsageInSubnetwork < gasUsageInAllSubnetworks[msgTx.SubnetworkID] { // protect from overflows
|
||||
str := fmt.Sprintf("Block gas usage in subnetwork with ID %s has overflown", msgTx.SubnetworkID)
|
||||
return ruleError(ErrInvalidGas, str)
|
||||
}
|
||||
gasUsageInAllSubnetworks[msgTx.SubnetworkID] = gasUsageInSubnetwork
|
||||
|
||||
gasLimit, err := dag.SubnetworkStore.GasLimit(&msgTx.SubnetworkID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if gasUsageInSubnetwork > gasLimit {
|
||||
str := fmt.Sprintf("Block wastes too much gas in subnetwork with ID %s", msgTx.SubnetworkID)
|
||||
return ruleError(ErrInvalidGas, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckBlockSanity performs some preliminary checks on a block to ensure it is
|
||||
// sane before continuing with block processing. These checks are context free.
|
||||
func CheckBlockSanity(block *util.Block, powLimit *big.Int, timeSource MedianTimeSource, subnetworkID *subnetworkid.SubnetworkID) error {
|
||||
return checkBlockSanity(block, powLimit, timeSource, subnetworkID, BFNone)
|
||||
func (dag *BlockDAG) CheckBlockSanity(block *util.Block, powLimit *big.Int,
|
||||
timeSource MedianTimeSource) error {
|
||||
|
||||
return dag.checkBlockSanity(block, BFNone)
|
||||
}
|
||||
|
||||
// ExtractCoinbaseHeight attempts to extract the height of the block from the
|
||||
@ -1106,7 +1134,7 @@ func (dag *BlockDAG) CheckConnectBlockTemplate(block *util.Block) error {
|
||||
return ruleError(ErrParentBlockNotCurrentTips, str)
|
||||
}
|
||||
|
||||
err := checkBlockSanity(block, dag.dagParams.PowLimit, dag.timeSource, dag.subnetworkID, flags)
|
||||
err := dag.checkBlockSanity(block, flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ func TestCheckConnectBlockTemplate(t *testing.T) {
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to setup chain instance: %v", err)
|
||||
t.Errorf("Failed to setup dag instance: %v", err)
|
||||
return
|
||||
}
|
||||
defer teardownFunc()
|
||||
@ -152,19 +152,30 @@ func TestCheckConnectBlockTemplate(t *testing.T) {
|
||||
// TestCheckBlockSanity tests the CheckBlockSanity function to ensure it works
|
||||
// as expected.
|
||||
func TestCheckBlockSanity(t *testing.T) {
|
||||
// Create a new database and dag instance to run tests against.
|
||||
dag, teardownFunc, err := DAGSetup("TestCheckBlockSanity", Config{
|
||||
DAGParams: &dagconfig.SimNetParams,
|
||||
SubnetworkID: &wire.SubnetworkIDSupportsAll,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to setup dag instance: %v", err)
|
||||
return
|
||||
}
|
||||
defer teardownFunc()
|
||||
|
||||
powLimit := dagconfig.MainNetParams.PowLimit
|
||||
block := util.NewBlock(&Block100000)
|
||||
timeSource := NewMedianTime()
|
||||
if len(block.Transactions()) < 3 {
|
||||
t.Fatalf("Too few transactions in block, expect at least 3, got %v", len(block.Transactions()))
|
||||
}
|
||||
err := CheckBlockSanity(block, powLimit, timeSource, &wire.SubnetworkIDNative)
|
||||
err = dag.CheckBlockSanity(block, powLimit, timeSource)
|
||||
if err != nil {
|
||||
t.Errorf("CheckBlockSanity: %v", err)
|
||||
}
|
||||
// Test with block with wrong transactions sorting order
|
||||
blockWithWrongTxOrder := util.NewBlock(&BlockWithWrongTxOrder)
|
||||
err = CheckBlockSanity(blockWithWrongTxOrder, powLimit, timeSource, &wire.SubnetworkIDNative)
|
||||
err = dag.CheckBlockSanity(blockWithWrongTxOrder, powLimit, timeSource)
|
||||
if err == nil {
|
||||
t.Errorf("CheckBlockSanity: transactions disorder is not detected")
|
||||
}
|
||||
@ -179,7 +190,7 @@ func TestCheckBlockSanity(t *testing.T) {
|
||||
// second fails.
|
||||
timestamp := block.MsgBlock().Header.Timestamp
|
||||
block.MsgBlock().Header.Timestamp = timestamp.Add(time.Nanosecond)
|
||||
err = CheckBlockSanity(block, powLimit, timeSource, &wire.SubnetworkIDNative)
|
||||
err = dag.CheckBlockSanity(block, powLimit, timeSource)
|
||||
if err == nil {
|
||||
t.Errorf("CheckBlockSanity: error is nil when it shouldn't be")
|
||||
}
|
||||
@ -445,7 +456,7 @@ func TestCheckBlockSanity(t *testing.T) {
|
||||
}
|
||||
|
||||
btcutilInvalidBlock := util.NewBlock(&invalidParentsOrderBlock)
|
||||
err = CheckBlockSanity(btcutilInvalidBlock, powLimit, timeSource, &wire.SubnetworkIDNative)
|
||||
err = dag.CheckBlockSanity(btcutilInvalidBlock, powLimit, timeSource)
|
||||
if err == nil {
|
||||
t.Errorf("CheckBlockSanity: error is nil when it shouldn't be")
|
||||
}
|
||||
|
@ -754,7 +754,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rateLimit, rejectDu
|
||||
if msgTx.SubnetworkID == wire.SubnetworkIDSupportsAll {
|
||||
return nil, nil, txRuleError(wire.RejectInvalid, "SubnetworkIDSupportsAll is not permited in transaction")
|
||||
} else if msgTx.SubnetworkID != wire.SubnetworkIDNative {
|
||||
gasLimit, err := mp.cfg.DAG.GasLimit(&msgTx.SubnetworkID)
|
||||
gasLimit, err := mp.cfg.DAG.SubnetworkStore.GasLimit(&msgTx.SubnetworkID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -494,7 +494,7 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
||||
if !ok {
|
||||
gasUsage = 0
|
||||
}
|
||||
gasLimit, err := g.dag.GasLimit(&subnetworkID)
|
||||
gasLimit, err := g.dag.SubnetworkStore.GasLimit(&subnetworkID)
|
||||
if err != nil {
|
||||
log.Errorf("Cannot get GAS limit for subnetwork %v", subnetworkID)
|
||||
continue
|
||||
|
@ -3398,7 +3398,7 @@ func verifyDAG(s *Server, level, depth int32) error {
|
||||
if finishHeight < 0 {
|
||||
finishHeight = 0
|
||||
}
|
||||
log.Infof("Verifying chain for %d blocks at level %d",
|
||||
log.Infof("Verifying DAG for %d blocks at level %d",
|
||||
s.cfg.DAG.Height()-finishHeight, level) //TODO: (Ori) This is probably wrong. Done only for compilation
|
||||
|
||||
currentHash := s.cfg.DAG.HighestTipHash()
|
||||
@ -3411,10 +3411,9 @@ func verifyDAG(s *Server, level, depth int32) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Level 1 does basic chain sanity checks.
|
||||
// Level 1 does basic DAG sanity checks.
|
||||
if level > 0 {
|
||||
err := blockdag.CheckBlockSanity(block, s.cfg.DAGParams.PowLimit,
|
||||
s.cfg.TimeSource, config.MainConfig().SubnetworkID)
|
||||
err := s.cfg.DAG.CheckBlockSanity(block, s.cfg.DAGParams.PowLimit, s.cfg.TimeSource)
|
||||
if err != nil {
|
||||
log.Errorf("Verify is unable to validate "+
|
||||
"block at hash %v height %d: %v",
|
||||
@ -3425,7 +3424,7 @@ func verifyDAG(s *Server, level, depth int32) error {
|
||||
|
||||
currentHash = *block.MsgBlock().Header.SelectedParentHash()
|
||||
}
|
||||
log.Infof("Chain verify completed successfully")
|
||||
log.Infof("DAG verify completed successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user