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

Feat/fevm genesis eth0 #5578

Merged
merged 2 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
59 changes: 55 additions & 4 deletions app/submodule/chain/chaininfo_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package chain

import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io"
"reflect"
"time"

"github.com/filecoin-project/go-address"
Expand All @@ -15,11 +18,13 @@ import (
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2"
cbg "github.com/whyrusleeping/cbor-gen"

"github.com/filecoin-project/venus/pkg/chain"
"github.com/filecoin-project/venus/venus-shared/actors"
v1api "github.com/filecoin-project/venus/venus-shared/api/chain/v1"
"github.com/filecoin-project/venus/venus-shared/types"
"github.com/filecoin-project/venus/venus-shared/utils"
)

var _ v1api.IChainInfo = &chainInfoAPI{}
Expand Down Expand Up @@ -512,6 +517,27 @@ func (cia *chainInfoAPI) StateSearchMsg(ctx context.Context, from types.TipSetKe
return nil, nil
}

var ErrMetadataNotFound = errors.New("actor metadata not found")

func (cia *chainInfoAPI) getReturnType(ctx context.Context, to address.Address, method abi.MethodNum) (cbg.CBORUnmarshaler, error) {
ts, err := cia.ChainHead(ctx)
if err != nil {
return nil, fmt.Errorf("failed to got head %v", err)
}
act, err := cia.chain.Stmgr.GetActorAt(ctx, to, ts)
if err != nil {
return nil, fmt.Errorf("(get sset) failed to load actor: %w", err)
}

m, found := utils.MethodsMap[act.Code][method]

if !found {
return nil, fmt.Errorf("unknown method %d for actor %s: %w", method, act.Code, ErrMetadataNotFound)
}

return reflect.New(m.Ret.Elem()).Interface().(cbg.CBORUnmarshaler), nil
}

// StateWaitMsg looks back in the chain for a message. If not found, it blocks until the
// message arrives on chain, and gets to the indicated confidence depth.
func (cia *chainInfoAPI) StateWaitMsg(ctx context.Context, mCid cid.Cid, confidence uint64, lookbackLimit abi.ChainEpoch, allowReplaced bool) (*types.MsgLookup, error) {
Expand All @@ -524,11 +550,36 @@ func (cia *chainInfoAPI) StateWaitMsg(ctx context.Context, mCid cid.Cid, confide
return nil, err
}
if msgResult != nil {
var returndec interface{}
recpt := msgResult.Receipt
if recpt.ExitCode == 0 && len(recpt.Return) > 0 {
vmsg := chainMsg.VMMessage()
t, err := cia.getReturnType(ctx, vmsg.To, vmsg.Method)
if err != nil {
if errors.Is(err, ErrMetadataNotFound) {
// This is not nececessary an error -- EVM methods (and in the future native actors) may
// return just bytes, and in the not so distant future we'll have native wasm actors
// that are by definition not in the registry.
// So in this case, log a debug message and retun the raw bytes.
log.Debugf("failed to get return type: %s", err)
returndec = recpt.Return
} else {
return nil, fmt.Errorf("failed to get return type: %w", err)
}
} else {
if err := t.UnmarshalCBOR(bytes.NewReader(recpt.Return)); err != nil {
return nil, err
}
returndec = t
}
}

return &types.MsgLookup{
Message: mCid,
Receipt: *msgResult.Receipt,
TipSet: msgResult.TS.Key(),
Height: msgResult.TS.Height(),
Message: mCid,
Receipt: *msgResult.Receipt,
ReturnDec: returndec,
TipSet: msgResult.TS.Key(),
Height: msgResult.TS.Height(),
}, nil
}
return nil, nil
Expand Down
124 changes: 124 additions & 0 deletions pkg/gen/genesis/fevm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package genesis

import (
"context"
"encoding/hex"
"fmt"

"github.com/ipfs/go-cid"

"github.com/filecoin-project/go-state-types/abi"
actorstypes "github.com/filecoin-project/go-state-types/actors"
"github.com/filecoin-project/go-state-types/big"
builtintypes "github.com/filecoin-project/go-state-types/builtin"
evm10 "github.com/filecoin-project/go-state-types/builtin/v10/evm"
init10 "github.com/filecoin-project/go-state-types/builtin/v10/init"
"github.com/filecoin-project/go-state-types/network"
"github.com/filecoin-project/venus/pkg/chain"
"github.com/filecoin-project/venus/pkg/config"
"github.com/filecoin-project/venus/pkg/consensusfault"
"github.com/filecoin-project/venus/pkg/fork"
"github.com/filecoin-project/venus/pkg/fvm"
"github.com/filecoin-project/venus/pkg/state/tree"
"github.com/filecoin-project/venus/pkg/util/ffiwrapper/impl"
"github.com/filecoin-project/venus/pkg/vm"
"github.com/filecoin-project/venus/pkg/vm/gas"
"github.com/filecoin-project/venus/pkg/vm/vmcontext"
"github.com/filecoin-project/venus/pkg/vmsupport"
"github.com/filecoin-project/venus/venus-shared/actors"
)

func SetupFEVM(ctx context.Context, cs *chain.Store, sroot cid.Cid, nv network.Version, para *config.ForkUpgradeConfig) (cid.Cid, error) {
av, err := actorstypes.VersionForNetwork(nv)
if err != nil {
return cid.Undef, fmt.Errorf("failed to get actors version for network version %d: %w", nv, err)
}

if av < actorstypes.Version10 {
// Not defined before version 10; migration has to setup.
return sroot, nil
}

csc := func(context.Context, abi.ChainEpoch, tree.Tree) (abi.TokenAmount, error) {
return big.Zero(), nil
}
faultChecker := consensusfault.NewFaultChecker(cs, fork.NewMockFork())
syscalls := vmsupport.NewSyscalls(faultChecker, impl.ProofVerifier)
gasPirceSchedule := gas.NewPricesSchedule(para)

newVM := func(base cid.Cid) (vmcontext.Interface, error) {
vmopt := vmcontext.VmOption{
PRoot: base,
Epoch: 0,
Rnd: &fakeRand{},
Bsstore: cs.Blockstore(),
ActorCodeLoader: vm.GetDefaultActors(),
SysCallsImpl: mkFakedSigSyscalls(syscalls),
CircSupplyCalculator: csc,
NetworkVersion: nv,
BaseFee: big.Zero(),
GasPriceSchedule: gasPirceSchedule,
}

return fvm.NewVM(ctx, vmopt)
}

genesisVm, err := newVM(sroot)
if err != nil {
return cid.Undef, fmt.Errorf("creating vm: %w", err)
}

// The ETH0 address is occupied by an empty contract EVM actor
evmCodeCid, ok := actors.GetActorCodeID(av, actors.EvmKey)
if !ok {
return cid.Undef, fmt.Errorf("failed to get CodeCID for EVM during genesis")
}

// initcode:
// %push(code_end - code_begin)
// dup1
// %push(code_begin)
// push1 0x00
// codecopy
// push1 0x00
// return
// code_begin:
// push1 0x00
// push1 0x00
// return
// code_end:
initcode, err := hex.DecodeString("600580600b6000396000f360006000f3")
if err != nil {
return cid.Undef, fmt.Errorf("failed to parse ETH0 init code during genesis: %w", err)
}

ctorParams := &evm10.ConstructorParams{
Creator: make([]byte, 20), // self!
// TODO we have a bunch of bugs in the evm constructor around empty contracts
// - empty init code is not allowed
// - returning an empty contract is not allowed
// So this uses code that constructs a just return contract until that can be fixed
// and we can pass an empty byte array
Initcode: initcode,
}

params := &init10.Exec4Params{
CodeCID: evmCodeCid,
ConstructorParams: mustEnc(ctorParams),
SubAddress: make([]byte, 20),
}

// TODO method 3 is Exec4; we have to name the methods in go-state-types and avoid using the number
// directly.
if _, err := doExecValue(ctx, genesisVm, builtintypes.InitActorAddr, builtintypes.EthereumAddressManagerActorAddr, big.Zero(), 3, mustEnc(params)); err != nil {
return cid.Undef, fmt.Errorf("creating ETH0 actor: %w", err)
}

newroot, err := genesisVm.Flush(ctx)
if err != nil {
return cid.Undef, fmt.Errorf("flushing vm: %w", err)
}

return newroot, nil

}
38 changes: 38 additions & 0 deletions pkg/gen/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/filecoin-project/venus/pkg/config"
"github.com/filecoin-project/venus/pkg/vm/gas"
"github.com/filecoin-project/venus/pkg/vm/vmcontext"

"github.com/filecoin-project/venus/pkg/constants"
"github.com/filecoin-project/venus/pkg/state/tree"
Expand All @@ -26,6 +27,7 @@ import (
"github.com/filecoin-project/venus/venus-shared/actors/builtin/datacap"

actorstypes "github.com/filecoin-project/go-state-types/actors"
builtintypes "github.com/filecoin-project/go-state-types/builtin"
"github.com/filecoin-project/go-state-types/network"

"github.com/filecoin-project/venus/venus-shared/actors"
Expand Down Expand Up @@ -583,6 +585,11 @@ func MakeGenesisBlock(ctx context.Context, rep repo.Repo, bs bstore.Blockstore,
return nil, fmt.Errorf("make initial state tree failed: %w", err)
}

// Set up the Ethereum Address Manager
if err = SetupEAM(ctx, st, template.NetworkVersion); err != nil {
return nil, fmt.Errorf("failed to setup EAM: %w", err)
}

stateroot, err := st.Flush(ctx)
if err != nil {
return nil, fmt.Errorf("flush state tree failed: %w", err)
Expand All @@ -602,6 +609,12 @@ func MakeGenesisBlock(ctx context.Context, rep repo.Repo, bs bstore.Blockstore,
return nil, fmt.Errorf("setup miners failed: %w", err)
}

// setup FEVM
stateroot, err = SetupFEVM(ctx, cs, stateroot, template.NetworkVersion, para)
if err != nil {
return nil, fmt.Errorf("failed to setup FEVM functionality: %w", err)
}

store := adt.WrapStore(ctx, cbor.NewCborStore(bs))
emptyroot, err := adt0.MakeEmptyArray(store).Root()
if err != nil {
Expand Down Expand Up @@ -685,3 +698,28 @@ func MakeGenesisBlock(ctx context.Context, rep repo.Repo, bs bstore.Blockstore,
Genesis: b,
}, nil
}

func SetupEAM(ctx context.Context, nst tree.Tree, nv network.Version) error {
av, err := actorstypes.VersionForNetwork(nv)
if err != nil {
return fmt.Errorf("failed to get actors version for network version %d: %w", nv, err)
}

if av < actorstypes.Version10 {
// Not defined before version 10; migration has to create.
return nil
}

codecid, ok := actors.GetActorCodeID(av, actors.EamKey)
if !ok {
return fmt.Errorf("failed to get CodeCID for EAM during genesis")
}

header := &types.Actor{
Code: codecid,
Head: vmcontext.EmptyObjectCid,
Balance: big.Zero(),
Address: &builtintypes.EthereumAddressManagerActorAddr, // so that it can create ETH0
}
return nst.SetActor(ctx, builtintypes.EthereumAddressManagerActorAddr, header)
}