mirror of https://github.com/ethereum/go-ethereum
commit
0af0f0d890
@ -1,21 +1,16 @@ |
|||||||
The MIT License (MIT) |
Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. |
||||||
|
|
||||||
Copyright (c) 2013 Jeffrey Wilcke |
This library is free software; you can redistribute it and/or |
||||||
|
modify it under the terms of the GNU Lesser General Public |
||||||
|
License as published by the Free Software Foundation; either |
||||||
|
version 2.1 of the License, or (at your option) any later version. |
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
This library is distributed in the hope that it will be useful, |
||||||
of this software and associated documentation files (the "Software"), to deal |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
in the Software without restriction, including without limitation the rights |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
Lesser General Public License for more details. |
||||||
copies of the Software, and to permit persons to whom the Software is |
|
||||||
furnished to do so, subject to the following conditions: |
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in |
You should have received a copy of the GNU Lesser General Public |
||||||
all copies or substantial portions of the Software. |
License along with this library; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
MA 02110-1301 USA |
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
||||||
THE SOFTWARE. |
|
||||||
|
@ -0,0 +1,116 @@ |
|||||||
|
package eth |
||||||
|
|
||||||
|
import ( |
||||||
|
"math" |
||||||
|
"math/big" |
||||||
|
"sync" |
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethchain" |
||||||
|
"github.com/ethereum/eth-go/ethutil" |
||||||
|
) |
||||||
|
|
||||||
|
type block struct { |
||||||
|
peer *Peer |
||||||
|
block *ethchain.Block |
||||||
|
} |
||||||
|
|
||||||
|
type BlockPool struct { |
||||||
|
mut sync.Mutex |
||||||
|
|
||||||
|
eth *Ethereum |
||||||
|
|
||||||
|
hashPool [][]byte |
||||||
|
pool map[string]*block |
||||||
|
|
||||||
|
td *big.Int |
||||||
|
} |
||||||
|
|
||||||
|
func NewBlockPool(eth *Ethereum) *BlockPool { |
||||||
|
return &BlockPool{ |
||||||
|
eth: eth, |
||||||
|
pool: make(map[string]*block), |
||||||
|
td: ethutil.Big0, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *BlockPool) HasLatestHash() bool { |
||||||
|
return self.pool[string(self.eth.BlockChain().CurrentBlock.Hash())] != nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *BlockPool) HasCommonHash(hash []byte) bool { |
||||||
|
return self.eth.BlockChain().GetBlock(hash) != nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *BlockPool) AddHash(hash []byte) { |
||||||
|
if self.pool[string(hash)] == nil { |
||||||
|
self.pool[string(hash)] = &block{nil, nil} |
||||||
|
|
||||||
|
self.hashPool = append([][]byte{hash}, self.hashPool...) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *BlockPool) SetBlock(b *ethchain.Block) { |
||||||
|
hash := string(b.Hash()) |
||||||
|
|
||||||
|
if self.pool[string(hash)] == nil { |
||||||
|
self.pool[hash] = &block{nil, nil} |
||||||
|
} |
||||||
|
|
||||||
|
self.pool[hash].block = b |
||||||
|
} |
||||||
|
|
||||||
|
func (self *BlockPool) CheckLinkAndProcess(f func(block *ethchain.Block)) bool { |
||||||
|
self.mut.Lock() |
||||||
|
defer self.mut.Unlock() |
||||||
|
|
||||||
|
if self.IsLinked() { |
||||||
|
for i, hash := range self.hashPool { |
||||||
|
block := self.pool[string(hash)].block |
||||||
|
if block != nil { |
||||||
|
f(block) |
||||||
|
|
||||||
|
delete(self.pool, string(hash)) |
||||||
|
} else { |
||||||
|
self.hashPool = self.hashPool[i:] |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
func (self *BlockPool) IsLinked() bool { |
||||||
|
if len(self.hashPool) == 0 { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
block := self.pool[string(self.hashPool[0])].block |
||||||
|
if block != nil { |
||||||
|
return self.eth.BlockChain().HasBlock(block.PrevHash) |
||||||
|
} |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
func (self *BlockPool) Take(amount int, peer *Peer) (hashes [][]byte) { |
||||||
|
self.mut.Lock() |
||||||
|
defer self.mut.Unlock() |
||||||
|
|
||||||
|
num := int(math.Min(float64(amount), float64(len(self.pool)))) |
||||||
|
j := 0 |
||||||
|
for i := 0; i < len(self.hashPool) && j < num; i++ { |
||||||
|
hash := string(self.hashPool[i]) |
||||||
|
if self.pool[hash].peer == nil || self.pool[hash].peer == peer { |
||||||
|
self.pool[hash].peer = peer |
||||||
|
|
||||||
|
hashes = append(hashes, self.hashPool[i]) |
||||||
|
j++ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
package ethchain |
||||||
|
|
||||||
|
type BloomFilter struct { |
||||||
|
bin []byte |
||||||
|
} |
||||||
|
|
||||||
|
func NewBloomFilter(bin []byte) *BloomFilter { |
||||||
|
if bin == nil { |
||||||
|
bin = make([]byte, 256) |
||||||
|
} |
||||||
|
|
||||||
|
return &BloomFilter{ |
||||||
|
bin: bin, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *BloomFilter) Set(addr []byte) { |
||||||
|
if len(addr) < 8 { |
||||||
|
chainlogger.Warnf("err: bloom set to small: %x\n", addr) |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
for _, i := range addr[len(addr)-8:] { |
||||||
|
self.bin[i] = 1 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *BloomFilter) Search(addr []byte) bool { |
||||||
|
if len(addr) < 8 { |
||||||
|
chainlogger.Warnf("err: bloom search to small: %x\n", addr) |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
for _, i := range addr[len(addr)-8:] { |
||||||
|
if self.bin[i] == 0 { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
func (self *BloomFilter) Bin() []byte { |
||||||
|
return self.bin |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package ethchain |
||||||
|
|
||||||
|
import "testing" |
||||||
|
|
||||||
|
func TestBloomFilter(t *testing.T) { |
||||||
|
bf := NewBloomFilter(nil) |
||||||
|
|
||||||
|
a := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} |
||||||
|
bf.Set(a) |
||||||
|
|
||||||
|
b := []byte{10, 11, 12, 13, 14, 15, 16, 17, 18, 19} |
||||||
|
|
||||||
|
if bf.Search(a) == false { |
||||||
|
t.Error("Expected 'a' to yield true using a bloom filter") |
||||||
|
} |
||||||
|
|
||||||
|
if bf.Search(b) { |
||||||
|
t.Error("Expected 'b' not to field trie using a bloom filter") |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,304 @@ |
|||||||
|
package ethchain |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethstate" |
||||||
|
"github.com/ethereum/eth-go/ethutil" |
||||||
|
"gopkg.in/qml.v1" |
||||||
|
) |
||||||
|
|
||||||
|
type data struct { |
||||||
|
id, address []byte |
||||||
|
} |
||||||
|
|
||||||
|
// Filtering interface
|
||||||
|
type Filter struct { |
||||||
|
eth EthManager |
||||||
|
earliest []byte |
||||||
|
latest []byte |
||||||
|
skip int |
||||||
|
from, to [][]byte |
||||||
|
max int |
||||||
|
|
||||||
|
altered []data |
||||||
|
} |
||||||
|
|
||||||
|
// 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} |
||||||
|
} |
||||||
|
|
||||||
|
func NewFilterFromMap(object map[string]interface{}, eth EthManager) *Filter { |
||||||
|
filter := NewFilter(eth) |
||||||
|
|
||||||
|
if object["earliest"] != nil { |
||||||
|
earliest := object["earliest"] |
||||||
|
if e, ok := earliest.(string); ok { |
||||||
|
filter.SetEarliestBlock(ethutil.Hex2Bytes(e)) |
||||||
|
} else { |
||||||
|
filter.SetEarliestBlock(earliest) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if object["latest"] != nil { |
||||||
|
latest := object["latest"] |
||||||
|
if l, ok := latest.(string); ok { |
||||||
|
filter.SetLatestBlock(ethutil.Hex2Bytes(l)) |
||||||
|
} else { |
||||||
|
filter.SetLatestBlock(latest) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if object["to"] != nil { |
||||||
|
filter.AddTo(ethutil.Hex2Bytes(object["to"].(string))) |
||||||
|
} |
||||||
|
|
||||||
|
if object["from"] != nil { |
||||||
|
filter.AddFrom(ethutil.Hex2Bytes(object["from"].(string))) |
||||||
|
} |
||||||
|
|
||||||
|
if object["max"] != nil { |
||||||
|
filter.SetMax(object["max"].(int)) |
||||||
|
} |
||||||
|
|
||||||
|
if object["skip"] != nil { |
||||||
|
filter.SetSkip(object["skip"].(int)) |
||||||
|
} |
||||||
|
|
||||||
|
if object["altered"] != nil { |
||||||
|
filter.altered = makeAltered(object["altered"]) |
||||||
|
} |
||||||
|
|
||||||
|
return filter |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Filter) AddAltered(id, address []byte) { |
||||||
|
self.altered = append(self.altered, data{id, address}) |
||||||
|
} |
||||||
|
|
||||||
|
// Set the earliest and latest block for filtering.
|
||||||
|
// -1 = latest block (i.e., the current block)
|
||||||
|
// hash = particular hash from-to
|
||||||
|
func (self *Filter) SetEarliestBlock(earliest interface{}) { |
||||||
|
e := ethutil.NewValue(earliest) |
||||||
|
|
||||||
|
// Check for -1 (latest) otherwise assume bytes
|
||||||
|
if e.Int() == -1 { |
||||||
|
self.earliest = self.eth.BlockChain().CurrentBlock.Hash() |
||||||
|
} else if e.Len() > 0 { |
||||||
|
self.earliest = e.Bytes() |
||||||
|
} else { |
||||||
|
panic(fmt.Sprintf("earliest has to be either -1 or a valid hash: %v (%T)", e, e.Val)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Filter) SetLatestBlock(latest interface{}) { |
||||||
|
l := ethutil.NewValue(latest) |
||||||
|
|
||||||
|
// Check for -1 (latest) otherwise assume bytes
|
||||||
|
if l.Int() == -1 { |
||||||
|
self.latest = self.eth.BlockChain().CurrentBlock.Hash() |
||||||
|
} else if l.Len() > 0 { |
||||||
|
self.latest = l.Bytes() |
||||||
|
} else { |
||||||
|
panic(fmt.Sprintf("latest has to be either -1 or a valid hash: %v", l)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Filter) SetFrom(addr [][]byte) { |
||||||
|
self.from = addr |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Filter) AddFrom(addr []byte) { |
||||||
|
self.from = append(self.from, addr) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Filter) SetTo(addr [][]byte) { |
||||||
|
self.to = addr |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Filter) AddTo(addr []byte) { |
||||||
|
self.to = append(self.to, addr) |
||||||
|
} |
||||||
|
|
||||||
|
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 { |
||||||
|
var messages []*ethstate.Message |
||||||
|
|
||||||
|
block := self.eth.BlockChain().GetBlock(self.latest) |
||||||
|
|
||||||
|
// skip N blocks (useful for pagination)
|
||||||
|
if self.skip > 0 { |
||||||
|
for i := 0; i < i; i++ { |
||||||
|
block = self.eth.BlockChain().GetBlock(block.PrevHash) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Start block filtering
|
||||||
|
quit := false |
||||||
|
for i := 1; !quit && block != nil; i++ { |
||||||
|
// Mark last check
|
||||||
|
if self.max == i || (len(self.earliest) > 0 && bytes.Compare(block.Hash(), self.earliest) == 0) { |
||||||
|
quit = true |
||||||
|
} |
||||||
|
|
||||||
|
// 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 |
||||||
|
} |
||||||
|
|
||||||
|
messages = append(messages, self.FilterMessages(msgs)...) |
||||||
|
} |
||||||
|
|
||||||
|
block = self.eth.BlockChain().GetBlock(block.PrevHash) |
||||||
|
} |
||||||
|
|
||||||
|
return messages |
||||||
|
} |
||||||
|
|
||||||
|
func includes(addresses [][]byte, a []byte) (found bool) { |
||||||
|
for _, addr := range addresses { |
||||||
|
if bytes.Compare(addr, a) == 0 { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Filter) FilterMessages(msgs []*ethstate.Message) []*ethstate.Message { |
||||||
|
var messages []*ethstate.Message |
||||||
|
|
||||||
|
// 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 |
||||||
|
} |
||||||
|
|
||||||
|
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 |
||||||
|
} |
||||||
|
|
||||||
|
messages = append(messages, message) |
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
|
||||||
|
var fromIncluded, toIncluded bool |
||||||
|
if len(self.from) > 0 { |
||||||
|
for _, from := range self.from { |
||||||
|
if bloom.Search(from) { |
||||||
|
fromIncluded = true |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
fromIncluded = true |
||||||
|
} |
||||||
|
|
||||||
|
if len(self.to) > 0 { |
||||||
|
for _, to := range self.to { |
||||||
|
if bloom.Search(to) { |
||||||
|
toIncluded = true |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
toIncluded = true |
||||||
|
} |
||||||
|
|
||||||
|
return fromIncluded && toIncluded |
||||||
|
} |
||||||
|
|
||||||
|
// 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 |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
package ethchain |
||||||
|
|
||||||
|
import "testing" |
||||||
|
|
||||||
|
func TestFilter(t *testing.T) { |
||||||
|
filter := NewFilter() |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,33 @@ |
|||||||
|
package ethpipe |
||||||
|
|
||||||
|
import "github.com/ethereum/eth-go/ethutil" |
||||||
|
|
||||||
|
var cnfCtr = ethutil.Hex2Bytes("661005d2720d855f1d9976f88bb10c1a3398c77f") |
||||||
|
|
||||||
|
type Config struct { |
||||||
|
pipe *Pipe |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Config) Get(name string) *Object { |
||||||
|
configCtrl := self.pipe.World().safeGet(cnfCtr) |
||||||
|
var addr []byte |
||||||
|
|
||||||
|
switch name { |
||||||
|
case "NameReg": |
||||||
|
addr = []byte{0} |
||||||
|
case "DnsReg": |
||||||
|
objectAddr := configCtrl.GetStorage(ethutil.BigD([]byte{0})) |
||||||
|
domainAddr := (&Object{self.pipe.World().safeGet(objectAddr.Bytes())}).StorageString("DnsReg").Bytes() |
||||||
|
return &Object{self.pipe.World().safeGet(domainAddr)} |
||||||
|
default: |
||||||
|
addr = ethutil.RightPadBytes([]byte(name), 32) |
||||||
|
} |
||||||
|
|
||||||
|
objectAddr := configCtrl.GetStorage(ethutil.BigD(addr)) |
||||||
|
|
||||||
|
return &Object{self.pipe.World().safeGet(objectAddr.Bytes())} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Config) Exist() bool { |
||||||
|
return self.pipe.World().Get(cnfCtr) != nil |
||||||
|
} |
@ -0,0 +1,334 @@ |
|||||||
|
package ethpipe |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"sync/atomic" |
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethchain" |
||||||
|
"github.com/ethereum/eth-go/ethcrypto" |
||||||
|
"github.com/ethereum/eth-go/ethreact" |
||||||
|
"github.com/ethereum/eth-go/ethstate" |
||||||
|
"github.com/ethereum/eth-go/ethutil" |
||||||
|
) |
||||||
|
|
||||||
|
type JSPipe struct { |
||||||
|
*Pipe |
||||||
|
} |
||||||
|
|
||||||
|
func NewJSPipe(eth ethchain.EthManager) *JSPipe { |
||||||
|
return &JSPipe{New(eth)} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) BlockByHash(strHash string) *JSBlock { |
||||||
|
hash := ethutil.Hex2Bytes(strHash) |
||||||
|
block := self.obj.BlockChain().GetBlock(hash) |
||||||
|
|
||||||
|
return NewJSBlock(block) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) BlockByNumber(num int32) *JSBlock { |
||||||
|
if num == -1 { |
||||||
|
return NewJSBlock(self.obj.BlockChain().CurrentBlock) |
||||||
|
} |
||||||
|
|
||||||
|
return NewJSBlock(self.obj.BlockChain().GetBlockByNumber(uint64(num))) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) Block(v interface{}) *JSBlock { |
||||||
|
if n, ok := v.(int32); ok { |
||||||
|
return self.BlockByNumber(n) |
||||||
|
} else if str, ok := v.(string); ok { |
||||||
|
return self.BlockByHash(str) |
||||||
|
} else if f, ok := v.(float64); ok { // Don't ask ...
|
||||||
|
return self.BlockByNumber(int32(f)) |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) Key() *JSKey { |
||||||
|
return NewJSKey(self.obj.KeyManager().KeyPair()) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) StateObject(addr string) *JSObject { |
||||||
|
object := &Object{self.World().safeGet(ethutil.Hex2Bytes(addr))} |
||||||
|
|
||||||
|
return NewJSObject(object) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) PeerCount() int { |
||||||
|
return self.obj.PeerCount() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) Peers() []JSPeer { |
||||||
|
var peers []JSPeer |
||||||
|
for peer := self.obj.Peers().Front(); peer != nil; peer = peer.Next() { |
||||||
|
p := peer.Value.(ethchain.Peer) |
||||||
|
// we only want connected peers
|
||||||
|
if atomic.LoadInt32(p.Connected()) != 0 { |
||||||
|
peers = append(peers, *NewJSPeer(p)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return peers |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) IsMining() bool { |
||||||
|
return self.obj.IsMining() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) IsListening() bool { |
||||||
|
return self.obj.IsListening() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) CoinBase() string { |
||||||
|
return ethutil.Bytes2Hex(self.obj.KeyManager().Address()) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) BalanceAt(addr string) string { |
||||||
|
return self.World().SafeGet(ethutil.Hex2Bytes(addr)).Balance.String() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) NumberToHuman(balance string) string { |
||||||
|
b := ethutil.Big(balance) |
||||||
|
|
||||||
|
return ethutil.CurrencyToString(b) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) StorageAt(addr, storageAddr string) string { |
||||||
|
storage := self.World().SafeGet(ethutil.Hex2Bytes(addr)).Storage(ethutil.Hex2Bytes(storageAddr)) |
||||||
|
return storage.BigInt().String() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) TxCountAt(address string) int { |
||||||
|
return int(self.World().SafeGet(ethutil.Hex2Bytes(address)).Nonce) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) IsContract(address string) bool { |
||||||
|
return len(self.World().SafeGet(ethutil.Hex2Bytes(address)).Code) > 0 |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) SecretToAddress(key string) string { |
||||||
|
pair, err := ethcrypto.NewKeyPairFromSec(ethutil.Hex2Bytes(key)) |
||||||
|
if err != nil { |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
return ethutil.Bytes2Hex(pair.Address()) |
||||||
|
} |
||||||
|
|
||||||
|
type KeyVal struct { |
||||||
|
Key string `json:"key"` |
||||||
|
Value string `json:"value"` |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) EachStorage(addr string) string { |
||||||
|
var values []KeyVal |
||||||
|
object := self.World().SafeGet(ethutil.Hex2Bytes(addr)) |
||||||
|
object.EachStorage(func(name string, value *ethutil.Value) { |
||||||
|
value.Decode() |
||||||
|
values = append(values, KeyVal{ethutil.Bytes2Hex([]byte(name)), ethutil.Bytes2Hex(value.Bytes())}) |
||||||
|
}) |
||||||
|
|
||||||
|
valuesJson, err := json.Marshal(values) |
||||||
|
if err != nil { |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
return string(valuesJson) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) ToAscii(str string) string { |
||||||
|
padded := ethutil.RightPadBytes([]byte(str), 32) |
||||||
|
|
||||||
|
return "0x" + ethutil.Bytes2Hex(padded) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) FromAscii(str string) string { |
||||||
|
if ethutil.IsHex(str) { |
||||||
|
str = str[2:] |
||||||
|
} |
||||||
|
|
||||||
|
return string(bytes.Trim(ethutil.Hex2Bytes(str), "\x00")) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) FromNumber(str string) string { |
||||||
|
if ethutil.IsHex(str) { |
||||||
|
str = str[2:] |
||||||
|
} |
||||||
|
|
||||||
|
return ethutil.BigD(ethutil.Hex2Bytes(str)).String() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) Transact(key, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (*JSReceipt, error) { |
||||||
|
var hash []byte |
||||||
|
var contractCreation bool |
||||||
|
if len(toStr) == 0 { |
||||||
|
contractCreation = true |
||||||
|
} else { |
||||||
|
// Check if an address is stored by this address
|
||||||
|
addr := self.World().Config().Get("NameReg").StorageString(toStr).Bytes() |
||||||
|
if len(addr) > 0 { |
||||||
|
hash = addr |
||||||
|
} else { |
||||||
|
hash = ethutil.Hex2Bytes(toStr) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var keyPair *ethcrypto.KeyPair |
||||||
|
var err error |
||||||
|
if ethutil.IsHex(key) { |
||||||
|
keyPair, err = ethcrypto.NewKeyPairFromSec([]byte(ethutil.Hex2Bytes(key[2:]))) |
||||||
|
} else { |
||||||
|
keyPair, err = ethcrypto.NewKeyPairFromSec([]byte(ethutil.Hex2Bytes(key))) |
||||||
|
} |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
value = ethutil.Big(valueStr) |
||||||
|
gas = ethutil.Big(gasStr) |
||||||
|
gasPrice = ethutil.Big(gasPriceStr) |
||||||
|
data []byte |
||||||
|
tx *ethchain.Transaction |
||||||
|
) |
||||||
|
|
||||||
|
if ethutil.IsHex(codeStr) { |
||||||
|
data = ethutil.Hex2Bytes(codeStr[2:]) |
||||||
|
} else { |
||||||
|
data = ethutil.Hex2Bytes(codeStr) |
||||||
|
} |
||||||
|
|
||||||
|
if contractCreation { |
||||||
|
tx = ethchain.NewContractCreationTx(value, gas, gasPrice, data) |
||||||
|
} else { |
||||||
|
tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, data) |
||||||
|
} |
||||||
|
|
||||||
|
acc := self.obj.StateManager().TransState().GetOrNewStateObject(keyPair.Address()) |
||||||
|
tx.Nonce = acc.Nonce |
||||||
|
acc.Nonce += 1 |
||||||
|
self.obj.StateManager().TransState().UpdateStateObject(acc) |
||||||
|
|
||||||
|
tx.Sign(keyPair.PrivateKey) |
||||||
|
self.obj.TxPool().QueueTransaction(tx) |
||||||
|
|
||||||
|
if contractCreation { |
||||||
|
logger.Infof("Contract addr %x", tx.CreationAddress()) |
||||||
|
} |
||||||
|
|
||||||
|
return NewJSReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) CompileMutan(code string) string { |
||||||
|
data, err := self.Pipe.CompileMutan(code) |
||||||
|
if err != nil { |
||||||
|
return err.Error() |
||||||
|
} |
||||||
|
|
||||||
|
return ethutil.Bytes2Hex(data) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) Watch(object map[string]interface{}) *JSFilter { |
||||||
|
return NewJSFilterFromMap(object, self.Pipe.obj) |
||||||
|
/*} else if str, ok := object.(string); ok { |
||||||
|
println("str") |
||||||
|
return NewJSFilterFromString(str, self.Pipe.obj) |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSPipe) Messages(object map[string]interface{}) string { |
||||||
|
filter := self.Watch(object) |
||||||
|
filter.Uninstall() |
||||||
|
|
||||||
|
return filter.Messages() |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
type JSFilter struct { |
||||||
|
eth ethchain.EthManager |
||||||
|
*ethchain.Filter |
||||||
|
quit chan bool |
||||||
|
|
||||||
|
BlockCallback func(*ethchain.Block) |
||||||
|
MessageCallback func(ethstate.Messages) |
||||||
|
} |
||||||
|
|
||||||
|
func NewJSFilterFromMap(object map[string]interface{}, eth ethchain.EthManager) *JSFilter { |
||||||
|
filter := &JSFilter{eth, ethchain.NewFilterFromMap(object, eth), make(chan bool), nil, nil} |
||||||
|
|
||||||
|
go filter.mainLoop() |
||||||
|
|
||||||
|
return filter |
||||||
|
} |
||||||
|
|
||||||
|
func NewJSFilterFromString(str string, eth ethchain.EthManager) *JSFilter { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSFilter) MessagesToJson(messages ethstate.Messages) string { |
||||||
|
var msgs []JSMessage |
||||||
|
for _, m := range messages { |
||||||
|
msgs = append(msgs, NewJSMessage(m)) |
||||||
|
} |
||||||
|
|
||||||
|
// Return an empty array instead of "null"
|
||||||
|
if len(msgs) == 0 { |
||||||
|
return "[]" |
||||||
|
} |
||||||
|
|
||||||
|
b, err := json.Marshal(msgs) |
||||||
|
if err != nil { |
||||||
|
return "{\"error\":" + err.Error() + "}" |
||||||
|
} |
||||||
|
|
||||||
|
return string(b) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSFilter) Messages() string { |
||||||
|
return self.MessagesToJson(self.Find()) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSFilter) mainLoop() { |
||||||
|
blockChan := make(chan ethreact.Event, 5) |
||||||
|
messageChan := make(chan ethreact.Event, 5) |
||||||
|
// Subscribe to events
|
||||||
|
reactor := self.eth.Reactor() |
||||||
|
reactor.Subscribe("newBlock", blockChan) |
||||||
|
reactor.Subscribe("messages", messageChan) |
||||||
|
out: |
||||||
|
for { |
||||||
|
select { |
||||||
|
case <-self.quit: |
||||||
|
break out |
||||||
|
case block := <-blockChan: |
||||||
|
if block, ok := block.Resource.(*ethchain.Block); ok { |
||||||
|
if self.BlockCallback != nil { |
||||||
|
self.BlockCallback(block) |
||||||
|
} |
||||||
|
} |
||||||
|
case msg := <-messageChan: |
||||||
|
if messages, ok := msg.Resource.(ethstate.Messages); ok { |
||||||
|
if self.MessageCallback != nil { |
||||||
|
println("messages!") |
||||||
|
msgs := self.FilterMessages(messages) |
||||||
|
if len(msgs) > 0 { |
||||||
|
self.MessageCallback(msgs) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSFilter) Changed(object interface{}) { |
||||||
|
fmt.Printf("%T\n", object) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSFilter) Uninstall() { |
||||||
|
self.quit <- true |
||||||
|
} |
@ -0,0 +1,208 @@ |
|||||||
|
package ethpipe |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethchain" |
||||||
|
"github.com/ethereum/eth-go/ethcrypto" |
||||||
|
"github.com/ethereum/eth-go/ethstate" |
||||||
|
"github.com/ethereum/eth-go/ethutil" |
||||||
|
) |
||||||
|
|
||||||
|
// Block interface exposed to QML
|
||||||
|
type JSBlock struct { |
||||||
|
ref *ethchain.Block |
||||||
|
Number int `json:"number"` |
||||||
|
Hash string `json:"hash"` |
||||||
|
Transactions string `json:"transactions"` |
||||||
|
Time int64 `json:"time"` |
||||||
|
Coinbase string `json:"coinbase"` |
||||||
|
Name string `json:"name"` |
||||||
|
GasLimit string `json:"gasLimit"` |
||||||
|
GasUsed string `json:"gasUsed"` |
||||||
|
} |
||||||
|
|
||||||
|
// Creates a new QML Block from a chain block
|
||||||
|
func NewJSBlock(block *ethchain.Block) *JSBlock { |
||||||
|
if block == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
var ptxs []JSTransaction |
||||||
|
for _, tx := range block.Transactions() { |
||||||
|
ptxs = append(ptxs, *NewJSTx(tx)) |
||||||
|
} |
||||||
|
|
||||||
|
txJson, err := json.Marshal(ptxs) |
||||||
|
if err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
return &JSBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Bytes2Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Bytes2Hex(block.Coinbase)} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSBlock) ToString() string { |
||||||
|
if self.ref != nil { |
||||||
|
return self.ref.String() |
||||||
|
} |
||||||
|
|
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSBlock) GetTransaction(hash string) *JSTransaction { |
||||||
|
tx := self.ref.GetTransaction(ethutil.Hex2Bytes(hash)) |
||||||
|
if tx == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
return NewJSTx(tx) |
||||||
|
} |
||||||
|
|
||||||
|
type JSTransaction struct { |
||||||
|
ref *ethchain.Transaction |
||||||
|
|
||||||
|
Value string `json:"value"` |
||||||
|
Gas string `json:"gas"` |
||||||
|
GasPrice string `json:"gasPrice"` |
||||||
|
Hash string `json:"hash"` |
||||||
|
Address string `json:"address"` |
||||||
|
Sender string `json:"sender"` |
||||||
|
RawData string `json:"rawData"` |
||||||
|
Data string `json:"data"` |
||||||
|
Contract bool `json:"isContract"` |
||||||
|
CreatesContract bool `json:"createsContract"` |
||||||
|
Confirmations int `json:"confirmations"` |
||||||
|
} |
||||||
|
|
||||||
|
func NewJSTx(tx *ethchain.Transaction) *JSTransaction { |
||||||
|
hash := ethutil.Bytes2Hex(tx.Hash()) |
||||||
|
receiver := ethutil.Bytes2Hex(tx.Recipient) |
||||||
|
if receiver == "0000000000000000000000000000000000000000" { |
||||||
|
receiver = ethutil.Bytes2Hex(tx.CreationAddress()) |
||||||
|
} |
||||||
|
sender := ethutil.Bytes2Hex(tx.Sender()) |
||||||
|
createsContract := tx.CreatesContract() |
||||||
|
|
||||||
|
var data string |
||||||
|
if tx.CreatesContract() { |
||||||
|
data = strings.Join(ethchain.Disassemble(tx.Data), "\n") |
||||||
|
} else { |
||||||
|
data = ethutil.Bytes2Hex(tx.Data) |
||||||
|
} |
||||||
|
|
||||||
|
return &JSTransaction{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: ethutil.Bytes2Hex(tx.Data)} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *JSTransaction) ToString() string { |
||||||
|
return self.ref.String() |
||||||
|
} |
||||||
|
|
||||||
|
type JSKey struct { |
||||||
|
Address string `json:"address"` |
||||||
|
PrivateKey string `json:"privateKey"` |
||||||
|
PublicKey string `json:"publicKey"` |
||||||
|
} |
||||||
|
|
||||||
|
func NewJSKey(key *ethcrypto.KeyPair) *JSKey { |
||||||
|
return &JSKey{ethutil.Bytes2Hex(key.Address()), ethutil.Bytes2Hex(key.PrivateKey), ethutil.Bytes2Hex(key.PublicKey)} |
||||||
|
} |
||||||
|
|
||||||
|
type JSObject struct { |
||||||
|
*Object |
||||||
|
} |
||||||
|
|
||||||
|
func NewJSObject(object *Object) *JSObject { |
||||||
|
return &JSObject{object} |
||||||
|
} |
||||||
|
|
||||||
|
type PReceipt struct { |
||||||
|
CreatedContract bool `json:"createdContract"` |
||||||
|
Address string `json:"address"` |
||||||
|
Hash string `json:"hash"` |
||||||
|
Sender string `json:"sender"` |
||||||
|
} |
||||||
|
|
||||||
|
func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt { |
||||||
|
return &PReceipt{ |
||||||
|
contractCreation, |
||||||
|
ethutil.Bytes2Hex(creationAddress), |
||||||
|
ethutil.Bytes2Hex(hash), |
||||||
|
ethutil.Bytes2Hex(address), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Peer interface exposed to QML
|
||||||
|
|
||||||
|
type JSPeer struct { |
||||||
|
ref *ethchain.Peer |
||||||
|
Inbound bool `json:"isInbound"` |
||||||
|
LastSend int64 `json:"lastSend"` |
||||||
|
LastPong int64 `json:"lastPong"` |
||||||
|
Ip string `json:"ip"` |
||||||
|
Port int `json:"port"` |
||||||
|
Version string `json:"version"` |
||||||
|
LastResponse string `json:"lastResponse"` |
||||||
|
Latency string `json:"latency"` |
||||||
|
} |
||||||
|
|
||||||
|
func NewJSPeer(peer ethchain.Peer) *JSPeer { |
||||||
|
if peer == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
var ip []string |
||||||
|
for _, i := range peer.Host() { |
||||||
|
ip = append(ip, strconv.Itoa(int(i))) |
||||||
|
} |
||||||
|
ipAddress := strings.Join(ip, ".") |
||||||
|
|
||||||
|
return &JSPeer{ref: &peer, Inbound: peer.Inbound(), LastSend: peer.LastSend().Unix(), LastPong: peer.LastPong(), Version: peer.Version(), Ip: ipAddress, Port: int(peer.Port()), Latency: peer.PingTime()} |
||||||
|
} |
||||||
|
|
||||||
|
type JSReceipt struct { |
||||||
|
CreatedContract bool `json:"createdContract"` |
||||||
|
Address string `json:"address"` |
||||||
|
Hash string `json:"hash"` |
||||||
|
Sender string `json:"sender"` |
||||||
|
} |
||||||
|
|
||||||
|
func NewJSReciept(contractCreation bool, creationAddress, hash, address []byte) *JSReceipt { |
||||||
|
return &JSReceipt{ |
||||||
|
contractCreation, |
||||||
|
ethutil.Bytes2Hex(creationAddress), |
||||||
|
ethutil.Bytes2Hex(hash), |
||||||
|
ethutil.Bytes2Hex(address), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
type JSMessage struct { |
||||||
|
To string `json:"to"` |
||||||
|
From string `json:"from"` |
||||||
|
Input string `json:"input"` |
||||||
|
Output string `json:"output"` |
||||||
|
Path int32 `json:"path"` |
||||||
|
Origin string `json:"origin"` |
||||||
|
Timestamp int32 `json:"timestamp"` |
||||||
|
Coinbase string `json:"coinbase"` |
||||||
|
Block string `json:"block"` |
||||||
|
Number int32 `json:"number"` |
||||||
|
Value string `json:"value"` |
||||||
|
} |
||||||
|
|
||||||
|
func NewJSMessage(message *ethstate.Message) JSMessage { |
||||||
|
return JSMessage{ |
||||||
|
To: ethutil.Bytes2Hex(message.To), |
||||||
|
From: ethutil.Bytes2Hex(message.From), |
||||||
|
Input: ethutil.Bytes2Hex(message.Input), |
||||||
|
Output: ethutil.Bytes2Hex(message.Output), |
||||||
|
Path: int32(message.Path), |
||||||
|
Origin: ethutil.Bytes2Hex(message.Origin), |
||||||
|
Timestamp: int32(message.Timestamp), |
||||||
|
Coinbase: ethutil.Bytes2Hex(message.Origin), |
||||||
|
Block: ethutil.Bytes2Hex(message.Block), |
||||||
|
Number: int32(message.Number.Int64()), |
||||||
|
Value: message.Value.String(), |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package ethpipe |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/ethereum/eth-go/ethstate" |
||||||
|
"github.com/ethereum/eth-go/ethutil" |
||||||
|
) |
||||||
|
|
||||||
|
type Object struct { |
||||||
|
*ethstate.StateObject |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Object) StorageString(str string) *ethutil.Value { |
||||||
|
if ethutil.IsHex(str) { |
||||||
|
return self.Storage(ethutil.Hex2Bytes(str[2:])) |
||||||
|
} else { |
||||||
|
return self.Storage(ethutil.RightPadBytes([]byte(str), 32)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Object) StorageValue(addr *ethutil.Value) *ethutil.Value { |
||||||
|
return self.Storage(addr.Bytes()) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Object) Storage(addr []byte) *ethutil.Value { |
||||||
|
return self.StateObject.GetStorage(ethutil.BigD(addr)) |
||||||
|
} |
@ -0,0 +1,159 @@ |
|||||||
|
package ethpipe |
||||||
|
|
||||||
|
import ( |
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethchain" |
||||||
|
"github.com/ethereum/eth-go/ethcrypto" |
||||||
|
"github.com/ethereum/eth-go/ethlog" |
||||||
|
"github.com/ethereum/eth-go/ethstate" |
||||||
|
"github.com/ethereum/eth-go/ethutil" |
||||||
|
"github.com/ethereum/eth-go/ethvm" |
||||||
|
) |
||||||
|
|
||||||
|
var logger = ethlog.NewLogger("PIPE") |
||||||
|
|
||||||
|
type VmVars struct { |
||||||
|
State *ethstate.State |
||||||
|
} |
||||||
|
|
||||||
|
type Pipe struct { |
||||||
|
obj ethchain.EthManager |
||||||
|
stateManager *ethchain.StateManager |
||||||
|
blockChain *ethchain.BlockChain |
||||||
|
world *World |
||||||
|
|
||||||
|
Vm VmVars |
||||||
|
} |
||||||
|
|
||||||
|
func New(obj ethchain.EthManager) *Pipe { |
||||||
|
pipe := &Pipe{ |
||||||
|
obj: obj, |
||||||
|
stateManager: obj.StateManager(), |
||||||
|
blockChain: obj.BlockChain(), |
||||||
|
} |
||||||
|
pipe.world = NewWorld(pipe) |
||||||
|
|
||||||
|
return pipe |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) Balance(addr []byte) *ethutil.Value { |
||||||
|
return ethutil.NewValue(self.World().safeGet(addr).Balance) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) Nonce(addr []byte) uint64 { |
||||||
|
return self.World().safeGet(addr).Nonce |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) Execute(addr []byte, data []byte, value, gas, price *ethutil.Value) ([]byte, error) { |
||||||
|
return self.ExecuteObject(&Object{self.World().safeGet(addr)}, data, value, gas, price) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) ExecuteObject(object *Object, data []byte, value, gas, price *ethutil.Value) ([]byte, error) { |
||||||
|
var ( |
||||||
|
initiator = ethstate.NewStateObject([]byte{0}) |
||||||
|
block = self.blockChain.CurrentBlock |
||||||
|
stateObject = object.StateObject |
||||||
|
) |
||||||
|
if self.Vm.State == nil { |
||||||
|
self.Vm.State = self.World().State().Copy() |
||||||
|
} |
||||||
|
|
||||||
|
vm := ethvm.New(NewEnv(self.Vm.State, block, value.BigInt(), initiator.Address())) |
||||||
|
|
||||||
|
closure := ethvm.NewClosure(ðstate.Message{}, initiator, stateObject, object.Code, gas.BigInt(), price.BigInt()) |
||||||
|
ret, _, err := closure.Call(vm, data) |
||||||
|
|
||||||
|
return ret, err |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) Block(hash []byte) *ethchain.Block { |
||||||
|
return self.blockChain.GetBlock(hash) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) Storage(addr, storageAddr []byte) *ethutil.Value { |
||||||
|
return self.World().safeGet(addr).GetStorage(ethutil.BigD(storageAddr)) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) ToAddress(priv []byte) []byte { |
||||||
|
pair, err := ethcrypto.NewKeyPairFromSec(priv) |
||||||
|
if err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
return pair.Address() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) Exists(addr []byte) bool { |
||||||
|
return self.World().Get(addr) != nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) TransactString(key *ethcrypto.KeyPair, rec string, value, gas, price *ethutil.Value, data []byte) ([]byte, error) { |
||||||
|
// Check if an address is stored by this address
|
||||||
|
var hash []byte |
||||||
|
addr := self.World().Config().Get("NameReg").StorageString(rec).Bytes() |
||||||
|
if len(addr) > 0 { |
||||||
|
hash = addr |
||||||
|
} else if ethutil.IsHex(rec) { |
||||||
|
hash = ethutil.Hex2Bytes(rec[2:]) |
||||||
|
} else { |
||||||
|
hash = ethutil.Hex2Bytes(rec) |
||||||
|
} |
||||||
|
|
||||||
|
return self.Transact(key, hash, value, gas, price, data) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) Transact(key *ethcrypto.KeyPair, rec []byte, value, gas, price *ethutil.Value, data []byte) ([]byte, error) { |
||||||
|
var hash []byte |
||||||
|
var contractCreation bool |
||||||
|
if rec == nil { |
||||||
|
contractCreation = true |
||||||
|
} |
||||||
|
|
||||||
|
var tx *ethchain.Transaction |
||||||
|
// Compile and assemble the given data
|
||||||
|
if contractCreation { |
||||||
|
script, err := ethutil.Compile(string(data), false) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
tx = ethchain.NewContractCreationTx(value.BigInt(), gas.BigInt(), price.BigInt(), script) |
||||||
|
} else { |
||||||
|
data := ethutil.StringToByteFunc(string(data), func(s string) (ret []byte) { |
||||||
|
slice := strings.Split(s, "\n") |
||||||
|
for _, dataItem := range slice { |
||||||
|
d := ethutil.FormatData(dataItem) |
||||||
|
ret = append(ret, d...) |
||||||
|
} |
||||||
|
return |
||||||
|
}) |
||||||
|
|
||||||
|
tx = ethchain.NewTransactionMessage(hash, value.BigInt(), gas.BigInt(), price.BigInt(), data) |
||||||
|
} |
||||||
|
|
||||||
|
acc := self.stateManager.TransState().GetOrNewStateObject(key.Address()) |
||||||
|
tx.Nonce = acc.Nonce |
||||||
|
acc.Nonce += 1 |
||||||
|
self.stateManager.TransState().UpdateStateObject(acc) |
||||||
|
|
||||||
|
tx.Sign(key.PrivateKey) |
||||||
|
self.obj.TxPool().QueueTransaction(tx) |
||||||
|
|
||||||
|
if contractCreation { |
||||||
|
logger.Infof("Contract addr %x", tx.CreationAddress()) |
||||||
|
|
||||||
|
return tx.CreationAddress(), nil |
||||||
|
} |
||||||
|
|
||||||
|
return tx.Hash(), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) CompileMutan(code string) ([]byte, error) { |
||||||
|
data, err := ethutil.Compile(code, false) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
return data, nil |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
package ethpipe |
||||||
|
|
||||||
|
import ( |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethcrypto" |
||||||
|
"github.com/ethereum/eth-go/ethstate" |
||||||
|
"github.com/ethereum/eth-go/ethutil" |
||||||
|
) |
||||||
|
|
||||||
|
func Val(v interface{}) *ethutil.Value { |
||||||
|
return ethutil.NewValue(v) |
||||||
|
} |
||||||
|
|
||||||
|
func TestNew(t *testing.T) { |
||||||
|
pipe := New(nil) |
||||||
|
|
||||||
|
var addr, privy, recp, data []byte |
||||||
|
var object *ethstate.StateObject |
||||||
|
var key *ethcrypto.KeyPair |
||||||
|
|
||||||
|
world := pipe.World() |
||||||
|
world.Get(addr) |
||||||
|
world.Coinbase() |
||||||
|
world.IsMining() |
||||||
|
world.IsListening() |
||||||
|
world.State() |
||||||
|
peers := world.Peers() |
||||||
|
peers.Len() |
||||||
|
|
||||||
|
// Shortcut functions
|
||||||
|
pipe.Balance(addr) |
||||||
|
pipe.Nonce(addr) |
||||||
|
pipe.Block(addr) |
||||||
|
pipe.Storage(addr, addr) |
||||||
|
pipe.ToAddress(privy) |
||||||
|
pipe.Exists(addr) |
||||||
|
// Doesn't change state
|
||||||
|
pipe.Execute(addr, nil, Val(0), Val(1000000), Val(10)) |
||||||
|
// Doesn't change state
|
||||||
|
pipe.ExecuteObject(object, nil, Val(0), Val(1000000), Val(10)) |
||||||
|
|
||||||
|
conf := world.Config() |
||||||
|
namereg := conf.Get("NameReg") |
||||||
|
namereg.Storage(addr) |
||||||
|
|
||||||
|
var err error |
||||||
|
// Transact
|
||||||
|
err = pipe.Transact(key, recp, ethutil.NewValue(0), ethutil.NewValue(0), ethutil.NewValue(0), nil) |
||||||
|
if err != nil { |
||||||
|
t.Error(err) |
||||||
|
} |
||||||
|
// Create
|
||||||
|
err = pipe.Transact(key, nil, ethutil.NewValue(0), ethutil.NewValue(0), ethutil.NewValue(0), data) |
||||||
|
if err != nil { |
||||||
|
t.Error(err) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package ethpipe |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethchain" |
||||||
|
"github.com/ethereum/eth-go/ethstate" |
||||||
|
) |
||||||
|
|
||||||
|
type VMEnv struct { |
||||||
|
state *ethstate.State |
||||||
|
block *ethchain.Block |
||||||
|
value *big.Int |
||||||
|
sender []byte |
||||||
|
} |
||||||
|
|
||||||
|
func NewEnv(state *ethstate.State, block *ethchain.Block, value *big.Int, sender []byte) *VMEnv { |
||||||
|
return &VMEnv{ |
||||||
|
state: state, |
||||||
|
block: block, |
||||||
|
value: value, |
||||||
|
sender: sender, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *VMEnv) Origin() []byte { return self.sender } |
||||||
|
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number } |
||||||
|
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash } |
||||||
|
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase } |
||||||
|
func (self *VMEnv) Time() int64 { return self.block.Time } |
||||||
|
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty } |
||||||
|
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() } |
||||||
|
func (self *VMEnv) Value() *big.Int { return self.value } |
||||||
|
func (self *VMEnv) State() *ethstate.State { return self.state } |
@ -0,0 +1,64 @@ |
|||||||
|
package ethpipe |
||||||
|
|
||||||
|
import ( |
||||||
|
"container/list" |
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethstate" |
||||||
|
) |
||||||
|
|
||||||
|
type World struct { |
||||||
|
pipe *Pipe |
||||||
|
cfg *Config |
||||||
|
} |
||||||
|
|
||||||
|
func NewWorld(pipe *Pipe) *World { |
||||||
|
world := &World{pipe, nil} |
||||||
|
world.cfg = &Config{pipe} |
||||||
|
|
||||||
|
return world |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Pipe) World() *World { |
||||||
|
return self.world |
||||||
|
} |
||||||
|
|
||||||
|
func (self *World) State() *ethstate.State { |
||||||
|
return self.pipe.stateManager.CurrentState() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *World) Get(addr []byte) *Object { |
||||||
|
return &Object{self.State().GetStateObject(addr)} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *World) SafeGet(addr []byte) *Object { |
||||||
|
return &Object{self.safeGet(addr)} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *World) safeGet(addr []byte) *ethstate.StateObject { |
||||||
|
object := self.State().GetStateObject(addr) |
||||||
|
if object == nil { |
||||||
|
object = ethstate.NewStateObject(addr) |
||||||
|
} |
||||||
|
|
||||||
|
return object |
||||||
|
} |
||||||
|
|
||||||
|
func (self *World) Coinbase() *ethstate.StateObject { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *World) IsMining() bool { |
||||||
|
return self.pipe.obj.IsMining() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *World) IsListening() bool { |
||||||
|
return self.pipe.obj.IsListening() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *World) Peers() *list.List { |
||||||
|
return self.pipe.obj.Peers() |
||||||
|
} |
||||||
|
|
||||||
|
func (self *World) Config() *Config { |
||||||
|
return self.cfg |
||||||
|
} |
@ -1,273 +0,0 @@ |
|||||||
package ethpub |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/json" |
|
||||||
"github.com/ethereum/eth-go/ethchain" |
|
||||||
"github.com/ethereum/eth-go/ethcrypto" |
|
||||||
"github.com/ethereum/eth-go/ethlog" |
|
||||||
"github.com/ethereum/eth-go/ethstate" |
|
||||||
"github.com/ethereum/eth-go/ethutil" |
|
||||||
"math/big" |
|
||||||
"strings" |
|
||||||
"sync/atomic" |
|
||||||
) |
|
||||||
|
|
||||||
var logger = ethlog.NewLogger("PUB") |
|
||||||
|
|
||||||
// TODO this has to move elsewhere
|
|
||||||
var cnfCtr = ethutil.Hex2Bytes("661005d2720d855f1d9976f88bb10c1a3398c77f") |
|
||||||
|
|
||||||
type helper struct { |
|
||||||
sm *ethchain.StateManager |
|
||||||
} |
|
||||||
|
|
||||||
func EthereumConfig(stateManager *ethchain.StateManager) helper { |
|
||||||
return helper{stateManager} |
|
||||||
} |
|
||||||
func (self helper) obj() *ethstate.StateObject { |
|
||||||
return self.sm.CurrentState().GetStateObject(cnfCtr) |
|
||||||
} |
|
||||||
|
|
||||||
func (self helper) NameReg() *ethstate.StateObject { |
|
||||||
if self.obj() != nil { |
|
||||||
addr := self.obj().GetStorage(big.NewInt(0)) |
|
||||||
if len(addr.Bytes()) > 0 { |
|
||||||
return self.sm.CurrentState().GetStateObject(addr.Bytes()) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type PEthereum struct { |
|
||||||
manager ethchain.EthManager |
|
||||||
stateManager *ethchain.StateManager |
|
||||||
blockChain *ethchain.BlockChain |
|
||||||
txPool *ethchain.TxPool |
|
||||||
keyManager *ethcrypto.KeyManager |
|
||||||
} |
|
||||||
|
|
||||||
func NewPEthereum(manager ethchain.EthManager) *PEthereum { |
|
||||||
logger.Warnln("DEPRECATED: ethpub.New should be used in favour of ethpub.NewPEthereum") |
|
||||||
|
|
||||||
return New(manager) |
|
||||||
} |
|
||||||
|
|
||||||
func New(manager ethchain.EthManager) *PEthereum { |
|
||||||
return &PEthereum{ |
|
||||||
manager, |
|
||||||
manager.StateManager(), |
|
||||||
manager.BlockChain(), |
|
||||||
manager.TxPool(), |
|
||||||
manager.KeyManager(), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetBlock(hexHash string) *PBlock { |
|
||||||
hash := ethutil.Hex2Bytes(hexHash) |
|
||||||
block := lib.blockChain.GetBlock(hash) |
|
||||||
|
|
||||||
return NewPBlock(block) |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetKey() *PKey { |
|
||||||
return NewPKey(lib.keyManager.KeyPair()) |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetStateObject(address string) *PStateObject { |
|
||||||
stateObject := lib.stateManager.CurrentState().GetStateObject(ethutil.Hex2Bytes(address)) |
|
||||||
if stateObject != nil { |
|
||||||
return NewPStateObject(stateObject) |
|
||||||
} |
|
||||||
|
|
||||||
// See GetStorage for explanation on "nil"
|
|
||||||
return NewPStateObject(nil) |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetPeerCount() int { |
|
||||||
return lib.manager.PeerCount() |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetPeers() []PPeer { |
|
||||||
var peers []PPeer |
|
||||||
for peer := lib.manager.Peers().Front(); peer != nil; peer = peer.Next() { |
|
||||||
p := peer.Value.(ethchain.Peer) |
|
||||||
// we only want connected peers
|
|
||||||
if atomic.LoadInt32(p.Connected()) != 0 { |
|
||||||
peers = append(peers, *NewPPeer(p)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return peers |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetIsMining() bool { |
|
||||||
return lib.manager.IsMining() |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetIsListening() bool { |
|
||||||
return lib.manager.IsListening() |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetCoinBase() string { |
|
||||||
return ethutil.Bytes2Hex(lib.keyManager.Address()) |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetTransactionsFor(address string, asJson bool) interface{} { |
|
||||||
sBlk := lib.manager.BlockChain().LastBlockHash |
|
||||||
blk := lib.manager.BlockChain().GetBlock(sBlk) |
|
||||||
addr := []byte(ethutil.Hex2Bytes(address)) |
|
||||||
|
|
||||||
var txs []*PTx |
|
||||||
|
|
||||||
for ; blk != nil; blk = lib.manager.BlockChain().GetBlock(sBlk) { |
|
||||||
sBlk = blk.PrevHash |
|
||||||
|
|
||||||
// Loop through all transactions to see if we missed any while being offline
|
|
||||||
for _, tx := range blk.Transactions() { |
|
||||||
if bytes.Compare(tx.Sender(), addr) == 0 || bytes.Compare(tx.Recipient, addr) == 0 { |
|
||||||
ptx := NewPTx(tx) |
|
||||||
//TODO: somehow move this to NewPTx
|
|
||||||
ptx.Confirmations = int(lib.manager.BlockChain().LastBlockNumber - blk.BlockInfo().Number) |
|
||||||
txs = append(txs, ptx) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if asJson { |
|
||||||
txJson, err := json.Marshal(txs) |
|
||||||
if err != nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
return string(txJson) |
|
||||||
} |
|
||||||
return txs |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetStorage(address, storageAddress string) string { |
|
||||||
return lib.GetStateObject(address).GetStorage(storageAddress) |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) GetTxCountAt(address string) int { |
|
||||||
return lib.GetStateObject(address).Nonce() |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) IsContract(address string) bool { |
|
||||||
return lib.GetStateObject(address).IsContract() |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) SecretToAddress(key string) string { |
|
||||||
pair, err := ethcrypto.NewKeyPairFromSec(ethutil.Hex2Bytes(key)) |
|
||||||
if err != nil { |
|
||||||
return "" |
|
||||||
} |
|
||||||
|
|
||||||
return ethutil.Bytes2Hex(pair.Address()) |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) (*PReceipt, error) { |
|
||||||
return lib.createTx(key, recipient, valueStr, gasStr, gasPriceStr, dataStr) |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) Create(key, valueStr, gasStr, gasPriceStr, script string) (*PReceipt, error) { |
|
||||||
return lib.createTx(key, "", valueStr, gasStr, gasPriceStr, script) |
|
||||||
} |
|
||||||
|
|
||||||
func FindAddressInNameReg(stateManager *ethchain.StateManager, name string) []byte { |
|
||||||
nameReg := EthereumConfig(stateManager).NameReg() |
|
||||||
if nameReg != nil { |
|
||||||
addr := ethutil.RightPadBytes([]byte(name), 32) |
|
||||||
|
|
||||||
reg := nameReg.GetStorage(ethutil.BigD(addr)) |
|
||||||
|
|
||||||
return reg.Bytes() |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func FindNameInNameReg(stateManager *ethchain.StateManager, addr []byte) string { |
|
||||||
nameReg := EthereumConfig(stateManager).NameReg() |
|
||||||
if nameReg != nil { |
|
||||||
addr = ethutil.LeftPadBytes(addr, 32) |
|
||||||
|
|
||||||
reg := nameReg.GetStorage(ethutil.BigD(addr)) |
|
||||||
|
|
||||||
return strings.TrimRight(reg.Str(), "\x00") |
|
||||||
} |
|
||||||
|
|
||||||
return "" |
|
||||||
} |
|
||||||
|
|
||||||
func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, scriptStr string) (*PReceipt, error) { |
|
||||||
var hash []byte |
|
||||||
var contractCreation bool |
|
||||||
if len(recipient) == 0 { |
|
||||||
contractCreation = true |
|
||||||
} else { |
|
||||||
// Check if an address is stored by this address
|
|
||||||
addr := FindAddressInNameReg(lib.stateManager, recipient) |
|
||||||
if len(addr) > 0 { |
|
||||||
hash = addr |
|
||||||
} else { |
|
||||||
hash = ethutil.Hex2Bytes(recipient) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
var keyPair *ethcrypto.KeyPair |
|
||||||
var err error |
|
||||||
if ethutil.IsHex(key) { |
|
||||||
keyPair, err = ethcrypto.NewKeyPairFromSec([]byte(ethutil.Hex2Bytes(key[2:]))) |
|
||||||
} else { |
|
||||||
keyPair, err = ethcrypto.NewKeyPairFromSec([]byte(ethutil.Hex2Bytes(key))) |
|
||||||
} |
|
||||||
|
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
value := ethutil.Big(valueStr) |
|
||||||
gas := ethutil.Big(gasStr) |
|
||||||
gasPrice := ethutil.Big(gasPriceStr) |
|
||||||
var tx *ethchain.Transaction |
|
||||||
// Compile and assemble the given data
|
|
||||||
if contractCreation { |
|
||||||
var script []byte |
|
||||||
var err error |
|
||||||
if ethutil.IsHex(scriptStr) { |
|
||||||
script = ethutil.Hex2Bytes(scriptStr[2:]) |
|
||||||
} else { |
|
||||||
script, err = ethutil.Compile(scriptStr, false) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
tx = ethchain.NewContractCreationTx(value, gas, gasPrice, script) |
|
||||||
} else { |
|
||||||
data := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) { |
|
||||||
slice := strings.Split(s, "\n") |
|
||||||
for _, dataItem := range slice { |
|
||||||
d := ethutil.FormatData(dataItem) |
|
||||||
ret = append(ret, d...) |
|
||||||
} |
|
||||||
return |
|
||||||
}) |
|
||||||
|
|
||||||
tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, data) |
|
||||||
} |
|
||||||
|
|
||||||
acc := lib.stateManager.TransState().GetOrNewStateObject(keyPair.Address()) |
|
||||||
tx.Nonce = acc.Nonce |
|
||||||
acc.Nonce += 1 |
|
||||||
lib.stateManager.TransState().UpdateStateObject(acc) |
|
||||||
|
|
||||||
tx.Sign(keyPair.PrivateKey) |
|
||||||
lib.txPool.QueueTransaction(tx) |
|
||||||
|
|
||||||
if contractCreation { |
|
||||||
logger.Infof("Contract addr %x", tx.CreationAddress()) |
|
||||||
} |
|
||||||
|
|
||||||
return NewPReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil |
|
||||||
} |
|
@ -1,271 +0,0 @@ |
|||||||
package ethpub |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
"github.com/ethereum/eth-go/ethchain" |
|
||||||
"github.com/ethereum/eth-go/ethcrypto" |
|
||||||
"github.com/ethereum/eth-go/ethstate" |
|
||||||
"github.com/ethereum/eth-go/ethtrie" |
|
||||||
"github.com/ethereum/eth-go/ethutil" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
// Peer interface exposed to QML
|
|
||||||
|
|
||||||
type PPeer struct { |
|
||||||
ref *ethchain.Peer |
|
||||||
Inbound bool `json:"isInbound"` |
|
||||||
LastSend int64 `json:"lastSend"` |
|
||||||
LastPong int64 `json:"lastPong"` |
|
||||||
Ip string `json:"ip"` |
|
||||||
Port int `json:"port"` |
|
||||||
Version string `json:"version"` |
|
||||||
LastResponse string `json:"lastResponse"` |
|
||||||
Latency string `json:"latency"` |
|
||||||
} |
|
||||||
|
|
||||||
func NewPPeer(peer ethchain.Peer) *PPeer { |
|
||||||
if peer == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// TODO: There must be something build in to do this?
|
|
||||||
var ip []string |
|
||||||
for _, i := range peer.Host() { |
|
||||||
ip = append(ip, fmt.Sprintf("%d", i)) |
|
||||||
} |
|
||||||
ipAddress := strings.Join(ip, ".") |
|
||||||
|
|
||||||
return &PPeer{ref: &peer, Inbound: peer.Inbound(), LastSend: peer.LastSend().Unix(), LastPong: peer.LastPong(), Version: peer.Version(), Ip: ipAddress, Port: int(peer.Port()), Latency: peer.PingTime()} |
|
||||||
} |
|
||||||
|
|
||||||
// Block interface exposed to QML
|
|
||||||
type PBlock struct { |
|
||||||
ref *ethchain.Block |
|
||||||
Number int `json:"number"` |
|
||||||
Hash string `json:"hash"` |
|
||||||
Transactions string `json:"transactions"` |
|
||||||
Time int64 `json:"time"` |
|
||||||
Coinbase string `json:"coinbase"` |
|
||||||
Name string `json:"name"` |
|
||||||
GasLimit string `json:"gasLimit"` |
|
||||||
GasUsed string `json:"gasUsed"` |
|
||||||
} |
|
||||||
|
|
||||||
// Creates a new QML Block from a chain block
|
|
||||||
func NewPBlock(block *ethchain.Block) *PBlock { |
|
||||||
if block == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
var ptxs []PTx |
|
||||||
for _, tx := range block.Transactions() { |
|
||||||
ptxs = append(ptxs, *NewPTx(tx)) |
|
||||||
} |
|
||||||
|
|
||||||
txJson, err := json.Marshal(ptxs) |
|
||||||
if err != nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
return &PBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Bytes2Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Bytes2Hex(block.Coinbase)} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *PBlock) ToString() string { |
|
||||||
if self.ref != nil { |
|
||||||
return self.ref.String() |
|
||||||
} |
|
||||||
|
|
||||||
return "" |
|
||||||
} |
|
||||||
|
|
||||||
func (self *PBlock) GetTransaction(hash string) *PTx { |
|
||||||
tx := self.ref.GetTransaction(ethutil.Hex2Bytes(hash)) |
|
||||||
if tx == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
return NewPTx(tx) |
|
||||||
} |
|
||||||
|
|
||||||
type PTx struct { |
|
||||||
ref *ethchain.Transaction |
|
||||||
|
|
||||||
Value string `json:"value"` |
|
||||||
Gas string `json:"gas"` |
|
||||||
GasPrice string `json:"gasPrice"` |
|
||||||
Hash string `json:"hash"` |
|
||||||
Address string `json:"address"` |
|
||||||
Sender string `json:"sender"` |
|
||||||
RawData string `json:"rawData"` |
|
||||||
Data string `json:"data"` |
|
||||||
Contract bool `json:"isContract"` |
|
||||||
CreatesContract bool `json:"createsContract"` |
|
||||||
Confirmations int `json:"confirmations"` |
|
||||||
} |
|
||||||
|
|
||||||
func NewPTx(tx *ethchain.Transaction) *PTx { |
|
||||||
hash := ethutil.Bytes2Hex(tx.Hash()) |
|
||||||
receiver := ethutil.Bytes2Hex(tx.Recipient) |
|
||||||
if receiver == "0000000000000000000000000000000000000000" { |
|
||||||
receiver = ethutil.Bytes2Hex(tx.CreationAddress()) |
|
||||||
} |
|
||||||
sender := ethutil.Bytes2Hex(tx.Sender()) |
|
||||||
createsContract := tx.CreatesContract() |
|
||||||
|
|
||||||
var data string |
|
||||||
if tx.CreatesContract() { |
|
||||||
data = strings.Join(ethchain.Disassemble(tx.Data), "\n") |
|
||||||
} else { |
|
||||||
data = ethutil.Bytes2Hex(tx.Data) |
|
||||||
} |
|
||||||
|
|
||||||
return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: ethutil.Bytes2Hex(tx.Data)} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *PTx) ToString() string { |
|
||||||
return self.ref.String() |
|
||||||
} |
|
||||||
|
|
||||||
type PKey struct { |
|
||||||
Address string `json:"address"` |
|
||||||
PrivateKey string `json:"privateKey"` |
|
||||||
PublicKey string `json:"publicKey"` |
|
||||||
} |
|
||||||
|
|
||||||
func NewPKey(key *ethcrypto.KeyPair) *PKey { |
|
||||||
return &PKey{ethutil.Bytes2Hex(key.Address()), ethutil.Bytes2Hex(key.PrivateKey), ethutil.Bytes2Hex(key.PublicKey)} |
|
||||||
} |
|
||||||
|
|
||||||
type PReceipt struct { |
|
||||||
CreatedContract bool `json:"createdContract"` |
|
||||||
Address string `json:"address"` |
|
||||||
Hash string `json:"hash"` |
|
||||||
Sender string `json:"sender"` |
|
||||||
} |
|
||||||
|
|
||||||
func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt { |
|
||||||
return &PReceipt{ |
|
||||||
contractCreation, |
|
||||||
ethutil.Bytes2Hex(creationAddress), |
|
||||||
ethutil.Bytes2Hex(hash), |
|
||||||
ethutil.Bytes2Hex(address), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
type PStateObject struct { |
|
||||||
object *ethstate.StateObject |
|
||||||
} |
|
||||||
|
|
||||||
func NewPStateObject(object *ethstate.StateObject) *PStateObject { |
|
||||||
return &PStateObject{object: object} |
|
||||||
} |
|
||||||
|
|
||||||
func (c *PStateObject) GetStorage(address string) string { |
|
||||||
// Because somehow, even if you return nil to QML it
|
|
||||||
// still has some magical object so we can't rely on
|
|
||||||
// undefined or null at the QML side
|
|
||||||
if c.object != nil { |
|
||||||
val := c.object.GetStorage(ethutil.Big("0x" + address)) |
|
||||||
|
|
||||||
return val.BigInt().String() |
|
||||||
} |
|
||||||
|
|
||||||
return "" |
|
||||||
} |
|
||||||
|
|
||||||
func (c *PStateObject) Value() string { |
|
||||||
if c.object != nil { |
|
||||||
return c.object.Amount.String() |
|
||||||
} |
|
||||||
|
|
||||||
return "" |
|
||||||
} |
|
||||||
|
|
||||||
func (c *PStateObject) Address() string { |
|
||||||
if c.object != nil { |
|
||||||
return ethutil.Bytes2Hex(c.object.Address()) |
|
||||||
} |
|
||||||
|
|
||||||
return "" |
|
||||||
} |
|
||||||
|
|
||||||
func (c *PStateObject) Nonce() int { |
|
||||||
if c.object != nil { |
|
||||||
return int(c.object.Nonce) |
|
||||||
} |
|
||||||
|
|
||||||
return 0 |
|
||||||
} |
|
||||||
|
|
||||||
func (c *PStateObject) Root() string { |
|
||||||
if c.object != nil { |
|
||||||
return ethutil.Bytes2Hex(ethutil.NewValue(c.object.State.Root()).Bytes()) |
|
||||||
} |
|
||||||
|
|
||||||
return "<err>" |
|
||||||
} |
|
||||||
|
|
||||||
func (c *PStateObject) IsContract() bool { |
|
||||||
if c.object != nil { |
|
||||||
return len(c.object.Code) > 0 |
|
||||||
} |
|
||||||
|
|
||||||
return false |
|
||||||
} |
|
||||||
|
|
||||||
func (self *PStateObject) EachStorage(cb ethtrie.EachCallback) { |
|
||||||
self.object.EachStorage(cb) |
|
||||||
} |
|
||||||
|
|
||||||
type KeyVal struct { |
|
||||||
Key string |
|
||||||
Value string |
|
||||||
} |
|
||||||
|
|
||||||
func (c *PStateObject) StateKeyVal(asJson bool) interface{} { |
|
||||||
var values []KeyVal |
|
||||||
if c.object != nil { |
|
||||||
c.object.EachStorage(func(name string, value *ethutil.Value) { |
|
||||||
values = append(values, KeyVal{name, ethutil.Bytes2Hex(value.Bytes())}) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
if asJson { |
|
||||||
valuesJson, err := json.Marshal(values) |
|
||||||
if err != nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
fmt.Println(string(valuesJson)) |
|
||||||
return string(valuesJson) |
|
||||||
} |
|
||||||
|
|
||||||
return values |
|
||||||
} |
|
||||||
|
|
||||||
func (c *PStateObject) Script() string { |
|
||||||
if c.object != nil { |
|
||||||
return strings.Join(ethchain.Disassemble(c.object.Code), " ") |
|
||||||
} |
|
||||||
|
|
||||||
return "" |
|
||||||
} |
|
||||||
|
|
||||||
func (c *PStateObject) HexScript() string { |
|
||||||
if c.object != nil { |
|
||||||
return ethutil.Bytes2Hex(c.object.Code) |
|
||||||
} |
|
||||||
|
|
||||||
return "" |
|
||||||
} |
|
||||||
|
|
||||||
type PStorageState struct { |
|
||||||
StateAddress string |
|
||||||
Address string |
|
||||||
Value string |
|
||||||
} |
|
||||||
|
|
||||||
func NewPStorageState(storageObject *ethstate.StorageState) *PStorageState { |
|
||||||
return &PStorageState{ethutil.Bytes2Hex(storageObject.StateAddress), ethutil.Bytes2Hex(storageObject.Address), storageObject.Value.String()} |
|
||||||
} |
|
@ -0,0 +1,28 @@ |
|||||||
|
## Reactor |
||||||
|
|
||||||
|
Reactor is the internal broadcast engine that allows components to be notified of ethereum stack events such as finding new blocks or change in state. |
||||||
|
Event notification is handled via subscription: |
||||||
|
|
||||||
|
var blockChan = make(chan ethreact.Event, 10) |
||||||
|
reactor.Subscribe("newBlock", blockChan) |
||||||
|
|
||||||
|
ethreact.Event broadcast on the channel are |
||||||
|
|
||||||
|
type Event struct { |
||||||
|
Resource interface{} |
||||||
|
Name string |
||||||
|
} |
||||||
|
|
||||||
|
Resource is polimorphic depending on the event type and should be typecast before use, e.g: |
||||||
|
|
||||||
|
b := <-blockChan: |
||||||
|
block := b.Resource.(*ethchain.Block) |
||||||
|
|
||||||
|
Events are guaranteed to be broadcast in order but the broadcast never blocks or leaks which means while the subscribing event channel is blocked (e.g., full if buffered) further messages will be skipped. |
||||||
|
|
||||||
|
The engine allows arbitrary events to be posted and subscribed to. |
||||||
|
|
||||||
|
ethereum.Reactor().Post("newBlock", newBlock) |
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,182 @@ |
|||||||
|
package ethreact |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/ethereum/eth-go/ethlog" |
||||||
|
"sync" |
||||||
|
) |
||||||
|
|
||||||
|
var logger = ethlog.NewLogger("REACTOR") |
||||||
|
|
||||||
|
const ( |
||||||
|
eventBufferSize int = 10 |
||||||
|
) |
||||||
|
|
||||||
|
type EventHandler struct { |
||||||
|
lock sync.RWMutex |
||||||
|
name string |
||||||
|
chans []chan Event |
||||||
|
} |
||||||
|
|
||||||
|
// Post the Event with the reactor resource on the channels
|
||||||
|
// currently subscribed to the event
|
||||||
|
func (e *EventHandler) Post(event Event) { |
||||||
|
e.lock.RLock() |
||||||
|
defer e.lock.RUnlock() |
||||||
|
|
||||||
|
// if we want to preserve order pushing to subscibed channels
|
||||||
|
// dispatching should be syncrounous
|
||||||
|
// this means if subscribed event channel is blocked
|
||||||
|
// the reactor dispatch will be blocked, so we need to mitigate by skipping
|
||||||
|
// rogue blocking subscribers
|
||||||
|
for i, ch := range e.chans { |
||||||
|
select { |
||||||
|
case ch <- event: |
||||||
|
default: |
||||||
|
logger.Warnf("subscribing channel %d to event %s blocked. skipping\n", i, event.Name) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Add a subscriber to this event
|
||||||
|
func (e *EventHandler) Add(ch chan Event) { |
||||||
|
e.lock.Lock() |
||||||
|
defer e.lock.Unlock() |
||||||
|
|
||||||
|
e.chans = append(e.chans, ch) |
||||||
|
} |
||||||
|
|
||||||
|
// Remove a subscriber
|
||||||
|
func (e *EventHandler) Remove(ch chan Event) int { |
||||||
|
e.lock.Lock() |
||||||
|
defer e.lock.Unlock() |
||||||
|
|
||||||
|
for i, c := range e.chans { |
||||||
|
if c == ch { |
||||||
|
e.chans = append(e.chans[:i], e.chans[i+1:]...) |
||||||
|
} |
||||||
|
} |
||||||
|
return len(e.chans) |
||||||
|
} |
||||||
|
|
||||||
|
// Basic reactor event
|
||||||
|
type Event struct { |
||||||
|
Resource interface{} |
||||||
|
Name string |
||||||
|
} |
||||||
|
|
||||||
|
// The reactor basic engine. Acts as bridge
|
||||||
|
// between the events and the subscribers/posters
|
||||||
|
type ReactorEngine struct { |
||||||
|
lock sync.RWMutex |
||||||
|
eventChannel chan Event |
||||||
|
eventHandlers map[string]*EventHandler |
||||||
|
quit chan chan error |
||||||
|
running bool |
||||||
|
drained chan bool |
||||||
|
} |
||||||
|
|
||||||
|
func New() *ReactorEngine { |
||||||
|
return &ReactorEngine{ |
||||||
|
eventHandlers: make(map[string]*EventHandler), |
||||||
|
eventChannel: make(chan Event, eventBufferSize), |
||||||
|
quit: make(chan chan error, 1), |
||||||
|
drained: make(chan bool, 1), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (reactor *ReactorEngine) Start() { |
||||||
|
reactor.lock.Lock() |
||||||
|
defer reactor.lock.Unlock() |
||||||
|
if !reactor.running { |
||||||
|
go func() { |
||||||
|
for { |
||||||
|
select { |
||||||
|
case status := <-reactor.quit: |
||||||
|
reactor.lock.Lock() |
||||||
|
defer reactor.lock.Unlock() |
||||||
|
reactor.running = false |
||||||
|
logger.Infoln("stopped") |
||||||
|
status <- nil |
||||||
|
return |
||||||
|
case event := <-reactor.eventChannel: |
||||||
|
// needs to be called syncronously to keep order of events
|
||||||
|
reactor.dispatch(event) |
||||||
|
default: |
||||||
|
reactor.drained <- true // blocking till message is coming in
|
||||||
|
} |
||||||
|
} |
||||||
|
}() |
||||||
|
reactor.running = true |
||||||
|
logger.Infoln("started") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (reactor *ReactorEngine) Stop() { |
||||||
|
if reactor.running { |
||||||
|
status := make(chan error) |
||||||
|
reactor.quit <- status |
||||||
|
select { |
||||||
|
case <-reactor.drained: |
||||||
|
default: |
||||||
|
} |
||||||
|
<-status |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (reactor *ReactorEngine) Flush() { |
||||||
|
<-reactor.drained |
||||||
|
} |
||||||
|
|
||||||
|
// Subscribe a channel to the specified event
|
||||||
|
func (reactor *ReactorEngine) Subscribe(event string, eventChannel chan Event) { |
||||||
|
reactor.lock.Lock() |
||||||
|
defer reactor.lock.Unlock() |
||||||
|
|
||||||
|
eventHandler := reactor.eventHandlers[event] |
||||||
|
// Create a new event handler if one isn't available
|
||||||
|
if eventHandler == nil { |
||||||
|
eventHandler = &EventHandler{name: event} |
||||||
|
reactor.eventHandlers[event] = eventHandler |
||||||
|
} |
||||||
|
// Add the events channel to reactor event handler
|
||||||
|
eventHandler.Add(eventChannel) |
||||||
|
logger.Debugf("added new subscription to %s", event) |
||||||
|
} |
||||||
|
|
||||||
|
func (reactor *ReactorEngine) Unsubscribe(event string, eventChannel chan Event) { |
||||||
|
reactor.lock.Lock() |
||||||
|
defer reactor.lock.Unlock() |
||||||
|
|
||||||
|
eventHandler := reactor.eventHandlers[event] |
||||||
|
if eventHandler != nil { |
||||||
|
len := eventHandler.Remove(eventChannel) |
||||||
|
if len == 0 { |
||||||
|
reactor.eventHandlers[event] = nil |
||||||
|
} |
||||||
|
logger.Debugf("removed subscription to %s", event) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (reactor *ReactorEngine) Post(event string, resource interface{}) { |
||||||
|
reactor.lock.Lock() |
||||||
|
defer reactor.lock.Unlock() |
||||||
|
|
||||||
|
if reactor.running { |
||||||
|
reactor.eventChannel <- Event{Resource: resource, Name: event} |
||||||
|
select { |
||||||
|
case <-reactor.drained: |
||||||
|
default: |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (reactor *ReactorEngine) dispatch(event Event) { |
||||||
|
name := event.Name |
||||||
|
eventHandler := reactor.eventHandlers[name] |
||||||
|
// if no subscriptions to this event type - no event handler created
|
||||||
|
// then noone to notify
|
||||||
|
if eventHandler != nil { |
||||||
|
// needs to be called syncronously
|
||||||
|
eventHandler.Post(event) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
package ethreact |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
func TestReactorAdd(t *testing.T) { |
||||||
|
reactor := New() |
||||||
|
ch := make(chan Event) |
||||||
|
reactor.Subscribe("test", ch) |
||||||
|
if reactor.eventHandlers["test"] == nil { |
||||||
|
t.Error("Expected new eventHandler to be created") |
||||||
|
} |
||||||
|
reactor.Unsubscribe("test", ch) |
||||||
|
if reactor.eventHandlers["test"] != nil { |
||||||
|
t.Error("Expected eventHandler to be removed") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestReactorEvent(t *testing.T) { |
||||||
|
var name string |
||||||
|
reactor := New() |
||||||
|
// Buffer the channel, so it doesn't block for this test
|
||||||
|
cap := 20 |
||||||
|
ch := make(chan Event, cap) |
||||||
|
reactor.Subscribe("even", ch) |
||||||
|
reactor.Subscribe("odd", ch) |
||||||
|
reactor.Post("even", "disappears") // should not broadcast if engine not started
|
||||||
|
reactor.Start() |
||||||
|
for i := 0; i < cap; i++ { |
||||||
|
if i%2 == 0 { |
||||||
|
name = "even" |
||||||
|
} else { |
||||||
|
name = "odd" |
||||||
|
} |
||||||
|
reactor.Post(name, i) |
||||||
|
} |
||||||
|
reactor.Post("test", cap) // this should not block
|
||||||
|
i := 0 |
||||||
|
reactor.Flush() |
||||||
|
close(ch) |
||||||
|
for event := range ch { |
||||||
|
fmt.Printf("%d: %v", i, event) |
||||||
|
if i%2 == 0 { |
||||||
|
name = "even" |
||||||
|
} else { |
||||||
|
name = "odd" |
||||||
|
} |
||||||
|
if val, ok := event.Resource.(int); ok { |
||||||
|
if i != val || event.Name != name { |
||||||
|
t.Error("Expected event %d to be of type %s and resource %d, got ", i, name, i, val) |
||||||
|
} |
||||||
|
} else { |
||||||
|
t.Error("Unable to cast") |
||||||
|
} |
||||||
|
i++ |
||||||
|
} |
||||||
|
if i != cap { |
||||||
|
t.Error("excpected exactly %d events, got ", i) |
||||||
|
} |
||||||
|
reactor.Stop() |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
package ethstate |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethutil" |
||||||
|
) |
||||||
|
|
||||||
|
type Account struct { |
||||||
|
Balance string `json:"balance"` |
||||||
|
Nonce uint64 `json:"nonce"` |
||||||
|
CodeHash string `json:"codeHash"` |
||||||
|
Storage map[string]string `json:"storage"` |
||||||
|
} |
||||||
|
|
||||||
|
type World struct { |
||||||
|
Root string `json:"root"` |
||||||
|
Accounts map[string]Account `json:"accounts"` |
||||||
|
} |
||||||
|
|
||||||
|
func (self *State) Dump() []byte { |
||||||
|
world := World{ |
||||||
|
Root: ethutil.Bytes2Hex(self.Trie.Root.([]byte)), |
||||||
|
Accounts: make(map[string]Account), |
||||||
|
} |
||||||
|
|
||||||
|
self.Trie.NewIterator().Each(func(key string, value *ethutil.Value) { |
||||||
|
stateObject := NewStateObjectFromBytes([]byte(key), value.Bytes()) |
||||||
|
|
||||||
|
account := Account{Balance: stateObject.Balance.String(), Nonce: stateObject.Nonce, CodeHash: ethutil.Bytes2Hex(stateObject.CodeHash)} |
||||||
|
account.Storage = make(map[string]string) |
||||||
|
|
||||||
|
stateObject.EachStorage(func(key string, value *ethutil.Value) { |
||||||
|
value.Decode() |
||||||
|
account.Storage[ethutil.Bytes2Hex([]byte(key))] = ethutil.Bytes2Hex(value.Bytes()) |
||||||
|
}) |
||||||
|
world.Accounts[ethutil.Bytes2Hex([]byte(key))] = account |
||||||
|
}) |
||||||
|
|
||||||
|
json, err := json.MarshalIndent(world, "", " ") |
||||||
|
if err != nil { |
||||||
|
fmt.Println("dump err", err) |
||||||
|
} |
||||||
|
|
||||||
|
return json |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
package ethstate |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
) |
||||||
|
|
||||||
|
// Object manifest
|
||||||
|
//
|
||||||
|
// The object manifest is used to keep changes to the state so we can keep track of the changes
|
||||||
|
// that occurred during a state transitioning phase.
|
||||||
|
type Manifest struct { |
||||||
|
Messages Messages |
||||||
|
} |
||||||
|
|
||||||
|
func NewManifest() *Manifest { |
||||||
|
m := &Manifest{} |
||||||
|
m.Reset() |
||||||
|
|
||||||
|
return m |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Manifest) Reset() { |
||||||
|
m.Messages = nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Manifest) AddMessage(msg *Message) *Message { |
||||||
|
self.Messages = append(self.Messages, msg) |
||||||
|
|
||||||
|
return msg |
||||||
|
} |
||||||
|
|
||||||
|
type Messages []*Message |
||||||
|
type Message struct { |
||||||
|
To, From []byte |
||||||
|
Input []byte |
||||||
|
Output []byte |
||||||
|
Path int |
||||||
|
Origin []byte |
||||||
|
Timestamp int64 |
||||||
|
Coinbase []byte |
||||||
|
Block []byte |
||||||
|
Number *big.Int |
||||||
|
Value *big.Int |
||||||
|
|
||||||
|
ChangedAddresses [][]byte |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Message) AddStorageChange(addr []byte) { |
||||||
|
self.ChangedAddresses = append(self.ChangedAddresses, addr) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Message) String() string { |
||||||
|
return fmt.Sprintf("Message{to: %x from: %x input: %x output: %x origin: %x coinbase: %x block: %x number: %v timestamp: %d path: %d value: %v", self.To, self.From, self.Input, self.Output, self.Origin, self.Coinbase, self.Block, self.Number, self.Timestamp, self.Path, self.Value) |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package ethutil |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
func TestParseData(t *testing.T) { |
||||||
|
data := ParseData("hello", "world", "0x0106") |
||||||
|
exp := "68656c6c6f000000000000000000000000000000000000000000000000000000776f726c640000000000000000000000000000000000000000000000000000000106000000000000000000000000000000000000000000000000000000000000" |
||||||
|
if bytes.Compare(data, Hex2Bytes(exp)) != 0 { |
||||||
|
t.Error("Error parsing data") |
||||||
|
} |
||||||
|
} |
@ -1,87 +0,0 @@ |
|||||||
package ethutil |
|
||||||
|
|
||||||
import ( |
|
||||||
"sync" |
|
||||||
) |
|
||||||
|
|
||||||
type ReactorEvent struct { |
|
||||||
mut sync.Mutex |
|
||||||
event string |
|
||||||
chans []chan React |
|
||||||
} |
|
||||||
|
|
||||||
// Post the specified reactor resource on the channels
|
|
||||||
// currently subscribed
|
|
||||||
func (e *ReactorEvent) Post(react React) { |
|
||||||
e.mut.Lock() |
|
||||||
defer e.mut.Unlock() |
|
||||||
|
|
||||||
for _, ch := range e.chans { |
|
||||||
go func(ch chan React) { |
|
||||||
ch <- react |
|
||||||
}(ch) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Add a subscriber to this event
|
|
||||||
func (e *ReactorEvent) Add(ch chan React) { |
|
||||||
e.mut.Lock() |
|
||||||
defer e.mut.Unlock() |
|
||||||
|
|
||||||
e.chans = append(e.chans, ch) |
|
||||||
} |
|
||||||
|
|
||||||
// Remove a subscriber
|
|
||||||
func (e *ReactorEvent) Remove(ch chan React) { |
|
||||||
e.mut.Lock() |
|
||||||
defer e.mut.Unlock() |
|
||||||
|
|
||||||
for i, c := range e.chans { |
|
||||||
if c == ch { |
|
||||||
e.chans = append(e.chans[:i], e.chans[i+1:]...) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Basic reactor resource
|
|
||||||
type React struct { |
|
||||||
Resource interface{} |
|
||||||
Event string |
|
||||||
} |
|
||||||
|
|
||||||
// The reactor basic engine. Acts as bridge
|
|
||||||
// between the events and the subscribers/posters
|
|
||||||
type ReactorEngine struct { |
|
||||||
patterns map[string]*ReactorEvent |
|
||||||
} |
|
||||||
|
|
||||||
func NewReactorEngine() *ReactorEngine { |
|
||||||
return &ReactorEngine{patterns: make(map[string]*ReactorEvent)} |
|
||||||
} |
|
||||||
|
|
||||||
// Subscribe a channel to the specified event
|
|
||||||
func (reactor *ReactorEngine) Subscribe(event string, ch chan React) { |
|
||||||
ev := reactor.patterns[event] |
|
||||||
// Create a new event if one isn't available
|
|
||||||
if ev == nil { |
|
||||||
ev = &ReactorEvent{event: event} |
|
||||||
reactor.patterns[event] = ev |
|
||||||
} |
|
||||||
|
|
||||||
// Add the channel to reactor event handler
|
|
||||||
ev.Add(ch) |
|
||||||
} |
|
||||||
|
|
||||||
func (reactor *ReactorEngine) Unsubscribe(event string, ch chan React) { |
|
||||||
ev := reactor.patterns[event] |
|
||||||
if ev != nil { |
|
||||||
ev.Remove(ch) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (reactor *ReactorEngine) Post(event string, resource interface{}) { |
|
||||||
ev := reactor.patterns[event] |
|
||||||
if ev != nil { |
|
||||||
ev.Post(React{Resource: resource, Event: event}) |
|
||||||
} |
|
||||||
} |
|
@ -1,30 +0,0 @@ |
|||||||
package ethutil |
|
||||||
|
|
||||||
import "testing" |
|
||||||
|
|
||||||
func TestReactorAdd(t *testing.T) { |
|
||||||
engine := NewReactorEngine() |
|
||||||
ch := make(chan React) |
|
||||||
engine.Subscribe("test", ch) |
|
||||||
if len(engine.patterns) != 1 { |
|
||||||
t.Error("Expected patterns to be 1, got", len(engine.patterns)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestReactorEvent(t *testing.T) { |
|
||||||
engine := NewReactorEngine() |
|
||||||
|
|
||||||
// Buffer 1, so it doesn't block for this test
|
|
||||||
ch := make(chan React, 1) |
|
||||||
engine.Subscribe("test", ch) |
|
||||||
engine.Post("test", "hello") |
|
||||||
|
|
||||||
value := <-ch |
|
||||||
if val, ok := value.Resource.(string); ok { |
|
||||||
if val != "hello" { |
|
||||||
t.Error("Expected Resource to be 'hello', got", val) |
|
||||||
} |
|
||||||
} else { |
|
||||||
t.Error("Unable to cast") |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue