Merge pull request #355 from filecoin-project/feat/mpool-dos-help
Some simple measures to avoid mpool DoSing
This commit is contained in:
commit
1b0498236a
@ -1,6 +1,7 @@
|
|||||||
package chain
|
package chain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||||
@ -12,19 +13,35 @@ import (
|
|||||||
"github.com/filecoin-project/go-lotus/chain/types"
|
"github.com/filecoin-project/go-lotus/chain/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrMessageTooBig = fmt.Errorf("message too big")
|
||||||
|
|
||||||
|
ErrMessageValueTooHigh = fmt.Errorf("cannot send more filecoin than will ever exist")
|
||||||
|
|
||||||
|
ErrNonceTooLow = fmt.Errorf("message nonce too low")
|
||||||
|
|
||||||
|
ErrNotEnoughFunds = fmt.Errorf("not enough funds to execute transaction")
|
||||||
|
)
|
||||||
|
|
||||||
type MessagePool struct {
|
type MessagePool struct {
|
||||||
lk sync.Mutex
|
lk sync.Mutex
|
||||||
|
|
||||||
pending map[address.Address]*msgSet
|
pending map[address.Address]*msgSet
|
||||||
|
pendingCount int
|
||||||
|
|
||||||
sm *stmgr.StateManager
|
sm *stmgr.StateManager
|
||||||
|
|
||||||
ps *pubsub.PubSub
|
ps *pubsub.PubSub
|
||||||
|
|
||||||
|
minGasPrice types.BigInt
|
||||||
|
|
||||||
|
maxTxPoolSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
type msgSet struct {
|
type msgSet struct {
|
||||||
msgs map[uint64]*types.SignedMessage
|
msgs map[uint64]*types.SignedMessage
|
||||||
nextNonce uint64
|
nextNonce uint64
|
||||||
|
curBalance types.BigInt
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMsgSet() *msgSet {
|
func newMsgSet() *msgSet {
|
||||||
@ -54,6 +71,8 @@ func NewMessagePool(sm *stmgr.StateManager, ps *pubsub.PubSub) *MessagePool {
|
|||||||
pending: make(map[address.Address]*msgSet),
|
pending: make(map[address.Address]*msgSet),
|
||||||
sm: sm,
|
sm: sm,
|
||||||
ps: ps,
|
ps: ps,
|
||||||
|
minGasPrice: types.NewInt(0),
|
||||||
|
maxTxPoolSize: 100000,
|
||||||
}
|
}
|
||||||
sm.ChainStore().SubscribeHeadChanges(mp.HeadChange)
|
sm.ChainStore().SubscribeHeadChanges(mp.HeadChange)
|
||||||
|
|
||||||
@ -74,6 +93,38 @@ func (mp *MessagePool) Push(m *types.SignedMessage) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mp *MessagePool) Add(m *types.SignedMessage) error {
|
func (mp *MessagePool) Add(m *types.SignedMessage) error {
|
||||||
|
// big messages are bad, anti DOS
|
||||||
|
if m.Size() > 32*1024 {
|
||||||
|
return ErrMessageTooBig
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m.Message.Value.LessThan(types.TotalFilecoinInt) {
|
||||||
|
return ErrMessageValueTooHigh
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.Signature.Verify(m.Message.From, m.Message.Cid().Bytes()); err != nil {
|
||||||
|
log.Warnf("mpooladd signature verification failed: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
snonce, err := mp.getStateNonce(m.Message.From)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to look up actor state nonce: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if snonce > m.Message.Nonce {
|
||||||
|
return ErrNonceTooLow
|
||||||
|
}
|
||||||
|
|
||||||
|
balance, err := mp.getStateBalance(m.Message.From)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to check sender balance: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if balance.LessThan(m.Message.RequiredFunds()) {
|
||||||
|
return ErrNotEnoughFunds
|
||||||
|
}
|
||||||
|
|
||||||
mp.lk.Lock()
|
mp.lk.Lock()
|
||||||
defer mp.lk.Unlock()
|
defer mp.lk.Unlock()
|
||||||
|
|
||||||
@ -83,11 +134,6 @@ func (mp *MessagePool) Add(m *types.SignedMessage) error {
|
|||||||
func (mp *MessagePool) addLocked(m *types.SignedMessage) error {
|
func (mp *MessagePool) addLocked(m *types.SignedMessage) error {
|
||||||
log.Debugf("mpooladd: %s %s", m.Message.From, m.Message.Nonce)
|
log.Debugf("mpooladd: %s %s", m.Message.From, m.Message.Nonce)
|
||||||
|
|
||||||
if err := m.Signature.Verify(m.Message.From, m.Message.Cid().Bytes()); err != nil {
|
|
||||||
log.Warnf("mpooladd signature verification failed: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := mp.sm.ChainStore().PutMessage(m); err != nil {
|
if _, err := mp.sm.ChainStore().PutMessage(m); err != nil {
|
||||||
log.Warnf("mpooladd cs.PutMessage failed: %s", err)
|
log.Warnf("mpooladd cs.PutMessage failed: %s", err)
|
||||||
return err
|
return err
|
||||||
@ -116,6 +162,10 @@ func (mp *MessagePool) getNonceLocked(addr address.Address) (uint64, error) {
|
|||||||
return mset.nextNonce, nil
|
return mset.nextNonce, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return mp.getStateNonce(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MessagePool) getStateNonce(addr address.Address) (uint64, error) {
|
||||||
act, err := mp.sm.GetActor(addr, nil)
|
act, err := mp.sm.GetActor(addr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -124,6 +174,15 @@ func (mp *MessagePool) getNonceLocked(addr address.Address) (uint64, error) {
|
|||||||
return act.Nonce, nil
|
return act.Nonce, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mp *MessagePool) getStateBalance(addr address.Address) (types.BigInt, error) {
|
||||||
|
act, err := mp.sm.GetActor(addr, nil)
|
||||||
|
if err != nil {
|
||||||
|
return types.EmptyInt, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return act.Balance, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (mp *MessagePool) PushWithNonce(addr address.Address, cb func(uint64) (*types.SignedMessage, error)) (*types.SignedMessage, error) {
|
func (mp *MessagePool) PushWithNonce(addr address.Address, cb func(uint64) (*types.SignedMessage, error)) (*types.SignedMessage, error) {
|
||||||
mp.lk.Lock()
|
mp.lk.Lock()
|
||||||
defer mp.lk.Unlock()
|
defer mp.lk.Unlock()
|
||||||
|
@ -16,6 +16,8 @@ import (
|
|||||||
|
|
||||||
const BigIntMaxSerializedLen = 128 // is this big enough? or too big?
|
const BigIntMaxSerializedLen = 128 // is this big enough? or too big?
|
||||||
|
|
||||||
|
var TotalFilecoinInt = FromFil(build.TotalFilecoin)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cbor.RegisterCborType(atlas.BuildEntry(BigInt{}).Transform().
|
cbor.RegisterCborType(atlas.BuildEntry(BigInt{}).Transform().
|
||||||
TransformMarshal(atlas.MakeMarshalTransformFunc(
|
TransformMarshal(atlas.MakeMarshalTransformFunc(
|
||||||
|
@ -62,6 +62,16 @@ func (sm *SignedMessage) Serialize() ([]byte, error) {
|
|||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sm *SignedMessage) Size() int {
|
||||||
|
serdata, err := sm.Serialize()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("serializing message failed: %s", err)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(serdata)
|
||||||
|
}
|
||||||
|
|
||||||
func (sm *SignedMessage) VMMessage() *Message {
|
func (sm *SignedMessage) VMMessage() *Message {
|
||||||
return &sm.Message
|
return &sm.Message
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user