Merge remote-tracking branch 'origin/next' into feat/merge-master

This commit is contained in:
Łukasz Magiera 2020-06-15 14:40:29 +02:00
commit 7296ce7287
18 changed files with 616 additions and 211 deletions

View File

@ -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)
} }

View File

@ -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
} }

View File

@ -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)
}

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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 {

View File

@ -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

View File

@ -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,

View File

@ -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> -&gt; <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> -&gt; <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> -&gt; <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) {

View File

@ -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:

View File

@ -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
} }

View File

@ -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)),
) )
} }

View File

@ -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,

View File

@ -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 {

View File

@ -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()

View File

@ -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

View File

@ -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 {

View File

@ -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")