mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
eth/filters,interfaces.go: Expand & modify TxFilterQuery
This commit is contained in:
parent
a92c854be5
commit
20eba82751
5 changed files with 82 additions and 49 deletions
|
|
@ -234,7 +234,7 @@ func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, er
|
||||||
func (api *PublicFilterAPI) NewReturnDataFilter(crit TxFilterCriteria) rpc.ID {
|
func (api *PublicFilterAPI) NewReturnDataFilter(crit TxFilterCriteria) rpc.ID {
|
||||||
var (
|
var (
|
||||||
retCh = make(chan []*types.ReturnData)
|
retCh = make(chan []*types.ReturnData)
|
||||||
retSub = api.events.SubscribeReturnData(retCh, ethereum.TxFilterQuery(crit))
|
retSub = api.events.SubscribeReturnData(retCh, crit)
|
||||||
)
|
)
|
||||||
|
|
||||||
api.filtersMu.Lock()
|
api.filtersMu.Lock()
|
||||||
|
|
@ -280,7 +280,7 @@ func (api *PublicFilterAPI) ReturnData(ctx context.Context, crit TxFilterCriteri
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
retCh := make(chan []*types.ReturnData)
|
retCh := make(chan []*types.ReturnData)
|
||||||
retSub := api.events.SubscribeReturnData(retCh, ethereum.TxFilterQuery(crit))
|
retSub := api.events.SubscribeReturnData(retCh, crit)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
@ -623,10 +623,16 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
// UnmarshalJSON sets *args fields with given data.
|
// UnmarshalJSON sets *args fields with given data.
|
||||||
func (args *TxFilterCriteria) UnmarshalJSON(data []byte) error {
|
func (args *TxFilterCriteria) UnmarshalJSON(data []byte) error {
|
||||||
var err error
|
var (
|
||||||
|
err error
|
||||||
|
from []common.Address
|
||||||
|
to []common.Address
|
||||||
|
)
|
||||||
|
|
||||||
type input struct {
|
type input struct {
|
||||||
From interface{} `json:"from"`
|
From interface{} `json:"from"`
|
||||||
To interface{} `json:"to"`
|
To interface{} `json:"to"`
|
||||||
|
HasReturnData interface{} `json:"hasReturnData"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var raw input
|
var raw input
|
||||||
|
|
@ -638,17 +644,34 @@ func (args *TxFilterCriteria) UnmarshalJSON(data []byte) error {
|
||||||
args.To = nil
|
args.To = nil
|
||||||
|
|
||||||
if raw.From != nil {
|
if raw.From != nil {
|
||||||
args.From, err = decodeAddresses(raw.From, "from")
|
args.From = make(map[common.Address]struct{})
|
||||||
|
from, err = decodeAddresses(raw.From, "from")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for _, addr := range from {
|
||||||
|
args.From[addr] = struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if raw.To != nil {
|
if raw.To != nil {
|
||||||
args.To, err = decodeAddresses(raw.To, "to")
|
args.To = make(map[common.Address]struct{})
|
||||||
|
to, err = decodeAddresses(raw.To, "to")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for _, addr := range to {
|
||||||
|
args.To[addr] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw.HasReturnData != nil {
|
||||||
|
switch hasReturnData := raw.HasReturnData.(type) {
|
||||||
|
case bool:
|
||||||
|
args.HasReturnData = hasReturnData
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Invalid HasReturnData field specified--must be true or false")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -656,7 +679,7 @@ func (args *TxFilterCriteria) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
// Decodes address field from JSON, which could be either
|
// Decodes address field from JSON, which could be either
|
||||||
// a single common.Address or an array []common.Address.
|
// a single common.Address or an array []common.Address.
|
||||||
// second arg is the name of the field being decoded, for error reporting.
|
// Second arg is the name of the field being decoded, for error reporting.
|
||||||
func decodeAddresses(addresses interface{}, field string) ([]common.Address, error) {
|
func decodeAddresses(addresses interface{}, field string) ([]common.Address, error) {
|
||||||
decoded := []common.Address{}
|
decoded := []common.Address{}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -276,34 +276,35 @@ Logs:
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterTxs(txEvents []*core.TransactionEvent, from []common.Address, to []common.Address) (retData []*types.ReturnData) {
|
func filterTxs(txEvents []*core.TransactionEvent, crit TxFilterCriteria) (retData []*types.ReturnData) {
|
||||||
for _, ev := range txEvents {
|
for _, ev := range txEvents {
|
||||||
// check that From and To fields each match one address in the lists from and to, if specified
|
// if HasReturnData is true, filter out any transactions with no return data
|
||||||
|
if crit.HasReturnData {
|
||||||
if from != nil {
|
if len(ev.RetData.Data) == 0 {
|
||||||
for _, addr := range from {
|
continue
|
||||||
if ev.From != nil && addr == *ev.From {
|
|
||||||
goto checkRecipient
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkRecipient:
|
// check that From field of event matches an address in From, if specified
|
||||||
if to != nil {
|
if crit.From != nil && ev.From != nil {
|
||||||
for _, addr := range to {
|
_, found := crit.From[*ev.From]
|
||||||
ev_to := ev.To
|
if !found {
|
||||||
if ev_to == nil { // contract creation
|
continue
|
||||||
ev_to = &common.Address{}
|
}
|
||||||
}
|
}
|
||||||
if addr == *ev_to {
|
|
||||||
goto bothMatch
|
// check that To field of event matches an address in To, if specified
|
||||||
}
|
if crit.To != nil {
|
||||||
|
ev_to := ev.To
|
||||||
|
if ev_to == nil {
|
||||||
|
ev_to = &common.Address{}
|
||||||
|
}
|
||||||
|
_, found := crit.To[*ev.To]
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bothMatch:
|
|
||||||
retData = append(retData, ev.RetData)
|
retData = append(retData, ev.RetData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ type subscription struct {
|
||||||
logs chan []*types.Log
|
logs chan []*types.Log
|
||||||
hashes chan common.Hash
|
hashes chan common.Hash
|
||||||
headers chan *types.Header
|
headers chan *types.Header
|
||||||
txCrit ethereum.TxFilterQuery
|
txCrit TxFilterCriteria
|
||||||
retData chan []*types.ReturnData
|
retData chan []*types.ReturnData
|
||||||
installed chan struct{} // closed when the filter is installed
|
installed chan struct{} // closed when the filter is installed
|
||||||
err chan error // closed when the filter is uninstalled
|
err chan error // closed when the filter is uninstalled
|
||||||
|
|
@ -328,7 +328,7 @@ func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscr
|
||||||
|
|
||||||
// SubscribeReturnData creates a subscription that captures return data for transactions
|
// SubscribeReturnData creates a subscription that captures return data for transactions
|
||||||
// executed by a particular rpc client
|
// executed by a particular rpc client
|
||||||
func (es *EventSystem) SubscribeReturnData(retCh chan []*types.ReturnData, crit ethereum.TxFilterQuery) *Subscription {
|
func (es *EventSystem) SubscribeReturnData(retCh chan []*types.ReturnData, crit TxFilterCriteria) *Subscription {
|
||||||
sub := &subscription{
|
sub := &subscription{
|
||||||
id: rpc.NewID(),
|
id: rpc.NewID(),
|
||||||
typ: ReturnDataSubscription,
|
typ: ReturnDataSubscription,
|
||||||
|
|
@ -382,7 +382,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev interface{}) {
|
||||||
case []*core.TransactionEvent:
|
case []*core.TransactionEvent:
|
||||||
if len(e) > 0 {
|
if len(e) > 0 {
|
||||||
for _, f := range filters[ReturnDataSubscription] {
|
for _, f := range filters[ReturnDataSubscription] {
|
||||||
if matchedTxs := filterTxs(e, f.txCrit.From, f.txCrit.To); len(matchedTxs) > 0 {
|
if matchedTxs := filterTxs(e, f.txCrit); len(matchedTxs) > 0 {
|
||||||
f.retData <- matchedTxs
|
f.retData <- matchedTxs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@ func TestReturnDataFilter(t *testing.T) {
|
||||||
firstRetData = common.Hex2Bytes("535353535353535353535353535353535353535353535353535353")
|
firstRetData = common.Hex2Bytes("535353535353535353535353535353535353535353535353535353")
|
||||||
secondRetData = common.Hex2Bytes("8888888888888888888888888888888888888888888888888888888888888888888888888888888888888")
|
secondRetData = common.Hex2Bytes("8888888888888888888888888888888888888888888888888888888888888888888888888888888888888")
|
||||||
thirdRetData []byte = nil // no return data for this tx
|
thirdRetData []byte = nil // no return data for this tx
|
||||||
forthRetData = common.Hex2Bytes("0x77")
|
forthRetData = common.Hex2Bytes("77")
|
||||||
backend = &testBackend{mux, db, 0, txPreFeed, txPostFeed, rmLogsFeed, logsFeed, chainFeed}
|
backend = &testBackend{mux, db, 0, txPreFeed, txPostFeed, rmLogsFeed, logsFeed, chainFeed}
|
||||||
api = NewPublicFilterAPI(backend, false)
|
api = NewPublicFilterAPI(backend, false)
|
||||||
)
|
)
|
||||||
|
|
@ -320,24 +320,33 @@ func TestReturnDataFilter(t *testing.T) {
|
||||||
{TxHash: retData[4].TxHash, RetData: &retData[4], From: &firstFromAddr, To: &secondToAddr},
|
{TxHash: retData[4].TxHash, RetData: &retData[4], From: &firstFromAddr, To: &secondToAddr},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
makeMap := func(addressList ...common.Address) map[common.Address]struct{} {
|
||||||
|
addressSet := make(map[common.Address]struct{})
|
||||||
|
for _, address := range addressList {
|
||||||
|
addressSet[address] = struct{}{}
|
||||||
|
}
|
||||||
|
return addressSet
|
||||||
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
crit TxFilterCriteria
|
crit TxFilterCriteria
|
||||||
expected []types.ReturnData
|
expected []types.ReturnData
|
||||||
id rpc.ID
|
id rpc.ID
|
||||||
}{
|
}{
|
||||||
0: {TxFilterCriteria{From: []common.Address{secondFromAddr}}, []types.ReturnData{retData[1], retData[3]}, ""},
|
0: {TxFilterCriteria{From: makeMap(secondFromAddr)}, []types.ReturnData{retData[1], retData[3]}, ""},
|
||||||
1: {TxFilterCriteria{From: []common.Address{firstFromAddr}}, []types.ReturnData{retData[0], retData[2], retData[4]}, ""},
|
1: {TxFilterCriteria{From: makeMap(firstFromAddr)}, []types.ReturnData{retData[0], retData[2], retData[4]}, ""},
|
||||||
2: {TxFilterCriteria{From: []common.Address{notUsedAddr}}, []types.ReturnData{}, ""},
|
2: {TxFilterCriteria{From: makeMap(notUsedAddr)}, []types.ReturnData{}, ""},
|
||||||
3: {TxFilterCriteria{From: []common.Address{notUsedAddr, secondFromAddr}}, []types.ReturnData{retData[1], retData[3]}, ""},
|
3: {TxFilterCriteria{From: makeMap(notUsedAddr, secondFromAddr)}, []types.ReturnData{retData[1], retData[3]}, ""},
|
||||||
4: {TxFilterCriteria{From: []common.Address{notUsedAddr, firstFromAddr, secondFromAddr}}, []types.ReturnData{retData[0], retData[1], retData[2], retData[3], retData[4]}, ""},
|
4: {TxFilterCriteria{From: makeMap(notUsedAddr, firstFromAddr, secondFromAddr)}, []types.ReturnData{retData[0], retData[1], retData[2], retData[3], retData[4]}, ""},
|
||||||
5: {TxFilterCriteria{To: []common.Address{firstToAddr}}, []types.ReturnData{retData[0], retData[3]}, ""},
|
5: {TxFilterCriteria{To: makeMap(firstToAddr)}, []types.ReturnData{retData[0], retData[3]}, ""},
|
||||||
6: {TxFilterCriteria{To: []common.Address{secondToAddr}}, []types.ReturnData{retData[1], retData[2], retData[4]}, ""},
|
6: {TxFilterCriteria{To: makeMap(secondToAddr)}, []types.ReturnData{retData[1], retData[2], retData[4]}, ""},
|
||||||
7: {TxFilterCriteria{To: []common.Address{firstToAddr, secondToAddr}}, []types.ReturnData{retData[0], retData[1], retData[2], retData[3], retData[4]}, ""},
|
7: {TxFilterCriteria{To: makeMap(firstToAddr, secondToAddr)}, []types.ReturnData{retData[0], retData[1], retData[2], retData[3], retData[4]}, ""},
|
||||||
8: {TxFilterCriteria{To: []common.Address{notUsedAddr}}, []types.ReturnData{}, ""},
|
8: {TxFilterCriteria{To: makeMap(notUsedAddr)}, []types.ReturnData{}, ""},
|
||||||
9: {TxFilterCriteria{From: []common.Address{firstFromAddr}, To: []common.Address{secondToAddr}}, []types.ReturnData{retData[2], retData[4]}, ""},
|
9: {TxFilterCriteria{From: makeMap(firstFromAddr), To: makeMap(secondToAddr)}, []types.ReturnData{retData[2], retData[4]}, ""},
|
||||||
10: {TxFilterCriteria{From: []common.Address{notUsedAddr}, To: []common.Address{secondToAddr}}, []types.ReturnData{}, ""},
|
10: {TxFilterCriteria{From: makeMap(notUsedAddr), To: makeMap(secondToAddr)}, []types.ReturnData{}, ""},
|
||||||
11: {TxFilterCriteria{From: []common.Address{secondFromAddr}, To: []common.Address{secondToAddr, notUsedAddr}}, []types.ReturnData{retData[1]}, ""},
|
11: {TxFilterCriteria{From: makeMap(secondFromAddr), To: makeMap(secondToAddr, notUsedAddr)}, []types.ReturnData{retData[1]}, ""},
|
||||||
12: {TxFilterCriteria{}, []types.ReturnData{retData[0], retData[1], retData[2], retData[3], retData[4]}, ""},
|
12: {TxFilterCriteria{}, []types.ReturnData{retData[0], retData[1], retData[2], retData[3], retData[4]}, ""},
|
||||||
|
13: {TxFilterCriteria{HasReturnData: true}, []types.ReturnData{retData[0], retData[1], retData[3]}, ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
// create all filters
|
// create all filters
|
||||||
|
|
@ -373,8 +382,7 @@ func TestReturnDataFilter(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(fetched) != len(tt.expected) {
|
if len(fetched) != len(tt.expected) {
|
||||||
t.Errorf("invalid number of return data events for case %d, want %d events, got %d", i, len(tt.expected), len(fetched))
|
t.Fatalf("invalid number of return data events for case %d, want %d events, got %d", i, len(tt.expected), len(fetched))
|
||||||
//return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for j := range fetched {
|
for j := range fetched {
|
||||||
|
|
|
||||||
|
|
@ -150,8 +150,9 @@ type FilterQuery struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type TxFilterQuery struct {
|
type TxFilterQuery struct {
|
||||||
From []common.Address // restricts matches to transactions originating from specific addresses
|
From map[common.Address]struct{} // restricts matches to transactions originating from specific addresses
|
||||||
To []common.Address // restricts matches to transactions sent to specific addresses
|
To map[common.Address]struct{} // restricts matches to transactions sent to specific addresses
|
||||||
|
HasReturnData bool // restricts matches to transactions which executed a contract function returning data.
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogFilterer provides access to contract log events using a one-off query or continuous
|
// LogFilterer provides access to contract log events using a one-off query or continuous
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue