2014-08-11 14:23:17 +00:00
|
|
|
package ethchain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
2014-09-26 11:32:54 +00:00
|
|
|
"math"
|
2014-08-11 14:23:17 +00:00
|
|
|
|
|
|
|
"github.com/ethereum/eth-go/ethstate"
|
|
|
|
"github.com/ethereum/eth-go/ethutil"
|
2014-08-15 14:19:10 +00:00
|
|
|
"gopkg.in/qml.v1"
|
2014-08-11 14:23:17 +00:00
|
|
|
)
|
|
|
|
|
2014-08-15 14:19:10 +00:00
|
|
|
type data struct {
|
|
|
|
id, address []byte
|
|
|
|
}
|
|
|
|
|
2014-08-11 14:23:17 +00:00
|
|
|
// Filtering interface
|
|
|
|
type Filter struct {
|
|
|
|
eth EthManager
|
2014-09-26 11:32:54 +00:00
|
|
|
earliest int64
|
|
|
|
latest int64
|
2014-08-11 14:23:17 +00:00
|
|
|
skip int
|
2014-08-14 15:02:39 +00:00
|
|
|
from, to [][]byte
|
2014-08-11 14:23:17 +00:00
|
|
|
max int
|
2014-08-15 14:19:10 +00:00
|
|
|
|
|
|
|
altered []data
|
2014-09-13 22:13:23 +00:00
|
|
|
|
|
|
|
BlockCallback func(*Block)
|
|
|
|
MessageCallback func(ethstate.Messages)
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new filter which uses a bloom filter on blocks to figure out whether a particular block
|
|
|
|
// is interesting or not.
|
|
|
|
func NewFilter(eth EthManager) *Filter {
|
|
|
|
return &Filter{eth: eth}
|
|
|
|
}
|
|
|
|
|
2014-08-14 22:24:37 +00:00
|
|
|
func NewFilterFromMap(object map[string]interface{}, eth EthManager) *Filter {
|
|
|
|
filter := NewFilter(eth)
|
|
|
|
|
|
|
|
if object["earliest"] != nil {
|
2014-09-26 11:32:54 +00:00
|
|
|
val := ethutil.NewValue(object["earliest"])
|
|
|
|
filter.SetEarliestBlock(val.Int())
|
2014-08-14 22:24:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if object["latest"] != nil {
|
2014-09-26 11:32:54 +00:00
|
|
|
val := ethutil.NewValue(object["latest"])
|
|
|
|
filter.SetLatestBlock(val.Int())
|
2014-08-14 22:24:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if object["to"] != nil {
|
2014-09-26 11:32:54 +00:00
|
|
|
val := ethutil.NewValue(object["to"])
|
|
|
|
filter.AddTo(ethutil.Hex2Bytes(val.Str()))
|
2014-08-14 22:24:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if object["from"] != nil {
|
2014-09-26 11:32:54 +00:00
|
|
|
val := ethutil.NewValue(object["from"])
|
|
|
|
filter.AddFrom(ethutil.Hex2Bytes(val.Str()))
|
2014-08-14 22:24:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if object["max"] != nil {
|
2014-09-26 11:32:54 +00:00
|
|
|
val := ethutil.NewValue(object["max"])
|
|
|
|
filter.SetMax(int(val.Uint()))
|
2014-08-14 22:24:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if object["skip"] != nil {
|
2014-09-26 11:32:54 +00:00
|
|
|
val := ethutil.NewValue(object["skip"])
|
|
|
|
filter.SetSkip(int(val.Uint()))
|
2014-08-14 22:24:37 +00:00
|
|
|
}
|
|
|
|
|
2014-08-15 14:19:10 +00:00
|
|
|
if object["altered"] != nil {
|
|
|
|
filter.altered = makeAltered(object["altered"])
|
|
|
|
}
|
|
|
|
|
2014-08-14 22:24:37 +00:00
|
|
|
return filter
|
|
|
|
}
|
|
|
|
|
2014-08-15 14:19:10 +00:00
|
|
|
func (self *Filter) AddAltered(id, address []byte) {
|
|
|
|
self.altered = append(self.altered, data{id, address})
|
|
|
|
}
|
|
|
|
|
2014-08-11 14:23:17 +00:00
|
|
|
// Set the earliest and latest block for filtering.
|
|
|
|
// -1 = latest block (i.e., the current block)
|
|
|
|
// hash = particular hash from-to
|
2014-09-26 11:32:54 +00:00
|
|
|
func (self *Filter) SetEarliestBlock(earliest int64) {
|
|
|
|
self.earliest = earliest
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
|
|
|
|
2014-09-26 11:32:54 +00:00
|
|
|
func (self *Filter) SetLatestBlock(latest int64) {
|
|
|
|
self.latest = latest
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
|
|
|
|
2014-08-14 15:02:39 +00:00
|
|
|
func (self *Filter) SetFrom(addr [][]byte) {
|
2014-08-11 14:23:17 +00:00
|
|
|
self.from = addr
|
|
|
|
}
|
|
|
|
|
2014-08-14 15:02:39 +00:00
|
|
|
func (self *Filter) AddFrom(addr []byte) {
|
|
|
|
self.from = append(self.from, addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *Filter) SetTo(addr [][]byte) {
|
2014-08-11 14:23:17 +00:00
|
|
|
self.to = addr
|
|
|
|
}
|
|
|
|
|
2014-08-14 15:02:39 +00:00
|
|
|
func (self *Filter) AddTo(addr []byte) {
|
2014-08-20 11:05:26 +00:00
|
|
|
self.to = append(self.to, addr)
|
2014-08-14 15:02:39 +00:00
|
|
|
}
|
|
|
|
|
2014-08-11 14:23:17 +00:00
|
|
|
func (self *Filter) SetMax(max int) {
|
|
|
|
self.max = max
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *Filter) SetSkip(skip int) {
|
|
|
|
self.skip = skip
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run filters messages with the current parameters set
|
|
|
|
func (self *Filter) Find() []*ethstate.Message {
|
2014-09-26 11:32:54 +00:00
|
|
|
var earliestBlockNo uint64 = uint64(self.earliest)
|
|
|
|
if self.earliest == -1 {
|
|
|
|
earliestBlockNo = self.eth.BlockChain().CurrentBlock.Number.Uint64()
|
|
|
|
}
|
|
|
|
var latestBlockNo uint64 = uint64(self.latest)
|
|
|
|
if self.latest == -1 {
|
|
|
|
latestBlockNo = self.eth.BlockChain().CurrentBlock.Number.Uint64()
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
|
|
|
|
2014-09-26 11:32:54 +00:00
|
|
|
var (
|
|
|
|
messages []*ethstate.Message
|
|
|
|
block = self.eth.BlockChain().GetBlockByNumber(latestBlockNo)
|
|
|
|
quit bool
|
|
|
|
)
|
|
|
|
for i := 0; !quit && block != nil; i++ {
|
|
|
|
// Quit on latest
|
|
|
|
switch {
|
|
|
|
case block.Number.Uint64() == earliestBlockNo:
|
2014-08-11 14:23:17 +00:00
|
|
|
quit = true
|
2014-09-26 11:32:54 +00:00
|
|
|
case self.max <= len(messages):
|
|
|
|
break
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Use bloom filtering to see if this block is interesting given the
|
|
|
|
// current parameters
|
|
|
|
if self.bloomFilter(block) {
|
|
|
|
// Get the messages of the block
|
|
|
|
msgs, err := self.eth.StateManager().GetMessages(block)
|
|
|
|
if err != nil {
|
|
|
|
chainlogger.Warnln("err: filter get messages ", err)
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2014-08-14 22:24:37 +00:00
|
|
|
messages = append(messages, self.FilterMessages(msgs)...)
|
|
|
|
}
|
2014-08-14 15:02:39 +00:00
|
|
|
|
2014-08-14 22:24:37 +00:00
|
|
|
block = self.eth.BlockChain().GetBlock(block.PrevHash)
|
|
|
|
}
|
2014-08-11 14:23:17 +00:00
|
|
|
|
2014-09-26 11:32:54 +00:00
|
|
|
skip := int(math.Min(float64(len(messages)), float64(self.skip)))
|
|
|
|
|
|
|
|
return messages[skip:]
|
2014-08-14 22:24:37 +00:00
|
|
|
}
|
2014-08-11 14:23:17 +00:00
|
|
|
|
2014-08-15 14:19:10 +00:00
|
|
|
func includes(addresses [][]byte, a []byte) (found bool) {
|
|
|
|
for _, addr := range addresses {
|
|
|
|
if bytes.Compare(addr, a) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-08-14 22:24:37 +00:00
|
|
|
func (self *Filter) FilterMessages(msgs []*ethstate.Message) []*ethstate.Message {
|
|
|
|
var messages []*ethstate.Message
|
2014-08-11 14:23:17 +00:00
|
|
|
|
2014-08-14 22:24:37 +00:00
|
|
|
// Filter the messages for interesting stuff
|
|
|
|
for _, message := range msgs {
|
|
|
|
if len(self.to) > 0 && !includes(self.to, message.To) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(self.from) > 0 && !includes(self.from, message.From) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2014-08-15 14:19:10 +00:00
|
|
|
var match bool
|
|
|
|
if len(self.altered) == 0 {
|
|
|
|
match = true
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range self.altered {
|
|
|
|
if len(item.id) > 0 && bytes.Compare(message.To, item.id) != 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(item.address) > 0 && !includes(message.ChangedAddresses, item.address) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
match = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if !match {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2014-08-14 22:24:37 +00:00
|
|
|
messages = append(messages, message)
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return messages
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *Filter) bloomFilter(block *Block) bool {
|
|
|
|
fk := append([]byte("bloom"), block.Hash()...)
|
|
|
|
bin, err := self.eth.Db().Get(fk)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
bloom := NewBloomFilter(bin)
|
|
|
|
|
2014-08-14 15:02:39 +00:00
|
|
|
var fromIncluded, toIncluded bool
|
2014-08-11 14:23:17 +00:00
|
|
|
if len(self.from) > 0 {
|
2014-08-14 15:02:39 +00:00
|
|
|
for _, from := range self.from {
|
|
|
|
if bloom.Search(from) {
|
|
|
|
fromIncluded = true
|
|
|
|
break
|
|
|
|
}
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
2014-08-14 15:02:39 +00:00
|
|
|
} else {
|
|
|
|
fromIncluded = true
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(self.to) > 0 {
|
2014-08-14 15:02:39 +00:00
|
|
|
for _, to := range self.to {
|
|
|
|
if bloom.Search(to) {
|
|
|
|
toIncluded = true
|
|
|
|
break
|
|
|
|
}
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
2014-08-14 15:02:39 +00:00
|
|
|
} else {
|
|
|
|
toIncluded = true
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
|
|
|
|
2014-08-14 15:02:39 +00:00
|
|
|
return fromIncluded && toIncluded
|
2014-08-11 14:23:17 +00:00
|
|
|
}
|
2014-08-15 14:19:10 +00:00
|
|
|
|
|
|
|
// Conversion methodn
|
|
|
|
func mapToData(m map[string]interface{}) (d data) {
|
|
|
|
if str, ok := m["id"].(string); ok {
|
|
|
|
d.id = ethutil.Hex2Bytes(str)
|
|
|
|
}
|
|
|
|
|
|
|
|
if str, ok := m["at"].(string); ok {
|
|
|
|
d.address = ethutil.Hex2Bytes(str)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// data can come in in the following formats:
|
|
|
|
// ["aabbccdd", {id: "ccddee", at: "11223344"}], "aabbcc", {id: "ccddee", at: "1122"}
|
|
|
|
func makeAltered(v interface{}) (d []data) {
|
|
|
|
if str, ok := v.(string); ok {
|
|
|
|
d = append(d, data{ethutil.Hex2Bytes(str), nil})
|
|
|
|
} else if obj, ok := v.(map[string]interface{}); ok {
|
|
|
|
d = append(d, mapToData(obj))
|
|
|
|
} else if slice, ok := v.([]interface{}); ok {
|
|
|
|
for _, item := range slice {
|
|
|
|
d = append(d, makeAltered(item)...)
|
|
|
|
}
|
|
|
|
} else if qList, ok := v.(*qml.List); ok {
|
|
|
|
var s []interface{}
|
|
|
|
qList.Convert(&s)
|
|
|
|
|
|
|
|
fmt.Println(s)
|
|
|
|
|
|
|
|
d = makeAltered(s)
|
|
|
|
} else if qMap, ok := v.(*qml.Map); ok {
|
|
|
|
var m map[string]interface{}
|
|
|
|
qMap.Convert(&m)
|
|
|
|
fmt.Println(m)
|
|
|
|
|
|
|
|
d = makeAltered(m)
|
|
|
|
} else {
|
|
|
|
panic(fmt.Sprintf("makeAltered err (unknown conversion): %T\n", v))
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|