Merge pull request #355 from filecoin-project/feat/mpool-dos-help

Some simple measures to avoid mpool DoSing
This commit is contained in:
Łukasz Magiera 2019-10-14 00:54:35 +02:00 committed by GitHub
commit 1b0498236a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 11 deletions

View File

@ -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 {
@ -51,9 +68,11 @@ func (ms *msgSet) add(m *types.SignedMessage) error {
func NewMessagePool(sm *stmgr.StateManager, ps *pubsub.PubSub) *MessagePool { func NewMessagePool(sm *stmgr.StateManager, ps *pubsub.PubSub) *MessagePool {
mp := &MessagePool{ mp := &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()

View File

@ -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(

View File

@ -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
} }