Message validation changes

This commit is contained in:
Aayush Rajasekaran 2020-05-11 16:19:35 -04:00
parent 66d67f66a7
commit 6bf2dcd97c
4 changed files with 56 additions and 0 deletions

View File

@ -97,6 +97,7 @@ const BlsSignatureCacheSize = 40000
// /////// // ///////
// Limits // Limits
// TODO: If this is gonna stay, it should move to specs-actors
const BlockMessageLimit = 512 const BlockMessageLimit = 512
var DrandCoeffs = []string{ var DrandCoeffs = []string{

View File

@ -5,6 +5,8 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"os" "os"
"sort" "sort"
"strings" "strings"
@ -805,6 +807,7 @@ func (syncer *Syncer) VerifyWinningPoStProof(ctx context.Context, h *types.Block
return nil return nil
} }
// TODO: We should extract this somewhere else and make the message pool and miner use the same logic
func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock, baseTs *types.TipSet) error { func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock, baseTs *types.TipSet) error {
{ {
var sigCids []cid.Cid // this is what we get for people not wanting the marshalcbor method on the cid type var sigCids []cid.Cid // this is what we get for people not wanting the marshalcbor method on the cid type
@ -840,16 +843,59 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
} }
checkMsg := func(m *types.Message) error { checkMsg := func(m *types.Message) error {
// Phase 1: syntactic validation, as defined in the spec
if m.Version != 0 {
return xerrors.New("'Version' unsupported")
}
if m.To == address.Undef { if m.To == address.Undef {
return xerrors.New("'To' address cannot be empty") return xerrors.New("'To' address cannot be empty")
} }
if m.From == address.Undef {
return xerrors.New("'From' address cannot be empty")
}
if m.Value.LessThan(big.Zero()) {
return xerrors.New("'Value' field cannot be negative")
}
if m.Value.GreaterThan(types.TotalFilecoinInt) {
return xerrors.New("'Value' field cannot be greater than total filecoin supply")
}
if len(m.Params) != 0 && m.Method == 0 {
return xerrors.New("'Params' field should be empty if no 'Method' is being called")
}
if m.GasPrice.LessThan(big.Zero()) {
return xerrors.New("'GasPrice' field cannot be negative")
}
// TODO: This should be a thing
//if m.GasLimit > BLOCK_GAS_LIMIT {
// return xerrors.New("'GasLimit' field cannot be greater than a block's gas limit")
//}
// since prices might vary with time, this is technically semantic validation
if m.GasLimit < vm.PricelistByEpoch(baseTs.Height()).OnChainMessage(m.ChainLength()) {
return xerrors.New("'GasLimit' field cannot be less than the cost of storing a message on chain")
}
// Phase 2: (Partial) semantic validation:
// the sender exists and is an account actor, and the nonces make sense
if _, ok := nonces[m.From]; !ok { if _, ok := nonces[m.From]; !ok {
// `GetActor` does not validate that this is an account actor. // `GetActor` does not validate that this is an account actor.
act, err := st.GetActor(m.From) act, err := st.GetActor(m.From)
if err != nil { if err != nil {
return xerrors.Errorf("failed to get actor: %w", err) return xerrors.Errorf("failed to get actor: %w", err)
} }
// redundant check
if !act.IsAccountActor() {
return xerrors.New("Sender must be an account actor")
}
nonces[m.From] = act.Nonce nonces[m.From] = act.Nonce
} }

View File

@ -1,6 +1,7 @@
package types package types
import ( import (
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init" init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
@ -15,3 +16,7 @@ type Actor struct {
Nonce uint64 Nonce uint64
Balance BigInt Balance BigInt
} }
func (a *Actor) IsAccountActor() bool {
return a.Code == builtin.AccountActorCodeID
}

View File

@ -277,6 +277,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
pl := PricelistByEpoch(vm.blockHeight) pl := PricelistByEpoch(vm.blockHeight)
msgGasCost := pl.OnChainMessage(cmsg.ChainLength()) msgGasCost := pl.OnChainMessage(cmsg.ChainLength())
// this should never happen, but is currently still exercised by some tests
if msgGasCost > msg.GasLimit { if msgGasCost > msg.GasLimit {
return &ApplyRet{ return &ApplyRet{
MessageReceipt: types.MessageReceipt{ MessageReceipt: types.MessageReceipt{
@ -292,6 +293,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
minerPenaltyAmount := types.BigMul(msg.GasPrice, types.NewInt(uint64(msgGasCost))) minerPenaltyAmount := types.BigMul(msg.GasPrice, types.NewInt(uint64(msgGasCost)))
fromActor, err := st.GetActor(msg.From) fromActor, err := st.GetActor(msg.From)
// this should never happen, but is currently still exercised by some tests
if err != nil { if err != nil {
if xerrors.Is(err, types.ErrActorNotFound) { if xerrors.Is(err, types.ErrActorNotFound) {
return &ApplyRet{ return &ApplyRet{
@ -306,6 +308,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
return nil, xerrors.Errorf("failed to look up from actor: %w", err) return nil, xerrors.Errorf("failed to look up from actor: %w", err)
} }
// this should never happen, but is currently still exercised by some tests
if !fromActor.Code.Equals(builtin.AccountActorCodeID) { if !fromActor.Code.Equals(builtin.AccountActorCodeID) {
return &ApplyRet{ return &ApplyRet{
MessageReceipt: types.MessageReceipt{ MessageReceipt: types.MessageReceipt{
@ -317,6 +320,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
}, nil }, nil
} }
// TODO: We should remove this, we might punish miners for no fault of their own
if msg.Nonce != fromActor.Nonce { if msg.Nonce != fromActor.Nonce {
return &ApplyRet{ return &ApplyRet{
MessageReceipt: types.MessageReceipt{ MessageReceipt: types.MessageReceipt{