From 8f0e1537bba14031152315a46ad7f047f3b5eec5 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 4 Jun 2018 14:31:44 +0800 Subject: [PATCH] eth, internal: implements contract backend --- eth/bind.go | 181 +++++++++++++++++++++++++++++++++++++ eth/filters/api.go | 5 + internal/ethapi/backend.go | 3 + 3 files changed, 189 insertions(+) create mode 100644 eth/bind.go diff --git a/eth/bind.go b/eth/bind.go new file mode 100644 index 0000000000..21259b3e50 --- /dev/null +++ b/eth/bind.go @@ -0,0 +1,181 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" +) + +// ContractBackend implements bind.ContractBackend with direct calls to Ethereum +// internals to support operating on contracts within subprotocols like eth, les and +// swarm. +// +// Internally this backend uses the already exposed API endpoints of the Ethereum +// object. These should be rewritten to internal Go method calls when the Go API +// is refactored to support a clean library use. +type ContractBackend struct { + eapi *ethapi.PublicEthereumAPI // Wrapper around the Ethereum object to access metadata + bcapi *ethapi.PublicBlockChainAPI // Wrapper around the blockchain to access chain data + txapi *ethapi.PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data + filterapi *filters.PublicFilterAPI // Wrapper around the filter to watch and retrieve contract logs +} + +// NewContractBackend creates a new native contract backend using an existing +// Ethereum object. +func NewContractBackend(apiBackend ethapi.Backend, lightMode bool) *ContractBackend { + return &ContractBackend{ + eapi: ethapi.NewPublicEthereumAPI(apiBackend), + bcapi: ethapi.NewPublicBlockChainAPI(apiBackend), + txapi: ethapi.NewPublicTransactionPoolAPI(apiBackend, new(ethapi.AddrLocker)), + filterapi: filters.NewPublicFilterAPI(apiBackend.(filters.Backend), lightMode), + } +} + +// CodeAt implements bind.ContractCaller retrieving any code associated +// with the contract from the local API. +func (b *ContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNum *big.Int) ([]byte, error) { + return b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum)) +} + +// ContractCall implements bind.ContractCaller executing an Ethereum contract +// call with the specified data as the input. The pending flag requests execution +// against the pending block, not the stable head of the chain. +func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) { + out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum)) + return out, err +} + +// PendingCodeAt implements bind.ContractTransactor retrieving any code associated +// with the contract from the local API. +func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + return b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber) +} + +// PendingAccountNonce implements bind.ContractTransactor retrieving the current +// pending nonce associated with an account. +func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (nonce uint64, err error) { + out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber) + if out != nil { + nonce = uint64(*out) + } + return nonce, err +} + +// SuggestGasPrice implements bind.ContractTransactor retrieving the currently +// suggested gas price to allow a timely execution of a transaction. +func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + return b.eapi.GasPrice(ctx) +} + +// EstimateGasLimit implements bind.ContractTransactor trying to estimate the gas +// needed to execute a specific transaction based on the current pending state of +// the backend blockchain. There is no guarantee that this is the true gas limit +// requirement as other transactions may be added or removed by miners, but it +// should provide a basis for setting a reasonable default. +func (b *ContractBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) { + gas, err := b.bcapi.EstimateGas(ctx, toCallArgs(msg)) + return uint64(gas), err +} + +// SendTransaction implements bind.ContractTransactor injecting the transaction +// into the pending pool for execution. +func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { + raw, _ := rlp.EncodeToBytes(tx) + _, err := b.txapi.SendRawTransaction(ctx, raw) + return err +} + +// FilterLogs implements bind.ContractFilterer returning logs matching the given argument +// that are stored within the state. +func (b *ContractBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { + ret, err := b.filterapi.GetLogs(ctx, filters.FilterCriteria(query)) + if err != nil { + return nil, err + } + logs := make([]types.Log, len(ret)) + for idx, log := range ret { + logs[idx] = *log + } + return logs, nil +} + +// SubscribeFilterLogs implements bind.ContractFilterer watching new fired logs matching the given argument. +func (b *ContractBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { + // Subscribe to contract events + sink := make(chan []*types.Log) + + sub, err := b.filterapi.EventSystem().SubscribeLogs(query, sink) + if err != nil { + return nil, err + } + // Since we're getting logs in batches, we need to flatten them into a plain stream + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case logs := <-sink: + for _, log := range logs { + select { + case ch <- *log: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs { + args := ethapi.CallArgs{ + To: msg.To, + From: msg.From, + Data: msg.Data, + Gas: hexutil.Uint64(msg.Gas), + } + if msg.GasPrice != nil { + args.GasPrice = hexutil.Big(*msg.GasPrice) + } + if msg.Value != nil { + args.Value = hexutil.Big(*msg.Value) + } + return args +} + +func toBlockNumber(num *big.Int) rpc.BlockNumber { + if num == nil { + return rpc.LatestBlockNumber + } + return rpc.BlockNumber(num.Int64()) +} diff --git a/eth/filters/api.go b/eth/filters/api.go index 5ed80a8875..9c2cb093c9 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -75,6 +75,11 @@ func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { return api } +// EventSystem returns +func (filter *PublicFilterAPI) EventSystem() *EventSystem { + return filter.events +} + // timeoutLoop runs every 5 minutes and deletes filters that have not been recently used. // Tt is started when the api is created. func (api *PublicFilterAPI) timeoutLoop() { diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index c9ffe230c6..444f051652 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -44,6 +45,8 @@ type Backend interface { ChainDb() ethdb.Database EventMux() *event.TypeMux AccountManager() *accounts.Manager + BloomStatus() (uint64, uint64) + ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) // BlockChain API SetHead(number uint64)