diff --git a/chain/messagepool.go b/chain/messagepool.go index b4bacd7a5..0afde83c1 100644 --- a/chain/messagepool.go +++ b/chain/messagepool.go @@ -1,6 +1,7 @@ package chain import ( + "fmt" "sync" pubsub "github.com/libp2p/go-libp2p-pubsub" @@ -12,19 +13,35 @@ import ( "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 { lk sync.Mutex - pending map[address.Address]*msgSet + pending map[address.Address]*msgSet + pendingCount int sm *stmgr.StateManager ps *pubsub.PubSub + + minGasPrice types.BigInt + + maxTxPoolSize int } type msgSet struct { - msgs map[uint64]*types.SignedMessage - nextNonce uint64 + msgs map[uint64]*types.SignedMessage + nextNonce uint64 + curBalance types.BigInt } func newMsgSet() *msgSet { @@ -51,9 +68,11 @@ func (ms *msgSet) add(m *types.SignedMessage) error { func NewMessagePool(sm *stmgr.StateManager, ps *pubsub.PubSub) *MessagePool { mp := &MessagePool{ - pending: make(map[address.Address]*msgSet), - sm: sm, - ps: ps, + pending: make(map[address.Address]*msgSet), + sm: sm, + ps: ps, + minGasPrice: types.NewInt(0), + maxTxPoolSize: 100000, } 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 { + // 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() defer mp.lk.Unlock() @@ -83,11 +134,6 @@ func (mp *MessagePool) Add(m *types.SignedMessage) error { func (mp *MessagePool) addLocked(m *types.SignedMessage) error { 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 { log.Warnf("mpooladd cs.PutMessage failed: %s", err) return err @@ -116,6 +162,10 @@ func (mp *MessagePool) getNonceLocked(addr address.Address) (uint64, error) { return mset.nextNonce, nil } + return mp.getStateNonce(addr) +} + +func (mp *MessagePool) getStateNonce(addr address.Address) (uint64, error) { act, err := mp.sm.GetActor(addr, nil) if err != nil { return 0, err @@ -124,6 +174,15 @@ func (mp *MessagePool) getNonceLocked(addr address.Address) (uint64, error) { 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) { mp.lk.Lock() defer mp.lk.Unlock() diff --git a/chain/types/bigint.go b/chain/types/bigint.go index a1c4e8982..329781b9f 100644 --- a/chain/types/bigint.go +++ b/chain/types/bigint.go @@ -16,6 +16,8 @@ import ( const BigIntMaxSerializedLen = 128 // is this big enough? or too big? +var TotalFilecoinInt = FromFil(build.TotalFilecoin) + func init() { cbor.RegisterCborType(atlas.BuildEntry(BigInt{}).Transform(). TransformMarshal(atlas.MakeMarshalTransformFunc( diff --git a/chain/types/signedmessage.go b/chain/types/signedmessage.go index a08f4fba8..0ede20a4f 100644 --- a/chain/types/signedmessage.go +++ b/chain/types/signedmessage.go @@ -62,6 +62,16 @@ func (sm *SignedMessage) Serialize() ([]byte, error) { 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 { return &sm.Message }