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 {
var (
retCh = make(chan []*types.ReturnData)
retSub = api.events.SubscribeReturnData(retCh, ethereum.TxFilterQuery(crit))
retSub = api.events.SubscribeReturnData(retCh, crit)
)
api.filtersMu.Lock()
@ -280,7 +280,7 @@ func (api *PublicFilterAPI) ReturnData(ctx context.Context, crit TxFilterCriteri
go func() {
retCh := make(chan []*types.ReturnData)
retSub := api.events.SubscribeReturnData(retCh, ethereum.TxFilterQuery(crit))
retSub := api.events.SubscribeReturnData(retCh, crit)
for {
select {
@ -623,10 +623,16 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
// UnmarshalJSON sets *args fields with given data.
func (args *TxFilterCriteria) UnmarshalJSON(data []byte) error {
var err error
var (
err error
from []common.Address
to []common.Address
)
type input struct {
From interface{} `json:"from"`
To interface{} `json:"to"`
HasReturnData interface{} `json:"hasReturnData"`
}
var raw input
@ -638,17 +644,34 @@ func (args *TxFilterCriteria) UnmarshalJSON(data []byte) error {
args.To = 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 {
return err
}
for _, addr := range from {
args.From[addr] = struct{}{}
}
}
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 {
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
@ -656,7 +679,7 @@ func (args *TxFilterCriteria) UnmarshalJSON(data []byte) error {
// Decodes address field from JSON, which could be either
// 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) {
decoded := []common.Address{}

View file

@ -276,34 +276,35 @@ Logs:
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 {
// check that From and To fields each match one address in the lists from and to, if specified
if from != nil {
for _, addr := range from {
if ev.From != nil && addr == *ev.From {
goto checkRecipient
}
}
// if HasReturnData is true, filter out any transactions with no return data
if crit.HasReturnData {
if len(ev.RetData.Data) == 0 {
continue
}
}
checkRecipient:
if to != nil {
for _, addr := range to {
// check that From field of event matches an address in From, if specified
if crit.From != nil && ev.From != nil {
_, 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
if ev_to == nil { // contract creation
if ev_to == nil {
ev_to = &common.Address{}
}
if addr == *ev_to {
goto bothMatch
}
}
_, found := crit.To[*ev.To]
if !found {
continue
}
}
bothMatch:
retData = append(retData, ev.RetData)
}

View file

@ -88,7 +88,7 @@ type subscription struct {
logs chan []*types.Log
hashes chan common.Hash
headers chan *types.Header
txCrit ethereum.TxFilterQuery
txCrit TxFilterCriteria
retData chan []*types.ReturnData
installed chan struct{} // closed when the filter is installed
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
// 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{
id: rpc.NewID(),
typ: ReturnDataSubscription,
@ -382,7 +382,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev interface{}) {
case []*core.TransactionEvent:
if len(e) > 0 {
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
}
}

View file

@ -299,7 +299,7 @@ func TestReturnDataFilter(t *testing.T) {
firstRetData = common.Hex2Bytes("535353535353535353535353535353535353535353535353535353")
secondRetData = common.Hex2Bytes("8888888888888888888888888888888888888888888888888888888888888888888888888888888888888")
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}
api = NewPublicFilterAPI(backend, false)
)
@ -320,24 +320,33 @@ func TestReturnDataFilter(t *testing.T) {
{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 {
crit TxFilterCriteria
expected []types.ReturnData
id rpc.ID
}{
0: {TxFilterCriteria{From: []common.Address{secondFromAddr}}, []types.ReturnData{retData[1], retData[3]}, ""},
1: {TxFilterCriteria{From: []common.Address{firstFromAddr}}, []types.ReturnData{retData[0], retData[2], retData[4]}, ""},
2: {TxFilterCriteria{From: []common.Address{notUsedAddr}}, []types.ReturnData{}, ""},
3: {TxFilterCriteria{From: []common.Address{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]}, ""},
5: {TxFilterCriteria{To: []common.Address{firstToAddr}}, []types.ReturnData{retData[0], retData[3]}, ""},
6: {TxFilterCriteria{To: []common.Address{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]}, ""},
8: {TxFilterCriteria{To: []common.Address{notUsedAddr}}, []types.ReturnData{}, ""},
9: {TxFilterCriteria{From: []common.Address{firstFromAddr}, To: []common.Address{secondToAddr}}, []types.ReturnData{retData[2], retData[4]}, ""},
10: {TxFilterCriteria{From: []common.Address{notUsedAddr}, To: []common.Address{secondToAddr}}, []types.ReturnData{}, ""},
11: {TxFilterCriteria{From: []common.Address{secondFromAddr}, To: []common.Address{secondToAddr, notUsedAddr}}, []types.ReturnData{retData[1]}, ""},
0: {TxFilterCriteria{From: makeMap(secondFromAddr)}, []types.ReturnData{retData[1], retData[3]}, ""},
1: {TxFilterCriteria{From: makeMap(firstFromAddr)}, []types.ReturnData{retData[0], retData[2], retData[4]}, ""},
2: {TxFilterCriteria{From: makeMap(notUsedAddr)}, []types.ReturnData{}, ""},
3: {TxFilterCriteria{From: makeMap(notUsedAddr, secondFromAddr)}, []types.ReturnData{retData[1], retData[3]}, ""},
4: {TxFilterCriteria{From: makeMap(notUsedAddr, firstFromAddr, secondFromAddr)}, []types.ReturnData{retData[0], retData[1], retData[2], retData[3], retData[4]}, ""},
5: {TxFilterCriteria{To: makeMap(firstToAddr)}, []types.ReturnData{retData[0], retData[3]}, ""},
6: {TxFilterCriteria{To: makeMap(secondToAddr)}, []types.ReturnData{retData[1], retData[2], retData[4]}, ""},
7: {TxFilterCriteria{To: makeMap(firstToAddr, secondToAddr)}, []types.ReturnData{retData[0], retData[1], retData[2], retData[3], retData[4]}, ""},
8: {TxFilterCriteria{To: makeMap(notUsedAddr)}, []types.ReturnData{}, ""},
9: {TxFilterCriteria{From: makeMap(firstFromAddr), To: makeMap(secondToAddr)}, []types.ReturnData{retData[2], retData[4]}, ""},
10: {TxFilterCriteria{From: makeMap(notUsedAddr), To: makeMap(secondToAddr)}, []types.ReturnData{}, ""},
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]}, ""},
13: {TxFilterCriteria{HasReturnData: true}, []types.ReturnData{retData[0], retData[1], retData[3]}, ""},
}
// create all filters
@ -373,8 +382,7 @@ func TestReturnDataFilter(t *testing.T) {
}
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))
//return
t.Fatalf("invalid number of return data events for case %d, want %d events, got %d", i, len(tt.expected), len(fetched))
}
for j := range fetched {

View file

@ -150,8 +150,9 @@ type FilterQuery struct {
}
type TxFilterQuery struct {
From []common.Address // restricts matches to transactions originating from specific addresses
To []common.Address // restricts matches to transactions sent to specific addresses
From map[common.Address]struct{} // restricts matches to transactions originating from 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