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")
|
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 {
|
type ChainStore struct {
|
||||||
bs bstore.Blockstore
|
bs bstore.Blockstore
|
||||||
ds dstore.Datastore
|
ds dstore.Datastore
|
||||||
@ -63,8 +66,8 @@ type ChainStore struct {
|
|||||||
|
|
||||||
cindex *ChainIndex
|
cindex *ChainIndex
|
||||||
|
|
||||||
reorgCh chan<- reorg
|
reorgCh chan<- reorg
|
||||||
headChangeNotifs []func(rev, app []*types.TipSet) error
|
reorgNotifeeCh chan ReorgNotifee
|
||||||
|
|
||||||
mmCache *lru.ARCCache
|
mmCache *lru.ARCCache
|
||||||
tsCache *lru.ARCCache
|
tsCache *lru.ARCCache
|
||||||
@ -89,8 +92,6 @@ func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls runtime.Sys
|
|||||||
|
|
||||||
cs.cindex = ci
|
cs.cindex = ci
|
||||||
|
|
||||||
cs.reorgCh = cs.reorgWorker(context.TODO())
|
|
||||||
|
|
||||||
hcnf := func(rev, app []*types.TipSet) error {
|
hcnf := func(rev, app []*types.TipSet) error {
|
||||||
cs.pubLk.Lock()
|
cs.pubLk.Lock()
|
||||||
defer cs.pubLk.Unlock()
|
defer cs.pubLk.Unlock()
|
||||||
@ -122,7 +123,8 @@ func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls runtime.Sys
|
|||||||
return nil
|
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
|
return cs
|
||||||
}
|
}
|
||||||
@ -211,8 +213,8 @@ func (cs *ChainStore) SubHeadChanges(ctx context.Context) chan []*api.HeadChange
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *ChainStore) SubscribeHeadChanges(f func(rev, app []*types.TipSet) error) {
|
func (cs *ChainStore) SubscribeHeadChanges(f ReorgNotifee) {
|
||||||
cs.headChangeNotifs = append(cs.headChangeNotifs, f)
|
cs.reorgNotifeeCh <- f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *ChainStore) SetGenesis(b *types.BlockHeader) error {
|
func (cs *ChainStore) SetGenesis(b *types.BlockHeader) error {
|
||||||
@ -273,13 +275,19 @@ type reorg struct {
|
|||||||
new *types.TipSet
|
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)
|
out := make(chan reorg, 32)
|
||||||
|
notifees := make([]ReorgNotifee, len(initialNotifees))
|
||||||
|
copy(notifees, initialNotifees)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer log.Warn("reorgWorker quit")
|
defer log.Warn("reorgWorker quit")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case n := <-cs.reorgNotifeeCh:
|
||||||
|
notifees = append(notifees, n)
|
||||||
|
|
||||||
case r := <-out:
|
case r := <-out:
|
||||||
revert, apply, err := cs.ReorgOps(r.old, r.new)
|
revert, apply, err := cs.ReorgOps(r.old, r.new)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -293,7 +301,7 @@ func (cs *ChainStore) reorgWorker(ctx context.Context) chan<- reorg {
|
|||||||
apply[i], apply[opp] = apply[opp], apply[i]
|
apply[i], apply[opp] = apply[opp], apply[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, hcf := range cs.headChangeNotifs {
|
for _, hcf := range notifees {
|
||||||
if err := hcf(revert, apply); err != nil {
|
if err := hcf(revert, apply); err != nil {
|
||||||
log.Error("head change func errored (BAD): ", err)
|
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
|
// Phase 1: syntactic validation, as defined in the spec
|
||||||
minGas := vm.PricelistByEpoch(baseTs.Height()).OnChainMessage(msg.ChainLength())
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,100 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type ExecutionTrace struct {
|
type ExecutionTrace struct {
|
||||||
Msg *Message
|
Msg *Message
|
||||||
MsgRct *MessageReceipt
|
MsgRct *MessageReceipt
|
||||||
Error string
|
Error string
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
|
GasCharges []*GasTrace
|
||||||
|
|
||||||
Subcalls []ExecutionTrace
|
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"
|
"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.
|
// Pricelist provides prices for operations in the VM.
|
||||||
//
|
//
|
||||||
// Note: this interface should be APPEND ONLY since last chain checkpoint
|
// Note: this interface should be APPEND ONLY since last chain checkpoint
|
||||||
type Pricelist interface {
|
type Pricelist interface {
|
||||||
// OnChainMessage returns the gas used for storing a message of a given size in the chain.
|
// 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 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 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 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 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 returns the gas used for creating an actor
|
||||||
OnCreateActor() int64
|
OnCreateActor() GasCharge
|
||||||
// OnDeleteActor returns the gas used for deleting an actor
|
// OnDeleteActor returns the gas used for deleting an actor
|
||||||
OnDeleteActor() int64
|
OnDeleteActor() GasCharge
|
||||||
|
|
||||||
OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error)
|
OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error)
|
||||||
OnHashing(dataSize int) int64
|
OnHashing(dataSize int) GasCharge
|
||||||
OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64
|
OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) GasCharge
|
||||||
OnVerifySeal(info abi.SealVerifyInfo) int64
|
OnVerifySeal(info abi.SealVerifyInfo) GasCharge
|
||||||
OnVerifyPost(info abi.WindowPoStVerifyInfo) int64
|
OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge
|
||||||
OnVerifyConsensusFault() int64
|
OnVerifyConsensusFault() GasCharge
|
||||||
}
|
}
|
||||||
|
|
||||||
var prices = map[abi.ChainEpoch]Pricelist{
|
var prices = map[abi.ChainEpoch]Pricelist{
|
||||||
@ -93,7 +116,7 @@ func PricelistByEpoch(epoch abi.ChainEpoch) Pricelist {
|
|||||||
type pricedSyscalls struct {
|
type pricedSyscalls struct {
|
||||||
under vmr.Syscalls
|
under vmr.Syscalls
|
||||||
pl Pricelist
|
pl Pricelist
|
||||||
chargeGas func(int64)
|
chargeGas func(GasCharge)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifies that a signature is valid for an address and plaintext.
|
// 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) {
|
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)
|
return ps.under.BatchVerifySeals(inp)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package vm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||||
@ -84,17 +85,17 @@ type pricelistV0 struct {
|
|||||||
var _ Pricelist = (*pricelistV0)(nil)
|
var _ Pricelist = (*pricelistV0)(nil)
|
||||||
|
|
||||||
// OnChainMessage returns the gas used for storing a message of a given size in the chain.
|
// OnChainMessage returns the gas used for storing a message of a given size in the chain.
|
||||||
func (pl *pricelistV0) OnChainMessage(msgSize int) int64 {
|
func (pl *pricelistV0) OnChainMessage(msgSize int) GasCharge {
|
||||||
return pl.onChainMessageBase + pl.onChainMessagePerByte*int64(msgSize)
|
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.
|
// OnChainReturnValue returns the gas used for storing the response of a message in the chain.
|
||||||
func (pl *pricelistV0) OnChainReturnValue(dataSize int) int64 {
|
func (pl *pricelistV0) OnChainReturnValue(dataSize int) GasCharge {
|
||||||
return int64(dataSize) * pl.onChainReturnValuePerByte
|
return newGasCharge("OnChainReturnValue", 0, int64(dataSize)*pl.onChainReturnValuePerByte)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnMethodInvocation returns the gas used when invoking a method.
|
// 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
|
ret := pl.sendBase
|
||||||
if value != abi.NewTokenAmount(0) {
|
if value != abi.NewTokenAmount(0) {
|
||||||
ret += pl.sendTransferFunds
|
ret += pl.sendTransferFunds
|
||||||
@ -102,62 +103,63 @@ func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.M
|
|||||||
if methodNum != builtin.MethodSend {
|
if methodNum != builtin.MethodSend {
|
||||||
ret += pl.sendInvokeMethod
|
ret += pl.sendInvokeMethod
|
||||||
}
|
}
|
||||||
return ret
|
return newGasCharge("OnMethodInvocation", ret, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnIpldGet returns the gas used for storing an object
|
// OnIpldGet returns the gas used for storing an object
|
||||||
func (pl *pricelistV0) OnIpldGet(dataSize int) int64 {
|
func (pl *pricelistV0) OnIpldGet(dataSize int) GasCharge {
|
||||||
return pl.ipldGetBase + int64(dataSize)*pl.ipldGetPerByte
|
return newGasCharge(fmt.Sprintf("OnIpldGet:%db", dataSize), pl.ipldGetBase+int64(dataSize)*pl.ipldGetPerByte, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnIpldPut returns the gas used for storing an object
|
// OnIpldPut returns the gas used for storing an object
|
||||||
func (pl *pricelistV0) OnIpldPut(dataSize int) int64 {
|
func (pl *pricelistV0) OnIpldPut(dataSize int) GasCharge {
|
||||||
return pl.ipldPutBase + int64(dataSize)*pl.ipldPutPerByte
|
return newGasCharge(fmt.Sprintf("OnIpldPut:%db", dataSize), pl.ipldPutBase, int64(dataSize)*pl.ipldPutPerByte)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnCreateActor returns the gas used for creating an actor
|
// OnCreateActor returns the gas used for creating an actor
|
||||||
func (pl *pricelistV0) OnCreateActor() int64 {
|
func (pl *pricelistV0) OnCreateActor() GasCharge {
|
||||||
return pl.createActorBase + pl.createActorExtra
|
return newGasCharge("OnCreateActor", pl.createActorBase, pl.createActorExtra)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnDeleteActor returns the gas used for deleting an actor
|
// OnDeleteActor returns the gas used for deleting an actor
|
||||||
func (pl *pricelistV0) OnDeleteActor() int64 {
|
func (pl *pricelistV0) OnDeleteActor() GasCharge {
|
||||||
return pl.deleteActor
|
return newGasCharge("OnDeleteActor", 0, pl.deleteActor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnVerifySignature
|
// 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]
|
costFn, ok := pl.verifySignature[sigType]
|
||||||
if !ok {
|
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
|
// OnHashing
|
||||||
func (pl *pricelistV0) OnHashing(dataSize int) int64 {
|
func (pl *pricelistV0) OnHashing(dataSize int) GasCharge {
|
||||||
return pl.hashingBase + int64(dataSize)*pl.hashingPerByte
|
return newGasCharge("OnHashing", pl.hashingBase+int64(dataSize)*pl.hashingPerByte, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnComputeUnsealedSectorCid
|
// 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
|
// TODO: this needs more cost tunning, check with @lotus
|
||||||
return pl.computeUnsealedSectorCidBase
|
return newGasCharge("OnComputeUnsealedSectorCid", pl.computeUnsealedSectorCidBase, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnVerifySeal
|
// 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
|
// TODO: this needs more cost tunning, check with @lotus
|
||||||
return pl.verifySealBase
|
return newGasCharge("OnVerifySeal", pl.verifySealBase, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnVerifyPost
|
// 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
|
// TODO: this needs more cost tunning, check with @lotus
|
||||||
return pl.verifyPostBase
|
return newGasCharge("OnVerifyPost", pl.verifyPostBase, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnVerifyConsensusFault
|
// OnVerifyConsensusFault
|
||||||
func (pl *pricelistV0) OnVerifyConsensusFault() int64 {
|
func (pl *pricelistV0) OnVerifyConsensusFault() GasCharge {
|
||||||
return pl.verifyConsensusFault
|
return newGasCharge("OnVerifyConsensusFault", pl.verifyConsensusFault, 0)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
gruntime "runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||||
@ -49,10 +52,12 @@ type Runtime struct {
|
|||||||
origin address.Address
|
origin address.Address
|
||||||
originNonce uint64
|
originNonce uint64
|
||||||
|
|
||||||
executionTrace types.ExecutionTrace
|
executionTrace types.ExecutionTrace
|
||||||
numActorsCreated uint64
|
numActorsCreated uint64
|
||||||
allowInternal bool
|
allowInternal bool
|
||||||
callerValidated bool
|
callerValidated bool
|
||||||
|
lastGasChargeTime time.Time
|
||||||
|
lastGasCharge *types.GasTrace
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount {
|
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{}) {
|
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...))
|
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) {
|
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")
|
ctx, span := trace.StartSpan(rt.ctx, "vmc.Send")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if span.IsRecordingEvents() {
|
if span.IsRecordingEvents() {
|
||||||
@ -390,7 +398,7 @@ func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum,
|
|||||||
}
|
}
|
||||||
defer st.ClearSnapshot()
|
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 errSend != nil {
|
||||||
if errRevert := st.Revert(); errRevert != nil {
|
if errRevert := st.Revert(); errRevert != nil {
|
||||||
return nil, aerrors.Escalate(errRevert, "failed to revert state tree after failed subcall")
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rt *Runtime) ChargeGas(toUse int64) {
|
func (rt *Runtime) finilizeGasTracing() {
|
||||||
err := rt.chargeGasInternal(toUse)
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
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 {
|
if rt.gasUsed+toUse > rt.gasAvailable {
|
||||||
rt.gasUsed = rt.gasAvailable
|
rt.gasUsed = rt.gasAvailable
|
||||||
return aerrors.Newf(exitcode.SysErrOutOfGas, "not enough gas: used=%d, available=%d", 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError {
|
func (rt *Runtime) chargeGasSafe(gas GasCharge) aerrors.ActorError {
|
||||||
return rt.chargeGasInternal(toUse)
|
return rt.chargeGasInternal(gas, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rt *Runtime) Pricelist() Pricelist {
|
func (rt *Runtime) Pricelist() Pricelist {
|
||||||
|
@ -115,7 +115,7 @@ func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime.Consen
|
|||||||
// (b) time-offset mining fault
|
// (b) time-offset mining fault
|
||||||
// strictly speaking no need to compare heights based on double fork mining check above,
|
// strictly speaking no need to compare heights based on double fork mining check above,
|
||||||
// but at same height this would be a different fault.
|
// 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{
|
consensusFault = &runtime.ConsensusFault{
|
||||||
Target: blockA.Miner,
|
Target: blockA.Miner,
|
||||||
Epoch: blockB.Height,
|
Epoch: blockB.Height,
|
||||||
@ -159,7 +159,7 @@ func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime.Consen
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sigErr := ss.VerifyBlockSig(&blockB); sigErr != nil {
|
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
|
return consensusFault, nil
|
||||||
|
@ -62,7 +62,7 @@ func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Ad
|
|||||||
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
|
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
|
||||||
|
|
||||||
type gasChargingBlocks struct {
|
type gasChargingBlocks struct {
|
||||||
chargeGas func(int64)
|
chargeGas func(GasCharge)
|
||||||
pricelist Pricelist
|
pricelist Pricelist
|
||||||
under cbor.IpldBlockstore
|
under cbor.IpldBlockstore
|
||||||
}
|
}
|
||||||
@ -106,12 +106,12 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
|
|||||||
}
|
}
|
||||||
|
|
||||||
rt.cst = &cbor.BasicIpldStore{
|
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,
|
Atlas: vm.cst.Atlas,
|
||||||
}
|
}
|
||||||
rt.sys = pricedSyscalls{
|
rt.sys = pricedSyscalls{
|
||||||
under: vm.Syscalls,
|
under: vm.Syscalls,
|
||||||
chargeGas: rt.ChargeGas,
|
chargeGas: rt.chargeGasFunc(1),
|
||||||
pl: rt.pricelist,
|
pl: rt.pricelist,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,29 +171,40 @@ type ApplyRet struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
|
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
|
||||||
gasCharge int64) ([]byte, aerrors.ActorError, *Runtime) {
|
gasCharge *GasCharge, start time.Time) ([]byte, aerrors.ActorError, *Runtime) {
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
st := vm.cstate
|
st := vm.cstate
|
||||||
gasUsed := gasCharge
|
|
||||||
|
|
||||||
origin := msg.From
|
origin := msg.From
|
||||||
on := msg.Nonce
|
on := msg.Nonce
|
||||||
var nac uint64 = 0
|
var nac uint64 = 0
|
||||||
|
var gasUsed int64
|
||||||
if parent != nil {
|
if parent != nil {
|
||||||
gasUsed = parent.gasUsed + gasUsed
|
gasUsed = parent.gasUsed
|
||||||
origin = parent.origin
|
origin = parent.origin
|
||||||
on = parent.originNonce
|
on = parent.originNonce
|
||||||
nac = parent.numActorsCreated
|
nac = parent.numActorsCreated
|
||||||
}
|
}
|
||||||
|
|
||||||
rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac)
|
rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac)
|
||||||
|
rt.lastGasChargeTime = start
|
||||||
if parent != nil {
|
if parent != nil {
|
||||||
|
rt.lastGasChargeTime = parent.lastGasChargeTime
|
||||||
|
rt.lastGasCharge = parent.lastGasCharge
|
||||||
defer func() {
|
defer func() {
|
||||||
parent.gasUsed = rt.gasUsed
|
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) {
|
ret, err := func() ([]byte, aerrors.ActorError) {
|
||||||
if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil {
|
if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil {
|
||||||
return nil, aerrors.Wrap(aerr, "not enough gas for method invocation")
|
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) {
|
func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) {
|
||||||
start := time.Now()
|
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{
|
return &ApplyRet{
|
||||||
MessageReceipt: types.MessageReceipt{
|
MessageReceipt: types.MessageReceipt{
|
||||||
ExitCode: aerrors.RetCode(actorErr),
|
ExitCode: aerrors.RetCode(actorErr),
|
||||||
@ -294,7 +306,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
|||||||
|
|
||||||
pl := PricelistByEpoch(vm.blockHeight)
|
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
|
// this should never happen, but is currently still exercised by some tests
|
||||||
if msgGasCost > msg.GasLimit {
|
if msgGasCost > msg.GasLimit {
|
||||||
return &ApplyRet{
|
return &ApplyRet{
|
||||||
@ -377,7 +390,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
|||||||
}
|
}
|
||||||
defer st.ClearSnapshot()
|
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) {
|
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)
|
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")
|
return nil, xerrors.Errorf("gas handling math is wrong")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rt.finilizeGasTracing()
|
||||||
|
|
||||||
return &ApplyRet{
|
return &ApplyRet{
|
||||||
MessageReceipt: types.MessageReceipt{
|
MessageReceipt: types.MessageReceipt{
|
||||||
ExitCode: errcode,
|
ExitCode: errcode,
|
||||||
|
342
cli/state.go
342
cli/state.go
@ -5,6 +5,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -31,6 +33,7 @@ import (
|
|||||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
"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/reward"
|
||||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
"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/specs-actors/actors/util/adt"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
@ -40,7 +43,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type methodMeta struct {
|
type methodMeta struct {
|
||||||
name string
|
Name string
|
||||||
|
|
||||||
params reflect.Type
|
params reflect.Type
|
||||||
ret reflect.Type
|
ret reflect.Type
|
||||||
@ -68,7 +71,7 @@ func init() {
|
|||||||
nf := rt.NumField()
|
nf := rt.NumField()
|
||||||
|
|
||||||
methods[c] = append(methods[c], methodMeta{
|
methods[c] = append(methods[c], methodMeta{
|
||||||
name: "Send",
|
Name: "Send",
|
||||||
params: reflect.TypeOf(new(adt.EmptyValue)),
|
params: reflect.TypeOf(new(adt.EmptyValue)),
|
||||||
ret: reflect.TypeOf(new(adt.EmptyValue)),
|
ret: reflect.TypeOf(new(adt.EmptyValue)),
|
||||||
})
|
})
|
||||||
@ -78,7 +81,7 @@ func init() {
|
|||||||
export := reflect.TypeOf(exports[i+1])
|
export := reflect.TypeOf(exports[i+1])
|
||||||
|
|
||||||
methods[c] = append(methods[c], methodMeta{
|
methods[c] = append(methods[c], methodMeta{
|
||||||
name: rt.Field(i).Name,
|
Name: rt.Field(i).Name,
|
||||||
params: export.In(1),
|
params: export.In(1),
|
||||||
ret: export.Out(0),
|
ret: export.Out(0),
|
||||||
})
|
})
|
||||||
@ -924,7 +927,7 @@ var stateComputeStateCmd = &cli.Command{
|
|||||||
return c.Code, nil
|
return c.Code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return computeStateHtml(ts, stout, getCode)
|
return computeStateHTMLTempl(ts, stout, getCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("computed state cid: ", stout.Root)
|
fmt.Println("computed state cid: ", stout.Root)
|
||||||
@ -945,16 +948,8 @@ func printInternalExecutions(prefix string, trace []types.ExecutionTrace) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func codeStr(c cid.Cid) string {
|
var compStateTemplate = `
|
||||||
cmh, err := multihash.Decode(c.Hash())
|
<html>
|
||||||
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>
|
|
||||||
<head>
|
<head>
|
||||||
<style>
|
<style>
|
||||||
html, body { font-family: monospace; }
|
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-false { color: #660; }
|
||||||
.slow-true-true { color: #f80; }
|
.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>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div>Tipset: <b>%s</b></div>
|
<div>Tipset: <b>{{.TipSet.Key}}</b></div>
|
||||||
<div>Height: %d</div>
|
<div>Epoch: {{.TipSet.Height}}</div>
|
||||||
<div>State CID: <b>%s</b></div>
|
<div>State CID: <b>{{.Comp.Root}}</b></div>
|
||||||
<div>Calls</div>`, ts.Key(), ts.Height(), o.Root)
|
<div>Calls</div>
|
||||||
|
{{range .Comp.Trace}}
|
||||||
|
{{template "message" (Call .ExecutionTrace false .Msg.Cid.String)}}
|
||||||
|
{{end}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
||||||
for _, ir := range o.Trace {
|
var compStateMsg = `
|
||||||
toCode, err := getCode(ir.Msg.To)
|
<div class="exec" id="{{.Hash}}">
|
||||||
if err != nil {
|
{{$code := GetCode .Msg.To}}
|
||||||
return xerrors.Errorf("getting code for %s: %w", toCode, err)
|
<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)
|
<div><b>{{.Msg.From}}</b> -> <b>{{.Msg.To}}</b> ({{ToFil .Msg.Value}} FIL), M{{.Msg.Method}}</div>
|
||||||
if err != nil {
|
{{if not .Subcall}}<div><small>Msg CID: {{.Msg.Cid}}</small></div>{{end}}
|
||||||
return xerrors.Errorf("decoding params: %w", err)
|
{{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 {
|
{{if ne .MsgRct.ExitCode 0}}
|
||||||
params = `<div><pre class="params">` + params + `</pre></div>`
|
<div class="error">Error: <pre>{{.Error}}</pre></div>
|
||||||
} else {
|
{{end}}
|
||||||
params = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
ret, err := jsonReturn(toCode, ir.Msg.Method, ir.MsgRct.Return)
|
<details>
|
||||||
if err != nil {
|
<summary>Gas Trace</summary>
|
||||||
return xerrors.Errorf("decoding return value: %w", err)
|
<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 {
|
{{- if .Important }}<b>{{end -}}
|
||||||
ret = "</div>"
|
{{- . -}}
|
||||||
} else {
|
{{if .Important }}</b>{{end}}
|
||||||
ret = `, Return</div><div><pre class="ret">` + ret + `</pre></div>`
|
{{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">
|
type compStateHTMLIn struct {
|
||||||
<div><a href="#%s"><h2 class="call">%s:%s</h2></a></div>
|
TipSet *types.TipSet
|
||||||
<div><b>%s</b> -> <b>%s</b> (%s FIL), M%d</div>
|
Comp *api.ComputeStateOutput
|
||||||
<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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func printInternalExecutionsHtml(hashName string, trace []types.ExecutionTrace, getCode func(addr address.Address) (cid.Cid, error)) error {
|
func computeStateHTMLTempl(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error {
|
||||||
for i, im := range trace {
|
t, err := template.New("compute_state").Funcs(map[string]interface{}{
|
||||||
hashName := fmt.Sprintf("%s-r%d", hashName, i)
|
"GetCode": getCode,
|
||||||
|
"GetMethod": getMethod,
|
||||||
toCode, err := getCode(im.Msg.To)
|
"ToFil": toFil,
|
||||||
if err != nil {
|
"JsonParams": jsonParams,
|
||||||
return xerrors.Errorf("getting code for %s: %w", toCode, err)
|
"JsonReturn": jsonReturn,
|
||||||
}
|
"IsSlow": isSlow,
|
||||||
|
"IsVerySlow": isVerySlow,
|
||||||
params, err := jsonParams(toCode, im.Msg.Method, im.Msg.Params)
|
"IntExit": func(i exitcode.ExitCode) int64 { return int64(i) },
|
||||||
if err != nil {
|
"SumGas": sumGas,
|
||||||
return xerrors.Errorf("decoding params: %w", err)
|
"CodeStr": codeStr,
|
||||||
}
|
"Call": call,
|
||||||
|
"FirstImportant": func(locs []types.Loc) *types.Loc {
|
||||||
if len(im.Msg.Params) != 0 {
|
if len(locs) != 0 {
|
||||||
params = `<div><pre class="params">` + params + `</pre></div>`
|
for _, l := range locs {
|
||||||
} else {
|
if l.Important() {
|
||||||
params = ""
|
return &l
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ret, err := jsonReturn(toCode, im.Msg.Method, im.MsgRct.Return)
|
return &locs[0]
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
fmt.Println("</div>")
|
},
|
||||||
|
}).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) {
|
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**.
|
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
|
## Ubuntu's Uncomplicated Firewall
|
||||||
|
|
||||||
Open firewall manually:
|
Open firewall manually:
|
||||||
|
@ -188,6 +188,8 @@ func (m *Miner) mine(ctx context.Context) {
|
|||||||
log.Errorf("failed to submit newly mined block: %s", err)
|
log.Errorf("failed to submit newly mined block: %s", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
base.NullRounds++
|
||||||
|
|
||||||
// Wait until the next epoch, plus the propagation delay, so a new tipset
|
// Wait until the next epoch, plus the propagation delay, so a new tipset
|
||||||
// has enough time to form.
|
// 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)
|
return nil, xerrors.Errorf("failed to get mining base info: %w", err)
|
||||||
}
|
}
|
||||||
if mbi == nil {
|
if mbi == nil {
|
||||||
base.NullRounds++
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +279,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
|||||||
}
|
}
|
||||||
if !hasPower {
|
if !hasPower {
|
||||||
// slashed or just have no power yet
|
// slashed or just have no power yet
|
||||||
base.NullRounds++
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +302,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if winner == nil {
|
if winner == nil {
|
||||||
base.NullRounds++
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,7 +484,7 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
|
|||||||
vmstart := time.Now()
|
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
|
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)
|
log.Warnf("invalid message in message pool: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -371,6 +371,9 @@ func ConfigCommon(cfg *config.Common) Option {
|
|||||||
Override(new(dtypes.BootstrapPeers), modules.ConfigBootstrap(cfg.Libp2p.BootstrapPeers)),
|
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
|
// Libp2p contains configs for libp2p
|
||||||
type Libp2p struct {
|
type Libp2p struct {
|
||||||
ListenAddresses []string
|
ListenAddresses []string
|
||||||
BootstrapPeers []string
|
AnnounceAddresses []string
|
||||||
ProtectedPeers []string
|
NoAnnounceAddresses []string
|
||||||
|
BootstrapPeers []string
|
||||||
|
ProtectedPeers []string
|
||||||
|
|
||||||
ConnMgrLow uint
|
ConnMgrLow uint
|
||||||
ConnMgrHigh uint
|
ConnMgrHigh uint
|
||||||
@ -78,6 +80,8 @@ func defCommon() Common {
|
|||||||
"/ip4/0.0.0.0/tcp/0",
|
"/ip4/0.0.0.0/tcp/0",
|
||||||
"/ip6/::/tcp/0",
|
"/ip6/::/tcp/0",
|
||||||
},
|
},
|
||||||
|
AnnounceAddresses: []string{},
|
||||||
|
NoAnnounceAddresses: []string{},
|
||||||
|
|
||||||
ConnMgrLow: 150,
|
ConnMgrLow: 150,
|
||||||
ConnMgrHigh: 180,
|
ConnMgrHigh: 180,
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// FromFile loads config from a specified file overriding defaults specified in
|
// 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) {
|
func FromFile(path string, def interface{}) (interface{}, error) {
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
switch {
|
switch {
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/ipfs/go-datastore"
|
"github.com/ipfs/go-datastore"
|
||||||
fslock "github.com/ipfs/go-fs-lock"
|
fslock "github.com/ipfs/go-fs-lock"
|
||||||
logging "github.com/ipfs/go-log/v2"
|
logging "github.com/ipfs/go-log/v2"
|
||||||
@ -229,6 +230,7 @@ type fsLockedRepo struct {
|
|||||||
dsOnce sync.Once
|
dsOnce sync.Once
|
||||||
|
|
||||||
storageLk sync.Mutex
|
storageLk sync.Mutex
|
||||||
|
configLk sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fsr *fsLockedRepo) Path() string {
|
func (fsr *fsLockedRepo) Path() string {
|
||||||
@ -265,12 +267,50 @@ func (fsr *fsLockedRepo) stillValid() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fsr *fsLockedRepo) Config() (interface{}, error) {
|
func (fsr *fsLockedRepo) Config() (interface{}, error) {
|
||||||
if err := fsr.stillValid(); err != nil {
|
fsr.configLk.Lock()
|
||||||
return nil, err
|
defer fsr.configLk.Unlock()
|
||||||
}
|
|
||||||
|
return fsr.loadConfigFromDisk()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fsr *fsLockedRepo) loadConfigFromDisk() (interface{}, error) {
|
||||||
return config.FromFile(fsr.join(fsConfig), defConfForType(fsr.repoType))
|
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) {
|
func (fsr *fsLockedRepo) GetStorage() (stores.StorageConfig, error) {
|
||||||
fsr.storageLk.Lock()
|
fsr.storageLk.Lock()
|
||||||
defer fsr.storageLk.Unlock()
|
defer fsr.storageLk.Unlock()
|
||||||
|
@ -38,6 +38,7 @@ type LockedRepo interface {
|
|||||||
|
|
||||||
// Returns config in this repo
|
// Returns config in this repo
|
||||||
Config() (interface{}, error)
|
Config() (interface{}, error)
|
||||||
|
SetConfig(func(interface{})) error
|
||||||
|
|
||||||
GetStorage() (stores.StorageConfig, error)
|
GetStorage() (stores.StorageConfig, error)
|
||||||
SetStorage(func(*stores.StorageConfig)) error
|
SetStorage(func(*stores.StorageConfig)) error
|
||||||
|
@ -30,8 +30,16 @@ type MemRepo struct {
|
|||||||
token *byte
|
token *byte
|
||||||
|
|
||||||
datastore datastore.Datastore
|
datastore datastore.Datastore
|
||||||
configF func(t RepoType) interface{}
|
|
||||||
keystore map[string]types.KeyInfo
|
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 {
|
type lockedMemRepo struct {
|
||||||
@ -45,6 +53,10 @@ type lockedMemRepo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (lmem *lockedMemRepo) GetStorage() (stores.StorageConfig, error) {
|
func (lmem *lockedMemRepo) GetStorage() (stores.StorageConfig, error) {
|
||||||
|
if err := lmem.checkToken(); err != nil {
|
||||||
|
return stores.StorageConfig{}, err
|
||||||
|
}
|
||||||
|
|
||||||
if lmem.sc == nil {
|
if lmem.sc == nil {
|
||||||
lmem.sc = &stores.StorageConfig{StoragePaths: []stores.LocalPath{
|
lmem.sc = &stores.StorageConfig{StoragePaths: []stores.LocalPath{
|
||||||
{Path: lmem.Path()},
|
{Path: lmem.Path()},
|
||||||
@ -55,6 +67,10 @@ func (lmem *lockedMemRepo) GetStorage() (stores.StorageConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (lmem *lockedMemRepo) SetStorage(c func(*stores.StorageConfig)) error {
|
func (lmem *lockedMemRepo) SetStorage(c func(*stores.StorageConfig)) error {
|
||||||
|
if err := lmem.checkToken(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
_, _ = lmem.GetStorage()
|
_, _ = lmem.GetStorage()
|
||||||
|
|
||||||
c(lmem.sc)
|
c(lmem.sc)
|
||||||
@ -221,11 +237,32 @@ func (lmem *lockedMemRepo) Config() (interface{}, error) {
|
|||||||
if err := lmem.checkToken(); err != nil {
|
if err := lmem.checkToken(); err != nil {
|
||||||
return nil, err
|
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) {
|
func (lmem *lockedMemRepo) SetConfig(c func(interface{})) error {
|
||||||
panic("implement me")
|
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 {
|
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/chain/types"
|
||||||
"github.com/filecoin-project/lotus/node/config"
|
"github.com/filecoin-project/lotus/node/config"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func basicTest(t *testing.T, repo Repo) {
|
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.NoError(t, err, "setting multiaddr shouldn't error")
|
||||||
assert.Equal(t, ma, apima, "returned API multiaddr should be the same")
|
assert.Equal(t, ma, apima, "returned API multiaddr should be the same")
|
||||||
|
|
||||||
cfg, err := lrepo.Config()
|
c1, err := lrepo.Config()
|
||||||
assert.Equal(t, config.DefaultFullNode(), cfg, "there should be a default config")
|
assert.Equal(t, config.DefaultFullNode(), c1, "there should be a default config")
|
||||||
assert.NoError(t, err, "config should not error")
|
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()
|
err = lrepo.Close()
|
||||||
assert.NoError(t, err, "should be able to close")
|
assert.NoError(t, err, "should be able to close")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user