lotus/chain/stmgr/stmgr.go

277 lines
7.2 KiB
Go
Raw Normal View History

package stmgr
import (
"context"
2019-09-06 20:03:28 +00:00
"sync"
2019-09-27 23:55:15 +00:00
amt "github.com/filecoin-project/go-amt-ipld"
2019-10-02 15:50:10 +00:00
"github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/state"
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm"
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/go-bls-sigs"
"github.com/ipfs/go-cid"
hamt "github.com/ipfs/go-hamt-ipld"
logging "github.com/ipfs/go-log"
)
var log = logging.Logger("statemgr")
type StateManager struct {
cs *store.ChainStore
2019-09-06 20:03:28 +00:00
2019-09-27 23:55:15 +00:00
stCache map[string][]cid.Cid
2019-09-06 20:03:28 +00:00
stlk sync.Mutex
}
func NewStateManager(cs *store.ChainStore) *StateManager {
2019-09-06 20:03:28 +00:00
return &StateManager{
cs: cs,
2019-09-27 23:55:15 +00:00
stCache: make(map[string][]cid.Cid),
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
}
2019-10-02 20:03:27 +00:00
func (sm *StateManager) TipSetState(ts *types.TipSet) (cid.Cid, cid.Cid, error) {
2019-09-19 20:25:18 +00:00
ctx := context.TODO()
2019-10-02 20:03:27 +00:00
ck := cidsToKey(ts.Cids())
2019-09-06 20:03:28 +00:00
sm.stlk.Lock()
cached, ok := sm.stCache[ck]
sm.stlk.Unlock()
if ok {
2019-09-27 23:55:15 +00:00
return cached[0], cached[1], nil
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
}
2019-09-27 23:55:15 +00:00
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
}
sm.stlk.Lock()
2019-09-27 23:55:15 +00:00
sm.stCache[ck] = []cid.Cid{st, rec}
2019-09-06 20:03:28 +00:00
sm.stlk.Unlock()
2019-09-27 23:55:15 +00:00
return st, rec, nil
2019-09-06 20:03:28 +00:00
}
2019-09-27 23:55:15 +00:00
func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.BlockHeader, cb func(cid.Cid, *types.Message, *vm.ApplyRet) error) (cid.Cid, cid.Cid, error) {
pstate := blks[0].ParentStateRoot
2019-09-20 12:22:46 +00:00
cids := make([]cid.Cid, len(blks))
for i, v := range blks {
cids[i] = v.Cid()
}
r := vm.NewChainRand(sm.cs, cids, blks[0].Height, nil)
vmi, err := vm.NewVM(pstate, blks[0].Height, r, address.Undef, sm.cs)
if err != nil {
2019-09-27 23:55:15 +00:00
return cid.Undef, cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err)
}
2019-10-02 15:50:10 +00:00
netact, err := vmi.StateTree().GetActor(actors.NetworkAddress)
2019-09-27 23:55:15 +00:00
if err != nil {
2019-10-02 15:50:10 +00:00
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get network actor: %w", err)
2019-09-27 23:55:15 +00:00
}
2019-10-02 15:50:10 +00:00
reward := vm.MiningReward(netact.Balance)
for _, b := range blks {
owner, err := GetMinerOwner(ctx, sm, pstate, b.Miner)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get owner for miner %s: %w", b.Miner, err)
}
act, err := vmi.StateTree().GetActor(owner)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get miner owner actor")
}
if err := vm.DeductFunds(netact, reward); err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("failed to deduct funds from network actor: %w", err)
}
vm.DepositFunds(act, reward)
}
2019-09-27 23:55:15 +00:00
// TODO: can't use method from chainstore because it doesnt let us know who the block miners were
2019-09-27 23:55:15 +00:00
applied := make(map[address.Address]uint64)
balances := make(map[address.Address]types.BigInt)
preloadAddr := func(a address.Address) error {
if _, ok := applied[a]; !ok {
act, err := vmi.StateTree().GetActor(a)
if err != nil {
return err
}
applied[a] = act.Nonce
balances[a] = act.Balance
}
return nil
}
2019-09-27 23:55:15 +00:00
var receipts []cbg.CBORMarshaler
2019-09-19 20:25:18 +00:00
for _, b := range blks {
vmi.SetBlockMiner(b.Miner)
bms, sms, err := sm.cs.MessagesForBlock(b)
if err != nil {
2019-09-27 23:55:15 +00:00
return cid.Undef, cid.Undef, xerrors.Errorf("failed to get messages for block: %w", err)
}
cmsgs := make([]store.ChainMsg, 0, len(bms)+len(sms))
for _, m := range bms {
2019-09-27 23:55:15 +00:00
cmsgs = append(cmsgs, m)
}
for _, sm := range sms {
cmsgs = append(cmsgs, sm)
}
2019-09-27 23:55:15 +00:00
for _, cm := range cmsgs {
m := cm.VMMessage()
if err := preloadAddr(m.From); err != nil {
return cid.Undef, cid.Undef, err
}
2019-09-19 20:25:18 +00:00
2019-09-27 23:55:15 +00:00
if applied[m.From] != m.Nonce {
continue
2019-09-19 20:25:18 +00:00
}
2019-09-27 23:55:15 +00:00
applied[m.From]++
2019-09-27 23:55:15 +00:00
if balances[m.From].LessThan(m.RequiredFunds()) {
continue
}
2019-09-27 23:55:15 +00:00
balances[m.From] = types.BigSub(balances[m.From], m.RequiredFunds())
2019-09-27 23:55:15 +00:00
r, err := vmi.ApplyMessage(ctx, m)
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)
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
}
}
}
}
2019-09-27 23:55:15 +00:00
bs := amt.WrapBlockstore(sm.cs.Blockstore())
rectroot, err := amt.FromArray(bs, receipts)
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
}
2019-09-17 22:43:47 +00:00
func (sm *StateManager) GetActor(addr address.Address, ts *types.TipSet) (*types.Actor, error) {
if ts == nil {
ts = sm.cs.GetHeaviestTipSet()
}
2019-10-02 20:03:27 +00:00
stcid := ts.ParentState()
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
state, err := state.LoadStateTree(cst, stcid)
if err != nil {
return nil, xerrors.Errorf("load state tree: %w", err)
}
return state.GetActor(addr)
}
2019-09-17 22:43:47 +00:00
func (sm *StateManager) GetBalance(addr address.Address, ts *types.TipSet) (types.BigInt, error) {
act, err := sm.GetActor(addr, ts)
if err != nil {
return types.BigInt{}, xerrors.Errorf("get actor: %w", err)
}
return act.Balance, nil
}
func (sm *StateManager) ChainStore() *store.ChainStore {
return sm.cs
}
2019-09-17 22:43:47 +00:00
func (sm *StateManager) LoadActorState(ctx context.Context, a address.Address, out interface{}, ts *types.TipSet) (*types.Actor, error) {
act, err := sm.GetActor(a, ts)
if err != nil {
return nil, err
}
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
if err := cst.Get(ctx, act.Head, out); err != nil {
return nil, err
}
return act, nil
}
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()
}
st, _, err := sm.TipSetState(ts)
if err != nil {
return address.Undef, xerrors.Errorf("resolve address failed to get tipset state: %w", err)
}
cst := hamt.CSTFromBstore(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
}