Skip to content

Commit

Permalink
Fix: Protocol transaction validation where stake amount is not update…
Browse files Browse the repository at this point in the history
…d while validating first block of an epoch
  • Loading branch information
cyyber committed Sep 16, 2022
1 parent a109eb7 commit d376401
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 26 deletions.
14 changes: 7 additions & 7 deletions chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,30 +677,30 @@ func (c *Chain) ValidateTransaction(protoTx *protos.Transaction) error {
return core.ValidateTransaction(protoTx, statedb)
}

func (c *Chain) ValidateProtocolTransaction(protoTx *protos.ProtocolTransaction, slotValidatorsMetaData *metadata.SlotValidatorsMetaData, blockSigningHash common.Hash, slotNumber uint64, isGenesis bool) error {
func (c *Chain) ValidateProtocolTransaction(protoTx *protos.ProtocolTransaction, slotValidatorsMetaData *metadata.SlotValidatorsMetaData, blockSigningHash common.Hash, slotNumber, parentSlotNumber uint64, isGenesis bool) error {
statedb, err := c.AccountDB()
if err != nil {
return fmt.Errorf("failed to get statedb, cannot verify protocol transaction")
}
return core.ValidateProtocolTransaction(protoTx, statedb, slotValidatorsMetaData, blockSigningHash, slotNumber, isGenesis)
return core.ValidateProtocolTransaction(protoTx, statedb, slotValidatorsMetaData, blockSigningHash, slotNumber, parentSlotNumber, isGenesis)
}

func (c *Chain) ValidateCoinBaseTransaction(protoTx *protos.ProtocolTransaction, validatorsType *metadata.SlotValidatorsMetaData, blockSigningHash common.Hash, slotNumber uint64, isGenesis bool) error {
func (c *Chain) ValidateCoinBaseTransaction(protoTx *protos.ProtocolTransaction, validatorsType *metadata.SlotValidatorsMetaData, blockSigningHash common.Hash, slotNumber, parentSlotNumber uint64, isGenesis bool) error {
statedb, err := c.AccountDB()
if err != nil {
return fmt.Errorf("failed to get statedb, cannot verify protocol transaction")
}
tx := transactions.CoinBaseTransactionFromPBData(protoTx)
return core.ValidateCoinBaseTx(tx, statedb, validatorsType, blockSigningHash, slotNumber, isGenesis)
return core.ValidateCoinBaseTx(tx, statedb, validatorsType, blockSigningHash, slotNumber, parentSlotNumber, isGenesis)
}

func (c *Chain) ValidateAttestTransaction(protoTx *protos.ProtocolTransaction, validatorsType *metadata.SlotValidatorsMetaData, partialBlockSigningHash common.Hash, slotNumber uint64) error {
func (c *Chain) ValidateAttestTransaction(protoTx *protos.ProtocolTransaction, validatorsType *metadata.SlotValidatorsMetaData, partialBlockSigningHash common.Hash, slotNumber, parentSlotNumber uint64) error {
statedb, err := c.AccountDB()
if err != nil {
return fmt.Errorf("failed to get statedb, cannot verify protocol transaction")
}
tx := transactions.AttestTransactionFromPBData(protoTx)
return core.ValidateAttestTx(tx, statedb, validatorsType, partialBlockSigningHash, slotNumber)
return core.ValidateAttestTx(tx, statedb, validatorsType, partialBlockSigningHash, slotNumber, parentSlotNumber)
}

func (c *Chain) AddBlock(b *block.Block) bool {
Expand Down Expand Up @@ -779,7 +779,7 @@ func (c *Chain) AddBlock(b *block.Block) bool {
stateProcessor := core.NewStateProcessor(&params.ChainConfig{ChainID: c.config.Dev.ChainID}, c.GetBlockHashBySlotNumber)

//receipts, logs, usedGas, err := stateProcessor.Process(b, statedb, stateContext, validators, false, vm.Config{})
_, _, _, err = stateProcessor.Process(b, statedb, stateContext, validators, false, vm.Config{})
_, _, _, err = stateProcessor.Process(b, parentBlock, statedb, stateContext, validators, false, vm.Config{})
if err != nil {
log.Error(fmt.Sprintf("Failed to process block #%d %s | Error %s", b.SlotNumber(),
misc.BytesToHexStr(bHash[:]), err.Error()))
Expand Down
8 changes: 6 additions & 2 deletions consensus/pos.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,12 @@ running:
// log.Error("Error creating NewStateContext")
// continue
//}

if err := p.chain.ValidateAttestTransaction(tx.PBData(), p.slotValidatorsMetaData, p.blockBeingAttested.PartialBlockSigningHash(), p.blockBeingAttested.SlotNumber()); err != nil {
parentBlock, err := p.chain.GetBlock(p.blockBeingAttested.ParentHash())
if err != nil {
log.Warn("failed to load parent block ", err)
continue
}
if err := p.chain.ValidateAttestTransaction(tx.PBData(), p.slotValidatorsMetaData, p.blockBeingAttested.PartialBlockSigningHash(), p.blockBeingAttested.SlotNumber(), parentBlock.SlotNumber()); err != nil {
log.Warn("Attestor transaction validation failed: ", err)
continue
}
Expand Down
9 changes: 9 additions & 0 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,15 @@ func (s *StateDB) GetStakeBalance(addr common.Address) *big.Int {
return common.Big0
}

func (s *StateDB) GetPendingStakeBalance(addr common.Address) *big.Int {
stateObject := s.getStateObject(addr)
if stateObject != nil {
return stateObject.PendingStakeBalance()
}

return common.Big0
}

func (s *StateDB) GetNonce(addr common.Address) uint64 {
stateObject := s.getStateObject(addr)
if stateObject != nil {
Expand Down
62 changes: 46 additions & 16 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (p *StateProcessor) ProcessGenesis(b *block.Block, statedb *state.StateDB,

coinBaseTx := transactions.CoinBaseTransactionFromPBData(b.ProtocolTransactions()[0])

if err := ValidateCoinBaseTx(coinBaseTx, statedb, slotValidatorsMetaData, b.BlockSigningHash(), b.SlotNumber(), true); err != nil {
if err := ValidateCoinBaseTx(coinBaseTx, statedb, slotValidatorsMetaData, b.BlockSigningHash(), b.SlotNumber(), b.SlotNumber(), true); err != nil {
return nil, nil, 0, err
}

Expand All @@ -146,7 +146,7 @@ func (p *StateProcessor) ProcessGenesis(b *block.Block, statedb *state.StateDB,
case *protos.ProtocolTransaction_Attest:
attestTx := transactions.AttestTransactionFromPBData(protoTx)

if err := ValidateAttestTx(attestTx, statedb, slotValidatorsMetaData, b.PartialBlockSigningHash(), b.SlotNumber()); err != nil {
if err := ValidateAttestTx(attestTx, statedb, slotValidatorsMetaData, b.PartialBlockSigningHash(), b.SlotNumber(), b.SlotNumber()); err != nil {
return nil, nil, 0, err
}

Expand Down Expand Up @@ -258,7 +258,7 @@ func (p *StateProcessor) ProcessGenesis(b *block.Block, statedb *state.StateDB,
return txReceipts, allLogs, *usedGas, nil
}

func (p *StateProcessor) Process(b *block.Block, statedb *state.StateDB, stateContext *state2.StateContext, slotValidatorsMetaData *metadata.SlotValidatorsMetaData, isFinalizedState bool, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
func (p *StateProcessor) Process(b, parentBlock *block.Block, statedb *state.StateDB, stateContext *state2.StateContext, slotValidatorsMetaData *metadata.SlotValidatorsMetaData, isFinalizedState bool, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
var (
protocolTxReceipts types.Receipts
txReceipts types.Receipts
Expand Down Expand Up @@ -293,7 +293,7 @@ func (p *StateProcessor) Process(b *block.Block, statedb *state.StateDB, stateCo

coinBaseTx := transactions.CoinBaseTransactionFromPBData(b.ProtocolTransactions()[0])

if err := ValidateCoinBaseTx(coinBaseTx, statedb, slotValidatorsMetaData, b.BlockSigningHash(), b.SlotNumber(), false); err != nil {
if err := ValidateCoinBaseTx(coinBaseTx, statedb, slotValidatorsMetaData, b.BlockSigningHash(), b.SlotNumber(), parentBlock.SlotNumber(), false); err != nil {
return nil, nil, 0, err
}

Expand All @@ -307,7 +307,7 @@ func (p *StateProcessor) Process(b *block.Block, statedb *state.StateDB, stateCo
case *protos.ProtocolTransaction_Attest:
attestTx := transactions.AttestTransactionFromPBData(protoTx)

if err := ValidateAttestTx(attestTx, statedb, slotValidatorsMetaData, b.PartialBlockSigningHash(), b.SlotNumber()); err != nil {
if err := ValidateAttestTx(attestTx, statedb, slotValidatorsMetaData, b.PartialBlockSigningHash(), b.SlotNumber(), parentBlock.SlotNumber()); err != nil {
return nil, nil, 0, err
}

Expand Down Expand Up @@ -888,7 +888,7 @@ func ValidateStakeTxn(tx *transactions.Stake, statedb *state.StateDB) error {
return nil
}

func ValidateAttestTx(tx *transactions.Attest, statedb *state.StateDB, slotValidatorsMetaData *metadata.SlotValidatorsMetaData, partialBlockSigningHash common.Hash, slotNumber uint64) error {
func ValidateAttestTx(tx *transactions.Attest, statedb *state.StateDB, slotValidatorsMetaData *metadata.SlotValidatorsMetaData, partialBlockSigningHash common.Hash, slotNumber uint64, parentSlotNumber uint64) error {
signedMessage := tx.GetSigningHash(partialBlockSigningHash)
txHash := tx.TxHash(signedMessage)

Expand Down Expand Up @@ -917,15 +917,30 @@ func ValidateAttestTx(tx *transactions.Attest, statedb *state.StateDB, slotValid
"attest", misc.BytesToHexStr(txHash[:]), signerAddr, slotNumber)
}

if accountState.StakeBalance().Uint64() < config.GetDevConfig().StakeAmount {
return fmt.Errorf(errmsg.TXInsufficientStakeBalance,
"attest", tx.Hash(), signerAddr, accountState.StakeBalance())
parentEpoch := parentSlotNumber / config.GetDevConfig().BlocksPerEpoch
currentEpoch := slotNumber / config.GetDevConfig().BlocksPerEpoch
if parentEpoch == currentEpoch {
if accountState.StakeBalance().Uint64() < config.GetDevConfig().StakeAmount {
return fmt.Errorf(errmsg.TXInsufficientStakeBalance,
"attest", tx.Hash(), signerAddr, accountState.StakeBalance())
}
} else {
/*
This case happens, when the first block of epoch is created.
During this case, the pendingStakeBalance is not yet added to the StakeBalance,
and so we need to compare it with pendingStakeBalance, as that's the effective
balance for this epoch
*/
if accountState.PendingStakeBalance().Uint64() < config.GetDevConfig().StakeAmount {
return fmt.Errorf(errmsg.TXInsufficientStakeBalance,
"attest", tx.Hash(), signerAddr, accountState.StakeBalance())
}
}

return nil
}

func ValidateCoinBaseTx(tx *transactions.CoinBase, statedb *state.StateDB, slotValidatorsMetaData *metadata.SlotValidatorsMetaData, blockSigningHash common.Hash, slotNumber uint64, isGenesis bool) error {
func ValidateCoinBaseTx(tx *transactions.CoinBase, statedb *state.StateDB, slotValidatorsMetaData *metadata.SlotValidatorsMetaData, blockSigningHash common.Hash, slotNumber, parentSlotNumber uint64, isGenesis bool) error {
signedMessage := tx.GetSigningHash(blockSigningHash)
txHash := tx.TxHash(signedMessage)

Expand Down Expand Up @@ -971,9 +986,24 @@ func ValidateCoinBaseTx(tx *transactions.CoinBase, statedb *state.StateDB, slotV

// Genesis block proposer will have 0 stake balance initially
if !isGenesis {
if accountState.StakeBalance().Uint64() < config.GetDevConfig().StakeAmount {
return fmt.Errorf(errmsg.TXInsufficientStakeBalance,
"coinbase", tx.Hash(), signerAddr, accountState.StakeBalance())
parentEpoch := parentSlotNumber / config.GetDevConfig().BlocksPerEpoch
currentEpoch := slotNumber / config.GetDevConfig().BlocksPerEpoch
if parentEpoch == currentEpoch {
if accountState.StakeBalance().Uint64() < config.GetDevConfig().StakeAmount {
return fmt.Errorf(errmsg.TXInsufficientStakeBalance,
"coinbase", tx.Hash(), signerAddr, accountState.StakeBalance())
}
} else {
/*
This case happens, when the first block of epoch is created.
During this case, the pendingStakeBalance is not yet added to the StakeBalance,
and so we need to compare it with pendingStakeBalance, as that's the effective
balance for this epoch
*/
if accountState.PendingStakeBalance().Uint64() < config.GetDevConfig().StakeAmount {
return fmt.Errorf(errmsg.TXInsufficientStakeBalance,
"coinbase", tx.Hash(), signerAddr, accountState.StakeBalance())
}
}
}

Expand Down Expand Up @@ -1022,14 +1052,14 @@ func ValidateTransaction(protoTx *protos.Transaction, statedb *state.StateDB) er
}
}

func ValidateProtocolTransaction(protoTx *protos.ProtocolTransaction, statedb *state.StateDB, slotValidatorsMetaData *metadata.SlotValidatorsMetaData, hash common.Hash, slotNumber uint64, isGenesis bool) error {
func ValidateProtocolTransaction(protoTx *protos.ProtocolTransaction, statedb *state.StateDB, slotValidatorsMetaData *metadata.SlotValidatorsMetaData, hash common.Hash, slotNumber, parentSlotNumber uint64, isGenesis bool) error {
switch protoTx.Type.(type) {
case *protos.ProtocolTransaction_CoinBase:
coinBaseTx := transactions.CoinBaseTransactionFromPBData(protoTx)
return ValidateCoinBaseTx(coinBaseTx, statedb, slotValidatorsMetaData, hash, slotNumber, isGenesis)
return ValidateCoinBaseTx(coinBaseTx, statedb, slotValidatorsMetaData, hash, slotNumber, parentSlotNumber, isGenesis)
case *protos.ProtocolTransaction_Attest:
attestTx := transactions.AttestTransactionFromPBData(protoTx)
return ValidateAttestTx(attestTx, statedb, slotValidatorsMetaData, hash, slotNumber)
return ValidateAttestTx(attestTx, statedb, slotValidatorsMetaData, hash, slotNumber, parentSlotNumber)
default:
return fmt.Errorf("unkown transaction type")
}
Expand Down
2 changes: 1 addition & 1 deletion p2p/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ func (p *Peer) HandleAttestTransaction(msg *Msg, txData *protos.ProtocolTransact
return nil
}

if err := p.chain.ValidateAttestTransaction(pbData, slotValidatorsMetaData, partialBlockSigningHash, txData.SlotNumber); err != nil {
if err := p.chain.ValidateAttestTransaction(pbData, slotValidatorsMetaData, partialBlockSigningHash, txData.SlotNumber, parentMetaData.SlotNumber()); err != nil {
log.Error("[HandleAttestTransaction] Attest Transaction Validation Failed ", err)
return nil
}
Expand Down

0 comments on commit d376401

Please sign in to comment.