Merge branch 'conversion' of github.com-obscure:ethereum/go-ethereum into conversion

This commit is contained in:
obscuren 2015-03-21 14:36:20 +01:00
commit 06697775d1
16 changed files with 208 additions and 362 deletions

@ -1,213 +0,0 @@
/*
This file is part of go-ethereum
go-ethereum 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 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Gustav Simonsson <gustav.simonsson@gmail.com>
* @date 2015
*
*/
package main
import (
"bytes"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"math/big"
"os"
"runtime"
"strings"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core"
types "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/rlp"
)
type Account struct {
Balance string
Code string
Nonce string
Storage map[string]string
}
type BlockHeader struct {
Bloom string
Coinbase string
Difficulty string
ExtraData string
GasLimit string
GasUsed string
MixHash string
Nonce string
Number string
ParentHash string
ReceiptTrie string
SeedHash string
StateRoot string
Timestamp string
TransactionsTrie string
UncleHash string
}
type Tx struct {
Data string
GasLimit string
GasPrice string
Nonce string
R string
S string
To string
V string
Value string
}
type Block struct {
BlockHeader BlockHeader
Rlp string
Transactions []Tx
UncleHeaders []string
}
type Test struct {
Blocks []Block
GenesisBlockHeader BlockHeader
Pre map[string]Account
}
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "%s <testfile>\n", os.Args[0])
flag.PrintDefaults()
}
flag.Parse()
runtime.GOMAXPROCS(runtime.NumCPU())
logger.AddLogSystem(logger.NewStdLogSystem(os.Stderr, log.LstdFlags, logger.DebugDetailLevel))
defer func() { logger.Flush() }()
if len(os.Args) < 2 {
utils.Fatalf("Please specify a test file as the first argument.")
}
blocks, err := loadBlocksFromTestFile(os.Args[1])
if err != nil {
utils.Fatalf("Could not load blocks: %v", err)
}
chain := memchain()
chain.ResetWithGenesisBlock(blocks[0])
if err = chain.InsertChain(types.Blocks{blocks[1]}); err != nil {
utils.Fatalf("Error: %v", err)
} else {
fmt.Println("PASS")
}
}
func memchain() *core.ChainManager {
blockdb, err := ethdb.NewMemDatabase()
if err != nil {
utils.Fatalf("Could not create in-memory database: %v", err)
}
statedb, err := ethdb.NewMemDatabase()
if err != nil {
utils.Fatalf("Could not create in-memory database: %v", err)
}
return core.NewChainManager(blockdb, statedb, new(event.TypeMux))
}
func loadBlocksFromTestFile(filePath string) (blocks types.Blocks, err error) {
fileContent, err := ioutil.ReadFile(filePath)
if err != nil {
return
}
bt := make(map[string]Test)
if err = json.Unmarshal(fileContent, &bt); err != nil {
return
}
// TODO: support multiple blocks; loop over all blocks
gbh := new(types.Header)
// Let's use slighlty different namings for the same things, because that's awesome.
gbh.ParentHash, err = hex_decode(bt["SimpleTx"].GenesisBlockHeader.ParentHash)
gbh.UncleHash, err = hex_decode(bt["SimpleTx"].GenesisBlockHeader.UncleHash)
gbh.Coinbase, err = hex_decode(bt["SimpleTx"].GenesisBlockHeader.Coinbase)
gbh.Root, err = hex_decode(bt["SimpleTx"].GenesisBlockHeader.StateRoot)
gbh.TxHash, err = hex_decode(bt["SimpleTx"].GenesisBlockHeader.TransactionsTrie)
gbh.ReceiptHash, err = hex_decode(bt["SimpleTx"].GenesisBlockHeader.ReceiptTrie)
gbh.Bloom, err = hex_decode(bt["SimpleTx"].GenesisBlockHeader.Bloom)
gbh.MixDigest, err = hex_decode(bt["SimpleTx"].GenesisBlockHeader.MixHash)
//gbh.SeedHash, err = hex_decode(bt["SimpleTx"].GenesisBlockHeader.SeedHash)
d, _ := new(big.Int).SetString(bt["SimpleTx"].GenesisBlockHeader.Difficulty, 10)
gbh.Difficulty = d
n, _ := new(big.Int).SetString(bt["SimpleTx"].GenesisBlockHeader.Number, 10)
gbh.Number = n
gl, _ := new(big.Int).SetString(bt["SimpleTx"].GenesisBlockHeader.GasLimit, 10)
gbh.GasLimit = gl
gu, _ := new(big.Int).SetString(bt["SimpleTx"].GenesisBlockHeader.GasUsed, 10)
gbh.GasUsed = gu
ts, _ := new(big.Int).SetString(bt["SimpleTx"].GenesisBlockHeader.Timestamp, 0)
gbh.Time = ts.Uint64()
extra, err := hex_decode(bt["SimpleTx"].GenesisBlockHeader.ExtraData)
gbh.Extra = string(extra) // TODO: change ExtraData to byte array
nonce, _ := hex_decode(bt["SimpleTx"].GenesisBlockHeader.Nonce)
gbh.Nonce = nonce
if err != nil {
return
}
gb := types.NewBlockWithHeader(gbh)
//gb.uncles = *new([]*types.Header)
//gb.transactions = *new(types.Transactions)
gb.Td = new(big.Int)
gb.Reward = new(big.Int)
testBlock := new(types.Block)
rlpBytes, err := hex_decode(bt["SimpleTx"].Blocks[0].Rlp)
err = rlp.Decode(bytes.NewReader(rlpBytes), &testBlock)
if err != nil {
return
}
blocks = types.Blocks{
gb,
testBlock,
}
return
}
func hex_decode(s string) (res []byte, err error) {
return hex.DecodeString(strings.TrimPrefix(s, "0x"))
}

@ -112,7 +112,7 @@ func Encode(object interface{}) []byte {
if object != nil { if object != nil {
switch t := object.(type) { switch t := object.(type) {
case *Value: case *Value:
buff.Write(Encode(t.Raw())) buff.Write(Encode(t.Val))
case RlpEncodable: case RlpEncodable:
buff.Write(Encode(t.RlpData())) buff.Write(Encode(t.RlpData()))
// Code dup :-/ // Code dup :-/

@ -5,6 +5,8 @@ import (
"math/big" "math/big"
"reflect" "reflect"
"testing" "testing"
"github.com/ethereum/go-ethereum/rlp"
) )
func TestNonInterfaceSlice(t *testing.T) { func TestNonInterfaceSlice(t *testing.T) {
@ -19,13 +21,16 @@ func TestNonInterfaceSlice(t *testing.T) {
func TestRlpValueEncoding(t *testing.T) { func TestRlpValueEncoding(t *testing.T) {
val := EmptyValue() val := EmptyValue()
val.AppendList().Append(1).Append(2).Append(3) val.AppendList().Append(byte(1)).Append(byte(2)).Append(byte(3))
val.Append("4").AppendList().Append(5) val.Append("4").AppendList().Append(byte(5))
res := val.Encode() res, err := rlp.EncodeToBytes(val)
if err != nil {
t.Fatalf("encode error: %v", err)
}
exp := Encode([]interface{}{[]interface{}{1, 2, 3}, "4", []interface{}{5}}) exp := Encode([]interface{}{[]interface{}{1, 2, 3}, "4", []interface{}{5}})
if bytes.Compare(res, exp) != 0 { if bytes.Compare(res, exp) != 0 {
t.Errorf("expected %q, got %q", res, exp) t.Errorf("expected %x, got %x", exp, res)
} }
} }
@ -57,9 +62,7 @@ func TestValueSlice(t *testing.T) {
func TestLargeData(t *testing.T) { func TestLargeData(t *testing.T) {
data := make([]byte, 100000) data := make([]byte, 100000)
enc := Encode(data) enc := Encode(data)
value := NewValue(enc) value := NewValueFromBytes(enc)
value.Decode()
if value.Len() != len(data) { if value.Len() != len(data) {
t.Error("Expected data to be", len(data), "got", value.Len()) t.Error("Expected data to be", len(data), "got", value.Len())
} }
@ -133,15 +136,16 @@ func TestEncodeDecodeBigInt(t *testing.T) {
} }
func TestEncodeDecodeBytes(t *testing.T) { func TestEncodeDecodeBytes(t *testing.T) {
b := NewValue([]interface{}{[]byte{1, 2, 3, 4, 5}, byte(6)}) bv := NewValue([]interface{}{[]byte{1, 2, 3, 4, 5}, []byte{6}})
val := NewValueFromBytes(b.Encode()) b, _ := rlp.EncodeToBytes(bv)
if !b.Cmp(val) { val := NewValueFromBytes(b)
t.Errorf("Expected %v, got %v", val, b) if !bv.Cmp(val) {
t.Errorf("Expected %#v, got %#v", bv, val)
} }
} }
func TestEncodeZero(t *testing.T) { func TestEncodeZero(t *testing.T) {
b := NewValue(0).Encode() b, _ := rlp.EncodeToBytes(NewValue(0))
exp := []byte{0xc0} exp := []byte{0xc0}
if bytes.Compare(b, exp) == 0 { if bytes.Compare(b, exp) == 0 {
t.Error("Expected", exp, "got", b) t.Error("Expected", exp, "got", b)

@ -3,18 +3,30 @@ package common
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"math/big" "math/big"
"reflect" "reflect"
"strconv" "strconv"
"github.com/ethereum/go-ethereum/rlp"
) )
// Data values are returned by the rlp decoder. The data values represents // Value can hold values of certain basic types and provides ways to
// one item within the rlp data structure. It's responsible for all the casting // convert between types without bothering to check whether the
// It always returns something valid // conversion is actually meaningful.
type Value struct { //
Val interface{} // It currently supports the following types:
kind reflect.Value //
} // - int{,8,16,32,64}
// - uint{,8,16,32,64}
// - *big.Int
// - []byte, string
// - []interface{}
//
// Value is useful whenever you feel that Go's types limit your
// ability to express yourself. In these situations, use Value and
// forget about this strong typing nonsense.
type Value struct{ Val interface{} }
func (val *Value) String() string { func (val *Value) String() string {
return fmt.Sprintf("%x", val.Val) return fmt.Sprintf("%x", val.Val)
@ -38,7 +50,6 @@ func (val *Value) IsNil() bool {
} }
func (val *Value) Len() int { func (val *Value) Len() int {
//return val.kind.Len()
if data, ok := val.Val.([]interface{}); ok { if data, ok := val.Val.([]interface{}); ok {
return len(data) return len(data)
} }
@ -46,14 +57,6 @@ func (val *Value) Len() int {
return len(val.Bytes()) return len(val.Bytes())
} }
func (val *Value) Raw() interface{} {
return val.Val
}
func (val *Value) Interface() interface{} {
return val.Val
}
func (val *Value) Uint() uint64 { func (val *Value) Uint() uint64 {
if Val, ok := val.Val.(uint8); ok { if Val, ok := val.Val.(uint8); ok {
return uint64(Val) return uint64(Val)
@ -260,26 +263,34 @@ func (self *Value) DeepCmp(o *Value) bool {
return bytes.Compare(self.Bytes(), o.Bytes()) == 0 return bytes.Compare(self.Bytes(), o.Bytes()) == 0
} }
func (val *Value) Encode() []byte { func (self *Value) DecodeRLP(s *rlp.Stream) error {
return Encode(val.Val) var v interface{}
} if err := s.Decode(&v); err != nil {
return err
// Assume that the data we have is encoded
func (self *Value) Decode() {
v, _ := Decode(self.Bytes(), 0)
self.Val = v
//self.Val = DecodeWithReader(bytes.NewBuffer(self.Bytes()))
}
func NewValueFromBytes(data []byte) *Value {
if len(data) != 0 {
value := NewValue(data)
value.Decode()
return value
} }
self.Val = v
return nil
}
return NewValue(nil) func (self *Value) EncodeRLP(w io.Writer) error {
if self == nil {
w.Write(rlp.EmptyList)
return nil
} else {
return rlp.Encode(w, self.Val)
}
}
// NewValueFromBytes decodes RLP data.
// The contained value will be nil if data contains invalid RLP.
func NewValueFromBytes(data []byte) *Value {
v := new(Value)
if len(data) != 0 {
if err := rlp.DecodeBytes(data, v); err != nil {
v.Val = nil
}
}
return v
} }
// Value setters // Value setters

@ -35,7 +35,7 @@ func (s *ValueSuite) TestValueTypes(c *checker.C) {
c.Assert(str.Str(), checker.Equals, strExp) c.Assert(str.Str(), checker.Equals, strExp)
c.Assert(num.Uint(), checker.Equals, numExp) c.Assert(num.Uint(), checker.Equals, numExp)
c.Assert(NewValue(inter.Interface()).Cmp(NewValue(interExp)), checker.Equals, true) c.Assert(NewValue(inter.Val).Cmp(NewValue(interExp)), checker.Equals, true)
c.Assert(byt.Bytes(), checker.DeepEquals, bytExp) c.Assert(byt.Bytes(), checker.DeepEquals, bytExp)
c.Assert(bigInt.BigInt(), checker.DeepEquals, bigExp) c.Assert(bigInt.BigInt(), checker.DeepEquals, bigExp)
} }

@ -3,8 +3,8 @@ package crypto
import ( import (
"strings" "strings"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
) )
type KeyPair struct { type KeyPair struct {
@ -48,11 +48,3 @@ func (k *KeyPair) Mnemonic() string {
func (k *KeyPair) AsStrings() (string, string, string, string) { func (k *KeyPair) AsStrings() (string, string, string, string) {
return k.Mnemonic(), common.Bytes2Hex(k.Address()), common.Bytes2Hex(k.PrivateKey), common.Bytes2Hex(k.PublicKey) return k.Mnemonic(), common.Bytes2Hex(k.Address()), common.Bytes2Hex(k.PrivateKey), common.Bytes2Hex(k.PublicKey)
} }
func (k *KeyPair) RlpEncode() []byte {
return k.RlpValue().Encode()
}
func (k *KeyPair) RlpValue() *common.Value {
return common.NewValue(k.PrivateKey)
}

@ -206,7 +206,7 @@ func New(config *Config) (*Ethereum, error) {
ethProto := EthProtocol(config.ProtocolVersion, config.NetworkId, eth.txPool, eth.chainManager, eth.blockPool) ethProto := EthProtocol(config.ProtocolVersion, config.NetworkId, eth.txPool, eth.chainManager, eth.blockPool)
protocols := []p2p.Protocol{ethProto} protocols := []p2p.Protocol{ethProto}
if config.Shh { if config.Shh {
//protocols = append(protocols, eth.whisper.Protocol()) protocols = append(protocols, eth.whisper.Protocol())
} }
eth.net = &p2p.Server{ eth.net = &p2p.Server{

@ -49,7 +49,7 @@ func (db *MemDatabase) Print() {
for key, val := range db.db { for key, val := range db.db {
fmt.Printf("%x(%d): ", key, len(key)) fmt.Printf("%x(%d): ", key, len(key))
node := common.NewValueFromBytes(val) node := common.NewValueFromBytes(val)
fmt.Printf("%q\n", node.Interface()) fmt.Printf("%q\n", node.Val)
} }
} }

@ -367,7 +367,12 @@ func makePtrDecoder(typ reflect.Type) (decoder, error) {
dec := func(s *Stream, val reflect.Value) (err error) { dec := func(s *Stream, val reflect.Value) (err error) {
_, size, err := s.Kind() _, size, err := s.Kind()
if err != nil || size == 0 && s.byteval == 0 { if err != nil || size == 0 && s.byteval == 0 {
val.Set(reflect.Zero(typ)) // set to nil // rearm s.Kind. This is important because the input
// position must advance to the next value even though
// we don't read anything.
s.kind = -1
// set the pointer to nil.
val.Set(reflect.Zero(typ))
return err return err
} }
newval := val newval := val
@ -535,6 +540,31 @@ func (s *Stream) Bytes() ([]byte, error) {
} }
} }
// Raw reads a raw encoded value including RLP type information.
func (s *Stream) Raw() ([]byte, error) {
kind, size, err := s.Kind()
if err != nil {
return nil, err
}
if kind == Byte {
s.kind = -1 // rearm Kind
return []byte{s.byteval}, nil
}
// the original header has already been read and is no longer
// available. read content and put a new header in front of it.
start := headsize(size)
buf := make([]byte, uint64(start)+size)
if err := s.readFull(buf[start:]); err != nil {
return nil, err
}
if kind == String {
puthead(buf, 0x80, 0xB8, size)
} else {
puthead(buf, 0xC0, 0xF7, size)
}
return buf, nil
}
var errUintOverflow = errors.New("rlp: uint overflow") var errUintOverflow = errors.New("rlp: uint overflow")
// Uint reads an RLP string of up to 8 bytes and returns its contents // Uint reads an RLP string of up to 8 bytes and returns its contents

@ -39,7 +39,7 @@ func TestStreamKind(t *testing.T) {
s := NewStream(bytes.NewReader(unhex(test.input))) s := NewStream(bytes.NewReader(unhex(test.input)))
kind, len, err := s.Kind() kind, len, err := s.Kind()
if err != nil { if err != nil {
t.Errorf("test %d: Type returned error: %v", i, err) t.Errorf("test %d: Kind returned error: %v", i, err)
continue continue
} }
if kind != test.wantKind { if kind != test.wantKind {
@ -93,6 +93,23 @@ func TestStreamErrors(t *testing.T) {
{"C3C2010201", calls{"List", "List", "Uint", "Uint", "ListEnd", "Uint"}, EOL}, {"C3C2010201", calls{"List", "List", "Uint", "Uint", "ListEnd", "Uint"}, EOL},
{"00", calls{"ListEnd"}, errNotInList}, {"00", calls{"ListEnd"}, errNotInList},
{"C40102", calls{"List", "Uint", "ListEnd"}, errNotAtEOL}, {"C40102", calls{"List", "Uint", "ListEnd"}, errNotAtEOL},
// This test verifies that the input position is advanced
// correctly when calling Bytes for empty strings. Kind can be called
// any number of times in between and doesn't advance.
{"C3808080", calls{
"List", // enter the list
"Bytes", // past first element
"Kind", "Kind", "Kind", // this shouldn't advance
"Bytes", // past second element
"Kind", "Kind", // can't hurt to try
"Bytes", // past final element
"Bytes", // this one should fail
}, EOL},
} }
testfor: testfor:
@ -148,6 +165,20 @@ func TestStreamList(t *testing.T) {
} }
} }
func TestStreamRaw(t *testing.T) {
s := NewStream(bytes.NewReader(unhex("C58401010101")))
s.List()
want := unhex("8401010101")
raw, err := s.Raw()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(want, raw) {
t.Errorf("raw mismatch: got %x, want %x", raw, want)
}
}
func TestDecodeErrors(t *testing.T) { func TestDecodeErrors(t *testing.T) {
r := bytes.NewReader(nil) r := bytes.NewReader(nil)
@ -314,6 +345,9 @@ var decodeTests = []decodeTest{
{input: "C109", ptr: new(*[]uint), value: &[]uint{9}}, {input: "C109", ptr: new(*[]uint), value: &[]uint{9}},
{input: "C58403030303", ptr: new(*[][]byte), value: &[][]byte{{3, 3, 3, 3}}}, {input: "C58403030303", ptr: new(*[][]byte), value: &[][]byte{{3, 3, 3, 3}}},
// check that input position is advanced also for empty values.
{input: "C3808005", ptr: new([]*uint), value: []*uint{nil, nil, uintp(5)}},
// pointer should be reset to nil // pointer should be reset to nil
{input: "05", ptr: sharedPtr, value: uintp(5)}, {input: "05", ptr: sharedPtr, value: uintp(5)},
{input: "80", ptr: sharedPtr, value: (*uint)(nil)}, {input: "80", ptr: sharedPtr, value: (*uint)(nil)},

@ -70,7 +70,7 @@ func (e flatenc) EncodeRLP(out io.Writer) error {
newhead := eb.lheads[prevnheads] newhead := eb.lheads[prevnheads]
copy(eb.lheads[prevnheads:], eb.lheads[prevnheads+1:]) copy(eb.lheads[prevnheads:], eb.lheads[prevnheads+1:])
eb.lheads = eb.lheads[:len(eb.lheads)-1] eb.lheads = eb.lheads[:len(eb.lheads)-1]
eb.lhsize -= newhead.tagsize() eb.lhsize -= headsize(uint64(newhead.size))
return nil return nil
} }
@ -155,21 +155,29 @@ type listhead struct {
// encode writes head to the given buffer, which must be at least // encode writes head to the given buffer, which must be at least
// 9 bytes long. It returns the encoded bytes. // 9 bytes long. It returns the encoded bytes.
func (head *listhead) encode(buf []byte) []byte { func (head *listhead) encode(buf []byte) []byte {
if head.size < 56 { return buf[:puthead(buf, 0xC0, 0xF7, uint64(head.size))]
buf[0] = 0xC0 + byte(head.size)
return buf[:1]
} else {
sizesize := putint(buf[1:], uint64(head.size))
buf[0] = 0xF7 + byte(sizesize)
return buf[:sizesize+1]
}
} }
func (head *listhead) tagsize() int { // headsize returns the size of a list or string header
if head.size < 56 { // for a value of the given size.
func headsize(size uint64) int {
if size < 56 {
return 1 return 1
} }
return 1 + intsize(uint64(head.size)) return 1 + intsize(size)
}
// puthead writes a list or string header to buf.
// buf must be at least 9 bytes long.
func puthead(buf []byte, smalltag, largetag byte, size uint64) int {
if size < 56 {
buf[0] = smalltag + byte(size)
return 1
} else {
sizesize := putint(buf[1:], size)
buf[0] = largetag + byte(sizesize)
return sizesize + 1
}
} }
func newencbuf() *encbuf { func newencbuf() *encbuf {

@ -18,26 +18,31 @@ const (
type Envelope struct { type Envelope struct {
Expiry uint32 // Whisper protocol specifies int32, really should be int64 Expiry uint32 // Whisper protocol specifies int32, really should be int64
Ttl uint32 // ^^^^^^ TTL uint32 // ^^^^^^
Topics [][]byte Topics [][]byte
Data []byte Data []byte
Nonce uint32 Nonce uint32
hash Hash hash common.Hash
} }
func (self *Envelope) Hash() Hash { func (self *Envelope) Hash() common.Hash {
if self.hash == EmptyHash { if (self.hash == common.Hash{}) {
self.hash = H(crypto.Sha3(common.Encode(self))) enc, _ := rlp.EncodeToBytes(self)
self.hash = crypto.Sha3Hash(enc)
} }
return self.hash return self.hash
} }
func NewEnvelope(ttl time.Duration, topics [][]byte, data *Message) *Envelope { func NewEnvelope(ttl time.Duration, topics [][]byte, data *Message) *Envelope {
exp := time.Now().Add(ttl) exp := time.Now().Add(ttl)
return &Envelope{
return &Envelope{uint32(exp.Unix()), uint32(ttl.Seconds()), topics, data.Bytes(), 0, Hash{}} Expiry: uint32(exp.Unix()),
TTL: uint32(ttl.Seconds()),
Topics: topics,
Data: data.Bytes(),
Nonce: 0,
}
} }
func (self *Envelope) Seal(pow time.Duration) { func (self *Envelope) Seal(pow time.Duration) {
@ -76,7 +81,8 @@ func (self *Envelope) Open(prv *ecdsa.PrivateKey) (msg *Message, err error) {
func (self *Envelope) proveWork(dura time.Duration) { func (self *Envelope) proveWork(dura time.Duration) {
var bestBit int var bestBit int
d := make([]byte, 64) d := make([]byte, 64)
copy(d[:32], common.Encode(self.withoutNonce())) enc, _ := rlp.EncodeToBytes(self.withoutNonce())
copy(d[:32], enc)
then := time.Now().Add(dura).UnixNano() then := time.Now().Add(dura).UnixNano()
for n := uint32(0); time.Now().UnixNano() < then; { for n := uint32(0); time.Now().UnixNano() < then; {
@ -96,39 +102,28 @@ func (self *Envelope) proveWork(dura time.Duration) {
func (self *Envelope) valid() bool { func (self *Envelope) valid() bool {
d := make([]byte, 64) d := make([]byte, 64)
copy(d[:32], common.Encode(self.withoutNonce())) enc, _ := rlp.EncodeToBytes(self.withoutNonce())
copy(d[:32], enc)
binary.BigEndian.PutUint32(d[60:], self.Nonce) binary.BigEndian.PutUint32(d[60:], self.Nonce)
return common.FirstBitSet(common.BigD(crypto.Sha3(d))) > 0 return common.FirstBitSet(common.BigD(crypto.Sha3(d))) > 0
} }
func (self *Envelope) withoutNonce() interface{} { func (self *Envelope) withoutNonce() interface{} {
return []interface{}{self.Expiry, self.Ttl, common.ByteSliceToInterface(self.Topics), self.Data} return []interface{}{self.Expiry, self.TTL, self.Topics, self.Data}
} }
func (self *Envelope) RlpData() interface{} { // rlpenv is an Envelope but is not an rlp.Decoder.
return []interface{}{self.Expiry, self.Ttl, common.ByteSliceToInterface(self.Topics), self.Data, self.Nonce} // It is used for decoding because we need to
} type rlpenv Envelope
func (self *Envelope) DecodeRLP(s *rlp.Stream) error { func (self *Envelope) DecodeRLP(s *rlp.Stream) error {
var extenv struct { raw, err := s.Raw()
Expiry uint32 if err != nil {
Ttl uint32
Topics [][]byte
Data []byte
Nonce uint32
}
if err := s.Decode(&extenv); err != nil {
return err return err
} }
if err := rlp.DecodeBytes(raw, (*rlpenv)(self)); err != nil {
self.Expiry = extenv.Expiry return err
self.Ttl = extenv.Ttl }
self.Topics = extenv.Topics self.hash = crypto.Sha3Hash(raw)
self.Data = extenv.Data
self.Nonce = extenv.Nonce
// TODO We should use the stream directly here.
self.hash = H(crypto.Sha3(common.Encode(self)))
return nil return nil
} }

@ -10,7 +10,7 @@ import (
) )
const ( const (
protocolVersion = 0x02 protocolVersion uint64 = 0x02
) )
type peer struct { type peer struct {
@ -66,21 +66,18 @@ out:
} }
func (self *peer) broadcast(envelopes []*Envelope) error { func (self *peer) broadcast(envelopes []*Envelope) error {
envs := make([]interface{}, len(envelopes)) envs := make([]*Envelope, 0, len(envelopes))
i := 0 for _, env := range envelopes {
for _, envelope := range envelopes { if !self.known.Has(env.Hash()) {
if !self.known.Has(envelope.Hash()) { envs = append(envs, env)
envs[i] = envelope self.known.Add(env.Hash())
self.known.Add(envelope.Hash())
i++
} }
} }
if len(envs) > 0 {
if i > 0 { if err := p2p.Send(self.ws, envelopesMsg, envs); err != nil {
if err := p2p.Send(self.ws, envelopesMsg, envs[:i]); err != nil {
return err return err
} }
self.peer.DebugDetailln("broadcasted", i, "message(s)") self.peer.DebugDetailln("broadcasted", len(envs), "message(s)")
} }
return nil return nil
} }

@ -1,6 +1,10 @@
package whisper package whisper
import "sort" import (
"sort"
"github.com/ethereum/go-ethereum/common"
)
type sortedKeys struct { type sortedKeys struct {
k []int32 k []int32
@ -10,7 +14,7 @@ func (self *sortedKeys) Len() int { return len(self.k) }
func (self *sortedKeys) Less(i, j int) bool { return self.k[i] < self.k[j] } func (self *sortedKeys) Less(i, j int) bool { return self.k[i] < self.k[j] }
func (self *sortedKeys) Swap(i, j int) { self.k[i], self.k[j] = self.k[j], self.k[i] } func (self *sortedKeys) Swap(i, j int) { self.k[i], self.k[j] = self.k[j], self.k[i] }
func sortKeys(m map[int32]Hash) []int32 { func sortKeys(m map[int32]common.Hash) []int32 {
sorted := new(sortedKeys) sorted := new(sortedKeys)
sorted.k = make([]int32, len(m)) sorted.k = make([]int32, len(m))
i := 0 i := 0

@ -1,13 +1,17 @@
package whisper package whisper
import "testing" import (
"testing"
"github.com/ethereum/go-ethereum/common"
)
func TestSorting(t *testing.T) { func TestSorting(t *testing.T) {
m := map[int32]Hash{ m := map[int32]common.Hash{
1: HS("1"), 1: {1},
3: HS("3"), 3: {3},
2: HS("2"), 2: {2},
5: HS("5"), 5: {5},
} }
exp := []int32{1, 2, 3, 5} exp := []int32{1, 2, 3, 5}
res := sortKeys(m) res := sortKeys(m)

@ -1,12 +1,12 @@
package whisper package whisper
import ( import (
"bytes"
"crypto/ecdsa" "crypto/ecdsa"
"errors" "errors"
"sync" "sync"
"time" "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/event/filter" "github.com/ethereum/go-ethereum/event/filter"
@ -15,26 +15,6 @@ import (
"gopkg.in/fatih/set.v0" "gopkg.in/fatih/set.v0"
) )
// MOVE ME
type Hash struct {
hash string
}
var EmptyHash Hash
func H(hash []byte) Hash {
return Hash{string(hash)}
}
func HS(hash string) Hash {
return Hash{hash}
}
func (self Hash) Compare(other Hash) int {
return bytes.Compare([]byte(self.hash), []byte(other.hash))
}
// MOVE ME END
const ( const (
statusMsg = 0x0 statusMsg = 0x0
envelopesMsg = 0x01 envelopesMsg = 0x01
@ -55,7 +35,7 @@ type Whisper struct {
filters *filter.Filters filters *filter.Filters
mmu sync.RWMutex mmu sync.RWMutex
messages map[Hash]*Envelope messages map[common.Hash]*Envelope
expiry map[uint32]*set.SetNonTS expiry map[uint32]*set.SetNonTS
quit chan struct{} quit chan struct{}
@ -65,7 +45,7 @@ type Whisper struct {
func New() *Whisper { func New() *Whisper {
whisper := &Whisper{ whisper := &Whisper{
messages: make(map[Hash]*Envelope), messages: make(map[common.Hash]*Envelope),
filters: filter.New(), filters: filter.New(),
expiry: make(map[uint32]*set.SetNonTS), expiry: make(map[uint32]*set.SetNonTS),
quit: make(chan struct{}), quit: make(chan struct{}),
@ -239,7 +219,7 @@ func (self *Whisper) expire() {
} }
hashSet.Each(func(v interface{}) bool { hashSet.Each(func(v interface{}) bool {
delete(self.messages, v.(Hash)) delete(self.messages, v.(common.Hash))
return true return true
}) })
self.expiry[then].Clear() self.expiry[then].Clear()