lotus/chain/stmgr/stmgr.go

761 lines
20 KiB
Go
Raw Normal View History

package stmgr
import (
"context"
"fmt"
2019-09-06 20:03:28 +00:00
"sync"
"github.com/filecoin-project/go-address"
2020-02-08 02:18:32 +00:00
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
2020-02-08 02:18:32 +00:00
"github.com/filecoin-project/specs-actors/actors/abi"
2020-02-23 15:50:36 +00:00
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
2020-02-25 20:35:15 +00:00
"github.com/filecoin-project/specs-actors/actors/builtin/market"
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
2020-02-08 02:18:32 +00:00
"github.com/filecoin-project/specs-actors/actors/util/adt"
2019-09-27 23:55:15 +00:00
cbg "github.com/whyrusleeping/cbor-gen"
2019-09-06 20:03:28 +00:00
"golang.org/x/xerrors"
bls "github.com/filecoin-project/filecoin-ffi"
"github.com/ipfs/go-cid"
blockstore "github.com/ipfs/go-ipfs-blockstore"
2020-02-04 22:19:05 +00:00
cbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log/v2"
2019-10-10 11:13:26 +00:00
"go.opencensus.io/trace"
)
var log = logging.Logger("statemgr")
type StateManager struct {
cs *store.ChainStore
2019-09-06 20:03:28 +00:00
stCache map[string][]cid.Cid
compWait map[string]chan struct{}
stlk sync.Mutex
2020-07-18 13:46:47 +00:00
newVM func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, vm.SyscallBuilder) (*vm.VM, error)
}
func NewStateManager(cs *store.ChainStore) *StateManager {
2019-09-06 20:03:28 +00:00
return &StateManager{
newVM: vm.NewVM,
cs: cs,
stCache: make(map[string][]cid.Cid),
compWait: make(map[string]chan struct{}),
2019-09-06 20:03:28 +00:00
}
}
func cidsToKey(cids []cid.Cid) string {
var out string
for _, c := range cids {
out += c.KeyString()
}
return out
}
func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st cid.Cid, rec cid.Cid, err error) {
ctx, span := trace.StartSpan(ctx, "tipSetState")
defer span.End()
if span.IsRecordingEvents() {
span.AddAttributes(trace.StringAttribute("tipset", fmt.Sprint(ts.Cids())))
}
2019-09-19 20:25:18 +00:00
2019-10-02 20:03:27 +00:00
ck := cidsToKey(ts.Cids())
2019-09-06 20:03:28 +00:00
sm.stlk.Lock()
cw, cwok := sm.compWait[ck]
if cwok {
sm.stlk.Unlock()
span.AddAttributes(trace.BoolAttribute("waited", true))
select {
case <-cw:
sm.stlk.Lock()
case <-ctx.Done():
return cid.Undef, cid.Undef, ctx.Err()
}
}
2019-09-06 20:03:28 +00:00
cached, ok := sm.stCache[ck]
if ok {
sm.stlk.Unlock()
span.AddAttributes(trace.BoolAttribute("cache", true))
2019-09-27 23:55:15 +00:00
return cached[0], cached[1], nil
2019-09-06 20:03:28 +00:00
}
ch := make(chan struct{})
sm.compWait[ck] = ch
defer func() {
sm.stlk.Lock()
delete(sm.compWait, ck)
if st != cid.Undef {
sm.stCache[ck] = []cid.Cid{st, rec}
}
sm.stlk.Unlock()
close(ch)
}()
sm.stlk.Unlock()
2019-09-06 20:03:28 +00:00
2019-10-02 18:00:08 +00:00
if ts.Height() == 0 {
// NB: This is here because the process that executes blocks requires that the
// block miner reference a valid miner in the state tree. Unless we create some
// magical genesis miner, this won't work properly, so we short circuit here
// This avoids the question of 'who gets paid the genesis block reward'
2019-10-02 18:00:08 +00:00
return ts.Blocks()[0].ParentStateRoot, ts.Blocks()[0].ParentMessageReceipts, nil
}
st, rec, err = sm.computeTipSetState(ctx, ts.Blocks(), nil)
2019-09-06 20:03:28 +00:00
if err != nil {
2019-09-27 23:55:15 +00:00
return cid.Undef, cid.Undef, err
2019-09-06 20:03:28 +00:00
}
2019-09-27 23:55:15 +00:00
return st, rec, nil
2019-09-06 20:03:28 +00:00
}
2020-03-08 02:31:36 +00:00
func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) {
var trace []*api.InvocResult
st, _, err := sm.computeTipSetState(ctx, ts.Blocks(), func(mcid cid.Cid, msg *types.Message, ret *vm.ApplyRet) error {
ir := &api.InvocResult{
Msg: msg,
MsgRct: &ret.MessageReceipt,
ExecutionTrace: ret.ExecutionTrace,
Duration: ret.Duration,
2020-03-08 02:31:36 +00:00
}
if ret.ActorErr != nil {
ir.Error = ret.ActorErr.Error()
}
trace = append(trace, ir)
return nil
})
if err != nil {
return cid.Undef, nil, err
}
return st, trace, nil
}
type BlockMessages struct {
Miner address.Address
2020-03-25 19:13:09 +00:00
BlsMessages []types.ChainMsg
SecpkMessages []types.ChainMsg
WinCount int64
}
2019-09-20 12:22:46 +00:00
type ExecCallback func(cid.Cid, *types.Message, *vm.ApplyRet) error
func (sm *StateManager) ApplyBlocks(ctx context.Context, pstate cid.Cid, bms []BlockMessages, epoch abi.ChainEpoch, r vm.Rand, cb ExecCallback) (cid.Cid, cid.Cid, error) {
2020-04-07 18:26:43 +00:00
vmi, err := sm.newVM(pstate, epoch, r, sm.cs.Blockstore(), sm.cs.VMSys())
if err != nil {
2019-09-27 23:55:15 +00:00
return cid.Undef, cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err)
}
var receipts []cbg.CBORMarshaler
processedMsgs := map[cid.Cid]bool{}
for _, b := range bms {
penalty := types.NewInt(0)
2020-03-24 06:50:20 +00:00
gasReward := big.Zero()
for _, cm := range append(b.BlsMessages, b.SecpkMessages...) {
2019-09-27 23:55:15 +00:00
m := cm.VMMessage()
if _, found := processedMsgs[m.Cid()]; found {
continue
}
2020-03-25 19:13:09 +00:00
r, err := vmi.ApplyMessage(ctx, cm)
if err != nil {
2019-09-27 23:55:15 +00:00
return cid.Undef, cid.Undef, err
}
2019-09-19 20:25:18 +00:00
2019-09-27 23:55:15 +00:00
receipts = append(receipts, &r.MessageReceipt)
2020-04-02 04:16:16 +00:00
gasReward = big.Add(gasReward, big.Mul(m.GasPrice, big.NewInt(r.GasUsed)))
penalty = big.Add(penalty, r.Penalty)
2019-09-27 23:55:15 +00:00
2019-09-19 20:25:18 +00:00
if cb != nil {
2019-09-27 23:55:15 +00:00
if err := cb(cm.Cid(), m, r); err != nil {
return cid.Undef, cid.Undef, err
2019-09-19 20:25:18 +00:00
}
}
processedMsgs[m.Cid()] = true
}
2020-03-05 21:02:33 +00:00
var err error
params, err := actors.SerializeParams(&reward.AwardBlockRewardParams{
2020-06-15 16:30:49 +00:00
Miner: b.Miner,
Penalty: penalty,
GasReward: gasReward,
WinCount: b.WinCount,
})
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("failed to serialize award params: %w", err)
}
sysAct, err := vmi.StateTree().GetActor(builtin.SystemActorAddr)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get system actor: %w", err)
}
2020-03-08 02:31:36 +00:00
rwMsg := &types.Message{
From: builtin.SystemActorAddr,
To: builtin.RewardActorAddr,
Nonce: sysAct.Nonce,
Value: types.NewInt(0),
GasPrice: types.NewInt(0),
2020-03-18 20:45:37 +00:00
GasLimit: 1 << 30,
Method: builtin.MethodsReward.AwardBlockReward,
Params: params,
2020-03-08 02:31:36 +00:00
}
ret, err := vmi.ApplyImplicitMessage(ctx, rwMsg)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("failed to apply reward message for miner %s: %w", b.Miner, err)
}
2020-03-08 02:31:36 +00:00
if cb != nil {
if err := cb(rwMsg.Cid(), rwMsg, ret); err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("callback failed on reward message: %w", err)
}
}
if ret.ExitCode != 0 {
return cid.Undef, cid.Undef, xerrors.Errorf("reward application message failed (exit %d): %s", ret.ExitCode, ret.ActorErr)
}
}
2019-11-22 22:51:44 +00:00
// TODO: this nonce-getting is a tiny bit ugly
2020-02-25 20:54:58 +00:00
ca, err := vmi.StateTree().GetActor(builtin.SystemActorAddr)
2019-11-14 00:02:24 +00:00
if err != nil {
return cid.Undef, cid.Undef, err
}
2020-03-08 02:31:36 +00:00
cronMsg := &types.Message{
2020-02-25 20:54:58 +00:00
To: builtin.CronActorAddr,
From: builtin.SystemActorAddr,
2019-11-22 22:51:44 +00:00
Nonce: ca.Nonce,
Value: types.NewInt(0),
GasPrice: types.NewInt(0),
GasLimit: build.BlockGasLimit * 10, // Make super sure this is never too little
Method: builtin.MethodsCron.EpochTick,
Params: nil,
2020-03-08 02:31:36 +00:00
}
ret, err := vmi.ApplyImplicitMessage(ctx, cronMsg)
if err != nil {
return cid.Undef, cid.Undef, err
}
2020-03-08 02:31:36 +00:00
if cb != nil {
if err := cb(cronMsg.Cid(), cronMsg, ret); err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("callback failed on cron message: %w", err)
}
}
if ret.ExitCode != 0 {
return cid.Undef, cid.Undef, xerrors.Errorf("CheckProofSubmissions exit was non-zero: %d", ret.ExitCode)
}
rectarr := adt.MakeEmptyArray(sm.cs.Store(ctx))
for i, receipt := range receipts {
if err := rectarr.Set(uint64(i), receipt); err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err)
}
}
rectroot, err := rectarr.Root()
2019-09-27 23:55:15 +00:00
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err)
}
st, err := vmi.Flush(ctx)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("vm flush failed: %w", err)
}
return st, rectroot, nil
}
func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.BlockHeader, cb ExecCallback) (cid.Cid, cid.Cid, error) {
ctx, span := trace.StartSpan(ctx, "computeTipSetState")
defer span.End()
for i := 0; i < len(blks); i++ {
for j := i + 1; j < len(blks); j++ {
if blks[i].Miner == blks[j].Miner {
return cid.Undef, cid.Undef,
xerrors.Errorf("duplicate miner in a tipset (%s %s)",
blks[i].Miner, blks[j].Miner)
}
}
}
pstate := blks[0].ParentStateRoot
if len(blks[0].Parents) > 0 { // don't support forks on genesis
parent, err := sm.cs.GetBlock(blks[0].Parents[0])
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("getting parent block: %w", err)
}
pstate, err = sm.handleStateForks(ctx, blks[0].ParentStateRoot, blks[0].Height, parent.Height)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("error handling state forks: %w", err)
}
}
cids := make([]cid.Cid, len(blks))
for i, v := range blks {
cids[i] = v.Cid()
}
r := store.NewChainRand(sm.cs, cids, blks[0].Height)
var blkmsgs []BlockMessages
for _, b := range blks {
bms, sms, err := sm.cs.MessagesForBlock(b)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get messages for block: %w", err)
}
bm := BlockMessages{
Miner: b.Miner,
2020-03-25 19:13:09 +00:00
BlsMessages: make([]types.ChainMsg, 0, len(bms)),
SecpkMessages: make([]types.ChainMsg, 0, len(sms)),
WinCount: b.ElectionProof.WinCount,
}
for _, m := range bms {
bm.BlsMessages = append(bm.BlsMessages, m)
}
for _, m := range sms {
bm.SecpkMessages = append(bm.SecpkMessages, m)
}
blkmsgs = append(blkmsgs, bm)
}
return sm.ApplyBlocks(ctx, pstate, blkmsgs, blks[0].Height, r, cb)
}
2020-02-11 02:33:27 +00:00
func (sm *StateManager) parentState(ts *types.TipSet) cid.Cid {
2019-09-17 22:43:47 +00:00
if ts == nil {
ts = sm.cs.GetHeaviestTipSet()
}
2020-02-11 02:33:27 +00:00
return ts.ParentState()
}
func (sm *StateManager) ChainStore() *store.ChainStore {
return sm.cs
}
// ResolveToKeyAddress is similar to `vm.ResolveToKeyAddr` but does not allow `Actor` type of addresses.
// Uses the `TipSet` `ts` to generate the VM state.
func (sm *StateManager) ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
switch addr.Protocol() {
case address.BLS, address.SECP256K1:
return addr, nil
case address.Actor:
return address.Undef, xerrors.New("cannot resolve actor address to key address")
default:
}
if ts == nil {
ts = sm.cs.GetHeaviestTipSet()
}
2019-10-10 11:13:26 +00:00
st, _, err := sm.TipSetState(ctx, ts)
if err != nil {
return address.Undef, xerrors.Errorf("resolve address failed to get tipset state: %w", err)
}
2020-02-04 22:19:05 +00:00
cst := cbor.NewCborStore(sm.cs.Blockstore())
tree, err := state.LoadStateTree(cst, st)
if err != nil {
return address.Undef, xerrors.Errorf("failed to load state tree")
}
return vm.ResolveToKeyAddr(tree, cst, addr)
}
func (sm *StateManager) GetBlsPublicKey(ctx context.Context, addr address.Address, ts *types.TipSet) (pubk bls.PublicKey, err error) {
kaddr, err := sm.ResolveToKeyAddress(ctx, addr, ts)
if err != nil {
return pubk, xerrors.Errorf("failed to resolve address to key address: %w", err)
}
if kaddr.Protocol() != address.BLS {
return pubk, xerrors.Errorf("address must be BLS address to load bls public key")
}
copy(pubk[:], kaddr.Payload())
return pubk, nil
}
2020-02-23 15:50:36 +00:00
func (sm *StateManager) LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
cst := cbor.NewCborStore(sm.cs.Blockstore())
state, err := state.LoadStateTree(cst, sm.parentState(ts))
if err != nil {
return address.Undef, xerrors.Errorf("load state tree: %w", err)
}
return state.LookupID(addr)
}
2019-11-19 21:27:25 +00:00
func (sm *StateManager) GetReceipt(ctx context.Context, msg cid.Cid, ts *types.TipSet) (*types.MessageReceipt, error) {
m, err := sm.cs.GetCMessage(msg)
if err != nil {
return nil, fmt.Errorf("failed to load message: %w", err)
}
r, err := sm.tipsetExecutedMessage(ts, msg, m.VMMessage())
2019-11-19 21:27:25 +00:00
if err != nil {
return nil, err
}
if r != nil {
return r, nil
}
_, r, err = sm.searchBackForMsg(ctx, ts, m)
if err != nil {
return nil, fmt.Errorf("failed to look back through chain for message: %w", err)
}
return r, nil
}
// WaitForMessage blocks until a message appears on chain. It looks backwards in the chain to see if this has already
// happened. It guarantees that the message has been on chain for at least confidence epochs without being reverted
// before returning.
2020-06-03 21:42:06 +00:00
func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confidence uint64) (*types.TipSet, *types.MessageReceipt, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
msg, err := sm.cs.GetCMessage(mcid)
if err != nil {
return nil, nil, fmt.Errorf("failed to load message: %w", err)
}
tsub := sm.cs.SubHeadChanges(ctx)
head, ok := <-tsub
if !ok {
return nil, nil, fmt.Errorf("SubHeadChanges stream was invalid")
}
if len(head) != 1 {
return nil, nil, fmt.Errorf("SubHeadChanges first entry should have been one item")
}
if head[0].Type != store.HCCurrent {
return nil, nil, fmt.Errorf("expected current head on SHC stream (got %s)", head[0].Type)
}
r, err := sm.tipsetExecutedMessage(head[0].Val, mcid, msg.VMMessage())
if err != nil {
return nil, nil, err
}
if r != nil {
return head[0].Val, r, nil
}
var backTs *types.TipSet
var backRcp *types.MessageReceipt
backSearchWait := make(chan struct{})
go func() {
fts, r, err := sm.searchBackForMsg(ctx, head[0].Val, msg)
if err != nil {
log.Warnf("failed to look back through chain for message: %w", err)
return
}
backTs = fts
backRcp = r
close(backSearchWait)
}()
var candidateTs *types.TipSet
var candidateRcp *types.MessageReceipt
heightOfHead := head[0].Val.Height()
reverts := map[types.TipSetKey]bool{}
for {
select {
case notif, ok := <-tsub:
if !ok {
return nil, nil, ctx.Err()
}
for _, val := range notif {
switch val.Type {
case store.HCRevert:
if val.Val.Equals(candidateTs) {
candidateTs = nil
candidateRcp = nil
}
if backSearchWait != nil {
reverts[val.Val.Key()] = true
}
case store.HCApply:
2020-06-04 02:30:09 +00:00
if candidateTs != nil && val.Val.Height() >= candidateTs.Height()+abi.ChainEpoch(confidence) {
return candidateTs, candidateRcp, nil
}
r, err := sm.tipsetExecutedMessage(val.Val, mcid, msg.VMMessage())
if err != nil {
return nil, nil, err
}
if r != nil {
if confidence == 0 {
return val.Val, r, err
}
candidateTs = val.Val
candidateRcp = r
}
heightOfHead = val.Val.Height()
}
}
case <-backSearchWait:
// check if we found the message in the chain and that is hasn't been reverted since we started searching
if backTs != nil && !reverts[backTs.Key()] {
// if head is at or past confidence interval, return immediately
2020-06-04 02:30:09 +00:00
if heightOfHead >= backTs.Height()+abi.ChainEpoch(confidence) {
return backTs, backRcp, nil
}
// wait for confidence interval
candidateTs = backTs
candidateRcp = backRcp
}
reverts = nil
backSearchWait = nil
case <-ctx.Done():
return nil, nil, ctx.Err()
}
}
}
func (sm *StateManager) SearchForMessage(ctx context.Context, mcid cid.Cid) (*types.TipSet, *types.MessageReceipt, error) {
msg, err := sm.cs.GetCMessage(mcid)
if err != nil {
return nil, nil, fmt.Errorf("failed to load message: %w", err)
}
head := sm.cs.GetHeaviestTipSet()
r, err := sm.tipsetExecutedMessage(head, mcid, msg.VMMessage())
if err != nil {
return nil, nil, err
}
if r != nil {
return head, r, nil
}
fts, r, err := sm.searchBackForMsg(ctx, head, msg)
if err != nil {
log.Warnf("failed to look back through chain for message %s", mcid)
return nil, nil, err
}
if fts == nil {
return nil, nil, nil
}
return fts, r, nil
}
2020-03-25 19:13:09 +00:00
func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet, m types.ChainMsg) (*types.TipSet, *types.MessageReceipt, error) {
cur := from
for {
if cur.Height() == 0 {
// it ain't here!
return nil, nil, nil
}
select {
case <-ctx.Done():
return nil, nil, nil
default:
}
var act types.Actor
err := sm.WithParentState(cur, sm.WithActor(m.VMMessage().From, GetActor(&act)))
if err != nil {
return nil, nil, err
}
// we either have no messages from the sender, or the latest message we found has a lower nonce than the one being searched for,
// either way, no reason to lookback, it ain't there
if act.Nonce == 0 || act.Nonce < m.VMMessage().Nonce {
return nil, nil, nil
}
ts, err := sm.cs.LoadTipSet(cur.Parents())
if err != nil {
return nil, nil, fmt.Errorf("failed to load tipset during msg wait searchback: %w", err)
}
r, err := sm.tipsetExecutedMessage(ts, m.Cid(), m.VMMessage())
if err != nil {
return nil, nil, fmt.Errorf("checking for message execution during lookback: %w", err)
}
if r != nil {
return ts, r, nil
}
cur = ts
}
}
func (sm *StateManager) tipsetExecutedMessage(ts *types.TipSet, msg cid.Cid, vmm *types.Message) (*types.MessageReceipt, error) {
// The genesis block did not execute any messages
if ts.Height() == 0 {
return nil, nil
}
pts, err := sm.cs.LoadTipSet(ts.Parents())
if err != nil {
return nil, err
}
cm, err := sm.cs.MessagesForTipset(pts)
if err != nil {
return nil, err
}
for ii := range cm {
// iterate in reverse because we going backwards through the chain
i := len(cm) - ii - 1
m := cm[i]
if m.VMMessage().From == vmm.From { // cheaper to just check origin first
if m.VMMessage().Nonce == vmm.Nonce {
if m.Cid() == msg {
return sm.cs.GetParentReceipt(ts.Blocks()[0], i)
}
// this should be that message
return nil, xerrors.Errorf("found message with equal nonce as the one we are looking for (F:%s n %d, TS: %s n%d)",
msg, vmm.Nonce, m.Cid(), m.VMMessage().Nonce)
}
if m.VMMessage().Nonce < vmm.Nonce {
return nil, nil // don't bother looking further
}
}
}
return nil, nil
}
func (sm *StateManager) ListAllActors(ctx context.Context, ts *types.TipSet) ([]address.Address, error) {
if ts == nil {
2019-11-14 06:34:54 +00:00
ts = sm.cs.GetHeaviestTipSet()
}
st, _, err := sm.TipSetState(ctx, ts)
if err != nil {
return nil, err
}
r, err := adt.AsMap(sm.cs.Store(ctx), st)
if err != nil {
return nil, err
}
var out []address.Address
err = r.ForEach(nil, func(k string) error {
addr, err := address.NewFromBytes([]byte(k))
if err != nil {
return xerrors.Errorf("address in state tree was not valid: %w", err)
}
out = append(out, addr)
return nil
})
if err != nil {
return nil, err
}
return out, nil
}
2020-02-08 02:18:32 +00:00
func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (api.MarketBalance, error) {
2020-02-25 20:35:15 +00:00
var state market.State
2020-02-25 20:54:58 +00:00
_, err := sm.LoadActorState(ctx, builtin.StorageMarketActorAddr, &state, ts)
2020-02-08 02:18:32 +00:00
if err != nil {
return api.MarketBalance{}, err
}
2020-02-08 02:18:32 +00:00
2020-02-23 15:50:36 +00:00
addr, err = sm.LookupID(ctx, addr, ts)
if err != nil {
return api.MarketBalance{}, err
}
2020-02-08 02:18:32 +00:00
var out api.MarketBalance
2020-02-23 15:50:36 +00:00
2020-04-13 21:05:34 +00:00
et, err := adt.AsBalanceTable(sm.cs.Store(ctx), state.EscrowTable)
if err != nil {
return api.MarketBalance{}, err
}
2020-02-23 15:50:36 +00:00
ehas, err := et.Has(addr)
if err != nil {
2020-02-23 15:50:36 +00:00
return api.MarketBalance{}, err
}
if ehas {
2020-07-16 12:48:20 +00:00
out.Escrow, _, err = et.Get(addr)
2020-02-23 15:50:36 +00:00
if err != nil {
return api.MarketBalance{}, xerrors.Errorf("getting escrow balance: %w", err)
}
} else {
out.Escrow = big.Zero()
}
2020-04-13 21:05:34 +00:00
lt, err := adt.AsBalanceTable(sm.cs.Store(ctx), state.LockedTable)
if err != nil {
return api.MarketBalance{}, err
}
2020-02-23 15:50:36 +00:00
lhas, err := lt.Has(addr)
2020-02-08 02:18:32 +00:00
if err != nil {
2020-02-23 15:50:36 +00:00
return api.MarketBalance{}, err
}
if lhas {
2020-07-16 12:48:20 +00:00
out.Locked, _, err = lt.Get(addr)
2020-02-23 15:50:36 +00:00
if err != nil {
return api.MarketBalance{}, xerrors.Errorf("getting locked balance: %w", err)
}
} else {
out.Locked = big.Zero()
2020-02-08 02:18:32 +00:00
}
return out, nil
}
2020-01-21 01:53:55 +00:00
func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) error {
tschain := []*types.TipSet{ts}
for ts.Height() != 0 {
next, err := sm.cs.LoadTipSet(ts.Parents())
if err != nil {
return err
}
tschain = append(tschain, next)
ts = next
}
lastState := tschain[len(tschain)-1].ParentState()
for i := len(tschain) - 1; i >= 0; i-- {
cur := tschain[i]
log.Infof("computing state (height: %d, ts=%s)", cur.Height(), cur.Cids())
if cur.ParentState() != lastState {
return xerrors.Errorf("tipset chain had state mismatch at height %d", cur.Height())
}
st, _, err := sm.TipSetState(ctx, cur)
if err != nil {
return err
}
lastState = st
}
return nil
}
2020-07-18 13:46:47 +00:00
func (sm *StateManager) SetVMConstructor(nvm func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, vm.SyscallBuilder) (*vm.VM, error)) {
sm.newVM = nvm
}