accounts/abi: add event binding for go language

This commit is contained in:
nkbai 2017-09-12 10:44:30 +08:00
parent 33cf1c74f2
commit 735f074c08
8 changed files with 121 additions and 2 deletions

View file

@ -50,6 +50,10 @@ type ContractCaller interface {
// ContractCall executes an Ethereum contract call with the specified data as the
// input.
CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
// SubscribeFilterLogs subscribes to the results of a streaming filter query.
SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
// FilterLogs executes a filter query.
FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]*types.Log, error)
}
// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.

View file

@ -40,6 +40,7 @@ import (
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
var errLogsUnsupported = errors.New("SimulatedBackend cannot query logs")
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
// the background. Its main purpose is to allow easily testing contract bindings.
@ -284,6 +285,16 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
return nil
}
//cannot query logs on simulated blockchain
func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
return nil, errLogsUnsupported
}
//cannot query logs on simulated blockchain
func (b *SimulatedBackend) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]*types.Log, error) {
return nil, errLogsUnsupported
}
// callmsg implements core.Message to allow passing it as a transaction simulator.
type callmsg struct {
ethereum.CallMsg

View file

@ -145,6 +145,33 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
return c.abi.Unpack(result, method, output)
}
// SubscribeFilterLogs query logs on blockchain with q.
// it returns a channel which tell all the already happed logs and the logs that will happen.
// you can cancel listen logs by Subscription's Unsubscribe
func (c *BoundContract) SubscribeFilterLogs(opts *CallOpts, q ethereum.FilterQuery) (<-chan types.Log, ethereum.Subscription, error) {
if opts == nil {
opts = new(CallOpts)
}
var ctx = ensureContext(opts.Context)
q.Addresses = []common.Address{c.address}
//first get all happened logs on the blockchain.
logs, err := c.caller.FilterLogs(ctx, q)
if err != nil {
return nil, nil, err
}
var ch = make(chan types.Log, len(logs))
//subcribe logs that will happen.
sub, err := c.caller.SubscribeFilterLogs(ctx, q, ch)
if err != nil {
close(ch)
return nil, nil, err
}
for _, log := range logs {
ch <- *log
}
return ch, sub, err
}
// Transact invokes the (paid) contract method with params as input values.
func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
// Otherwise pack up the parameters and invoke the contract

View file

@ -67,6 +67,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
var (
calls = make(map[string]*tmplMethod)
transacts = make(map[string]*tmplMethod)
events = make(map[string]string)
)
for _, original := range evmABI.Methods {
// Normalize the method for capital cases and non-anonymous inputs/outputs
@ -94,6 +95,9 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)}
}
}
for _, ev := range evmABI.Events {
events[ev.Name] = ev.Name
}
contracts[types[i]] = &tmplContract{
Type: capitalise(types[i]),
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
@ -101,6 +105,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
Constructor: evmABI.Constructor,
Calls: calls,
Transacts: transacts,
Events: events,
}
}
// Generate the contract template data content and render it

View file

@ -32,6 +32,7 @@ type tmplContract struct {
Constructor abi.Method // Contract constructor for deploy parametrization
Calls map[string]*tmplMethod // Contract calls that only read state data
Transacts map[string]*tmplMethod // Contract calls that write state data
Events map[string]string //all contract events except the anonymous events todo events's indexed argument information should be used to generate code.
}
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
@ -263,6 +264,56 @@ package {{.Package}}
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
}
{{end}}
func (_{{$contract.Type}} *{{$contract.Type}}Caller) EventSubscribe(opts *bind.CallOpts, fromBlock rpc.BlockNumber,
toBlock rpc.BlockNumber, eventName string) (<-chan types.Log, ethereum.Subscription, error) {
parsed, err := abi.JSON(strings.NewReader(MyTokenABI))
if err != nil {
return nil, nil, err
}
var q ethereum.FilterQuery
q.FromBlock = big.NewInt(int64(fromBlock))
if toBlock == rpc.LatestBlockNumber {
q.ToBlock = nil
} else {
q.ToBlock = big.NewInt(int64(toBlock))
}
q.Topics = [][]common.Hash{
{parsed.Events[eventName].Id()}, //event signature
}
return _{{$contract.Type}}.contract.SubscribeFilterLogs(opts, q)
}
{{range $name,$value := .Events}}
/*
get all event {{$name}} happened from [fromBlock] to [toBlock]
if [toBlock] is -1, you can get all the events that will happen later.
you can cancel event listenging through Subscription's Unsubscribe
*/
func (t *{{$contract.Type}}Caller) Event{{$name}}Subscribe(opts *bind.CallOpts, fromBlock rpc.BlockNumber,
toBlock rpc.BlockNumber) (<-chan types.Log, ethereum.Subscription, error) {
return t.EventSubscribe(opts, fromBlock, toBlock, "{{$name}}")
}
/*
get all event {{$name}} happened from [fromBlock] to [toBlock]
if [toBlock] is -1, you can get all the events that will happen later.
you can cancel event listenging through Subscription's Unsubscribe
*/
func (t *{{$contract.Type}}Session) Event{{$name}}Subscribe(fromBlock rpc.BlockNumber,
toBlock rpc.BlockNumber) (<-chan types.Log, ethereum.Subscription, error) {
return t.Contract.Event{{$name}}Subscribe(&t.CallOpts, fromBlock, toBlock)
}
/*
get all event {{$name}} happened from [fromBlock] to [toBlock]
if [toBlock] is -1, you can get all the events that will happen later.
you can cancel event listenging through Subscription's Unsubscribe
*/
func (t *{{$contract.Type}}CallerSession) Event{{$name}}Subscribe(opts *bind.CallOpts, fromBlock rpc.BlockNumber,
toBlock rpc.BlockNumber) (<-chan types.Log, ethereum.Subscription, error) {
return t.Contract.Event{{$name}}Subscribe(&t.CallOpts, fromBlock, toBlock)
}
{{end}}
{{end}}
`

View file

@ -20,10 +20,12 @@ import (
"context"
"math/big"
"errors"
"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/internal/ethapi"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
@ -40,6 +42,7 @@ 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
fapi *filters.PublicFilterAPI //wrapper around the filter to filter logs
}
// NewContractBackend creates a new native contract backend using an existing
@ -49,6 +52,7 @@ func NewContractBackend(apiBackend ethapi.Backend) *ContractBackend {
eapi: ethapi.NewPublicEthereumAPI(apiBackend),
bcapi: ethapi.NewPublicBlockChainAPI(apiBackend),
txapi: ethapi.NewPublicTransactionPoolAPI(apiBackend, new(ethapi.AddrLocker)),
fapi: filters.NewPublicFilterAPI(apiBackend, false),
}
}
@ -136,3 +140,17 @@ func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transac
_, err := b.txapi.SendRawTransaction(ctx, raw)
return err
}
func (b *ContractBackend) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
return nil, errors.New("not support right now")
}
func (b *ContractBackend) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]*types.Log, error) {
crit := filters.FilterCriteria{
FromBlock: q.FromBlock,
ToBlock: q.ToBlock,
Addresses: q.Addresses,
Topics: q.Topics,
}
return b.fapi.GetLogs(ctx, crit)
}

View file

@ -304,8 +304,8 @@ func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumb
// Filters
// FilterLogs executes a filter query.
func (ec *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
var result []types.Log
func (ec *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]*types.Log, error) {
var result []*types.Log
err := ec.c.CallContext(ctx, &result, "eth_getLogs", toFilterArg(q))
return result, err
}

View file

@ -67,6 +67,9 @@ type Backend interface {
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
ChainConfig() *params.ChainConfig
CurrentBlock() *types.Block
}