Skip to content

Commit

Permalink
Revert "Revert "Added the EVM as a plugin""
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenButtolph authored Apr 16, 2020
1 parent 756aac8 commit 7981c59
Show file tree
Hide file tree
Showing 10 changed files with 1,542 additions and 0 deletions.
619 changes: 619 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

75 changes: 75 additions & 0 deletions plugin/evm/block.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package evm

import (
"fmt"

"github.com/ava-labs/go-ethereum/core/types"
"github.com/ava-labs/go-ethereum/rlp"

"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/choices"
"github.com/ava-labs/gecko/snow/consensus/snowman"
)

// Block implements the snowman.Block interface
type Block struct {
id ids.ID
ethBlock *types.Block
vm *VM
}

// ID implements the snowman.Block interface
func (b *Block) ID() ids.ID { return b.id }

// Accept implements the snowman.Block interface
func (b *Block) Accept() {
b.vm.ctx.Log.Verbo("Block %s is accepted", b.ID())
b.vm.updateStatus(b.ID(), choices.Accepted)
}

// Reject implements the snowman.Block interface
func (b *Block) Reject() {
b.vm.ctx.Log.Verbo("Block %s is rejected", b.ID())
b.vm.updateStatus(b.ID(), choices.Rejected)
}

// Status implements the snowman.Block interface
func (b *Block) Status() choices.Status {
status := b.vm.getCachedStatus(b.ID())
if status == choices.Unknown && b.ethBlock != nil {
return choices.Processing
}
return status
}

// Parent implements the snowman.Block interface
func (b *Block) Parent() snowman.Block {
parentID := ids.NewID(b.ethBlock.ParentHash())
block := &Block{
id: parentID,
ethBlock: b.vm.getCachedBlock(parentID),
vm: b.vm,
}
b.vm.ctx.Log.Verbo("Parent(%s) has status: %s", block.ID(), block.Status())
return block
}

// Verify implements the snowman.Block interface
func (b *Block) Verify() error {
_, err := b.vm.chain.InsertChain([]*types.Block{b.ethBlock})
return err
}

// Bytes implements the snowman.Block interface
func (b *Block) Bytes() []byte {
res, err := rlp.EncodeToBytes(b.ethBlock)
if err != nil {
panic(err)
}
return res
}

func (b *Block) String() string { return fmt.Sprintf("EVM block, ID = %s", b.ID()) }
66 changes: 66 additions & 0 deletions plugin/evm/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package evm

import (
"errors"

"github.com/ava-labs/go-ethereum/ethdb"

"github.com/ava-labs/gecko/database"
)

var (
errOpNotSupported = errors.New("this operation is not supported")
)

// Database implements ethdb.Database
type Database struct{ database.Database }

// HasAncient returns an error as we don't have a backing chain freezer.
func (db Database) HasAncient(kind string, number uint64) (bool, error) {
return false, errOpNotSupported
}

// Ancient returns an error as we don't have a backing chain freezer.
func (db Database) Ancient(kind string, number uint64) ([]byte, error) { return nil, errOpNotSupported }

// Ancients returns an error as we don't have a backing chain freezer.
func (db Database) Ancients() (uint64, error) { return 0, errOpNotSupported }

// AncientSize returns an error as we don't have a backing chain freezer.
func (db Database) AncientSize(kind string) (uint64, error) { return 0, errOpNotSupported }

// AppendAncient returns an error as we don't have a backing chain freezer.
func (db Database) AppendAncient(number uint64, hash, header, body, receipts, td []byte) error {
return errOpNotSupported
}

// TruncateAncients returns an error as we don't have a backing chain freezer.
func (db Database) TruncateAncients(items uint64) error { return errOpNotSupported }

// Sync returns an error as we don't have a backing chain freezer.
func (db Database) Sync() error { return errOpNotSupported }

// NewBatch implements ethdb.Database
func (db Database) NewBatch() ethdb.Batch { return Batch{db.Database.NewBatch()} }

// NewIterator implements ethdb.Database
func (db Database) NewIterator() ethdb.Iterator { return db.Database.NewIterator() }

// NewIteratorWithPrefix implements ethdb.Database
func (db Database) NewIteratorWithPrefix(prefix []byte) ethdb.Iterator {
return db.NewIteratorWithPrefix(prefix)
}

// NewIteratorWithStart implements ethdb.Database
func (db Database) NewIteratorWithStart(start []byte) ethdb.Iterator {
return db.NewIteratorWithStart(start)
}

// Batch implements ethdb.Batch
type Batch struct{ database.Batch }

// Replay implements ethdb.Batch
func (batch Batch) Replay(w ethdb.KeyValueWriter) error { return batch.Batch.Replay(w) }
19 changes: 19 additions & 0 deletions plugin/evm/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package evm

import (
"github.com/ava-labs/gecko/ids"
)

// ID this VM should be referenced by
var (
ID = ids.NewID([32]byte{'e', 'v', 'm'})
)

// Factory ...
type Factory struct{}

// New ...
func (f *Factory) New() interface{} { return &VM{} }
122 changes: 122 additions & 0 deletions plugin/evm/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package evm

import (
"context"
"crypto/rand"
"fmt"
"math/big"

"github.com/ava-labs/coreth"

"github.com/ava-labs/go-ethereum/common"
"github.com/ava-labs/go-ethereum/common/hexutil"
"github.com/ava-labs/go-ethereum/core/types"
"github.com/ava-labs/go-ethereum/crypto"
)

const (
version = "Athereum 1.0"
)

// test constants
const (
GenesisTestAddr = "0x751a0b96e1042bee789452ecb20253fba40dbe85"
GenesisTestKey = "0xabd71b35d559563fea757f0f5edbde286fb8c043105b15abb7cd57189306d7d1"
)

// DebugAPI introduces helper functions for debuging
type DebugAPI struct{ vm *VM }

// SnowmanAPI introduces snowman specific functionality to the evm
type SnowmanAPI struct{ vm *VM }

// NetAPI offers network related API methods
type NetAPI struct{ vm *VM }

// NewNetAPI creates a new net API instance.
func NewNetAPI(vm *VM) *NetAPI { return &NetAPI{vm} }

// Listening returns an indication if the node is listening for network connections.
func (s *NetAPI) Listening() bool { return true } // always listening

// PeerCount returns the number of connected peers
func (s *NetAPI) PeerCount() hexutil.Uint { return hexutil.Uint(0) } // TODO: report number of connected peers

// Version returns the current ethereum protocol version.
func (s *NetAPI) Version() string { return fmt.Sprintf("%d", s.vm.networkID) }

// Web3API offers helper API methods
type Web3API struct{}

// ClientVersion returns the version of the vm running
func (s *Web3API) ClientVersion() string { return version }

// Sha3 returns the bytes returned by hashing [input] with Keccak256
func (s *Web3API) Sha3(input hexutil.Bytes) hexutil.Bytes { return crypto.Keccak256(input) }

// GetAcceptedFrontReply defines the reply that will be sent from the
// GetAcceptedFront API call
type GetAcceptedFrontReply struct {
Hash common.Hash `json:"hash"`
Number *big.Int `json:"number"`
}

// GetAcceptedFront returns the last accepted block's hash and height
func (api *SnowmanAPI) GetAcceptedFront(ctx context.Context) (*GetAcceptedFrontReply, error) {
blk := api.vm.getLastAccepted().ethBlock
return &GetAcceptedFrontReply{
Hash: blk.Hash(),
Number: blk.Number(),
}, nil
}

// GetGenesisBalance returns the current funds in the genesis
func (api *DebugAPI) GetGenesisBalance(ctx context.Context) (*hexutil.Big, error) {
lastAccepted := api.vm.getLastAccepted()
api.vm.ctx.Log.Verbo("Currently accepted block front: %s", lastAccepted.ethBlock.Hash().Hex())
state, err := api.vm.chain.BlockState(lastAccepted.ethBlock)
if err != nil {
return nil, err
}
return (*hexutil.Big)(state.GetBalance(common.HexToAddress(GenesisTestAddr))), nil
}

// SpendGenesis funds
func (api *DebugAPI) SpendGenesis(ctx context.Context, nonce uint64) error {
api.vm.ctx.Log.Info("Spending the genesis")

value := big.NewInt(1000000000000)
gasLimit := 21000
gasPrice := big.NewInt(1000000000)

genPrivateKey, err := crypto.HexToECDSA(GenesisTestKey[2:])
if err != nil {
return err
}
bob, err := coreth.NewKey(rand.Reader)
if err != nil {
return err
}

tx := types.NewTransaction(nonce, bob.Address, value, uint64(gasLimit), gasPrice, nil)
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(api.vm.chainID), genPrivateKey)
if err != nil {
return err
}

if err := api.vm.issueRemoteTxs([]*types.Transaction{signedTx}); err != nil {
return err
}

return nil
}

// IssueBlock to the chain
func (api *DebugAPI) IssueBlock(ctx context.Context) error {
api.vm.ctx.Log.Info("Issuing a new block")

return api.vm.tryBlockGen()
}
22 changes: 22 additions & 0 deletions plugin/evm/static_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package evm

import (
"context"
"encoding/json"

"github.com/ava-labs/coreth/core"
"github.com/ava-labs/gecko/utils/formatting"
)

// StaticService defines the static API services exposed by the evm
type StaticService struct{}

// BuildGenesis returns the UTXOs such that at least one address in [args.Addresses] is
// referenced in the UTXO.
func (*StaticService) BuildGenesis(_ context.Context, args *core.Genesis) (formatting.CB58, error) {
bytes, err := json.Marshal(args)
return formatting.CB58{Bytes: bytes}, err
}
64 changes: 64 additions & 0 deletions plugin/evm/static_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package evm

import (
"math/big"
"testing"

"github.com/ava-labs/go-ethereum/common"
"github.com/ava-labs/go-ethereum/params"

"github.com/ava-labs/coreth/core"
)

func TestBuildGenesis(t *testing.T) {
expected := "3wP629bGfSGj9trh1UNBp5qGRGCcma5d8ezLeSmd9hnUJjSMUJesHHoxbZNcVUC9CjH7PEGNA96htNTd1saZCMt1Mf1dZFG7JDhcYNok6RS4TZufejXdxbVVgquohSa7nCCcrXpiVeiRFwzLJAxyQbXzYRhaCRtcDDfCcqfaVdtkFsPbNeQ49pDTbEC5hVkmfopeQ2Zz8tAG5QXKBdbYBCukR3xNHJ4xDxeixmEwPr1odb42yQRYrL7xREKNn2LFoFwAWUjBTsCkf5GPNgY2GvvN9o8wFWXTroW5fp754DhpdxHYxkMTfuE9DGyNWHTyrEbrUHutUdsfitcSHVj5ctFtkN2wGCs3cyv1eRRNvFFMggWTbarjne6AYaeCrJ631qAu3CbrUtrTH5N2E6G2yQKX4sT4Sk3qWPJdsGXuT95iKKcgNn1u5QRHHw9DXXuGPpJjkcKQRGUCuqpXy61iF5RNPEwAwKDa8f2Y25WMmNgWynUuLj8iSAyePj7USPWk54QFUr86ApVzqAdzzdD1qSVScpmudGnGbz9UNXdzHqSot6XLrNTYsgkabiu6TGntFm7qywbCRmtNdBuT9aznGQdUVimjt5QzUz68HXhUxBzTkrz7yXfVGV5JcWxVHQXYS4oc41U5yu83mH3A7WBrZLVq6UyNrvQVbim5nDxeKKbALPxwzVwywjgY5cp39AvzGnY8CX2AtuBNnKmZaAvG8JWAkx3yxjnJrwWhLgpDQYcCvRp2jg1EPBqN8FKJxSPE6eedjDHDJfB57mNzyEtmg22BPnem3eLdiovX8awkhBUHdE7uPrapNSVprnS85u1saW2Kwza3FsS2jAM3LckGW8KdtfPTpHBTRKAUo49zZLuPsyGL5WduedGyAdaM3a2KPoyXuz4UbexTVUWFNypFvvgyoDS8FMxDCNoMMaD7y4yVnoDpSpVFEVZD6EuSGHe9U8Ew57xLPbjhepDx6"

balance, success := new(big.Int).SetString("33b2e3c9fd0804000000000", 16)
if !success {
t.Fatal("Failed to initialize balance")
}

args := core.Genesis{
Config: &params.ChainConfig{
ChainID: big.NewInt(43110),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
DAOForkSupport: true,
EIP150Block: big.NewInt(0),
EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
},
Nonce: 0,
Timestamp: 0,
ExtraData: []byte{},
GasLimit: 100000000,
Difficulty: big.NewInt(0),
Mixhash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
Coinbase: common.HexToAddress("0x0000000000000000000000000000000000000000"),
Alloc: core.GenesisAlloc{
common.HexToAddress("751a0b96e1042bee789452ecb20253fba40dbe85"): core.GenesisAccount{
Balance: balance,
},
},
Number: 0,
GasUsed: 0,
ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
}

ss := StaticService{}
result, err := ss.BuildGenesis(nil, &args)
if err != nil {
t.Fatal(err)
}

if result.String() != expected {
t.Fatalf("StaticService.BuildGenesis:\nReturned: %s\nExpected: %s", result, expected)
}
}
Loading

0 comments on commit 7981c59

Please sign in to comment.