From 1e9599e88a5d88e0090b0ebddfae756e343e605a Mon Sep 17 00:00:00 2001 From: Determinant Date: Wed, 19 Aug 2020 20:32:18 -0400 Subject: make the basic X-to-C logic work --- consensus/clique/clique.go | 2 +- consensus/dummy/consensus.go | 11 ++++++++--- consensus/ethash/consensus.go | 2 +- core/genesis.go | 2 +- core/rawdb/accessors_chain.go | 2 +- core/types/block.go | 14 ++++++++++--- miner/worker.go | 1 + plugin/evm/block.go | 2 +- plugin/evm/import_tx.go | 13 ++++-------- plugin/evm/service.go | 16 +++++++-------- plugin/evm/vm.go | 46 ++++++++++++++++++++++++++----------------- 11 files changed, 64 insertions(+), 47 deletions(-) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index d3ad1d9..3714733 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -566,7 +566,7 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainReader, header *types. header.UncleHash = types.CalcUncleHash(nil) // Assemble and return the final block for sealing - return types.NewBlock(header, txs, nil, receipts), nil + return types.NewBlock(header, txs, nil, receipts, nil), nil } // Authorize injects a private key into the consensus engine to mint new blocks diff --git a/consensus/dummy/consensus.go b/consensus/dummy/consensus.go index 9d8db1e..494e4be 100644 --- a/consensus/dummy/consensus.go +++ b/consensus/dummy/consensus.go @@ -19,7 +19,7 @@ import ( ) type OnFinalizeCallbackType = func(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) -type OnFinalizeAndAssembleCallbackType = func(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) +type OnFinalizeAndAssembleCallbackType = func(state *state.StateDB, txs []*types.Transaction) ([]byte, error) type OnAPIsCallbackType = func(consensus.ChainReader) []rpc.API type OnExtraStateChangeType = func(block *types.Block, statedb *state.StateDB) error @@ -261,14 +261,19 @@ func (self *DummyEngine) Finalize( func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { + var extdata []byte if self.cb.OnFinalizeAndAssemble != nil { - self.cb.OnFinalizeAndAssemble(chain, header, state, txs, uncles, receipts) + ret, err := self.cb.OnFinalizeAndAssemble(state, txs) + extdata = ret + if err != nil { + return nil, err + } } // commit the final state root header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) // Header seems complete, assemble into a block and return - return types.NewBlock(header, txs, uncles, receipts), nil + return types.NewBlock(header, txs, uncles, receipts, extdata), nil } func (self *DummyEngine) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) (err error) { diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index c325eea..dc88a79 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -576,7 +576,7 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainReader, header *t header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) // Header seems complete, assemble into a block and return - return types.NewBlock(header, txs, uncles, receipts), nil + return types.NewBlock(header, txs, uncles, receipts, nil), nil } // SealHash returns the hash of a block prior to it being sealed. diff --git a/core/genesis.go b/core/genesis.go index ef490bf..7d21d00 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -294,7 +294,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { statedb.Commit(false) statedb.Database().TrieDB().Commit(root, true) - return types.NewBlock(head, nil, nil, nil) + return types.NewBlock(head, nil, nil, nil, nil) } // Commit writes the block and state of a genesis specification to the database. diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 7620eac..fdfd6ec 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -475,7 +475,7 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block { if body == nil { return nil } - return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles) + return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles, body.Version, body.ExtData) } // WriteBlock serializes a block into the database, header and body separately. diff --git a/core/types/block.go b/core/types/block.go index b3dbcd3..4096d86 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -144,6 +144,8 @@ func rlpHash(x interface{}) (h common.Hash) { type Body struct { Transactions []*Transaction Uncles []*Header + Version uint32 + ExtData []byte `rlp:"nil"` } // Block represents an entire block in the Ethereum blockchain. @@ -212,7 +214,7 @@ type storageblock struct { // The values of TxHash, UncleHash, ReceiptHash and Bloom in header // are ignored and set to values derived from the given txs, uncles // and receipts. -func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block { +func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, extdata []byte) *Block { b := &Block{header: CopyHeader(header), td: new(big.Int)} // TODO: panic if len(txs) != len(receipts) @@ -241,6 +243,9 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []* } } + b.extdata = make([]byte, len(extdata)) + copy(b.extdata, extdata) + return b } @@ -387,7 +392,7 @@ func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Ext func (b *Block) Header() *Header { return CopyHeader(b.header) } // Body returns the non-header content of the block. -func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} } +func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles, b.version, b.extdata} } // Size returns the true RLP encoded storage size of the block, either by encoding // and returning it, or returning a previsouly cached value. @@ -434,13 +439,16 @@ func (b *Block) WithSeal(header *Header) *Block { } // WithBody returns a new block with the given transaction and uncle contents. -func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { +func (b *Block) WithBody(transactions []*Transaction, uncles []*Header, version uint32, extdata []byte) *Block { block := &Block{ header: CopyHeader(b.header), transactions: make([]*Transaction, len(transactions)), uncles: make([]*Header, len(uncles)), + extdata: make([]byte, len(extdata)), + version: version, } copy(block.transactions, transactions) + copy(block.extdata, extdata) for i := range uncles { block.uncles[i] = CopyHeader(uncles[i]) } diff --git a/miner/worker.go b/miner/worker.go index feab0b2..4a6303b 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -751,6 +751,7 @@ func (w *worker) updateSnapshot() { w.current.txs, uncles, w.current.receipts, + nil, ) w.snapshotState = w.current.state.Copy() diff --git a/plugin/evm/block.go b/plugin/evm/block.go index a2ffb69..1cbf0b7 100644 --- a/plugin/evm/block.go +++ b/plugin/evm/block.go @@ -69,7 +69,7 @@ func (b *Block) Verify() error { path := []*Block{} inputs := new(ids.Set) for { - if p.Status() == choices.Accepted { + if p.Status() == choices.Accepted || p.(*Block).ethBlock.Hash() == vm.genesisHash { break } if ret, hit := vm.blockAtomicInputCache.Get(p.ID()); hit { diff --git a/plugin/evm/import_tx.go b/plugin/evm/import_tx.go index ec2ffa4..a61adaa 100644 --- a/plugin/evm/import_tx.go +++ b/plugin/evm/import_tx.go @@ -4,21 +4,17 @@ package evm import ( - //"crypto/ecdsa" "errors" "fmt" "github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/snow" - avacrypto "github.com/ava-labs/gecko/utils/crypto" + crypto "github.com/ava-labs/gecko/utils/crypto" "github.com/ava-labs/gecko/utils/math" "github.com/ava-labs/gecko/vms/components/avax" - //"github.com/ava-labs/gecko/vms/components/verify" "github.com/ava-labs/gecko/vms/secp256k1fx" "github.com/ava-labs/go-ethereum/common" - "github.com/ava-labs/go-ethereum/log" - //"github.com/ava-labs/go-ethereum/crypto" ) var ( @@ -68,7 +64,6 @@ func (tx *UnsignedImportTx) Verify( case tx.SourceChain.IsZero(): return errWrongChainID case !tx.SourceChain.Equals(avmID): - // TODO: remove this check if we allow for P->C swaps return errWrongChainID case len(tx.ImportedInputs) == 0: return errNoImportInputs @@ -118,7 +113,7 @@ func (tx *UnsignedImportTx) Accept(batch database.Batch) error { func (vm *VM) newImportTx( chainID ids.ID, // chain to import from to common.Address, // Address of recipient - keys []*avacrypto.PrivateKeySECP256K1R, // Keys to import the funds + keys []*crypto.PrivateKeySECP256K1R, // Keys to import the funds ) (*Tx, error) { if !vm.avm.Equals(chainID) { return nil, errWrongChainID @@ -135,7 +130,7 @@ func (vm *VM) newImportTx( } importedInputs := []*avax.TransferableInput{} - signers := [][]*avacrypto.PrivateKeySECP256K1R{} + signers := [][]*crypto.PrivateKeySECP256K1R{} importedAmount := uint64(0) now := vm.clock.Unix() @@ -172,6 +167,7 @@ func (vm *VM) newImportTx( outs := []EVMOutput{} if importedAmount < vm.txFee { // imported amount goes toward paying tx fee // TODO: spend EVM balance to compensate vm.txFee-importedAmount + return nil, errNoFunds } else if importedAmount > vm.txFee { outs = append(outs, EVMOutput{ Address: to, @@ -192,7 +188,6 @@ func (vm *VM) newImportTx( } tx := &Tx{UnsignedTx: utx} if err := tx.Sign(vm.codec, signers); err != nil { - log.Info("hey here1", "err", err, "utx", utx) return nil, err } return tx, utx.Verify(vm.avm, vm.ctx, vm.txFee, vm.avaxAssetID) diff --git a/plugin/evm/service.go b/plugin/evm/service.go index 41cce50..86bc706 100644 --- a/plugin/evm/service.go +++ b/plugin/evm/service.go @@ -16,12 +16,11 @@ import ( "github.com/ava-labs/coreth/core/types" "github.com/ava-labs/gecko/api" "github.com/ava-labs/gecko/utils/constants" - avacrypto "github.com/ava-labs/gecko/utils/crypto" + "github.com/ava-labs/gecko/utils/crypto" "github.com/ava-labs/gecko/utils/formatting" "github.com/ava-labs/go-ethereum/common" "github.com/ava-labs/go-ethereum/common/hexutil" - "github.com/ava-labs/go-ethereum/crypto" - "github.com/ava-labs/go-ethereum/log" + ethcrypto "github.com/ava-labs/go-ethereum/crypto" ) const ( @@ -64,7 +63,7 @@ type Web3API struct{} 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) } +func (s *Web3API) Sha3(input hexutil.Bytes) hexutil.Bytes { return ethcrypto.Keccak256(input) } // GetAcceptedFrontReply defines the reply that will be sent from the // GetAcceptedFront API call @@ -101,7 +100,7 @@ func (api *DebugAPI) SpendGenesis(ctx context.Context, nonce uint64) error { gasLimit := 21000 gasPrice := big.NewInt(1000000000) - genPrivateKey, err := crypto.HexToECDSA(GenesisTestKey[2:]) + genPrivateKey, err := ethcrypto.HexToECDSA(GenesisTestKey[2:]) if err != nil { return err } @@ -180,7 +179,7 @@ func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *ap user := user{db: db} - factory := avacrypto.FactorySECP256K1R{} + factory := crypto.FactorySECP256K1R{} if !strings.HasPrefix(args.PrivateKey, constants.SecretKeyPrefix) { return fmt.Errorf("private key missing %s prefix", constants.SecretKeyPrefix) @@ -195,7 +194,7 @@ func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *ap if err != nil { return fmt.Errorf("problem parsing private key: %w", err) } - sk := skIntf.(*avacrypto.PrivateKeySECP256K1R) + sk := skIntf.(*crypto.PrivateKeySECP256K1R) if err := user.putAddress(sk); err != nil { return fmt.Errorf("problem saving key %w", err) @@ -203,7 +202,7 @@ func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *ap // TODO: return eth address here reply.Address, err = service.vm.FormatAddress( - crypto.PubkeyToAddress(*(sk.PublicKey().(*avacrypto.PublicKeySECP256K1R).ToECDSA()))) + ethcrypto.PubkeyToAddress(*(sk.PublicKey().(*crypto.PublicKeySECP256K1R).ToECDSA()))) if err != nil { return fmt.Errorf("problem formatting address: %w", err) } @@ -254,6 +253,5 @@ func (service *AvaAPI) ImportAVAX(_ *http.Request, args *ImportAVAXArgs, respons } response.TxID = tx.ID() - log.Info("hey here2") return service.vm.issueTx(tx) } diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 7cbfabd..baf6106 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -34,13 +34,11 @@ import ( "github.com/ava-labs/gecko/snow/choices" "github.com/ava-labs/gecko/snow/consensus/snowman" "github.com/ava-labs/gecko/utils/codec" - "github.com/ava-labs/gecko/vms/secp256k1fx" - //"github.com/ava-labs/gecko/utils/constants" - //"github.com/ava-labs/gecko/utils/formatting" avajson "github.com/ava-labs/gecko/utils/json" "github.com/ava-labs/gecko/utils/timer" "github.com/ava-labs/gecko/utils/wrappers" "github.com/ava-labs/gecko/vms/components/avax" + "github.com/ava-labs/gecko/vms/secp256k1fx" commonEng "github.com/ava-labs/gecko/snow/engine/common" ) @@ -153,9 +151,9 @@ type VM struct { } func (vm *VM) getAtomicTx(block *types.Block) *Tx { - var atx *Tx + atx := new(Tx) if extdata := block.ExtraData(); extdata != nil { - if err := vm.codec.Unmarshal(block.ExtraData(), atx); err != nil { + if err := vm.codec.Unmarshal(extdata, atx); err != nil { panic(err) } } @@ -212,19 +210,24 @@ func (vm *VM) Initialize( } header.Extra = append(header.Extra, hid...) }) - chain.SetOnSeal(func(block *types.Block) error { - if len(block.Transactions()) == 0 { - // this could happen due to the async logic of geth tx pool - vm.newBlockChan <- nil - return errEmptyBlock - } + chain.SetOnFinalizeAndAssemble(func(state *state.StateDB, txs []*types.Transaction) ([]byte, error) { select { case atx := <-vm.pendingAtomicTxs: + for _, to := range atx.UnsignedTx.(*UnsignedImportTx).Outs { + amount := new(big.Int) + amount.SetUint64(to.Amount) + state.AddBalance(to.Address, amount) + } raw, _ := vm.codec.Marshal(atx) - block.SetExtraData(raw) - // TODO: make sure the atomic Tx is valid + return raw, nil + default: + if len(txs) == 0 { + // this could happen due to the async logic of geth tx pool + vm.newBlockChan <- nil + return nil, errEmptyBlock + } } - return nil + return nil, nil }) chain.SetOnSealFinish(func(block *types.Block) error { vm.ctx.Log.Verbo("EVM sealed a block") @@ -234,6 +237,9 @@ func (vm *VM) Initialize( ethBlock: block, vm: vm, } + if blk.Verify() != nil { + return errInvalidBlock + } vm.newBlockChan <- blk vm.updateStatus(ids.NewID(block.Hash()), choices.Processing) vm.txPoolStabilizedLock.Lock() @@ -246,7 +252,6 @@ func (vm *VM) Initialize( }) chain.SetOnExtraStateChange(func(block *types.Block, statedb *state.StateDB) error { atx := vm.getAtomicTx(block).UnsignedTx.(*UnsignedImportTx) - vm.ctx.Log.Info(atx.ID().String()) for _, to := range atx.Outs { amount := new(big.Int) amount.SetUint64(to.Amount) @@ -282,6 +287,7 @@ func (vm *VM) Initialize( vm.txPoolStabilizedOk = make(chan struct{}, 1) // TODO: read size from options vm.pendingAtomicTxs = make(chan *Tx, 1024) + vm.atomicTxSubmitChan = make(chan struct{}, 1) chain.GetTxPool().SubscribeNewHeadEvent(vm.newTxPoolHeadChan) // TODO: shutdown this go routine go ctx.Log.RecoverAndPanic(func() { @@ -523,7 +529,7 @@ func (vm *VM) tryBlockGen() error { if err != nil { return err } - if size == 0 { + if size == 0 && len(vm.pendingAtomicTxs) == 0 { return nil } @@ -648,7 +654,10 @@ func (vm *VM) FormatAddress(addr common.Address) (string, error) { func (vm *VM) issueTx(tx *Tx) error { select { case vm.pendingAtomicTxs <- tx: - vm.atomicTxSubmitChan <- struct{}{} + select { + case vm.atomicTxSubmitChan <- struct{}{}: + default: + } default: return errTooManyAtomicTx } @@ -666,7 +675,8 @@ func (vm *VM) GetAtomicUTXOs( ) ([]*avax.UTXO, ids.ShortID, ids.ID, error) { // TODO: finish this function via gRPC utxos := []*avax.UTXO{{ - Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, + UTXOID: avax.UTXOID{TxID: ids.Empty}, + Asset: avax.Asset{ID: vm.ctx.AVAXAssetID}, Out: &secp256k1fx.TransferOutput{ Amt: 100, }, -- cgit v1.2.3