From 877ccb3803f52438d337095108944fe6434a7531 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 22 Jul 2019 20:17:42 +0200 Subject: [PATCH] make invoker work again License: MIT Signed-off-by: Jakub Sztandera --- chain/actors/actor_init.go | 7 +++++- chain/invoker.go | 27 ++++++++++---------- chain/invoker_test.go | 43 +++++++++++++++++--------------- chain/statetree.go | 7 ++---- chain/types/actor.go | 4 +++ chain/types/vmcontext.go | 4 +-- chain/vm.go | 50 ++++++++++++++++++++++---------------- 7 files changed, 80 insertions(+), 62 deletions(-) diff --git a/chain/actors/actor_init.go b/chain/actors/actor_init.go index 714560335..fdb53dd72 100644 --- a/chain/actors/actor_init.go +++ b/chain/actors/actor_init.go @@ -9,6 +9,7 @@ import ( "github.com/filecoin-project/go-lotus/chain/actors/aerrors" "github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/types" + "golang.org/x/xerrors" "github.com/ipfs/go-cid" hamt "github.com/ipfs/go-hamt-ipld" @@ -127,7 +128,11 @@ func (ia InitActor) Exec(act *types.Actor, vmctx types.VMContext, p *ExecParams) } if err := state.SetActor(idAddr, &actor); err != nil { - return nil, aerrors.Wrap(err, "inserting new actor into state tree") + if xerrors.Is(err, types.ErrActorNotFound) { + return nil, aerrors.Absorb(err, 1, "SetActor, actor not found") + } else { + return nil, aerrors.Escalate(err, "inserting new actor into state tree") + } } _, err = vmctx.Send(idAddr, 0, vmctx.Message().Value, p.Params) diff --git a/chain/invoker.go b/chain/invoker.go index 52bde0c7d..5cd32c3ec 100644 --- a/chain/invoker.go +++ b/chain/invoker.go @@ -5,17 +5,17 @@ import ( "reflect" actors "github.com/filecoin-project/go-lotus/chain/actors" + "github.com/filecoin-project/go-lotus/chain/actors/aerrors" "github.com/filecoin-project/go-lotus/chain/types" "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" - "golang.org/x/xerrors" ) type invoker struct { builtInCode map[cid.Cid]nativeCode } -type invokeFunc func(act *types.Actor, vmctx *VMContext, params []byte) (types.InvokeRet, error) +type invokeFunc func(act *types.Actor, vmctx *VMContext, params []byte) ([]byte, aerrors.ActorError) type nativeCode []invokeFunc func newInvoker() *invoker { @@ -31,14 +31,14 @@ func newInvoker() *invoker { return inv } -func (inv *invoker) Invoke(act *types.Actor, vmctx *VMContext, method uint64, params []byte) (types.InvokeRet, error) { +func (inv *invoker) Invoke(act *types.Actor, vmctx *VMContext, method uint64, params []byte) ([]byte, aerrors.ActorError) { code, ok := inv.builtInCode[act.Code] if !ok { - return types.InvokeRet{}, xerrors.Errorf("no code for actor %s", act.Code) + return nil, aerrors.Escalate(fmt.Errorf("no code for actor %s", act.Code), "") } if method >= uint64(len(code)) || code[method] == nil { - return types.InvokeRet{}, xerrors.Errorf("no method %d on actor", method) + return nil, aerrors.Escalate(fmt.Errorf("no method %d on actor", method), "") } return code[method](act, vmctx, params) @@ -57,7 +57,7 @@ type Invokee interface { } var tVMContext = reflect.TypeOf((*types.VMContext)(nil)).Elem() -var tError = reflect.TypeOf((*error)(nil)).Elem() +var tAError = reflect.TypeOf((*aerrors.ActorError)(nil)).Elem() func (*invoker) transform(instance Invokee) (nativeCode, error) { itype := reflect.TypeOf(instance) @@ -65,7 +65,7 @@ func (*invoker) transform(instance Invokee) (nativeCode, error) { for i, m := range exports { i := i newErr := func(format string, args ...interface{}) error { - str := fmt.Sprintf(format, args) + str := fmt.Sprintf(format, args...) return fmt.Errorf("transform(%s) export(%d): %s", itype.Name(), i, str) } if m == nil { @@ -96,11 +96,11 @@ func (*invoker) transform(instance Invokee) (nativeCode, error) { return nil, newErr("wrong number of outputs should be: " + "(InvokeRet, error)") } - if t.Out(0) != reflect.TypeOf(types.InvokeRet{}) { - return nil, newErr("first output should be of type InvokeRet") + if t.Out(0) != reflect.TypeOf([]byte{}) { + return nil, newErr("first output should be slice of bytes") } - if !t.Out(1).Implements(tError) { - return nil, newErr("second output should be error type") + if !t.Out(1).Implements(tAError) { + return nil, newErr("second output should be ActorError type") } } @@ -115,11 +115,12 @@ func (*invoker) transform(instance Invokee) (nativeCode, error) { inBytes := in[2].Interface().([]byte) err := cbor.DecodeInto(inBytes, param.Interface()) if err != nil { + aerr := aerrors.Absorb(err, 1, "failed to decode parameters") return []reflect.Value{ - reflect.ValueOf(types.InvokeRet{}), + reflect.ValueOf([]byte{}), // Below is a hack, fixed in Go 1.13 // https://git.io/fjXU6 - reflect.ValueOf(&err).Elem(), + reflect.ValueOf(&aerr).Elem(), } } diff --git a/chain/invoker_test.go b/chain/invoker_test.go index e6f64aed7..7afca2ab8 100644 --- a/chain/invoker_test.go +++ b/chain/invoker_test.go @@ -6,6 +6,7 @@ import ( cbor "github.com/ipfs/go-ipld-cbor" "github.com/stretchr/testify/assert" + "github.com/filecoin-project/go-lotus/chain/actors/aerrors" "github.com/filecoin-project/go-lotus/chain/types" ) @@ -35,23 +36,17 @@ func (b basicContract) Exports() []interface{} { } func (basicContract) InvokeSomething0(act *types.Actor, vmctx types.VMContext, - params *basicParams) (types.InvokeRet, error) { - return types.InvokeRet{ - ReturnCode: params.B, - }, nil + params *basicParams) ([]byte, aerrors.ActorError) { + return nil, aerrors.New(params.B, "params.B") } func (basicContract) BadParam(act *types.Actor, vmctx types.VMContext, - params *basicParams) (types.InvokeRet, error) { - return types.InvokeRet{ - ReturnCode: 255, - }, nil + params *basicParams) ([]byte, aerrors.ActorError) { + return nil, aerrors.New(255, "bad params") } func (basicContract) InvokeSomething10(act *types.Actor, vmctx types.VMContext, - params *basicParams) (types.InvokeRet, error) { - return types.InvokeRet{ - ReturnCode: params.B + 10, - }, nil + params *basicParams) ([]byte, aerrors.ActorError) { + return nil, aerrors.New(params.B+10, "params.B") } func TestInvokerBasic(t *testing.T) { @@ -63,21 +58,29 @@ func TestInvokerBasic(t *testing.T) { bParam, err := cbor.DumpObject(basicParams{B: 1}) assert.NoError(t, err) - ret, err := code[0](nil, &VMContext{}, bParam) - assert.NoError(t, err) - assert.Equal(t, byte(1), ret.ReturnCode, "return code should be 1") + _, aerr := code[0](nil, &VMContext{}, bParam) + + assert.Equal(t, byte(1), aerrors.RetCode(aerr), "return code should be 1") + if aerrors.IsFatal(aerr) { + t.Fatal("err should not be fatal") + } } { bParam, err := cbor.DumpObject(basicParams{B: 2}) assert.NoError(t, err) - ret, err := code[10](nil, &VMContext{}, bParam) - assert.NoError(t, err) - assert.Equal(t, byte(12), ret.ReturnCode, "return code should be 12") + _, aerr := code[10](nil, &VMContext{}, bParam) + assert.Equal(t, byte(12), aerrors.RetCode(aerr), "return code should be 12") + if aerrors.IsFatal(aerr) { + t.Fatal("err should not be fatal") + } } - _, err = code[1](nil, &VMContext{}, []byte{0}) - assert.Error(t, err) + _, aerr := code[1](nil, &VMContext{}, []byte{0}) + if aerrors.IsFatal(aerr) { + t.Fatal("err should not be fatal") + } + assert.Equal(t, byte(1), aerrors.RetCode(aerr), "return code should be 1") } diff --git a/chain/statetree.go b/chain/statetree.go index a19397141..2c9c91b7f 100644 --- a/chain/statetree.go +++ b/chain/statetree.go @@ -2,7 +2,6 @@ package chain import ( "context" - "fmt" "github.com/ipfs/go-cid" hamt "github.com/ipfs/go-hamt-ipld" @@ -13,8 +12,6 @@ import ( "github.com/filecoin-project/go-lotus/chain/types" ) -var ErrActorNotFound = fmt.Errorf("actor not found") - type StateTree struct { root *hamt.Node store *hamt.CborIpldStore @@ -83,7 +80,7 @@ func (st *StateTree) GetActor(addr address.Address) (*types.Actor, error) { iaddr, err := st.lookupID(addr) if err != nil { if err == hamt.ErrNotFound { - return nil, ErrActorNotFound + return nil, types.ErrActorNotFound } return nil, err } @@ -98,7 +95,7 @@ func (st *StateTree) GetActor(addr address.Address) (*types.Actor, error) { thing, err := st.root.Find(context.TODO(), string(addr.Bytes())) if err != nil { if err == hamt.ErrNotFound { - return nil, ErrActorNotFound + return nil, types.ErrActorNotFound } return nil, err } diff --git a/chain/types/actor.go b/chain/types/actor.go index 309602393..75fbf9b01 100644 --- a/chain/types/actor.go +++ b/chain/types/actor.go @@ -1,10 +1,14 @@ package types import ( + "fmt" + "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" ) +var ErrActorNotFound = fmt.Errorf("actor not found") + func init() { cbor.RegisterCborType(Actor{}) } diff --git a/chain/types/vmcontext.go b/chain/types/vmcontext.go index 8eb2ca361..b183bb7e3 100644 --- a/chain/types/vmcontext.go +++ b/chain/types/vmcontext.go @@ -19,8 +19,8 @@ type Storage interface { } type StateTree interface { - SetActor(addr address.Address, act *Actor) aerrors.ActorError - GetActor(addr address.Address) (*Actor, aerrors.ActorError) + SetActor(addr address.Address, act *Actor) error + GetActor(addr address.Address) (*Actor, error) } type VMContext interface { diff --git a/chain/vm.go b/chain/vm.go index 22114462c..44f354186 100644 --- a/chain/vm.go +++ b/chain/vm.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/filecoin-project/go-lotus/chain/actors" + "github.com/filecoin-project/go-lotus/chain/actors/aerrors" "github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/lib/bufbstore" @@ -45,21 +46,25 @@ type storage struct { head cid.Cid } -func (s *storage) Put(i interface{}) (cid.Cid, error) { - return s.cst.Put(context.TODO(), i) +func (s *storage) Put(i interface{}) (cid.Cid, aerrors.ActorError) { + c, err := s.cst.Put(context.TODO(), i) + if err != nil { + return cid.Undef, aerrors.Escalate(err, "putting cid") + } + return c, nil } -func (s *storage) Get(c cid.Cid, out interface{}) error { - return s.cst.Get(context.TODO(), c, out) +func (s *storage) Get(c cid.Cid, out interface{}) aerrors.ActorError { + return aerrors.Escalate(s.cst.Get(context.TODO(), c, out), "getting cid") } func (s *storage) GetHead() cid.Cid { return s.head } -func (s *storage) Commit(oldh, newh cid.Cid) error { +func (s *storage) Commit(oldh, newh cid.Cid) aerrors.ActorError { if s.head != oldh { - return fmt.Errorf("failed to update, inconsistent base reference") + return aerrors.New(1, "failed to update, inconsistent base reference") } s.head = newh @@ -80,7 +85,7 @@ func (vmc *VMContext) Origin() address.Address { } // Send allows the current execution context to invoke methods on other actors in the system -func (vmc *VMContext) Send(to address.Address, method uint64, value types.BigInt, params []byte) ([]byte, uint8, error) { +func (vmc *VMContext) Send(to address.Address, method uint64, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) { msg := &types.Message{ From: vmc.msg.To, @@ -92,19 +97,19 @@ func (vmc *VMContext) Send(to address.Address, method uint64, value types.BigInt toAct, err := vmc.state.GetActor(to) if err != nil { - return nil, 0, err + return nil, aerrors.Absorb(err, 2, "could not find actor for Send") } nvmctx := vmc.vm.makeVMContext(toAct.Head, vmc.origin, msg) - res, ret, err := vmc.vm.Invoke(toAct, nvmctx, method, params) - if err != nil { - return nil, 0, err + res, aerr := vmc.vm.Invoke(toAct, nvmctx, method, params) + if aerr != nil { + return nil, aerr } toAct.Head = nvmctx.Storage().GetHead() - return res, ret, err + return res, nil } // BlockHeight returns the height of the block this message was added to the chain in @@ -116,9 +121,9 @@ func (vmc *VMContext) GasUsed() types.BigInt { return types.NewInt(0) } -func (vmc *VMContext) StateTree() (types.StateTree, error) { +func (vmc *VMContext) StateTree() (types.StateTree, aerrors.ActorError) { if vmc.msg.To != actors.InitActorAddress { - return nil, fmt.Errorf("only init actor can access state tree directly") + return nil, aerrors.Escalate(fmt.Errorf("only init actor can access state tree directly"), "invalid use of StateTree") } return vmc.state, nil @@ -192,7 +197,7 @@ func (vm *VM) ApplyMessage(msg *types.Message) (*types.MessageReceipt, error) { toActor, err := st.GetActor(msg.To) if err != nil { - if err == ErrActorNotFound { + if xerrors.Is(err, types.ErrActorNotFound) { a, err := TryCreateAccountActor(st, msg.To) if err != nil { return nil, err @@ -213,18 +218,21 @@ func (vm *VM) ApplyMessage(msg *types.Message) (*types.MessageReceipt, error) { var errcode byte var ret []byte if msg.Method != 0 { - ret, errcode, err = vm.Invoke(toActor, vmctx, msg.Method, msg.Params) - if err != nil { + var err aerrors.ActorError + ret, err = vm.Invoke(toActor, vmctx, msg.Method, msg.Params) + + if aerrors.IsFatal(err) { return nil, xerrors.Errorf("during invoke: %w", err) } - if errcode != 0 { + if errcode = aerrors.RetCode(err); errcode != 0 { // revert all state changes since snapshot st.Revert() gascost := types.BigMul(vmctx.GasUsed(), msg.GasPrice) if err := DeductFunds(fromActor, gascost); err != nil { panic("invariant violated: " + err.Error()) } + } else { // Update actor head reference toActor.Head = vmctx.storage.head @@ -320,10 +328,10 @@ func (vm *VM) TransferFunds(from, to address.Address, amt types.BigInt) error { return nil } -func (vm *VM) Invoke(act *types.Actor, vmctx *VMContext, method uint64, params []byte) ([]byte, byte, error) { +func (vm *VM) Invoke(act *types.Actor, vmctx *VMContext, method uint64, params []byte) ([]byte, aerrors.ActorError) { ret, err := vm.inv.Invoke(act, vmctx, method, params) if err != nil { - return nil, 0, err + return nil, err } - return ret.Result, ret.ReturnCode, nil + return ret, nil }