Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resolve lotus, test-vectors, statediff dependency cycle #3688

Merged
merged 7 commits into from
Sep 9, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,17 @@ jobs:
cd extern/test-vectors
git fetch
git checkout origin/<< parameters.vectors-branch >>
- run:
name: go get vectors branch
command: go get github.com/filecoin-project/test-vectors@<< parameters.vectors-branch >>
- go/install-gotestsum:
gobin: $HOME/.local/bin
version: 0.5.2
- run:
name: install statediff globally
command: |
mkdir -p /tmp/statediff
git clone https://github.com/filecoin-project/statediff.git /tmp/statediff
cd /tmp/statediff
go generate ./...
go install ./cmd/statediff
Comment on lines +231 to +238
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now run statediff as a binary. This has two advantages:

  1. allows us to drop the go dependency, and thus mitigate the risk of dependency contagion (e.g. if statediff uses a newer version of go-state-types than Lotus, minimal version selection would've inadvertently chosen the newer one 😨 ).
  2. we use statediff just like other non-Go implementations would use it, which makes us even better as a reference implementation ;-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filecoin-project/statediff#15 will allow removal of the go generate

- run:
name: go test
environment:
Expand Down
232 changes: 232 additions & 0 deletions conformance/chaos/actor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
package chaos

import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/runtime"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/ipfs/go-cid"

typegen "github.com/whyrusleeping/cbor-gen"
)

//go:generate go run ./gen

// Actor is a chaos actor. It implements a variety of illegal behaviours that
// trigger violations of VM invariants. These behaviours are not found in
// production code, but are important to test that the VM constraints are
// properly enforced.
//
// The chaos actor is being incubated and its behaviour and ABI be standardised
// shortly. Its CID is ChaosActorCodeCID, and its singleton address is 98 (Address).
// It cannot be instantiated via the init actor, and its constructor panics.
//
// Test vectors relying on the chaos actor being deployed will carry selector
// "chaos_actor:true".
type Actor struct{}

// CallerValidationBranch is an enum used to select a branch in the
// CallerValidation method.
type CallerValidationBranch int64

const (
CallerValidationBranchNone CallerValidationBranch = iota
CallerValidationBranchTwice
CallerValidationBranchAddrNilSet
CallerValidationBranchTypeNilSet
)

// MutateStateBranch is an enum used to select the type of state mutation to attempt.
type MutateStateBranch int64

const (
// MutateInTransaction legally mutates state within a transaction.
MutateInTransaction MutateStateBranch = iota
// MutateReadonly ILLEGALLY mutates readonly state.
MutateReadonly
// MutateAfterTransaction ILLEGALLY mutates state after a transaction.
MutateAfterTransaction
)

const (
_ = 0 // skip zero iota value; first usage of iota gets 1.
MethodCallerValidation = builtin.MethodConstructor + iota
MethodCreateActor
MethodResolveAddress
// MethodDeleteActor is the identifier for the method that deletes this actor.
MethodDeleteActor
// MethodSend is the identifier for the method that sends a message to another actor.
MethodSend
// MethodMutateState is the identifier for the method that attempts to mutate
// a state value in the actor.
MethodMutateState
)

// Exports defines the methods this actor exposes publicly.
func (a Actor) Exports() []interface{} {
return []interface{}{
builtin.MethodConstructor: a.Constructor,
MethodCallerValidation: a.CallerValidation,
MethodCreateActor: a.CreateActor,
MethodResolveAddress: a.ResolveAddress,
MethodDeleteActor: a.DeleteActor,
MethodSend: a.Send,
MethodMutateState: a.MutateState,
}
}

var _ runtime.Invokee = Actor{}

// SendArgs are the arguments for the Send method.
type SendArgs struct {
To address.Address
Value abi.TokenAmount
Method abi.MethodNum
Params []byte
}

// SendReturn is the return values for the Send method.
type SendReturn struct {
Return runtime.CBORBytes
Code exitcode.ExitCode
}

// Send requests for this actor to send a message to an actor with the
// passed parameters.
func (a Actor) Send(rt runtime.Runtime, args *SendArgs) *SendReturn {
rt.ValidateImmediateCallerAcceptAny()
ret, code := rt.Send(
args.To,
args.Method,
runtime.CBORBytes(args.Params),
args.Value,
)
var out runtime.CBORBytes
if ret != nil {
if err := ret.Into(&out); err != nil {
rt.Abortf(exitcode.ErrIllegalState, "failed to unmarshal send return: %v", err)
}
}
return &SendReturn{
Return: out,
Code: code,
}
}

// Constructor will panic because the Chaos actor is a singleton.
func (a Actor) Constructor(_ runtime.Runtime, _ *adt.EmptyValue) *adt.EmptyValue {
panic("constructor should not be called; the Chaos actor is a singleton actor")
}

// CallerValidation violates VM call validation constraints.
//
// CallerValidationBranchNone performs no validation.
// CallerValidationBranchTwice validates twice.
// CallerValidationBranchAddrNilSet validates against an empty caller
// address set.
// CallerValidationBranchTypeNilSet validates against an empty caller type set.
func (a Actor) CallerValidation(rt runtime.Runtime, branch *typegen.CborInt) *adt.EmptyValue {
switch CallerValidationBranch(*branch) {
case CallerValidationBranchNone:
case CallerValidationBranchTwice:
rt.ValidateImmediateCallerAcceptAny()
rt.ValidateImmediateCallerAcceptAny()
case CallerValidationBranchAddrNilSet:
rt.ValidateImmediateCallerIs()
case CallerValidationBranchTypeNilSet:
rt.ValidateImmediateCallerType()
default:
panic("invalid branch passed to CallerValidation")
}

return nil
}

// CreateActorArgs are the arguments to CreateActor.
type CreateActorArgs struct {
// UndefActorCID instructs us to use cid.Undef; we can't pass cid.Undef
// in ActorCID because it doesn't serialize.
UndefActorCID bool
ActorCID cid.Cid

// UndefAddress is the same as UndefActorCID but for Address.
UndefAddress bool
Address address.Address
}

// CreateActor creates an actor with the supplied CID and Address.
func (a Actor) CreateActor(rt runtime.Runtime, args *CreateActorArgs) *adt.EmptyValue {
rt.ValidateImmediateCallerAcceptAny()

var (
acid = args.ActorCID
addr = args.Address
)

if args.UndefActorCID {
acid = cid.Undef
}
if args.UndefAddress {
addr = address.Undef
}

rt.CreateActor(acid, addr)
return nil
}

// ResolveAddressResponse holds the response of a call to runtime.ResolveAddress
type ResolveAddressResponse struct {
Address address.Address
Success bool
}

func (a Actor) ResolveAddress(rt runtime.Runtime, args *address.Address) *ResolveAddressResponse {
rt.ValidateImmediateCallerAcceptAny()

resolvedAddr, ok := rt.ResolveAddress(*args)
if !ok {
invalidAddr, _ := address.NewIDAddress(0)
resolvedAddr = invalidAddr
}
return &ResolveAddressResponse{resolvedAddr, ok}
}

// DeleteActor deletes the executing actor from the state tree, transferring any
// balance to beneficiary.
func (a Actor) DeleteActor(rt runtime.Runtime, beneficiary *address.Address) *adt.EmptyValue {
rt.ValidateImmediateCallerAcceptAny()
rt.DeleteActor(*beneficiary)
return nil
}

// MutateStateArgs specify the value to set on the state and the way in which
// it should be attempted to be set.
type MutateStateArgs struct {
Value string
Branch MutateStateBranch
}

// MutateState attempts to mutate a state value in the actor.
func (a Actor) MutateState(rt runtime.Runtime, args *MutateStateArgs) *adt.EmptyValue {
rt.ValidateImmediateCallerAcceptAny()
var st State
switch args.Branch {
case MutateInTransaction:
rt.State().Transaction(&st, func() {
st.Value = args.Value
})
case MutateReadonly:
rt.State().Readonly(&st)
st.Value = args.Value
case MutateAfterTransaction:
rt.State().Transaction(&st, func() {
st.Value = args.Value + "-in"
})
st.Value = args.Value
default:
panic("unknown mutation type")
}
return nil
}
Loading