eth/filters,interfaces.go: Expand & modify TxFilterQuery

This commit is contained in:
Domino Valdano 2018-06-12 19:34:10 -07:00
parent a92c854be5
commit 20eba82751
No known key found for this signature in database
GPG key ID: 3FDFE30EE92AC05E
5 changed files with 82 additions and 49 deletions

View file

@ -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{}

View file

@ -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 {
if ev.From != nil && addr == *ev.From {
goto checkRecipient
}
}
continue 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]
if !found {
continue
}
}
// check that To field of event matches an address in To, if specified
if crit.To != nil {
ev_to := ev.To ev_to := ev.To
if ev_to == nil { // contract creation if ev_to == nil {
ev_to = &common.Address{} ev_to = &common.Address{}
} }
if addr == *ev_to { _, found := crit.To[*ev.To]
goto bothMatch if !found {
}
}
continue continue
} }
}
bothMatch:
retData = append(retData, ev.RetData) retData = append(retData, ev.RetData)
} }

View file

@ -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
} }
} }

View file

@ -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 {

View file

@ -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