Merge branch 'next' of github.com:filecoin-project/go-lotus into next

This commit is contained in:
Łukasz Magiera 2020-06-11 18:24:51 +02:00
commit c8046f4597
16 changed files with 199 additions and 102 deletions

View File

@ -350,11 +350,11 @@ type RetrievalOrder struct {
} }
type InvocResult struct { type InvocResult struct {
Msg *types.Message Msg *types.Message
MsgRct *types.MessageReceipt MsgRct *types.MessageReceipt
InternalExecutions []*types.ExecutionResult ExecutionTrace types.ExecutionTrace
Error string Error string
Duration time.Duration Duration time.Duration
} }
type MethodCall struct { type MethodCall struct {

View File

@ -93,7 +93,7 @@ func init() {
addExample(build.APIVersion) addExample(build.APIVersion)
addExample(api.PCHInbound) addExample(api.PCHInbound)
addExample(time.Minute) addExample(time.Minute)
addExample(&types.ExecutionResult{ addExample(&types.ExecutionTrace{
Msg: exampleValue(reflect.TypeOf(&types.Message{})).(*types.Message), Msg: exampleValue(reflect.TypeOf(&types.Message{})).(*types.Message),
MsgRct: exampleValue(reflect.TypeOf(&types.MessageReceipt{})).(*types.MessageReceipt), MsgRct: exampleValue(reflect.TypeOf(&types.MessageReceipt{})).(*types.MessageReceipt),
}) })

View File

@ -62,11 +62,11 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate
} }
return &api.InvocResult{ return &api.InvocResult{
Msg: msg, Msg: msg,
MsgRct: &ret.MessageReceipt, MsgRct: &ret.MessageReceipt,
InternalExecutions: ret.InternalExecutions, ExecutionTrace: ret.ExecutionTrace,
Error: errs, Error: errs,
Duration: ret.Duration, Duration: ret.Duration,
}, nil }, nil
} }

View File

@ -121,10 +121,10 @@ func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (c
var trace []*api.InvocResult var trace []*api.InvocResult
st, _, err := sm.computeTipSetState(ctx, ts.Blocks(), func(mcid cid.Cid, msg *types.Message, ret *vm.ApplyRet) error { st, _, err := sm.computeTipSetState(ctx, ts.Blocks(), func(mcid cid.Cid, msg *types.Message, ret *vm.ApplyRet) error {
ir := &api.InvocResult{ ir := &api.InvocResult{
Msg: msg, Msg: msg,
MsgRct: &ret.MessageReceipt, MsgRct: &ret.MessageReceipt,
InternalExecutions: ret.InternalExecutions, ExecutionTrace: ret.ExecutionTrace,
Duration: ret.Duration, Duration: ret.Duration,
} }
if ret.ActorErr != nil { if ret.ActorErr != nil {
ir.Error = ret.ActorErr.Error() ir.Error = ret.ActorErr.Error()

View File

@ -2,11 +2,11 @@ package types
import "time" import "time"
type ExecutionResult struct { type ExecutionTrace struct {
Msg *Message Msg *Message
MsgRct *MessageReceipt MsgRct *MessageReceipt
Error string Error string
Duration time.Duration Duration time.Duration
Subcalls []*ExecutionResult Subcalls []ExecutionTrace
} }

View File

@ -5,7 +5,6 @@ import (
"context" "context"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"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"
@ -51,10 +50,10 @@ type Runtime struct {
origin address.Address origin address.Address
originNonce uint64 originNonce uint64
internalExecutions []*types.ExecutionResult executionTrace types.ExecutionTrace
numActorsCreated uint64 numActorsCreated uint64
allowInternal bool allowInternal bool
callerValidated bool callerValidated bool
} }
func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount { func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount {
@ -368,7 +367,6 @@ 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() {
@ -401,27 +399,10 @@ func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum,
} }
} }
mr := types.MessageReceipt{
ExitCode: aerrors.RetCode(errSend),
Return: ret,
GasUsed: 0,
}
er := types.ExecutionResult{
Msg: msg,
MsgRct: &mr,
Duration: time.Since(start),
}
if errSend != nil {
er.Error = errSend.Error()
}
if subrt != nil { if subrt != nil {
er.Subcalls = subrt.internalExecutions
rt.numActorsCreated = subrt.numActorsCreated rt.numActorsCreated = subrt.numActorsCreated
} }
rt.internalExecutions = append(rt.internalExecutions, &er) rt.executionTrace.Subcalls = append(rt.executionTrace.Subcalls, subrt.executionTrace) //&er)
return ret, errSend return ret, errSend
} }
@ -504,13 +485,13 @@ func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
} }
func (rt *Runtime) ChargeGas(toUse int64) { func (rt *Runtime) ChargeGas(toUse int64) {
err := rt.chargeGasSafe(toUse) err := rt.chargeGasInternal(toUse)
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError { func (rt *Runtime) chargeGasInternal(toUse int64) aerrors.ActorError {
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)
@ -519,6 +500,10 @@ func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError {
return nil return nil
} }
func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError {
return rt.chargeGasInternal(toUse)
}
func (rt *Runtime) Pricelist() Pricelist { func (rt *Runtime) Pricelist() Pricelist {
return rt.pricelist return rt.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

@ -102,6 +102,7 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
pricelist: PricelistByEpoch(vm.blockHeight), pricelist: PricelistByEpoch(vm.blockHeight),
allowInternal: true, allowInternal: true,
callerValidated: false, callerValidated: false,
executionTrace: types.ExecutionTrace{Msg: msg},
} }
rt.cst = &cbor.BasicIpldStore{ rt.cst = &cbor.BasicIpldStore{
@ -163,14 +164,16 @@ type Rand interface {
type ApplyRet struct { type ApplyRet struct {
types.MessageReceipt types.MessageReceipt
ActorErr aerrors.ActorError ActorErr aerrors.ActorError
Penalty types.BigInt Penalty types.BigInt
InternalExecutions []*types.ExecutionResult ExecutionTrace types.ExecutionTrace
Duration time.Duration Duration time.Duration
} }
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 int64) ([]byte, aerrors.ActorError, *Runtime) {
start := time.Now()
st := vm.cstate st := vm.cstate
gasUsed := gasCharge gasUsed := gasCharge
@ -191,35 +194,50 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
}() }()
} }
if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil { ret, err := func() ([]byte, aerrors.ActorError) {
return nil, aerrors.Wrap(aerr, "not enough gas for method invocation"), rt if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil {
} return nil, aerrors.Wrap(aerr, "not enough gas for method invocation")
}
toActor, err := st.GetActor(msg.To) toActor, err := st.GetActor(msg.To)
if err != nil { if err != nil {
if xerrors.Is(err, init_.ErrAddressNotFound) { if xerrors.Is(err, init_.ErrAddressNotFound) {
a, err := TryCreateAccountActor(rt, msg.To) a, err := TryCreateAccountActor(rt, msg.To)
if err != nil { if err != nil {
return nil, aerrors.Wrapf(err, "could not create account"), rt return nil, aerrors.Wrapf(err, "could not create account")
}
toActor = a
} else {
return nil, aerrors.Escalate(err, "getting actor")
} }
toActor = a
} else {
return nil, aerrors.Escalate(err, "getting actor"), rt
} }
}
if types.BigCmp(msg.Value, types.NewInt(0)) != 0 { if types.BigCmp(msg.Value, types.NewInt(0)) != 0 {
if err := vm.transfer(msg.From, msg.To, msg.Value); err != nil { if err := vm.transfer(msg.From, msg.To, msg.Value); err != nil {
return nil, aerrors.Wrap(err, "failed to transfer funds"), nil return nil, aerrors.Wrap(err, "failed to transfer funds")
}
} }
if msg.Method != 0 {
var ret []byte
ret, err := vm.Invoke(toActor, rt, msg.Method, msg.Params)
return ret, err
}
return nil, nil
}()
mr := types.MessageReceipt{
ExitCode: aerrors.RetCode(err),
Return: ret,
GasUsed: rt.gasUsed,
}
rt.executionTrace.MsgRct = &mr
rt.executionTrace.Duration = time.Since(start)
if err != nil {
rt.executionTrace.Error = err.Error()
} }
if msg.Method != 0 { return ret, err, rt
ret, err := vm.Invoke(toActor, rt, msg.Method, msg.Params)
return ret, err, rt
}
return nil, nil, rt
} }
func checkMessage(msg *types.Message) error { func checkMessage(msg *types.Message) error {
@ -250,10 +268,10 @@ func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*Ap
Return: ret, Return: ret,
GasUsed: 0, GasUsed: 0,
}, },
ActorErr: actorErr, ActorErr: actorErr,
InternalExecutions: rt.internalExecutions, ExecutionTrace: rt.executionTrace,
Penalty: types.NewInt(0), Penalty: types.NewInt(0),
Duration: time.Since(start), Duration: time.Since(start),
}, actorErr }, actorErr
} }
@ -419,10 +437,10 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
Return: ret, Return: ret,
GasUsed: gasUsed, GasUsed: gasUsed,
}, },
ActorErr: actorErr, ActorErr: actorErr,
InternalExecutions: rt.internalExecutions, ExecutionTrace: rt.executionTrace,
Penalty: types.NewInt(0), Penalty: types.NewInt(0),
Duration: time.Since(start), Duration: time.Since(start),
}, nil }, nil
} }

View File

@ -930,14 +930,14 @@ var stateComputeStateCmd = &cli.Command{
if cctx.Bool("show-trace") { if cctx.Bool("show-trace") {
for _, ir := range stout.Trace { for _, ir := range stout.Trace {
fmt.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", ir.Msg.From, ir.Msg.To, ir.Msg.Value, ir.Msg.Method, ir.Msg.Params, ir.MsgRct.ExitCode, ir.MsgRct.Return) fmt.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", ir.Msg.From, ir.Msg.To, ir.Msg.Value, ir.Msg.Method, ir.Msg.Params, ir.MsgRct.ExitCode, ir.MsgRct.Return)
printInternalExecutions("\t", ir.InternalExecutions) printInternalExecutions("\t", ir.ExecutionTrace.Subcalls)
} }
} }
return nil return nil
}, },
} }
func printInternalExecutions(prefix string, trace []*types.ExecutionResult) { func printInternalExecutions(prefix string, trace []types.ExecutionTrace) {
for _, im := range trace { for _, im := range trace {
fmt.Printf("%s%s\t%s\t%s\t%d\t%x\t%d\t%x\n", prefix, im.Msg.From, im.Msg.To, im.Msg.Value, im.Msg.Method, im.Msg.Params, im.MsgRct.ExitCode, im.MsgRct.Return) fmt.Printf("%s%s\t%s\t%s\t%d\t%x\t%d\t%x\n", prefix, im.Msg.From, im.Msg.To, im.Msg.Value, im.Msg.Method, im.Msg.Params, im.MsgRct.ExitCode, im.MsgRct.Return)
printInternalExecutions(prefix+"\t", im.Subcalls) printInternalExecutions(prefix+"\t", im.Subcalls)
@ -1027,11 +1027,9 @@ func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(
fmt.Printf(`<div class="error">Error: <pre>%s</pre></div>`, ir.Error) fmt.Printf(`<div class="error">Error: <pre>%s</pre></div>`, ir.Error)
} }
if len(ir.InternalExecutions) > 0 { fmt.Println("<div>Execution trace:</div>")
fmt.Println("<div>Internal executions:</div>") if err := printInternalExecutionsHtml(cid.String(), ir.ExecutionTrace.Subcalls, getCode); err != nil {
if err := printInternalExecutionsHtml(cid.String(), ir.InternalExecutions, getCode); err != nil { return err
return err
}
} }
fmt.Println("</div>") fmt.Println("</div>")
} }
@ -1041,7 +1039,7 @@ func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(
return nil return nil
} }
func printInternalExecutionsHtml(hashName string, trace []*types.ExecutionResult, getCode func(addr address.Address) (cid.Cid, error)) error { func printInternalExecutionsHtml(hashName string, trace []types.ExecutionTrace, getCode func(addr address.Address) (cid.Cid, error)) error {
for i, im := range trace { for i, im := range trace {
hashName := fmt.Sprintf("%s-r%d", hashName, i) hashName := fmt.Sprintf("%s-r%d", hashName, i)

3
go.sum
View File

@ -42,9 +42,11 @@ github.com/akavel/rsrc v0.8.0 h1:zjWn7ukO9Kc5Q62DOJCcxGpXC18RawVtYAGdz2aLlfw=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE= github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
@ -1575,6 +1577,7 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

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

@ -242,11 +242,11 @@ func (a *StateAPI) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid.
} }
return &api.InvocResult{ return &api.InvocResult{
Msg: m, Msg: m,
MsgRct: &r.MessageReceipt, MsgRct: &r.MessageReceipt,
InternalExecutions: r.InternalExecutions, ExecutionTrace: r.ExecutionTrace,
Error: errstr, Error: errstr,
Duration: r.Duration, Duration: r.Duration,
}, nil }, nil
} }

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