2020-10-07 09:26:15 +00:00
|
|
|
package modules
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-04-15 16:36:04 +00:00
|
|
|
"strings"
|
2020-10-07 09:26:15 +00:00
|
|
|
|
|
|
|
"go.uber.org/fx"
|
2020-10-23 00:24:42 +00:00
|
|
|
"golang.org/x/xerrors"
|
2020-10-07 09:26:15 +00:00
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/node/impl/full"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/chain/messagesigner"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
|
|
)
|
|
|
|
|
|
|
|
// MpoolNonceAPI substitutes the mpool nonce with an implementation that
|
|
|
|
// doesn't rely on the mpool - it just gets the nonce from actor state
|
|
|
|
type MpoolNonceAPI struct {
|
|
|
|
fx.In
|
|
|
|
|
2021-04-15 16:36:04 +00:00
|
|
|
ChainModule full.ChainModuleAPI
|
|
|
|
StateModule full.StateModuleAPI
|
2020-10-07 09:26:15 +00:00
|
|
|
}
|
|
|
|
|
2020-10-23 00:24:42 +00:00
|
|
|
// GetNonce gets the nonce from current chain head.
|
2021-04-15 16:36:04 +00:00
|
|
|
func (a *MpoolNonceAPI) GetNonce(ctx context.Context, addr address.Address, tsk types.TipSetKey) (uint64, error) {
|
|
|
|
var err error
|
|
|
|
var ts *types.TipSet
|
|
|
|
if tsk == types.EmptyTSK {
|
|
|
|
// we need consistent tsk
|
|
|
|
ts, err = a.ChainModule.ChainHead(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return 0, xerrors.Errorf("getting head: %w", err)
|
|
|
|
}
|
|
|
|
tsk = ts.Key()
|
|
|
|
} else {
|
|
|
|
ts, err = a.ChainModule.ChainGetTipSet(ctx, tsk)
|
|
|
|
if err != nil {
|
|
|
|
return 0, xerrors.Errorf("getting tipset: %w", err)
|
|
|
|
}
|
|
|
|
}
|
2020-10-23 00:24:42 +00:00
|
|
|
|
2021-04-15 16:36:04 +00:00
|
|
|
keyAddr := addr
|
|
|
|
|
|
|
|
if addr.Protocol() == address.ID {
|
|
|
|
// make sure we have a key address so we can compare with messages
|
|
|
|
keyAddr, err = a.StateModule.StateAccountKey(ctx, addr, tsk)
|
|
|
|
if err != nil {
|
|
|
|
return 0, xerrors.Errorf("getting account key: %w", err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
addr, err = a.StateModule.StateLookupID(ctx, addr, types.EmptyTSK)
|
|
|
|
if err != nil {
|
|
|
|
log.Infof("failed to look up id addr for %s: %w", addr, err)
|
|
|
|
addr = address.Undef
|
|
|
|
}
|
2020-10-23 00:24:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load the last nonce from the state, if it exists.
|
|
|
|
highestNonce := uint64(0)
|
2021-04-15 16:36:04 +00:00
|
|
|
act, err := a.StateModule.StateGetActor(ctx, keyAddr, ts.Key())
|
|
|
|
if err != nil {
|
|
|
|
if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) {
|
|
|
|
return 0, types.ErrActorNotFound
|
2020-10-23 00:24:42 +00:00
|
|
|
}
|
2021-04-15 16:36:04 +00:00
|
|
|
return 0, xerrors.Errorf("getting actor: %w", err)
|
2020-10-23 00:24:42 +00:00
|
|
|
}
|
2021-04-15 16:36:04 +00:00
|
|
|
highestNonce = act.Nonce
|
2020-10-23 00:24:42 +00:00
|
|
|
|
2021-04-15 16:36:04 +00:00
|
|
|
apply := func(msg *types.Message) {
|
|
|
|
if msg.From != addr && msg.From != keyAddr {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if msg.Nonce == highestNonce {
|
|
|
|
highestNonce = msg.Nonce + 1
|
|
|
|
}
|
2020-10-07 09:26:15 +00:00
|
|
|
}
|
2021-04-15 16:36:04 +00:00
|
|
|
|
|
|
|
for _, b := range ts.Blocks() {
|
|
|
|
msgs, err := a.ChainModule.ChainGetBlockMessages(ctx, b.Cid())
|
|
|
|
if err != nil {
|
|
|
|
return 0, xerrors.Errorf("getting block messages: %w", err)
|
2020-10-23 00:24:42 +00:00
|
|
|
}
|
2021-04-15 16:36:04 +00:00
|
|
|
if keyAddr.Protocol() == address.BLS {
|
|
|
|
for _, m := range msgs.BlsMessages {
|
|
|
|
apply(m)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for _, sm := range msgs.SecpkMessages {
|
|
|
|
apply(&sm.Message)
|
|
|
|
}
|
2020-10-23 00:24:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return highestNonce, nil
|
2020-10-07 09:26:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var _ messagesigner.MpoolNonceAPI = (*MpoolNonceAPI)(nil)
|