Merge remote-tracking branch 'origin/next' into feat/merge-master
This commit is contained in:
commit
7296ce7287
@ -48,6 +48,9 @@ var log = logging.Logger("chainstore")
|
||||
|
||||
var chainHeadKey = dstore.NewKey("head")
|
||||
|
||||
// ReorgNotifee represents a callback that gets called upon reorgs.
|
||||
type ReorgNotifee func(rev, app []*types.TipSet) error
|
||||
|
||||
type ChainStore struct {
|
||||
bs bstore.Blockstore
|
||||
ds dstore.Datastore
|
||||
@ -63,8 +66,8 @@ type ChainStore struct {
|
||||
|
||||
cindex *ChainIndex
|
||||
|
||||
reorgCh chan<- reorg
|
||||
headChangeNotifs []func(rev, app []*types.TipSet) error
|
||||
reorgCh chan<- reorg
|
||||
reorgNotifeeCh chan ReorgNotifee
|
||||
|
||||
mmCache *lru.ARCCache
|
||||
tsCache *lru.ARCCache
|
||||
@ -89,8 +92,6 @@ func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls runtime.Sys
|
||||
|
||||
cs.cindex = ci
|
||||
|
||||
cs.reorgCh = cs.reorgWorker(context.TODO())
|
||||
|
||||
hcnf := func(rev, app []*types.TipSet) error {
|
||||
cs.pubLk.Lock()
|
||||
defer cs.pubLk.Unlock()
|
||||
@ -122,7 +123,8 @@ func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls runtime.Sys
|
||||
return nil
|
||||
}
|
||||
|
||||
cs.headChangeNotifs = append(cs.headChangeNotifs, hcnf, hcmetric)
|
||||
cs.reorgNotifeeCh = make(chan ReorgNotifee)
|
||||
cs.reorgCh = cs.reorgWorker(context.TODO(), []ReorgNotifee{hcnf, hcmetric})
|
||||
|
||||
return cs
|
||||
}
|
||||
@ -211,8 +213,8 @@ func (cs *ChainStore) SubHeadChanges(ctx context.Context) chan []*api.HeadChange
|
||||
return out
|
||||
}
|
||||
|
||||
func (cs *ChainStore) SubscribeHeadChanges(f func(rev, app []*types.TipSet) error) {
|
||||
cs.headChangeNotifs = append(cs.headChangeNotifs, f)
|
||||
func (cs *ChainStore) SubscribeHeadChanges(f ReorgNotifee) {
|
||||
cs.reorgNotifeeCh <- f
|
||||
}
|
||||
|
||||
func (cs *ChainStore) SetGenesis(b *types.BlockHeader) error {
|
||||
@ -273,13 +275,19 @@ type reorg struct {
|
||||
new *types.TipSet
|
||||
}
|
||||
|
||||
func (cs *ChainStore) reorgWorker(ctx context.Context) chan<- reorg {
|
||||
func (cs *ChainStore) reorgWorker(ctx context.Context, initialNotifees []ReorgNotifee) chan<- reorg {
|
||||
out := make(chan reorg, 32)
|
||||
notifees := make([]ReorgNotifee, len(initialNotifees))
|
||||
copy(notifees, initialNotifees)
|
||||
|
||||
go func() {
|
||||
defer log.Warn("reorgWorker quit")
|
||||
|
||||
for {
|
||||
select {
|
||||
case n := <-cs.reorgNotifeeCh:
|
||||
notifees = append(notifees, n)
|
||||
|
||||
case r := <-out:
|
||||
revert, apply, err := cs.ReorgOps(r.old, r.new)
|
||||
if err != nil {
|
||||
@ -293,7 +301,7 @@ func (cs *ChainStore) reorgWorker(ctx context.Context) chan<- reorg {
|
||||
apply[i], apply[opp] = apply[opp], apply[i]
|
||||
}
|
||||
|
||||
for _, hcf := range cs.headChangeNotifs {
|
||||
for _, hcf := range notifees {
|
||||
if err := hcf(revert, apply); err != nil {
|
||||
log.Error("head change func errored (BAD): ", err)
|
||||
}
|
||||
|
@ -857,7 +857,7 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
|
||||
|
||||
// Phase 1: syntactic validation, as defined in the spec
|
||||
minGas := vm.PricelistByEpoch(baseTs.Height()).OnChainMessage(msg.ChainLength())
|
||||
if err := m.ValidForBlockInclusion(minGas); err != nil {
|
||||
if err := m.ValidForBlockInclusion(minGas.Total()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,100 @@
|
||||
package types
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ExecutionTrace struct {
|
||||
Msg *Message
|
||||
MsgRct *MessageReceipt
|
||||
Error string
|
||||
Duration time.Duration
|
||||
Msg *Message
|
||||
MsgRct *MessageReceipt
|
||||
Error string
|
||||
Duration time.Duration
|
||||
GasCharges []*GasTrace
|
||||
|
||||
Subcalls []ExecutionTrace
|
||||
}
|
||||
|
||||
type GasTrace struct {
|
||||
Name string
|
||||
|
||||
Location []Loc
|
||||
TotalGas int64
|
||||
ComputeGas int64
|
||||
StorageGas int64
|
||||
|
||||
TimeTaken time.Duration
|
||||
Extra interface{}
|
||||
|
||||
Callers []uintptr `json:"-"`
|
||||
}
|
||||
|
||||
type Loc struct {
|
||||
File string
|
||||
Line int
|
||||
Function string
|
||||
}
|
||||
|
||||
func (l Loc) Show() bool {
|
||||
ignorePrefix := []string{
|
||||
"reflect.",
|
||||
"github.com/filecoin-project/lotus/chain/vm.(*Invoker).transform",
|
||||
"github.com/filecoin-project/go-amt-ipld/",
|
||||
}
|
||||
for _, pre := range ignorePrefix {
|
||||
if strings.HasPrefix(l.Function, pre) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (l Loc) String() string {
|
||||
file := strings.Split(l.File, "/")
|
||||
|
||||
fn := strings.Split(l.Function, "/")
|
||||
var fnpkg string
|
||||
if len(fn) > 2 {
|
||||
fnpkg = strings.Join(fn[len(fn)-2:], "/")
|
||||
} else {
|
||||
fnpkg = l.Function
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s@%s:%d", fnpkg, file[len(file)-1], l.Line)
|
||||
}
|
||||
|
||||
func (l Loc) Important() bool {
|
||||
if strings.HasPrefix(l.Function, "github.com/filecoin-project/specs-actors/actors/builtin") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (gt *GasTrace) MarshalJSON() ([]byte, error) {
|
||||
type GasTraceCopy GasTrace
|
||||
if len(gt.Location) == 0 {
|
||||
if len(gt.Callers) != 0 {
|
||||
frames := runtime.CallersFrames(gt.Callers)
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
if frame.Function == "github.com/filecoin-project/lotus/chain/vm.(*VM).ApplyMessage" {
|
||||
break
|
||||
}
|
||||
l := Loc{
|
||||
File: frame.File,
|
||||
Line: frame.Line,
|
||||
Function: frame.Function,
|
||||
}
|
||||
gt.Location = append(gt.Location, l)
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cpy := (*GasTraceCopy)(gt)
|
||||
return json.Marshal(cpy)
|
||||
}
|
||||
|
@ -12,34 +12,57 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
const (
|
||||
GasStorageMulti = 1
|
||||
GasComputeMulti = 1
|
||||
)
|
||||
|
||||
type GasCharge struct {
|
||||
Name string
|
||||
ComputeGas int64
|
||||
StorageGas int64
|
||||
}
|
||||
|
||||
func (g GasCharge) Total() int64 {
|
||||
return g.ComputeGas*GasComputeMulti + g.StorageGas*GasStorageMulti
|
||||
}
|
||||
|
||||
func newGasCharge(name string, computeGas int64, storageGas int64) GasCharge {
|
||||
return GasCharge{
|
||||
Name: name,
|
||||
ComputeGas: computeGas,
|
||||
StorageGas: storageGas,
|
||||
}
|
||||
}
|
||||
|
||||
// Pricelist provides prices for operations in the VM.
|
||||
//
|
||||
// Note: this interface should be APPEND ONLY since last chain checkpoint
|
||||
type Pricelist interface {
|
||||
// OnChainMessage returns the gas used for storing a message of a given size in the chain.
|
||||
OnChainMessage(msgSize int) int64
|
||||
OnChainMessage(msgSize int) GasCharge
|
||||
// OnChainReturnValue returns the gas used for storing the response of a message in the chain.
|
||||
OnChainReturnValue(dataSize int) int64
|
||||
OnChainReturnValue(dataSize int) GasCharge
|
||||
|
||||
// OnMethodInvocation returns the gas used when invoking a method.
|
||||
OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64
|
||||
OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge
|
||||
|
||||
// OnIpldGet returns the gas used for storing an object
|
||||
OnIpldGet(dataSize int) int64
|
||||
OnIpldGet(dataSize int) GasCharge
|
||||
// OnIpldPut returns the gas used for storing an object
|
||||
OnIpldPut(dataSize int) int64
|
||||
OnIpldPut(dataSize int) GasCharge
|
||||
|
||||
// OnCreateActor returns the gas used for creating an actor
|
||||
OnCreateActor() int64
|
||||
OnCreateActor() GasCharge
|
||||
// OnDeleteActor returns the gas used for deleting an actor
|
||||
OnDeleteActor() int64
|
||||
OnDeleteActor() GasCharge
|
||||
|
||||
OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error)
|
||||
OnHashing(dataSize int) int64
|
||||
OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64
|
||||
OnVerifySeal(info abi.SealVerifyInfo) int64
|
||||
OnVerifyPost(info abi.WindowPoStVerifyInfo) int64
|
||||
OnVerifyConsensusFault() int64
|
||||
OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error)
|
||||
OnHashing(dataSize int) GasCharge
|
||||
OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) GasCharge
|
||||
OnVerifySeal(info abi.SealVerifyInfo) GasCharge
|
||||
OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge
|
||||
OnVerifyConsensusFault() GasCharge
|
||||
}
|
||||
|
||||
var prices = map[abi.ChainEpoch]Pricelist{
|
||||
@ -93,7 +116,7 @@ func PricelistByEpoch(epoch abi.ChainEpoch) Pricelist {
|
||||
type pricedSyscalls struct {
|
||||
under vmr.Syscalls
|
||||
pl Pricelist
|
||||
chargeGas func(int64)
|
||||
chargeGas func(GasCharge)
|
||||
}
|
||||
|
||||
// Verifies that a signature is valid for an address and plaintext.
|
||||
@ -146,6 +169,6 @@ func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte
|
||||
}
|
||||
|
||||
func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error) {
|
||||
ps.chargeGas(0) // TODO: this is only called by the cron actor. Should we even charge gas?
|
||||
ps.chargeGas(newGasCharge("BatchVerifySeals", 0, 0)) // TODO: this is only called by the cron actor. Should we even charge gas?
|
||||
return ps.under.BatchVerifySeals(inp)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
@ -84,17 +85,17 @@ type pricelistV0 struct {
|
||||
var _ Pricelist = (*pricelistV0)(nil)
|
||||
|
||||
// OnChainMessage returns the gas used for storing a message of a given size in the chain.
|
||||
func (pl *pricelistV0) OnChainMessage(msgSize int) int64 {
|
||||
return pl.onChainMessageBase + pl.onChainMessagePerByte*int64(msgSize)
|
||||
func (pl *pricelistV0) OnChainMessage(msgSize int) GasCharge {
|
||||
return newGasCharge("OnChainMessage", 0, pl.onChainMessageBase+pl.onChainMessagePerByte*int64(msgSize))
|
||||
}
|
||||
|
||||
// OnChainReturnValue returns the gas used for storing the response of a message in the chain.
|
||||
func (pl *pricelistV0) OnChainReturnValue(dataSize int) int64 {
|
||||
return int64(dataSize) * pl.onChainReturnValuePerByte
|
||||
func (pl *pricelistV0) OnChainReturnValue(dataSize int) GasCharge {
|
||||
return newGasCharge("OnChainReturnValue", 0, int64(dataSize)*pl.onChainReturnValuePerByte)
|
||||
}
|
||||
|
||||
// OnMethodInvocation returns the gas used when invoking a method.
|
||||
func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64 {
|
||||
func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge {
|
||||
ret := pl.sendBase
|
||||
if value != abi.NewTokenAmount(0) {
|
||||
ret += pl.sendTransferFunds
|
||||
@ -102,62 +103,63 @@ func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.M
|
||||
if methodNum != builtin.MethodSend {
|
||||
ret += pl.sendInvokeMethod
|
||||
}
|
||||
return ret
|
||||
return newGasCharge("OnMethodInvocation", ret, 0)
|
||||
}
|
||||
|
||||
// OnIpldGet returns the gas used for storing an object
|
||||
func (pl *pricelistV0) OnIpldGet(dataSize int) int64 {
|
||||
return pl.ipldGetBase + int64(dataSize)*pl.ipldGetPerByte
|
||||
func (pl *pricelistV0) OnIpldGet(dataSize int) GasCharge {
|
||||
return newGasCharge(fmt.Sprintf("OnIpldGet:%db", dataSize), pl.ipldGetBase+int64(dataSize)*pl.ipldGetPerByte, 0)
|
||||
}
|
||||
|
||||
// OnIpldPut returns the gas used for storing an object
|
||||
func (pl *pricelistV0) OnIpldPut(dataSize int) int64 {
|
||||
return pl.ipldPutBase + int64(dataSize)*pl.ipldPutPerByte
|
||||
func (pl *pricelistV0) OnIpldPut(dataSize int) GasCharge {
|
||||
return newGasCharge(fmt.Sprintf("OnIpldPut:%db", dataSize), pl.ipldPutBase, int64(dataSize)*pl.ipldPutPerByte)
|
||||
}
|
||||
|
||||
// OnCreateActor returns the gas used for creating an actor
|
||||
func (pl *pricelistV0) OnCreateActor() int64 {
|
||||
return pl.createActorBase + pl.createActorExtra
|
||||
func (pl *pricelistV0) OnCreateActor() GasCharge {
|
||||
return newGasCharge("OnCreateActor", pl.createActorBase, pl.createActorExtra)
|
||||
}
|
||||
|
||||
// OnDeleteActor returns the gas used for deleting an actor
|
||||
func (pl *pricelistV0) OnDeleteActor() int64 {
|
||||
return pl.deleteActor
|
||||
func (pl *pricelistV0) OnDeleteActor() GasCharge {
|
||||
return newGasCharge("OnDeleteActor", 0, pl.deleteActor)
|
||||
}
|
||||
|
||||
// OnVerifySignature
|
||||
func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error) {
|
||||
func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error) {
|
||||
costFn, ok := pl.verifySignature[sigType]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("cost function for signature type %d not supported", sigType)
|
||||
return GasCharge{}, fmt.Errorf("cost function for signature type %d not supported", sigType)
|
||||
}
|
||||
return costFn(int64(planTextSize)), nil
|
||||
sigName, _ := sigType.Name()
|
||||
return newGasCharge("OnVerifySignature/"+sigName, costFn(int64(planTextSize)), 0), nil
|
||||
}
|
||||
|
||||
// OnHashing
|
||||
func (pl *pricelistV0) OnHashing(dataSize int) int64 {
|
||||
return pl.hashingBase + int64(dataSize)*pl.hashingPerByte
|
||||
func (pl *pricelistV0) OnHashing(dataSize int) GasCharge {
|
||||
return newGasCharge("OnHashing", pl.hashingBase+int64(dataSize)*pl.hashingPerByte, 0)
|
||||
}
|
||||
|
||||
// OnComputeUnsealedSectorCid
|
||||
func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64 {
|
||||
func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) GasCharge {
|
||||
// TODO: this needs more cost tunning, check with @lotus
|
||||
return pl.computeUnsealedSectorCidBase
|
||||
return newGasCharge("OnComputeUnsealedSectorCid", pl.computeUnsealedSectorCidBase, 0)
|
||||
}
|
||||
|
||||
// OnVerifySeal
|
||||
func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) int64 {
|
||||
func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) GasCharge {
|
||||
// TODO: this needs more cost tunning, check with @lotus
|
||||
return pl.verifySealBase
|
||||
return newGasCharge("OnVerifySeal", pl.verifySealBase, 0)
|
||||
}
|
||||
|
||||
// OnVerifyPost
|
||||
func (pl *pricelistV0) OnVerifyPost(info abi.WindowPoStVerifyInfo) int64 {
|
||||
func (pl *pricelistV0) OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge {
|
||||
// TODO: this needs more cost tunning, check with @lotus
|
||||
return pl.verifyPostBase
|
||||
return newGasCharge("OnVerifyPost", pl.verifyPostBase, 0)
|
||||
}
|
||||
|
||||
// OnVerifyConsensusFault
|
||||
func (pl *pricelistV0) OnVerifyConsensusFault() int64 {
|
||||
return pl.verifyConsensusFault
|
||||
func (pl *pricelistV0) OnVerifyConsensusFault() GasCharge {
|
||||
return newGasCharge("OnVerifyConsensusFault", pl.verifyConsensusFault, 0)
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
gruntime "runtime"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
@ -49,10 +52,12 @@ type Runtime struct {
|
||||
origin address.Address
|
||||
originNonce uint64
|
||||
|
||||
executionTrace types.ExecutionTrace
|
||||
numActorsCreated uint64
|
||||
allowInternal bool
|
||||
callerValidated bool
|
||||
executionTrace types.ExecutionTrace
|
||||
numActorsCreated uint64
|
||||
allowInternal bool
|
||||
callerValidated bool
|
||||
lastGasChargeTime time.Time
|
||||
lastGasCharge *types.GasTrace
|
||||
}
|
||||
|
||||
func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount {
|
||||
@ -307,6 +312,7 @@ func (rt *Runtime) Context() context.Context {
|
||||
}
|
||||
|
||||
func (rt *Runtime) Abortf(code exitcode.ExitCode, msg string, args ...interface{}) {
|
||||
log.Warnf("Abortf: ", fmt.Sprintf(msg, args...))
|
||||
panic(aerrors.NewfSkip(2, code, msg, args...))
|
||||
}
|
||||
|
||||
@ -365,6 +371,8 @@ func (rt *Runtime) Send(to address.Address, method abi.MethodNum, m vmr.CBORMars
|
||||
}
|
||||
|
||||
func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) {
|
||||
|
||||
start := time.Now()
|
||||
ctx, span := trace.StartSpan(rt.ctx, "vmc.Send")
|
||||
defer span.End()
|
||||
if span.IsRecordingEvents() {
|
||||
@ -390,7 +398,7 @@ func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum,
|
||||
}
|
||||
defer st.ClearSnapshot()
|
||||
|
||||
ret, errSend, subrt := rt.vm.send(ctx, msg, rt, 0)
|
||||
ret, errSend, subrt := rt.vm.send(ctx, msg, rt, nil, start)
|
||||
if errSend != nil {
|
||||
if errRevert := st.Revert(); errRevert != nil {
|
||||
return nil, aerrors.Escalate(errRevert, "failed to revert state tree after failed subcall")
|
||||
@ -482,14 +490,50 @@ func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rt *Runtime) ChargeGas(toUse int64) {
|
||||
err := rt.chargeGasInternal(toUse)
|
||||
func (rt *Runtime) finilizeGasTracing() {
|
||||
if rt.lastGasCharge != nil {
|
||||
rt.lastGasCharge.TimeTaken = time.Since(rt.lastGasChargeTime)
|
||||
}
|
||||
}
|
||||
|
||||
func (rt *Runtime) ChargeGas(gas GasCharge) {
|
||||
err := rt.chargeGasInternal(gas, 1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (rt *Runtime) chargeGasInternal(toUse int64) aerrors.ActorError {
|
||||
func (rt *Runtime) chargeGasFunc(skip int) func(GasCharge) {
|
||||
return func(gas GasCharge) {
|
||||
err := rt.chargeGasInternal(gas, 1+skip)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (rt *Runtime) chargeGasInternal(gas GasCharge, skip int) aerrors.ActorError {
|
||||
toUse := gas.Total()
|
||||
var callers [10]uintptr
|
||||
cout := gruntime.Callers(2+skip, callers[:])
|
||||
|
||||
now := time.Now()
|
||||
if rt.lastGasCharge != nil {
|
||||
rt.lastGasCharge.TimeTaken = now.Sub(rt.lastGasChargeTime)
|
||||
}
|
||||
|
||||
gasTrace := types.GasTrace{
|
||||
Name: gas.Name,
|
||||
TotalGas: toUse,
|
||||
ComputeGas: gas.ComputeGas,
|
||||
StorageGas: gas.StorageGas,
|
||||
Callers: callers[:cout],
|
||||
}
|
||||
rt.executionTrace.GasCharges = append(rt.executionTrace.GasCharges, &gasTrace)
|
||||
rt.lastGasChargeTime = now
|
||||
rt.lastGasCharge = &gasTrace
|
||||
|
||||
if rt.gasUsed+toUse > rt.gasAvailable {
|
||||
rt.gasUsed = rt.gasAvailable
|
||||
return aerrors.Newf(exitcode.SysErrOutOfGas, "not enough gas: used=%d, available=%d", rt.gasUsed, rt.gasAvailable)
|
||||
@ -498,8 +542,8 @@ func (rt *Runtime) chargeGasInternal(toUse int64) aerrors.ActorError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError {
|
||||
return rt.chargeGasInternal(toUse)
|
||||
func (rt *Runtime) chargeGasSafe(gas GasCharge) aerrors.ActorError {
|
||||
return rt.chargeGasInternal(gas, 1)
|
||||
}
|
||||
|
||||
func (rt *Runtime) Pricelist() Pricelist {
|
||||
|
@ -115,7 +115,7 @@ func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime.Consen
|
||||
// (b) time-offset mining fault
|
||||
// strictly speaking no need to compare heights based on double fork mining check above,
|
||||
// but at same height this would be a different fault.
|
||||
if !types.CidArrsEqual(blockA.Parents, blockB.Parents) && blockA.Height != blockB.Height {
|
||||
if types.CidArrsEqual(blockA.Parents, blockB.Parents) && blockA.Height != blockB.Height {
|
||||
consensusFault = &runtime.ConsensusFault{
|
||||
Target: blockA.Miner,
|
||||
Epoch: blockB.Height,
|
||||
@ -159,7 +159,7 @@ func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime.Consen
|
||||
}
|
||||
|
||||
if sigErr := ss.VerifyBlockSig(&blockB); sigErr != nil {
|
||||
return nil, xerrors.Errorf("cannot verify first block sig: %w", sigErr)
|
||||
return nil, xerrors.Errorf("cannot verify second block sig: %w", sigErr)
|
||||
}
|
||||
|
||||
return consensusFault, nil
|
||||
|
@ -62,7 +62,7 @@ func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Ad
|
||||
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
|
||||
|
||||
type gasChargingBlocks struct {
|
||||
chargeGas func(int64)
|
||||
chargeGas func(GasCharge)
|
||||
pricelist Pricelist
|
||||
under cbor.IpldBlockstore
|
||||
}
|
||||
@ -106,12 +106,12 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
|
||||
}
|
||||
|
||||
rt.cst = &cbor.BasicIpldStore{
|
||||
Blocks: &gasChargingBlocks{rt.ChargeGas, rt.pricelist, vm.cst.Blocks},
|
||||
Blocks: &gasChargingBlocks{rt.chargeGasFunc(2), rt.pricelist, vm.cst.Blocks},
|
||||
Atlas: vm.cst.Atlas,
|
||||
}
|
||||
rt.sys = pricedSyscalls{
|
||||
under: vm.Syscalls,
|
||||
chargeGas: rt.ChargeGas,
|
||||
chargeGas: rt.chargeGasFunc(1),
|
||||
pl: rt.pricelist,
|
||||
}
|
||||
|
||||
@ -171,29 +171,40 @@ type ApplyRet struct {
|
||||
}
|
||||
|
||||
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
|
||||
gasCharge int64) ([]byte, aerrors.ActorError, *Runtime) {
|
||||
start := time.Now()
|
||||
gasCharge *GasCharge, start time.Time) ([]byte, aerrors.ActorError, *Runtime) {
|
||||
|
||||
st := vm.cstate
|
||||
gasUsed := gasCharge
|
||||
|
||||
origin := msg.From
|
||||
on := msg.Nonce
|
||||
var nac uint64 = 0
|
||||
var gasUsed int64
|
||||
if parent != nil {
|
||||
gasUsed = parent.gasUsed + gasUsed
|
||||
gasUsed = parent.gasUsed
|
||||
origin = parent.origin
|
||||
on = parent.originNonce
|
||||
nac = parent.numActorsCreated
|
||||
}
|
||||
|
||||
rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac)
|
||||
rt.lastGasChargeTime = start
|
||||
if parent != nil {
|
||||
rt.lastGasChargeTime = parent.lastGasChargeTime
|
||||
rt.lastGasCharge = parent.lastGasCharge
|
||||
defer func() {
|
||||
parent.gasUsed = rt.gasUsed
|
||||
parent.lastGasChargeTime = rt.lastGasChargeTime
|
||||
parent.lastGasCharge = rt.lastGasCharge
|
||||
}()
|
||||
}
|
||||
|
||||
if gasCharge != nil {
|
||||
if err := rt.chargeGasSafe(*gasCharge); err != nil {
|
||||
// this should never happen
|
||||
return nil, aerrors.Wrap(err, "not enough gas for initial message charge, this should not happen"), rt
|
||||
}
|
||||
}
|
||||
|
||||
ret, err := func() ([]byte, aerrors.ActorError) {
|
||||
if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil {
|
||||
return nil, aerrors.Wrap(aerr, "not enough gas for method invocation")
|
||||
@ -261,7 +272,8 @@ func checkMessage(msg *types.Message) error {
|
||||
|
||||
func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) {
|
||||
start := time.Now()
|
||||
ret, actorErr, rt := vm.send(ctx, msg, nil, 0)
|
||||
ret, actorErr, rt := vm.send(ctx, msg, nil, nil, start)
|
||||
rt.finilizeGasTracing()
|
||||
return &ApplyRet{
|
||||
MessageReceipt: types.MessageReceipt{
|
||||
ExitCode: aerrors.RetCode(actorErr),
|
||||
@ -294,7 +306,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
|
||||
pl := PricelistByEpoch(vm.blockHeight)
|
||||
|
||||
msgGasCost := pl.OnChainMessage(cmsg.ChainLength())
|
||||
msgGas := pl.OnChainMessage(cmsg.ChainLength())
|
||||
msgGasCost := msgGas.Total()
|
||||
// this should never happen, but is currently still exercised by some tests
|
||||
if msgGasCost > msg.GasLimit {
|
||||
return &ApplyRet{
|
||||
@ -377,7 +390,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
}
|
||||
defer st.ClearSnapshot()
|
||||
|
||||
ret, actorErr, rt := vm.send(ctx, msg, nil, msgGasCost)
|
||||
ret, actorErr, rt := vm.send(ctx, msg, nil, &msgGas, start)
|
||||
if aerrors.IsFatal(actorErr) {
|
||||
return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
|
||||
}
|
||||
@ -431,6 +444,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
return nil, xerrors.Errorf("gas handling math is wrong")
|
||||
}
|
||||
|
||||
rt.finilizeGasTracing()
|
||||
|
||||
return &ApplyRet{
|
||||
MessageReceipt: types.MessageReceipt{
|
||||
ExitCode: errcode,
|
||||
|
342
cli/state.go
342
cli/state.go
@ -5,6 +5,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
@ -31,6 +33,7 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
@ -40,7 +43,7 @@ import (
|
||||
)
|
||||
|
||||
type methodMeta struct {
|
||||
name string
|
||||
Name string
|
||||
|
||||
params reflect.Type
|
||||
ret reflect.Type
|
||||
@ -68,7 +71,7 @@ func init() {
|
||||
nf := rt.NumField()
|
||||
|
||||
methods[c] = append(methods[c], methodMeta{
|
||||
name: "Send",
|
||||
Name: "Send",
|
||||
params: reflect.TypeOf(new(adt.EmptyValue)),
|
||||
ret: reflect.TypeOf(new(adt.EmptyValue)),
|
||||
})
|
||||
@ -78,7 +81,7 @@ func init() {
|
||||
export := reflect.TypeOf(exports[i+1])
|
||||
|
||||
methods[c] = append(methods[c], methodMeta{
|
||||
name: rt.Field(i).Name,
|
||||
Name: rt.Field(i).Name,
|
||||
params: export.In(1),
|
||||
ret: export.Out(0),
|
||||
})
|
||||
@ -924,7 +927,7 @@ var stateComputeStateCmd = &cli.Command{
|
||||
return c.Code, nil
|
||||
}
|
||||
|
||||
return computeStateHtml(ts, stout, getCode)
|
||||
return computeStateHTMLTempl(ts, stout, getCode)
|
||||
}
|
||||
|
||||
fmt.Println("computed state cid: ", stout.Root)
|
||||
@ -945,16 +948,8 @@ func printInternalExecutions(prefix string, trace []types.ExecutionTrace) {
|
||||
}
|
||||
}
|
||||
|
||||
func codeStr(c cid.Cid) string {
|
||||
cmh, err := multihash.Decode(c.Hash())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(cmh.Digest)
|
||||
}
|
||||
|
||||
func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error {
|
||||
fmt.Printf(`<html>
|
||||
var compStateTemplate = `
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html, body { font-family: monospace; }
|
||||
@ -976,123 +971,240 @@ func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(
|
||||
}
|
||||
.slow-true-false { color: #660; }
|
||||
.slow-true-true { color: #f80; }
|
||||
table {
|
||||
font-size: 12px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
tr {
|
||||
border-top: 1px solid black;
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
tr.sum { border-top: 2px solid black; }
|
||||
tr:first-child { border-top: none; }
|
||||
tr:last-child { border-bottom: none; }
|
||||
|
||||
|
||||
.ellipsis-content,
|
||||
.ellipsis-toggle input {
|
||||
display: none;
|
||||
}
|
||||
.ellipsis-toggle {
|
||||
cursor: pointer;
|
||||
}
|
||||
/**
|
||||
Checked State
|
||||
**/
|
||||
|
||||
.ellipsis-toggle input:checked + .ellipsis {
|
||||
display: none;
|
||||
}
|
||||
.ellipsis-toggle input:checked ~ .ellipsis-content {
|
||||
display: inline;
|
||||
background-color: #ddd;
|
||||
}
|
||||
hr {
|
||||
border: none;
|
||||
height: 1px;
|
||||
background-color: black;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>Tipset: <b>%s</b></div>
|
||||
<div>Height: %d</div>
|
||||
<div>State CID: <b>%s</b></div>
|
||||
<div>Calls</div>`, ts.Key(), ts.Height(), o.Root)
|
||||
<div>Tipset: <b>{{.TipSet.Key}}</b></div>
|
||||
<div>Epoch: {{.TipSet.Height}}</div>
|
||||
<div>State CID: <b>{{.Comp.Root}}</b></div>
|
||||
<div>Calls</div>
|
||||
{{range .Comp.Trace}}
|
||||
{{template "message" (Call .ExecutionTrace false .Msg.Cid.String)}}
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
for _, ir := range o.Trace {
|
||||
toCode, err := getCode(ir.Msg.To)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting code for %s: %w", toCode, err)
|
||||
}
|
||||
var compStateMsg = `
|
||||
<div class="exec" id="{{.Hash}}">
|
||||
{{$code := GetCode .Msg.To}}
|
||||
<div>
|
||||
<a href="#{{.Hash}}">
|
||||
{{if not .Subcall}}
|
||||
<h2 class="call">
|
||||
{{else}}
|
||||
<h4 class="call">
|
||||
{{end}}
|
||||
{{- CodeStr $code}}:{{GetMethod ($code) (.Msg.Method)}}
|
||||
{{if not .Subcall}}
|
||||
</h2>
|
||||
{{else}}
|
||||
</h4>
|
||||
{{end}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
params, err := jsonParams(toCode, ir.Msg.Method, ir.Msg.Params)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("decoding params: %w", err)
|
||||
}
|
||||
<div><b>{{.Msg.From}}</b> -> <b>{{.Msg.To}}</b> ({{ToFil .Msg.Value}} FIL), M{{.Msg.Method}}</div>
|
||||
{{if not .Subcall}}<div><small>Msg CID: {{.Msg.Cid}}</small></div>{{end}}
|
||||
{{if gt (len .Msg.Params) 0}}
|
||||
<div><pre class="params">{{JsonParams ($code) (.Msg.Method) (.Msg.Params) | html}}</pre></div>
|
||||
{{end}}
|
||||
<div><span class="slow-{{IsSlow .Duration}}-{{IsVerySlow .Duration}}">Took {{.Duration}}</span>, <span class="exit{{IntExit .MsgRct.ExitCode}}">Exit: <b>{{.MsgRct.ExitCode}}</b></span>{{if gt (len .MsgRct.Return) 0}}, Return{{end}}</div>
|
||||
|
||||
{{if gt (len .MsgRct.Return) 0}}
|
||||
<div><pre class="ret">{{JsonReturn ($code) (.Msg.Method) (.MsgRct.Return) | html}}</pre></div>
|
||||
{{end}}
|
||||
|
||||
if len(ir.Msg.Params) != 0 {
|
||||
params = `<div><pre class="params">` + params + `</pre></div>`
|
||||
} else {
|
||||
params = ""
|
||||
}
|
||||
{{if ne .MsgRct.ExitCode 0}}
|
||||
<div class="error">Error: <pre>{{.Error}}</pre></div>
|
||||
{{end}}
|
||||
|
||||
ret, err := jsonReturn(toCode, ir.Msg.Method, ir.MsgRct.Return)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("decoding return value: %w", err)
|
||||
}
|
||||
<details>
|
||||
<summary>Gas Trace</summary>
|
||||
<table>
|
||||
<tr><th>Name</th><th>Total/Compute/Storage</th><th>Time Taken</th><th>Location</th></tr>
|
||||
{{range .GasCharges}}
|
||||
<tr><td>{{.Name}}</td><td>{{.TotalGas}}/{{.ComputeGas}}/{{.StorageGas}}</td><td>{{.TimeTaken}}</td>
|
||||
<td>
|
||||
{{ $fImp := FirstImportant .Location }}
|
||||
{{ if $fImp }}
|
||||
<details>
|
||||
<summary>{{ $fImp }}</summary><hr />
|
||||
{{ $elipOn := false }}
|
||||
{{ range $index, $ele := .Location -}}
|
||||
{{- if $index }}<br />{{end -}}
|
||||
{{- if .Show -}}
|
||||
{{ if $elipOn }}
|
||||
{{ $elipOn = false }}
|
||||
</span></label>
|
||||
{{end}}
|
||||
|
||||
if len(ir.MsgRct.Return) == 0 {
|
||||
ret = "</div>"
|
||||
} else {
|
||||
ret = `, Return</div><div><pre class="ret">` + ret + `</pre></div>`
|
||||
}
|
||||
{{- if .Important }}<b>{{end -}}
|
||||
{{- . -}}
|
||||
{{if .Important }}</b>{{end}}
|
||||
{{else}}
|
||||
{{ if not $elipOn }}
|
||||
{{ $elipOn = true }}
|
||||
<label class="ellipsis-toggle"><input type="checkbox" /><span class="ellipsis">[…]<br /></span>
|
||||
<span class="ellipsis-content">
|
||||
{{end}}
|
||||
{{- "" -}}
|
||||
{{- . -}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{ if $elipOn }}
|
||||
{{ $elipOn = false }}
|
||||
</span></label>
|
||||
{{end}}
|
||||
</details>
|
||||
{{end}}
|
||||
</td></tr>
|
||||
{{end}}
|
||||
{{with SumGas .GasCharges}}
|
||||
<tr class="sum"><td><b>Sum</b></td><td>{{.TotalGas}}/{{.ComputeGas}}/{{.StorageGas}}</td><td>{{.TimeTaken}}</td><td></td></tr>
|
||||
{{end}}
|
||||
</table>
|
||||
</details>
|
||||
|
||||
slow := ir.Duration > 10*time.Millisecond
|
||||
veryslow := ir.Duration > 50*time.Millisecond
|
||||
|
||||
cid := ir.Msg.Cid()
|
||||
{{if gt (len .Subcalls) 0}}
|
||||
<div>Subcalls:</div>
|
||||
{{$hash := .Hash}}
|
||||
{{range .Subcalls}}
|
||||
{{template "message" (Call . true (printf "%s-%s" $hash .Msg.Cid.String))}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>`
|
||||
|
||||
fmt.Printf(`<div class="exec" id="%s">
|
||||
<div><a href="#%s"><h2 class="call">%s:%s</h2></a></div>
|
||||
<div><b>%s</b> -> <b>%s</b> (%s FIL), M%d</div>
|
||||
<div><small>Msg CID: %s</small></div>
|
||||
%s
|
||||
<div><span class="slow-%t-%t">Took %s</span>, <span class="exit%d">Exit: <b>%d</b></span>%s
|
||||
`, cid, cid, codeStr(toCode), methods[toCode][ir.Msg.Method].name, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, cid, params, slow, veryslow, ir.Duration, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ret)
|
||||
if ir.MsgRct.ExitCode != 0 {
|
||||
fmt.Printf(`<div class="error">Error: <pre>%s</pre></div>`, ir.Error)
|
||||
}
|
||||
|
||||
fmt.Println("<div>Execution trace:</div>")
|
||||
if err := printInternalExecutionsHtml(cid.String(), ir.ExecutionTrace.Subcalls, getCode); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("</div>")
|
||||
}
|
||||
|
||||
fmt.Printf(`</body>
|
||||
</html>`)
|
||||
return nil
|
||||
type compStateHTMLIn struct {
|
||||
TipSet *types.TipSet
|
||||
Comp *api.ComputeStateOutput
|
||||
}
|
||||
|
||||
func printInternalExecutionsHtml(hashName string, trace []types.ExecutionTrace, getCode func(addr address.Address) (cid.Cid, error)) error {
|
||||
for i, im := range trace {
|
||||
hashName := fmt.Sprintf("%s-r%d", hashName, i)
|
||||
|
||||
toCode, err := getCode(im.Msg.To)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting code for %s: %w", toCode, err)
|
||||
}
|
||||
|
||||
params, err := jsonParams(toCode, im.Msg.Method, im.Msg.Params)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("decoding params: %w", err)
|
||||
}
|
||||
|
||||
if len(im.Msg.Params) != 0 {
|
||||
params = `<div><pre class="params">` + params + `</pre></div>`
|
||||
} else {
|
||||
params = ""
|
||||
}
|
||||
|
||||
ret, err := jsonReturn(toCode, im.Msg.Method, im.MsgRct.Return)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("decoding return value: %w", err)
|
||||
}
|
||||
|
||||
if len(im.MsgRct.Return) == 0 {
|
||||
ret = "</div>"
|
||||
} else {
|
||||
ret = `, Return</div><div><pre class="ret">` + ret + `</pre></div>`
|
||||
}
|
||||
|
||||
slow := im.Duration > 10*time.Millisecond
|
||||
veryslow := im.Duration > 50*time.Millisecond
|
||||
|
||||
fmt.Printf(`<div class="exec" id="%s">
|
||||
<div><a href="#%s"><h4 class="call">%s:%s</h4></a></div>
|
||||
<div><b>%s</b> -> <b>%s</b> (%s FIL), M%d</div>
|
||||
%s
|
||||
<div><span class="slow-%t-%t">Took %s</span>, <span class="exit%d">Exit: <b>%d</b></span>%s
|
||||
`, hashName, hashName, codeStr(toCode), methods[toCode][im.Msg.Method].name, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, params, slow, veryslow, im.Duration, im.MsgRct.ExitCode, im.MsgRct.ExitCode, ret)
|
||||
if im.MsgRct.ExitCode != 0 {
|
||||
fmt.Printf(`<div class="error">Error: <pre>%s</pre></div>`, im.Error)
|
||||
}
|
||||
if len(im.Subcalls) > 0 {
|
||||
fmt.Println("<div>Subcalls:</div>")
|
||||
if err := printInternalExecutionsHtml(hashName, im.Subcalls, getCode); err != nil {
|
||||
return err
|
||||
func computeStateHTMLTempl(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error {
|
||||
t, err := template.New("compute_state").Funcs(map[string]interface{}{
|
||||
"GetCode": getCode,
|
||||
"GetMethod": getMethod,
|
||||
"ToFil": toFil,
|
||||
"JsonParams": jsonParams,
|
||||
"JsonReturn": jsonReturn,
|
||||
"IsSlow": isSlow,
|
||||
"IsVerySlow": isVerySlow,
|
||||
"IntExit": func(i exitcode.ExitCode) int64 { return int64(i) },
|
||||
"SumGas": sumGas,
|
||||
"CodeStr": codeStr,
|
||||
"Call": call,
|
||||
"FirstImportant": func(locs []types.Loc) *types.Loc {
|
||||
if len(locs) != 0 {
|
||||
for _, l := range locs {
|
||||
if l.Important() {
|
||||
return &l
|
||||
}
|
||||
}
|
||||
return &locs[0]
|
||||
}
|
||||
}
|
||||
fmt.Println("</div>")
|
||||
return nil
|
||||
},
|
||||
}).Parse(compStateTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t, err = t.New("message").Parse(compStateMsg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return t.ExecuteTemplate(os.Stdout, "compute_state", &compStateHTMLIn{
|
||||
TipSet: ts,
|
||||
Comp: o,
|
||||
})
|
||||
}
|
||||
|
||||
type callMeta struct {
|
||||
types.ExecutionTrace
|
||||
Subcall bool
|
||||
Hash string
|
||||
}
|
||||
|
||||
func call(e types.ExecutionTrace, subcall bool, hash string) callMeta {
|
||||
return callMeta{
|
||||
ExecutionTrace: e,
|
||||
Subcall: subcall,
|
||||
Hash: hash,
|
||||
}
|
||||
}
|
||||
|
||||
func codeStr(c cid.Cid) string {
|
||||
cmh, err := multihash.Decode(c.Hash())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(cmh.Digest)
|
||||
}
|
||||
|
||||
func getMethod(code cid.Cid, method abi.MethodNum) string {
|
||||
return methods[code][method].Name
|
||||
}
|
||||
|
||||
func toFil(f types.BigInt) types.FIL {
|
||||
return types.FIL(f)
|
||||
}
|
||||
|
||||
func isSlow(t time.Duration) bool {
|
||||
return t > 10*time.Millisecond
|
||||
}
|
||||
|
||||
func isVerySlow(t time.Duration) bool {
|
||||
return t > 50*time.Millisecond
|
||||
}
|
||||
|
||||
func sumGas(changes []*types.GasTrace) types.GasTrace {
|
||||
var out types.GasTrace
|
||||
for _, gc := range changes {
|
||||
out.TotalGas += gc.TotalGas
|
||||
out.ComputeGas += gc.ComputeGas
|
||||
out.StorageGas += gc.StorageGas
|
||||
out.TimeTaken += gc.TimeTaken
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func jsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, error) {
|
||||
|
@ -15,6 +15,20 @@ To change the port to `1347`:
|
||||
|
||||
After changing the port value, restart your **daemon**.
|
||||
|
||||
## Announce Addresses
|
||||
|
||||
If the **swarm port** is port-forwarded from another address, it is possible to control what addresses
|
||||
are announced to the network.
|
||||
|
||||
```sh
|
||||
[Libp2p]
|
||||
AnnounceAddresses = ["/ip4/<public-ip>/tcp/1347"]
|
||||
```
|
||||
|
||||
If non-empty, this array specifies the swarm addresses to announce to the network. If empty, the daemon will announce inferred swarm addresses.
|
||||
|
||||
Similarly, it is possible to set `NoAnnounceAddresses` with an array of addresses to not announce to the network.
|
||||
|
||||
## Ubuntu's Uncomplicated Firewall
|
||||
|
||||
Open firewall manually:
|
||||
|
@ -188,6 +188,8 @@ func (m *Miner) mine(ctx context.Context) {
|
||||
log.Errorf("failed to submit newly mined block: %s", err)
|
||||
}
|
||||
} else {
|
||||
base.NullRounds++
|
||||
|
||||
// Wait until the next epoch, plus the propagation delay, so a new tipset
|
||||
// has enough time to form.
|
||||
//
|
||||
@ -261,7 +263,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
return nil, xerrors.Errorf("failed to get mining base info: %w", err)
|
||||
}
|
||||
if mbi == nil {
|
||||
base.NullRounds++
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -278,7 +279,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
}
|
||||
if !hasPower {
|
||||
// slashed or just have no power yet
|
||||
base.NullRounds++
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -302,7 +302,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
}
|
||||
|
||||
if winner == nil {
|
||||
base.NullRounds++
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -485,7 +484,7 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
|
||||
vmstart := time.Now()
|
||||
|
||||
minGas := vm.PricelistByEpoch(ts.Height()).OnChainMessage(msg.ChainLength()) // TODO: really should be doing just msg.ChainLength() but the sync side of this code doesnt seem to have access to that
|
||||
if err := msg.VMMessage().ValidForBlockInclusion(minGas); err != nil {
|
||||
if err := msg.VMMessage().ValidForBlockInclusion(minGas.Total()); err != nil {
|
||||
log.Warnf("invalid message in message pool: %s", err)
|
||||
continue
|
||||
}
|
||||
|
@ -371,6 +371,9 @@ func ConfigCommon(cfg *config.Common) Option {
|
||||
Override(new(dtypes.BootstrapPeers), modules.ConfigBootstrap(cfg.Libp2p.BootstrapPeers)),
|
||||
),
|
||||
),
|
||||
Override(AddrsFactoryKey, lp2p.AddrsFactory(
|
||||
cfg.Libp2p.AnnounceAddresses,
|
||||
cfg.Libp2p.NoAnnounceAddresses)),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -39,9 +39,11 @@ type API struct {
|
||||
|
||||
// Libp2p contains configs for libp2p
|
||||
type Libp2p struct {
|
||||
ListenAddresses []string
|
||||
BootstrapPeers []string
|
||||
ProtectedPeers []string
|
||||
ListenAddresses []string
|
||||
AnnounceAddresses []string
|
||||
NoAnnounceAddresses []string
|
||||
BootstrapPeers []string
|
||||
ProtectedPeers []string
|
||||
|
||||
ConnMgrLow uint
|
||||
ConnMgrHigh uint
|
||||
@ -78,6 +80,8 @@ func defCommon() Common {
|
||||
"/ip4/0.0.0.0/tcp/0",
|
||||
"/ip6/::/tcp/0",
|
||||
},
|
||||
AnnounceAddresses: []string{},
|
||||
NoAnnounceAddresses: []string{},
|
||||
|
||||
ConnMgrLow: 150,
|
||||
ConnMgrHigh: 180,
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// FromFile loads config from a specified file overriding defaults specified in
|
||||
// the def parameter. If file does not exist or is empty defaults are asummed.
|
||||
// the def parameter. If file does not exist or is empty defaults are assumed.
|
||||
func FromFile(path string, def interface{}) (interface{}, error) {
|
||||
file, err := os.Open(path)
|
||||
switch {
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/ipfs/go-datastore"
|
||||
fslock "github.com/ipfs/go-fs-lock"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
@ -229,6 +230,7 @@ type fsLockedRepo struct {
|
||||
dsOnce sync.Once
|
||||
|
||||
storageLk sync.Mutex
|
||||
configLk sync.Mutex
|
||||
}
|
||||
|
||||
func (fsr *fsLockedRepo) Path() string {
|
||||
@ -265,12 +267,50 @@ func (fsr *fsLockedRepo) stillValid() error {
|
||||
}
|
||||
|
||||
func (fsr *fsLockedRepo) Config() (interface{}, error) {
|
||||
if err := fsr.stillValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fsr.configLk.Lock()
|
||||
defer fsr.configLk.Unlock()
|
||||
|
||||
return fsr.loadConfigFromDisk()
|
||||
}
|
||||
|
||||
func (fsr *fsLockedRepo) loadConfigFromDisk() (interface{}, error) {
|
||||
return config.FromFile(fsr.join(fsConfig), defConfForType(fsr.repoType))
|
||||
}
|
||||
|
||||
func (fsr *fsLockedRepo) SetConfig(c func(interface{})) error {
|
||||
if err := fsr.stillValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fsr.configLk.Lock()
|
||||
defer fsr.configLk.Unlock()
|
||||
|
||||
cfg, err := fsr.loadConfigFromDisk()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// mutate in-memory representation of config
|
||||
c(cfg)
|
||||
|
||||
// buffer into which we write TOML bytes
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
// encode now-mutated config as TOML and write to buffer
|
||||
err = toml.NewEncoder(buf).Encode(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write buffer of TOML bytes to config file
|
||||
err = ioutil.WriteFile(fsr.join(fsConfig), buf.Bytes(), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fsr *fsLockedRepo) GetStorage() (stores.StorageConfig, error) {
|
||||
fsr.storageLk.Lock()
|
||||
defer fsr.storageLk.Unlock()
|
||||
|
@ -38,6 +38,7 @@ type LockedRepo interface {
|
||||
|
||||
// Returns config in this repo
|
||||
Config() (interface{}, error)
|
||||
SetConfig(func(interface{})) error
|
||||
|
||||
GetStorage() (stores.StorageConfig, error)
|
||||
SetStorage(func(*stores.StorageConfig)) error
|
||||
|
@ -30,8 +30,16 @@ type MemRepo struct {
|
||||
token *byte
|
||||
|
||||
datastore datastore.Datastore
|
||||
configF func(t RepoType) interface{}
|
||||
keystore map[string]types.KeyInfo
|
||||
|
||||
// given a repo type, produce the default config
|
||||
configF func(t RepoType) interface{}
|
||||
|
||||
// holds the current config value
|
||||
config struct {
|
||||
sync.Mutex
|
||||
val interface{}
|
||||
}
|
||||
}
|
||||
|
||||
type lockedMemRepo struct {
|
||||
@ -45,6 +53,10 @@ type lockedMemRepo struct {
|
||||
}
|
||||
|
||||
func (lmem *lockedMemRepo) GetStorage() (stores.StorageConfig, error) {
|
||||
if err := lmem.checkToken(); err != nil {
|
||||
return stores.StorageConfig{}, err
|
||||
}
|
||||
|
||||
if lmem.sc == nil {
|
||||
lmem.sc = &stores.StorageConfig{StoragePaths: []stores.LocalPath{
|
||||
{Path: lmem.Path()},
|
||||
@ -55,6 +67,10 @@ func (lmem *lockedMemRepo) GetStorage() (stores.StorageConfig, error) {
|
||||
}
|
||||
|
||||
func (lmem *lockedMemRepo) SetStorage(c func(*stores.StorageConfig)) error {
|
||||
if err := lmem.checkToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _ = lmem.GetStorage()
|
||||
|
||||
c(lmem.sc)
|
||||
@ -221,11 +237,32 @@ func (lmem *lockedMemRepo) Config() (interface{}, error) {
|
||||
if err := lmem.checkToken(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return lmem.mem.configF(lmem.t), nil
|
||||
|
||||
lmem.mem.config.Lock()
|
||||
defer lmem.mem.config.Unlock()
|
||||
|
||||
if lmem.mem.config.val == nil {
|
||||
lmem.mem.config.val = lmem.mem.configF(lmem.t)
|
||||
}
|
||||
|
||||
return lmem.mem.config.val, nil
|
||||
}
|
||||
|
||||
func (lmem *lockedMemRepo) Storage() (stores.StorageConfig, error) {
|
||||
panic("implement me")
|
||||
func (lmem *lockedMemRepo) SetConfig(c func(interface{})) error {
|
||||
if err := lmem.checkToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lmem.mem.config.Lock()
|
||||
defer lmem.mem.config.Unlock()
|
||||
|
||||
if lmem.mem.config.val == nil {
|
||||
lmem.mem.config.val = lmem.mem.configF(lmem.t)
|
||||
}
|
||||
|
||||
c(lmem.mem.config.val)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lmem *lockedMemRepo) SetAPIEndpoint(ma multiaddr.Multiaddr) error {
|
||||
|
@ -9,6 +9,8 @@ import (
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/config"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func basicTest(t *testing.T, repo Repo) {
|
||||
@ -47,10 +49,23 @@ func basicTest(t *testing.T, repo Repo) {
|
||||
assert.NoError(t, err, "setting multiaddr shouldn't error")
|
||||
assert.Equal(t, ma, apima, "returned API multiaddr should be the same")
|
||||
|
||||
cfg, err := lrepo.Config()
|
||||
assert.Equal(t, config.DefaultFullNode(), cfg, "there should be a default config")
|
||||
c1, err := lrepo.Config()
|
||||
assert.Equal(t, config.DefaultFullNode(), c1, "there should be a default config")
|
||||
assert.NoError(t, err, "config should not error")
|
||||
|
||||
// mutate config and persist back to repo
|
||||
err = lrepo.SetConfig(func(c interface{}) {
|
||||
cfg := c.(*config.FullNode)
|
||||
cfg.Client.IpfsMAddr = "duvall"
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// load config and verify changes
|
||||
c2, err := lrepo.Config()
|
||||
require.NoError(t, err)
|
||||
cfg2 := c2.(*config.FullNode)
|
||||
require.Equal(t, cfg2.Client.IpfsMAddr, "duvall")
|
||||
|
||||
err = lrepo.Close()
|
||||
assert.NoError(t, err, "should be able to close")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user