Skip to content

Commit

Permalink
Merge pull request #5578 from filecoin-project/feat/fevm-genesis-eth0
Browse files Browse the repository at this point in the history
Feat/fevm genesis eth0
  • Loading branch information
simlecode authored Dec 16, 2022
2 parents d0bf2ef + 7b84fce commit a8d8d23
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 4 deletions.
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)
}

0 comments on commit a8d8d23

Please sign in to comment.