Merge branch 'develop' into ui

This commit is contained in:
Alexandre Van de Sande 2015-02-23 09:05:15 -03:00
commit dea6584018
111 changed files with 1987 additions and 2976 deletions

View File

@ -6,18 +6,15 @@ before_install:
- sudo apt-get update -qq - sudo apt-get update -qq
- sudo apt-get install -yqq libgmp3-dev libreadline6-dev qt54quickcontrols qt54webengine - sudo apt-get install -yqq libgmp3-dev libreadline6-dev qt54quickcontrols qt54webengine
install: install:
- go get code.google.com/p/go.tools/cmd/goimports # - go get code.google.com/p/go.tools/cmd/goimports
- go get github.com/golang/lint/golint # - go get github.com/golang/lint/golint
# - go get golang.org/x/tools/cmd/vet # - go get golang.org/x/tools/cmd/vet
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
- go get github.com/mattn/goveralls - go get github.com/mattn/goveralls
- go get gopkg.in/check.v1
- go get github.com/tools/godep
before_script: before_script:
- godep restore # - gofmt -l -w .
- gofmt -l -w . # - goimports -l -w .
- goimports -l -w . # - golint .
- golint .
# - go vet ./... # - go vet ./...
# - go test -race ./... # - go test -race ./...
script: script:

46
Godeps/Godeps.json generated
View File

@ -1,6 +1,6 @@
{ {
"ImportPath": "github.com/ethereum/go-ethereum", "ImportPath": "github.com/ethereum/go-ethereum",
"GoVersion": "go1.4", "GoVersion": "go1.4.1",
"Packages": [ "Packages": [
"./..." "./..."
], ],
@ -15,26 +15,6 @@
"Comment": "null-12", "Comment": "null-12",
"Rev": "7dda39b2e7d5e265014674c5af696ba4186679e9" "Rev": "7dda39b2e7d5e265014674c5af696ba4186679e9"
}, },
{
"ImportPath": "code.google.com/p/go.crypto/pbkdf2",
"Comment": "null-236",
"Rev": "69e2a90ed92d03812364aeb947b7068dc42e561e"
},
{
"ImportPath": "code.google.com/p/go.crypto/ripemd160",
"Comment": "null-236",
"Rev": "69e2a90ed92d03812364aeb947b7068dc42e561e"
},
{
"ImportPath": "code.google.com/p/go.crypto/scrypt",
"Comment": "null-236",
"Rev": "69e2a90ed92d03812364aeb947b7068dc42e561e"
},
{
"ImportPath": "code.google.com/p/go.net/websocket",
"Comment": "null-173",
"Rev": "4231557d7c726df4cf9a4e8cdd8a417c8c200bdb"
},
{ {
"ImportPath": "code.google.com/p/snappy-go/snappy", "ImportPath": "code.google.com/p/snappy-go/snappy",
"Comment": "null-15", "Comment": "null-15",
@ -44,22 +24,18 @@
"ImportPath": "github.com/ethereum/serpent-go", "ImportPath": "github.com/ethereum/serpent-go",
"Rev": "5767a0dbd759d313df3f404dadb7f98d7ab51443" "Rev": "5767a0dbd759d313df3f404dadb7f98d7ab51443"
}, },
{
"ImportPath": "github.com/fjl/goupnp",
"Rev": "fa95df6feb61e136b499d01711fcd410ccaf20c1"
},
{ {
"ImportPath": "github.com/howeyc/fsnotify", "ImportPath": "github.com/howeyc/fsnotify",
"Comment": "v0.9.0-11-g6b1ef89", "Comment": "v0.9.0-11-g6b1ef89",
"Rev": "6b1ef893dc11e0447abda6da20a5203481878dda" "Rev": "6b1ef893dc11e0447abda6da20a5203481878dda"
}, },
{ {
"ImportPath": "github.com/jackpal/go-nat-pmp", "ImportPath": "github.com/huin/goupnp",
"Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1" "Rev": "4191d8a85005844ea202fde52799681971b12dfe"
}, },
{ {
"ImportPath": "github.com/obscuren/ecies", "ImportPath": "github.com/jackpal/go-nat-pmp",
"Rev": "d899334bba7bf4a157cab19d8ad836dcb1de0c34" "Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1"
}, },
{ {
"ImportPath": "github.com/obscuren/otto", "ImportPath": "github.com/obscuren/otto",
@ -109,6 +85,18 @@
"ImportPath": "golang.org/x/crypto/pbkdf2", "ImportPath": "golang.org/x/crypto/pbkdf2",
"Rev": "4ed45ec682102c643324fae5dff8dab085b6c300" "Rev": "4ed45ec682102c643324fae5dff8dab085b6c300"
}, },
{
"ImportPath": "golang.org/x/crypto/ripemd160",
"Rev": "4ed45ec682102c643324fae5dff8dab085b6c300"
},
{
"ImportPath": "golang.org/x/crypto/scrypt",
"Rev": "4ed45ec682102c643324fae5dff8dab085b6c300"
},
{
"ImportPath": "golang.org/x/net/websocket",
"Rev": "59b0df9b1f7abda5aab0495ee54f408daf182ce7"
},
{ {
"ImportPath": "gopkg.in/check.v1", "ImportPath": "gopkg.in/check.v1",
"Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673" "Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673"

View File

@ -1,77 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
2898 / PKCS #5 v2.0.
A key derivation function is useful when encrypting data based on a password
or any other not-fully-random data. It uses a pseudorandom function to derive
a secure encryption key based on the password.
While v2.0 of the standard defines only one pseudorandom function to use,
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
choose, you can pass the `New` functions from the different SHA packages to
pbkdf2.Key.
*/
package pbkdf2
import (
"crypto/hmac"
"hash"
)
// Key derives a key from the password, salt and iteration count, returning a
// []byte of length keylen that can be used as cryptographic key. The key is
// derived based on the method described as PBKDF2 with the HMAC variant using
// the supplied hash function.
//
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
// doing:
//
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
//
// Remember to get a good random salt. At least 8 bytes is recommended by the
// RFC.
//
// Using a higher iteration count will increase the cost of an exhaustive
// search but will also make derivation proportionally slower.
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
prf := hmac.New(h, password)
hashLen := prf.Size()
numBlocks := (keyLen + hashLen - 1) / hashLen
var buf [4]byte
dk := make([]byte, 0, numBlocks*hashLen)
U := make([]byte, hashLen)
for block := 1; block <= numBlocks; block++ {
// N.B.: || means concatenation, ^ means XOR
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
// U_1 = PRF(password, salt || uint(i))
prf.Reset()
prf.Write(salt)
buf[0] = byte(block >> 24)
buf[1] = byte(block >> 16)
buf[2] = byte(block >> 8)
buf[3] = byte(block)
prf.Write(buf[:4])
dk = prf.Sum(dk)
T := dk[len(dk)-hashLen:]
copy(U, T)
// U_n = PRF(password, U_(n-1))
for n := 2; n <= iter; n++ {
prf.Reset()
prf.Write(U)
U = U[:0]
U = prf.Sum(U)
for x := range U {
T[x] ^= U[x]
}
}
}
return dk[:keyLen]
}

View File

@ -1,157 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pbkdf2
import (
"bytes"
"crypto/sha1"
"crypto/sha256"
"hash"
"testing"
)
type testVector struct {
password string
salt string
iter int
output []byte
}
// Test vectors from RFC 6070, http://tools.ietf.org/html/rfc6070
var sha1TestVectors = []testVector{
{
"password",
"salt",
1,
[]byte{
0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
0x2f, 0xe0, 0x37, 0xa6,
},
},
{
"password",
"salt",
2,
[]byte{
0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
0xd8, 0xde, 0x89, 0x57,
},
},
{
"password",
"salt",
4096,
[]byte{
0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
0x65, 0xa4, 0x29, 0xc1,
},
},
// // This one takes too long
// {
// "password",
// "salt",
// 16777216,
// []byte{
// 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
// 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
// 0x26, 0x34, 0xe9, 0x84,
// },
// },
{
"passwordPASSWORDpassword",
"saltSALTsaltSALTsaltSALTsaltSALTsalt",
4096,
[]byte{
0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
0x38,
},
},
{
"pass\000word",
"sa\000lt",
4096,
[]byte{
0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3,
},
},
}
// Test vectors from
// http://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
var sha256TestVectors = []testVector{
{
"password",
"salt",
1,
[]byte{
0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c,
0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37,
0xa8, 0x65, 0x48, 0xc9,
},
},
{
"password",
"salt",
2,
[]byte{
0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3,
0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, 0x6d, 0xd0,
0x2a, 0x30, 0x3f, 0x8e,
},
},
{
"password",
"salt",
4096,
[]byte{
0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41,
0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d,
0x96, 0x28, 0x93, 0xa0,
},
},
{
"passwordPASSWORDpassword",
"saltSALTsaltSALTsaltSALTsaltSALTsalt",
4096,
[]byte{
0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f,
0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf,
0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18,
0x1c,
},
},
{
"pass\000word",
"sa\000lt",
4096,
[]byte{
0x89, 0xb6, 0x9d, 0x05, 0x16, 0xf8, 0x29, 0x89,
0x3c, 0x69, 0x62, 0x26, 0x65, 0x0a, 0x86, 0x87,
},
},
}
func testHash(t *testing.T, h func() hash.Hash, hashName string, vectors []testVector) {
for i, v := range vectors {
o := Key([]byte(v.password), []byte(v.salt), v.iter, len(v.output), h)
if !bytes.Equal(o, v.output) {
t.Errorf("%s %d: expected %x, got %x", hashName, i, v.output, o)
}
}
}
func TestWithHMACSHA1(t *testing.T) {
testHash(t, sha1.New, "SHA1", sha1TestVectors)
}
func TestWithHMACSHA256(t *testing.T) {
testHash(t, sha256.New, "SHA256", sha256TestVectors)
}

View File

@ -3,7 +3,7 @@ goupnp is a UPnP client library for Go
Installation Installation
------------ ------------
Run `go get -u github.com/fjl/goupnp`. Run `go get -u github.com/huin/goupnp`.
Regenerating dcps generated source code: Regenerating dcps generated source code:
---------------------------------------- ----------------------------------------

View File

@ -4,7 +4,7 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/fjl/goupnp/httpu" "github.com/huin/goupnp/httpu"
) )
func main() { func main() {

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/fjl/goupnp/dcps/internetgateway1" "github.com/huin/goupnp/dcps/internetgateway1"
) )
func main() { func main() {

View File

@ -11,8 +11,8 @@ package internetgateway1
import ( import (
"time" "time"
"github.com/fjl/goupnp" "github.com/huin/goupnp"
"github.com/fjl/goupnp/soap" "github.com/huin/goupnp/soap"
) )
// Hack to avoid Go complaining if time isn't used. // Hack to avoid Go complaining if time isn't used.

View File

@ -11,8 +11,8 @@ package internetgateway2
import ( import (
"time" "time"
"github.com/fjl/goupnp" "github.com/huin/goupnp"
"github.com/fjl/goupnp/soap" "github.com/huin/goupnp/soap"
) )
// Hack to avoid Go complaining if time isn't used. // Hack to avoid Go complaining if time isn't used.

View File

@ -8,8 +8,8 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"github.com/fjl/goupnp/scpd" "github.com/huin/goupnp/scpd"
"github.com/fjl/goupnp/soap" "github.com/huin/goupnp/soap"
) )
const ( const (

View File

@ -2,5 +2,5 @@
// //
// To run examples and see the output for your local network, run the following // To run examples and see the output for your local network, run the following
// command (specifically including the -v flag): // command (specifically including the -v flag):
// go test -v github.com/fjl/goupnp/example // go test -v github.com/huin/goupnp/example
package example package example

View File

@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/fjl/goupnp" "github.com/huin/goupnp"
"github.com/fjl/goupnp/dcps/internetgateway1" "github.com/huin/goupnp/dcps/internetgateway1"
) )
// Use discovered WANPPPConnection1 services to find external IP addresses. // Use discovered WANPPPConnection1 services to find external IP addresses.

View File

@ -17,8 +17,8 @@ import (
"strings" "strings"
"text/template" "text/template"
"github.com/fjl/goupnp" "github.com/huin/goupnp"
"github.com/fjl/goupnp/scpd" "github.com/huin/goupnp/scpd"
"github.com/huin/goutil/codegen" "github.com/huin/goutil/codegen"
"github.com/jingweno/gotask/tasking" "github.com/jingweno/gotask/tasking"
) )
@ -38,7 +38,7 @@ var (
// -s, --spec_filename=<upnpresources.zip> // -s, --spec_filename=<upnpresources.zip>
// Path to the specification file, available from http://upnp.org/resources/upnpresources.zip // Path to the specification file, available from http://upnp.org/resources/upnpresources.zip
// -o, --out_dir=<output directory> // -o, --out_dir=<output directory>
// Path to the output directory. This is is where the DCP source files will be placed. Should normally correspond to the directory for github.com/fjl/goupnp/dcps // Path to the output directory. This is is where the DCP source files will be placed. Should normally correspond to the directory for github.com/huin/goupnp/dcps
// --nogofmt // --nogofmt
// Disable passing the output through gofmt. Do this if debugging code output problems and needing to see the generated code prior to being passed through gofmt. // Disable passing the output through gofmt. Do this if debugging code output problems and needing to see the generated code prior to being passed through gofmt.
func TaskSpecgen(t *tasking.T) { func TaskSpecgen(t *tasking.T) {
@ -445,8 +445,8 @@ package {{$name}}
import ( import (
"time" "time"
"github.com/fjl/goupnp" "github.com/huin/goupnp"
"github.com/fjl/goupnp/soap" "github.com/huin/goupnp/soap"
) )
// Hack to avoid Go complaining if time isn't used. // Hack to avoid Go complaining if time isn't used.

View File

@ -1,11 +1,11 @@
// goupnp is an implementation of a client for various UPnP services. // goupnp is an implementation of a client for various UPnP services.
// //
// For most uses, it is recommended to use the code-generated packages under // For most uses, it is recommended to use the code-generated packages under
// github.com/fjl/goupnp/dcps. Example use is shown at // github.com/huin/goupnp/dcps. Example use is shown at
// http://godoc.org/github.com/fjl/goupnp/example // http://godoc.org/github.com/huin/goupnp/example
// //
// A commonly used client is internetgateway1.WANPPPConnection1: // A commonly used client is internetgateway1.WANPPPConnection1:
// http://godoc.org/github.com/fjl/goupnp/dcps/internetgateway1#WANPPPConnection1 // http://godoc.org/github.com/huin/goupnp/dcps/internetgateway1#WANPPPConnection1
// //
// Currently only a couple of schemas have code generated for them from the // Currently only a couple of schemas have code generated for them from the
// UPnP example XML specifications. Not all methods will work on these clients, // UPnP example XML specifications. Not all methods will work on these clients,
@ -20,8 +20,8 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"github.com/fjl/goupnp/httpu" "github.com/huin/goupnp/httpu"
"github.com/fjl/goupnp/ssdp" "github.com/huin/goupnp/ssdp"
) )
// ContextError is an error that wraps an error with some context information. // ContextError is an error that wraps an error with some context information.

View File

@ -2,8 +2,7 @@ package goupnp
import ( import (
"fmt" "fmt"
"github.com/huin/goupnp/soap"
"github.com/fjl/goupnp/soap"
) )
// ServiceClient is a SOAP client, root device and the service for the SOAP // ServiceClient is a SOAP client, root device and the service for the SOAP

View File

@ -10,7 +10,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/fjl/goupnp/httpu" "github.com/huin/goupnp/httpu"
) )
const ( const (

View File

@ -8,7 +8,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/fjl/goupnp/httpu" "github.com/huin/goupnp/httpu"
) )
const ( const (

View File

@ -1,24 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*~

View File

@ -1,28 +0,0 @@
Copyright (c) 2013 Kyle Isom <kyle@tyrfingr.is>
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,94 +0,0 @@
# NOTE
This implementation is direct fork of Kylom's implementation. I claim no authorship over this code apart from some minor modifications.
Please be aware this code **has not yet been reviewed**.
ecies implements the Elliptic Curve Integrated Encryption Scheme.
The package is designed to be compliant with the appropriate NIST
standards, and therefore doesn't support the full SEC 1 algorithm set.
STATUS:
ecies should be ready for use. The ASN.1 support is only complete so
far as to supported the listed algorithms before.
CAVEATS
1. CMAC support is currently not present.
SUPPORTED ALGORITHMS
SYMMETRIC CIPHERS HASH FUNCTIONS
AES128 SHA-1
AES192 SHA-224
AES256 SHA-256
SHA-384
ELLIPTIC CURVE SHA-512
P256
P384 KEY DERIVATION FUNCTION
P521 NIST SP 800-65a Concatenation KDF
Curve P224 isn't supported because it does not provide a minimum security
level of AES128 with HMAC-SHA1. According to NIST SP 800-57, the security
level of P224 is 112 bits of security. Symmetric ciphers use CTR-mode;
message tags are computed using HMAC-<HASH> function.
CURVE SELECTION
According to NIST SP 800-57, the following curves should be selected:
+----------------+-------+
| SYMMETRIC SIZE | CURVE |
+----------------+-------+
| 128-bit | P256 |
+----------------+-------+
| 192-bit | P384 |
+----------------+-------+
| 256-bit | P521 |
+----------------+-------+
TODO
1. Look at serialising the parameters with the SEC 1 ASN.1 module.
2. Validate ASN.1 formats with SEC 1.
TEST VECTORS
The only test vectors I've found so far date from 1993, predating AES
and including only 163-bit curves. Therefore, there are no published
test vectors to compare to.
LICENSE
ecies is released under the same license as the Go source code. See the
LICENSE file for details.
REFERENCES
* SEC (Standard for Efficient Cryptography) 1, version 2.0: Elliptic
Curve Cryptography; Certicom, May 2009.
http://www.secg.org/sec1-v2.pdf
* GEC (Guidelines for Efficient Cryptography) 2, version 0.3: Test
Vectors for SEC 1; Certicom, September 1999.
http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf
* NIST SP 800-56a: Recommendation for Pair-Wise Key Establishment Schemes
Using Discrete Logarithm Cryptography. National Institute of Standards
and Technology, May 2007.
http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf
* Suite B Implementers Guide to NIST SP 800-56A. National Security
Agency, July 28, 2009.
http://www.nsa.gov/ia/_files/SuiteB_Implementer_G-113808.pdf
* NIST SP 800-57: Recommendation for Key Management Part 1: General
(Revision 3). National Institute of Standards and Technology, July
2012.
http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf

View File

@ -1,556 +0,0 @@
package ecies
import (
"bytes"
"crypto"
"crypto/elliptic"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/asn1"
"encoding/pem"
"fmt"
"hash"
"math/big"
)
var (
secgScheme = []int{1, 3, 132, 1}
shaScheme = []int{2, 16, 840, 1, 101, 3, 4, 2}
ansiX962Scheme = []int{1, 2, 840, 10045}
x963Scheme = []int{1, 2, 840, 63, 0}
)
var ErrInvalidPrivateKey = fmt.Errorf("ecies: invalid private key")
func doScheme(base, v []int) asn1.ObjectIdentifier {
var oidInts asn1.ObjectIdentifier
oidInts = append(oidInts, base...)
return append(oidInts, v...)
}
// curve OID code taken from crypto/x509, including
// - oidNameCurve*
// - namedCurveFromOID
// - oidFromNamedCurve
// RFC 5480, 2.1.1.1. Named Curve
//
// secp224r1 OBJECT IDENTIFIER ::= {
// iso(1) identified-organization(3) certicom(132) curve(0) 33 }
//
// secp256r1 OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
// prime(1) 7 }
//
// secp384r1 OBJECT IDENTIFIER ::= {
// iso(1) identified-organization(3) certicom(132) curve(0) 34 }
//
// secp521r1 OBJECT IDENTIFIER ::= {
// iso(1) identified-organization(3) certicom(132) curve(0) 35 }
//
// NB: secp256r1 is equivalent to prime256v1
type secgNamedCurve asn1.ObjectIdentifier
var (
secgNamedCurveP224 = secgNamedCurve{1, 3, 132, 0, 33}
secgNamedCurveP256 = secgNamedCurve{1, 2, 840, 10045, 3, 1, 7}
secgNamedCurveP384 = secgNamedCurve{1, 3, 132, 0, 34}
secgNamedCurveP521 = secgNamedCurve{1, 3, 132, 0, 35}
rawCurveP224 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 3}
rawCurveP256 = []byte{6, 8, 4, 2, 1, 3, 4, 7, 2, 2, 0, 6, 6, 1, 3, 1, 7}
rawCurveP384 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 4}
rawCurveP521 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 5}
)
func rawCurve(curve elliptic.Curve) []byte {
switch curve {
case elliptic.P224():
return rawCurveP224
case elliptic.P256():
return rawCurveP256
case elliptic.P384():
return rawCurveP384
case elliptic.P521():
return rawCurveP521
default:
return nil
}
}
func (curve secgNamedCurve) Equal(curve2 secgNamedCurve) bool {
if len(curve) != len(curve2) {
return false
}
for i, _ := range curve {
if curve[i] != curve2[i] {
return false
}
}
return true
}
func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve {
switch {
case curve.Equal(secgNamedCurveP224):
return elliptic.P224()
case curve.Equal(secgNamedCurveP256):
return elliptic.P256()
case curve.Equal(secgNamedCurveP384):
return elliptic.P384()
case curve.Equal(secgNamedCurveP521):
return elliptic.P521()
}
return nil
}
func oidFromNamedCurve(curve elliptic.Curve) (secgNamedCurve, bool) {
switch curve {
case elliptic.P224():
return secgNamedCurveP224, true
case elliptic.P256():
return secgNamedCurveP256, true
case elliptic.P384():
return secgNamedCurveP384, true
case elliptic.P521():
return secgNamedCurveP521, true
}
return nil, false
}
// asnAlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
// 5280, section 4.1.1.2.
type asnAlgorithmIdentifier struct {
Algorithm asn1.ObjectIdentifier
Parameters asn1.RawValue `asn1:"optional"`
}
func (a asnAlgorithmIdentifier) Cmp(b asnAlgorithmIdentifier) bool {
if len(a.Algorithm) != len(b.Algorithm) {
return false
}
for i, _ := range a.Algorithm {
if a.Algorithm[i] != b.Algorithm[i] {
return false
}
}
return true
}
type asnHashFunction asnAlgorithmIdentifier
var (
oidSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
oidSHA224 = doScheme(shaScheme, []int{4})
oidSHA256 = doScheme(shaScheme, []int{1})
oidSHA384 = doScheme(shaScheme, []int{2})
oidSHA512 = doScheme(shaScheme, []int{3})
)
func hashFromOID(oid asn1.ObjectIdentifier) func() hash.Hash {
switch {
case oid.Equal(oidSHA1):
return sha1.New
case oid.Equal(oidSHA224):
return sha256.New224
case oid.Equal(oidSHA256):
return sha256.New
case oid.Equal(oidSHA384):
return sha512.New384
case oid.Equal(oidSHA512):
return sha512.New
}
return nil
}
func oidFromHash(hash crypto.Hash) (asn1.ObjectIdentifier, bool) {
switch hash {
case crypto.SHA1:
return oidSHA1, true
case crypto.SHA224:
return oidSHA224, true
case crypto.SHA256:
return oidSHA256, true
case crypto.SHA384:
return oidSHA384, true
case crypto.SHA512:
return oidSHA512, true
default:
return nil, false
}
}
var (
asnAlgoSHA1 = asnHashFunction{
Algorithm: oidSHA1,
}
asnAlgoSHA224 = asnHashFunction{
Algorithm: oidSHA224,
}
asnAlgoSHA256 = asnHashFunction{
Algorithm: oidSHA256,
}
asnAlgoSHA384 = asnHashFunction{
Algorithm: oidSHA384,
}
asnAlgoSHA512 = asnHashFunction{
Algorithm: oidSHA512,
}
)
// type ASNasnSubjectPublicKeyInfo struct {
//
// }
//
type asnSubjectPublicKeyInfo struct {
Algorithm asn1.ObjectIdentifier
PublicKey asn1.BitString
Supplements ecpksSupplements `asn1:"optional"`
}
type asnECPKAlgorithms struct {
Type asn1.ObjectIdentifier
}
var idPublicKeyType = doScheme(ansiX962Scheme, []int{2})
var idEcPublicKey = doScheme(idPublicKeyType, []int{1})
var idEcPublicKeySupplemented = doScheme(idPublicKeyType, []int{0})
func curveToRaw(curve elliptic.Curve) (rv asn1.RawValue, ok bool) {
switch curve {
case elliptic.P224(), elliptic.P256(), elliptic.P384(), elliptic.P521():
raw := rawCurve(curve)
return asn1.RawValue{
Tag: 30,
Bytes: raw[2:],
FullBytes: raw,
}, true
default:
return rv, false
}
}
func asnECPublicKeyType(curve elliptic.Curve) (algo asnAlgorithmIdentifier, ok bool) {
raw, ok := curveToRaw(curve)
if !ok {
return
} else {
return asnAlgorithmIdentifier{Algorithm: idEcPublicKey,
Parameters: raw}, true
}
}
type asnECPrivKeyVer int
var asnECPrivKeyVer1 asnECPrivKeyVer = 1
type asnPrivateKey struct {
Version asnECPrivKeyVer
Private []byte
Curve secgNamedCurve `asn1:"optional"`
Public asn1.BitString
}
var asnECDH = doScheme(secgScheme, []int{12})
type asnECDHAlgorithm asnAlgorithmIdentifier
var (
dhSinglePass_stdDH_sha1kdf = asnECDHAlgorithm{
Algorithm: doScheme(x963Scheme, []int{2}),
}
dhSinglePass_stdDH_sha256kdf = asnECDHAlgorithm{
Algorithm: doScheme(secgScheme, []int{11, 1}),
}
dhSinglePass_stdDH_sha384kdf = asnECDHAlgorithm{
Algorithm: doScheme(secgScheme, []int{11, 2}),
}
dhSinglePass_stdDH_sha224kdf = asnECDHAlgorithm{
Algorithm: doScheme(secgScheme, []int{11, 0}),
}
dhSinglePass_stdDH_sha512kdf = asnECDHAlgorithm{
Algorithm: doScheme(secgScheme, []int{11, 3}),
}
)
func (a asnECDHAlgorithm) Cmp(b asnECDHAlgorithm) bool {
if len(a.Algorithm) != len(b.Algorithm) {
return false
}
for i, _ := range a.Algorithm {
if a.Algorithm[i] != b.Algorithm[i] {
return false
}
}
return true
}
// asnNISTConcatenation is the only supported KDF at this time.
type asnKeyDerivationFunction asnAlgorithmIdentifier
var asnNISTConcatenationKDF = asnKeyDerivationFunction{
Algorithm: doScheme(secgScheme, []int{17, 1}),
}
func (a asnKeyDerivationFunction) Cmp(b asnKeyDerivationFunction) bool {
if len(a.Algorithm) != len(b.Algorithm) {
return false
}
for i, _ := range a.Algorithm {
if a.Algorithm[i] != b.Algorithm[i] {
return false
}
}
return true
}
var eciesRecommendedParameters = doScheme(secgScheme, []int{7})
var eciesSpecifiedParameters = doScheme(secgScheme, []int{8})
type asnECIESParameters struct {
KDF asnKeyDerivationFunction `asn1:"optional"`
Sym asnSymmetricEncryption `asn1:"optional"`
MAC asnMessageAuthenticationCode `asn1:"optional"`
}
type asnSymmetricEncryption asnAlgorithmIdentifier
var (
aes128CTRinECIES = asnSymmetricEncryption{
Algorithm: doScheme(secgScheme, []int{21, 0}),
}
aes192CTRinECIES = asnSymmetricEncryption{
Algorithm: doScheme(secgScheme, []int{21, 1}),
}
aes256CTRinECIES = asnSymmetricEncryption{
Algorithm: doScheme(secgScheme, []int{21, 2}),
}
)
func (a asnSymmetricEncryption) Cmp(b asnSymmetricEncryption) bool {
if len(a.Algorithm) != len(b.Algorithm) {
return false
}
for i, _ := range a.Algorithm {
if a.Algorithm[i] != b.Algorithm[i] {
return false
}
}
return true
}
type asnMessageAuthenticationCode asnAlgorithmIdentifier
var (
hmacFull = asnMessageAuthenticationCode{
Algorithm: doScheme(secgScheme, []int{22}),
}
)
func (a asnMessageAuthenticationCode) Cmp(b asnMessageAuthenticationCode) bool {
if len(a.Algorithm) != len(b.Algorithm) {
return false
}
for i, _ := range a.Algorithm {
if a.Algorithm[i] != b.Algorithm[i] {
return false
}
}
return true
}
type ecpksSupplements struct {
ECDomain secgNamedCurve
ECCAlgorithms eccAlgorithmSet
}
type eccAlgorithmSet struct {
ECDH asnECDHAlgorithm `asn1:"optional"`
ECIES asnECIESParameters `asn1:"optional"`
}
func marshalSubjectPublicKeyInfo(pub *PublicKey) (subj asnSubjectPublicKeyInfo, err error) {
subj.Algorithm = idEcPublicKeySupplemented
curve, ok := oidFromNamedCurve(pub.Curve)
if !ok {
err = ErrInvalidPublicKey
return
}
subj.Supplements.ECDomain = curve
if pub.Params != nil {
subj.Supplements.ECCAlgorithms.ECDH = paramsToASNECDH(pub.Params)
subj.Supplements.ECCAlgorithms.ECIES = paramsToASNECIES(pub.Params)
}
pubkey := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
subj.PublicKey = asn1.BitString{
BitLength: len(pubkey) * 8,
Bytes: pubkey,
}
return
}
// Encode a public key to DER format.
func MarshalPublic(pub *PublicKey) ([]byte, error) {
subj, err := marshalSubjectPublicKeyInfo(pub)
if err != nil {
return nil, err
}
return asn1.Marshal(subj)
}
// Decode a DER-encoded public key.
func UnmarshalPublic(in []byte) (pub *PublicKey, err error) {
var subj asnSubjectPublicKeyInfo
if _, err = asn1.Unmarshal(in, &subj); err != nil {
return
}
if !subj.Algorithm.Equal(idEcPublicKeySupplemented) {
err = ErrInvalidPublicKey
return
}
pub = new(PublicKey)
pub.Curve = namedCurveFromOID(subj.Supplements.ECDomain)
x, y := elliptic.Unmarshal(pub.Curve, subj.PublicKey.Bytes)
if x == nil {
err = ErrInvalidPublicKey
return
}
pub.X = x
pub.Y = y
pub.Params = new(ECIESParams)
asnECIEStoParams(subj.Supplements.ECCAlgorithms.ECIES, pub.Params)
asnECDHtoParams(subj.Supplements.ECCAlgorithms.ECDH, pub.Params)
if pub.Params == nil {
if pub.Params = ParamsFromCurve(pub.Curve); pub.Params == nil {
err = ErrInvalidPublicKey
}
}
return
}
func marshalPrivateKey(prv *PrivateKey) (ecprv asnPrivateKey, err error) {
ecprv.Version = asnECPrivKeyVer1
ecprv.Private = prv.D.Bytes()
var ok bool
ecprv.Curve, ok = oidFromNamedCurve(prv.PublicKey.Curve)
if !ok {
err = ErrInvalidPrivateKey
return
}
var pub []byte
if pub, err = MarshalPublic(&prv.PublicKey); err != nil {
return
} else {
ecprv.Public = asn1.BitString{
BitLength: len(pub) * 8,
Bytes: pub,
}
}
return
}
// Encode a private key to DER format.
func MarshalPrivate(prv *PrivateKey) ([]byte, error) {
ecprv, err := marshalPrivateKey(prv)
if err != nil {
return nil, err
}
return asn1.Marshal(ecprv)
}
// Decode a private key from a DER-encoded format.
func UnmarshalPrivate(in []byte) (prv *PrivateKey, err error) {
var ecprv asnPrivateKey
if _, err = asn1.Unmarshal(in, &ecprv); err != nil {
return
} else if ecprv.Version != asnECPrivKeyVer1 {
err = ErrInvalidPrivateKey
return
}
privateCurve := namedCurveFromOID(ecprv.Curve)
if privateCurve == nil {
err = ErrInvalidPrivateKey
return
}
prv = new(PrivateKey)
prv.D = new(big.Int).SetBytes(ecprv.Private)
if pub, err := UnmarshalPublic(ecprv.Public.Bytes); err != nil {
return nil, err
} else {
prv.PublicKey = *pub
}
return
}
// Export a public key to PEM format.
func ExportPublicPEM(pub *PublicKey) (out []byte, err error) {
der, err := MarshalPublic(pub)
if err != nil {
return
}
var block pem.Block
block.Type = "ELLIPTIC CURVE PUBLIC KEY"
block.Bytes = der
buf := new(bytes.Buffer)
err = pem.Encode(buf, &block)
if err != nil {
return
} else {
out = buf.Bytes()
}
return
}
// Export a private key to PEM format.
func ExportPrivatePEM(prv *PrivateKey) (out []byte, err error) {
der, err := MarshalPrivate(prv)
if err != nil {
return
}
var block pem.Block
block.Type = "ELLIPTIC CURVE PRIVATE KEY"
block.Bytes = der
buf := new(bytes.Buffer)
err = pem.Encode(buf, &block)
if err != nil {
return
} else {
out = buf.Bytes()
}
return
}
// Import a PEM-encoded public key.
func ImportPublicPEM(in []byte) (pub *PublicKey, err error) {
p, _ := pem.Decode(in)
if p == nil || p.Type != "ELLIPTIC CURVE PUBLIC KEY" {
return nil, ErrInvalidPublicKey
}
pub, err = UnmarshalPublic(p.Bytes)
return
}
// Import a PEM-encoded private key.
func ImportPrivatePEM(in []byte) (prv *PrivateKey, err error) {
p, _ := pem.Decode(in)
if p == nil || p.Type != "ELLIPTIC CURVE PRIVATE KEY" {
return nil, ErrInvalidPrivateKey
}
prv, err = UnmarshalPrivate(p.Bytes)
return
}

View File

@ -1,326 +0,0 @@
package ecies
import (
"crypto/cipher"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/hmac"
"crypto/subtle"
"fmt"
"hash"
"io"
"math/big"
)
var (
ErrImport = fmt.Errorf("ecies: failed to import key")
ErrInvalidCurve = fmt.Errorf("ecies: invalid elliptic curve")
ErrInvalidParams = fmt.Errorf("ecies: invalid ECIES parameters")
ErrInvalidPublicKey = fmt.Errorf("ecies: invalid public key")
ErrSharedKeyTooBig = fmt.Errorf("ecies: shared key is too big")
)
// PublicKey is a representation of an elliptic curve public key.
type PublicKey struct {
X *big.Int
Y *big.Int
elliptic.Curve
Params *ECIESParams
}
// Export an ECIES public key as an ECDSA public key.
func (pub *PublicKey) ExportECDSA() *ecdsa.PublicKey {
return &ecdsa.PublicKey{pub.Curve, pub.X, pub.Y}
}
// Import an ECDSA public key as an ECIES public key.
func ImportECDSAPublic(pub *ecdsa.PublicKey) *PublicKey {
return &PublicKey{
X: pub.X,
Y: pub.Y,
Curve: pub.Curve,
Params: ParamsFromCurve(pub.Curve),
}
}
// PrivateKey is a representation of an elliptic curve private key.
type PrivateKey struct {
PublicKey
D *big.Int
}
// Export an ECIES private key as an ECDSA private key.
func (prv *PrivateKey) ExportECDSA() *ecdsa.PrivateKey {
pub := &prv.PublicKey
pubECDSA := pub.ExportECDSA()
return &ecdsa.PrivateKey{*pubECDSA, prv.D}
}
// Import an ECDSA private key as an ECIES private key.
func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey {
pub := ImportECDSAPublic(&prv.PublicKey)
return &PrivateKey{*pub, prv.D}
}
// Generate an elliptic curve public / private keypair. If params is nil,
// the recommended default paramters for the key will be chosen.
func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) {
pb, x, y, err := elliptic.GenerateKey(curve, rand)
if err != nil {
return
}
prv = new(PrivateKey)
prv.PublicKey.X = x
prv.PublicKey.Y = y
prv.PublicKey.Curve = curve
prv.D = new(big.Int).SetBytes(pb)
if params == nil {
params = ParamsFromCurve(curve)
}
prv.PublicKey.Params = params
return
}
// MaxSharedKeyLength returns the maximum length of the shared key the
// public key can produce.
func MaxSharedKeyLength(pub *PublicKey) int {
return (pub.Curve.Params().BitSize + 7) / 8
}
// ECDH key agreement method used to establish secret keys for encryption.
func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []byte, err error) {
if prv.PublicKey.Curve != pub.Curve {
err = ErrInvalidCurve
return
}
x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes())
if x == nil || (x.BitLen()+7)/8 < (skLen+macLen) {
err = ErrSharedKeyTooBig
return
}
sk = x.Bytes()[:skLen+macLen]
return
}
var (
ErrKeyDataTooLong = fmt.Errorf("ecies: can't supply requested key data")
ErrSharedTooLong = fmt.Errorf("ecies: shared secret is too long")
ErrInvalidMessage = fmt.Errorf("ecies: invalid message")
)
var (
big2To32 = new(big.Int).Exp(big.NewInt(2), big.NewInt(32), nil)
big2To32M1 = new(big.Int).Sub(big2To32, big.NewInt(1))
)
func incCounter(ctr []byte) {
if ctr[3]++; ctr[3] != 0 {
return
} else if ctr[2]++; ctr[2] != 0 {
return
} else if ctr[1]++; ctr[1] != 0 {
return
} else if ctr[0]++; ctr[0] != 0 {
return
}
return
}
// NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1).
func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) (k []byte, err error) {
if s1 == nil {
s1 = make([]byte, 0)
}
reps := ((kdLen + 7) * 8) / (hash.BlockSize() * 8)
if big.NewInt(int64(reps)).Cmp(big2To32M1) > 0 {
fmt.Println(big2To32M1)
return nil, ErrKeyDataTooLong
}
counter := []byte{0, 0, 0, 1}
k = make([]byte, 0)
for i := 0; i <= reps; i++ {
hash.Write(counter)
hash.Write(z)
hash.Write(s1)
k = append(k, hash.Sum(nil)...)
hash.Reset()
incCounter(counter)
}
k = k[:kdLen]
return
}
// messageTag computes the MAC of a message (called the tag) as per
// SEC 1, 3.5.
func messageTag(hash func() hash.Hash, km, msg, shared []byte) []byte {
if shared == nil {
shared = make([]byte, 0)
}
mac := hmac.New(hash, km)
mac.Write(msg)
tag := mac.Sum(nil)
return tag
}
// Generate an initialisation vector for CTR mode.
func generateIV(params *ECIESParams, rand io.Reader) (iv []byte, err error) {
iv = make([]byte, params.BlockSize)
_, err = io.ReadFull(rand, iv)
return
}
// symEncrypt carries out CTR encryption using the block cipher specified in the
// parameters.
func symEncrypt(rand io.Reader, params *ECIESParams, key, m []byte) (ct []byte, err error) {
c, err := params.Cipher(key)
if err != nil {
return
}
iv, err := generateIV(params, rand)
if err != nil {
return
}
ctr := cipher.NewCTR(c, iv)
ct = make([]byte, len(m)+params.BlockSize)
copy(ct, iv)
ctr.XORKeyStream(ct[params.BlockSize:], m)
return
}
// symDecrypt carries out CTR decryption using the block cipher specified in
// the parameters
func symDecrypt(rand io.Reader, params *ECIESParams, key, ct []byte) (m []byte, err error) {
c, err := params.Cipher(key)
if err != nil {
return
}
ctr := cipher.NewCTR(c, ct[:params.BlockSize])
m = make([]byte, len(ct)-params.BlockSize)
ctr.XORKeyStream(m, ct[params.BlockSize:])
return
}
// Encrypt encrypts a message using ECIES as specified in SEC 1, 5.1. If
// the shared information parameters aren't being used, they should be
// nil.
func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err error) {
params := pub.Params
if params == nil {
if params = ParamsFromCurve(pub.Curve); params == nil {
err = ErrUnsupportedECIESParameters
return
}
}
R, err := GenerateKey(rand, pub.Curve, params)
if err != nil {
return
}
hash := params.Hash()
z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen)
if err != nil {
return
}
K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen)
if err != nil {
return
}
Ke := K[:params.KeyLen]
Km := K[params.KeyLen:]
hash.Write(Km)
Km = hash.Sum(nil)
hash.Reset()
em, err := symEncrypt(rand, params, Ke, m)
if err != nil || len(em) <= params.BlockSize {
return
}
d := messageTag(params.Hash, Km, em, s2)
Rb := elliptic.Marshal(pub.Curve, R.PublicKey.X, R.PublicKey.Y)
ct = make([]byte, len(Rb)+len(em)+len(d))
copy(ct, Rb)
copy(ct[len(Rb):], em)
copy(ct[len(Rb)+len(em):], d)
return
}
// Decrypt decrypts an ECIES ciphertext.
func (prv *PrivateKey) Decrypt(rand io.Reader, c, s1, s2 []byte) (m []byte, err error) {
if c == nil || len(c) == 0 {
err = ErrInvalidMessage
return
}
params := prv.PublicKey.Params
if params == nil {
if params = ParamsFromCurve(prv.PublicKey.Curve); params == nil {
err = ErrUnsupportedECIESParameters
return
}
}
hash := params.Hash()
var (
rLen int
hLen int = hash.Size()
mStart int
mEnd int
)
switch c[0] {
case 2, 3, 4:
rLen = ((prv.PublicKey.Curve.Params().BitSize + 7) / 4)
if len(c) < (rLen + hLen + 1) {
err = ErrInvalidMessage
return
}
default:
err = ErrInvalidPublicKey
return
}
mStart = rLen
mEnd = len(c) - hLen
R := new(PublicKey)
R.Curve = prv.PublicKey.Curve
R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen])
if R.X == nil {
err = ErrInvalidPublicKey
return
}
z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
if err != nil {
return
}
K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen)
if err != nil {
return
}
Ke := K[:params.KeyLen]
Km := K[params.KeyLen:]
hash.Write(Km)
Km = hash.Sum(nil)
hash.Reset()
d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
err = ErrInvalidMessage
return
}
m, err = symDecrypt(rand, params, Ke, c[mStart:mEnd])
return
}

View File

@ -1,489 +0,0 @@
package ecies
import (
"bytes"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"flag"
"fmt"
"io/ioutil"
"testing"
)
var dumpEnc bool
func init() {
flDump := flag.Bool("dump", false, "write encrypted test message to file")
flag.Parse()
dumpEnc = *flDump
}
// Ensure the KDF generates appropriately sized keys.
func TestKDF(t *testing.T) {
msg := []byte("Hello, world")
h := sha256.New()
k, err := concatKDF(h, msg, nil, 64)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
if len(k) != 64 {
fmt.Printf("KDF: generated key is the wrong size (%d instead of 64\n",
len(k))
t.FailNow()
}
}
var skLen int
var ErrBadSharedKeys = fmt.Errorf("ecies: shared keys don't match")
// cmpParams compares a set of ECIES parameters. We assume, as per the
// docs, that AES is the only supported symmetric encryption algorithm.
func cmpParams(p1, p2 *ECIESParams) bool {
if p1.hashAlgo != p2.hashAlgo {
return false
} else if p1.KeyLen != p2.KeyLen {
return false
} else if p1.BlockSize != p2.BlockSize {
return false
}
return true
}
// cmpPublic returns true if the two public keys represent the same pojnt.
func cmpPublic(pub1, pub2 PublicKey) bool {
if pub1.X == nil || pub1.Y == nil {
fmt.Println(ErrInvalidPublicKey.Error())
return false
}
if pub2.X == nil || pub2.Y == nil {
fmt.Println(ErrInvalidPublicKey.Error())
return false
}
pub1Out := elliptic.Marshal(pub1.Curve, pub1.X, pub1.Y)
pub2Out := elliptic.Marshal(pub2.Curve, pub2.X, pub2.Y)
return bytes.Equal(pub1Out, pub2Out)
}
// cmpPrivate returns true if the two private keys are the same.
func cmpPrivate(prv1, prv2 *PrivateKey) bool {
if prv1 == nil || prv1.D == nil {
return false
} else if prv2 == nil || prv2.D == nil {
return false
} else if prv1.D.Cmp(prv2.D) != 0 {
return false
} else {
return cmpPublic(prv1.PublicKey, prv2.PublicKey)
}
}
// Validate the ECDH component.
func TestSharedKey(t *testing.T) {
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
skLen = MaxSharedKeyLength(&prv1.PublicKey) / 2
prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
if !bytes.Equal(sk1, sk2) {
fmt.Println(ErrBadSharedKeys.Error())
t.FailNow()
}
}
// Verify that the key generation code fails when too much key data is
// requested.
func TestTooBigSharedKey(t *testing.T) {
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
_, err = prv1.GenerateShared(&prv2.PublicKey, skLen*2, skLen*2)
if err != ErrSharedKeyTooBig {
fmt.Println("ecdh: shared key should be too large for curve")
t.FailNow()
}
_, err = prv2.GenerateShared(&prv1.PublicKey, skLen*2, skLen*2)
if err != ErrSharedKeyTooBig {
fmt.Println("ecdh: shared key should be too large for curve")
t.FailNow()
}
}
// Ensure a public key can be successfully marshalled and unmarshalled, and
// that the decoded key is the same as the original.
func TestMarshalPublic(t *testing.T) {
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
out, err := MarshalPublic(&prv.PublicKey)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
pub, err := UnmarshalPublic(out)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
if !cmpPublic(prv.PublicKey, *pub) {
fmt.Println("ecies: failed to unmarshal public key")
t.FailNow()
}
}
// Ensure that a private key can be encoded into DER format, and that
// the resulting key is properly parsed back into a public key.
func TestMarshalPrivate(t *testing.T) {
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
out, err := MarshalPrivate(prv)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
if dumpEnc {
ioutil.WriteFile("test.out", out, 0644)
}
prv2, err := UnmarshalPrivate(out)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
if !cmpPrivate(prv, prv2) {
fmt.Println("ecdh: private key import failed")
t.FailNow()
}
}
// Ensure that a private key can be successfully encoded to PEM format, and
// the resulting key is properly parsed back in.
func TestPrivatePEM(t *testing.T) {
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
out, err := ExportPrivatePEM(prv)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
if dumpEnc {
ioutil.WriteFile("test.key", out, 0644)
}
prv2, err := ImportPrivatePEM(out)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
} else if !cmpPrivate(prv, prv2) {
fmt.Println("ecdh: import from PEM failed")
t.FailNow()
}
}
// Ensure that a public key can be successfully encoded to PEM format, and
// the resulting key is properly parsed back in.
func TestPublicPEM(t *testing.T) {
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
out, err := ExportPublicPEM(&prv.PublicKey)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
if dumpEnc {
ioutil.WriteFile("test.pem", out, 0644)
}
pub2, err := ImportPublicPEM(out)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
} else if !cmpPublic(prv.PublicKey, *pub2) {
fmt.Println("ecdh: import from PEM failed")
t.FailNow()
}
}
// Benchmark the generation of P256 keys.
func BenchmarkGenerateKeyP256(b *testing.B) {
for i := 0; i < b.N; i++ {
if _, err := GenerateKey(rand.Reader, elliptic.P256(), nil); err != nil {
fmt.Println(err.Error())
b.FailNow()
}
}
}
// Benchmark the generation of P256 shared keys.
func BenchmarkGenSharedKeyP256(b *testing.B) {
prv, err := GenerateKey(rand.Reader, elliptic.P256(), nil)
if err != nil {
fmt.Println(err.Error())
b.FailNow()
}
for i := 0; i < b.N; i++ {
_, err := prv.GenerateShared(&prv.PublicKey, skLen, skLen)
if err != nil {
fmt.Println(err.Error())
b.FailNow()
}
}
}
// Verify that an encrypted message can be successfully decrypted.
func TestEncryptDecrypt(t *testing.T) {
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
message := []byte("Hello, world.")
ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
if !bytes.Equal(pt, message) {
fmt.Println("ecies: plaintext doesn't match message")
t.FailNow()
}
_, err = prv1.Decrypt(rand.Reader, ct, nil, nil)
if err == nil {
fmt.Println("ecies: encryption should not have succeeded")
t.FailNow()
}
}
// TestMarshalEncryption validates the encode/decode produces a valid
// ECIES encryption key.
func TestMarshalEncryption(t *testing.T) {
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
out, err := MarshalPrivate(prv1)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
prv2, err := UnmarshalPrivate(out)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
message := []byte("Hello, world.")
ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
if !bytes.Equal(pt, message) {
fmt.Println("ecies: plaintext doesn't match message")
t.FailNow()
}
_, err = prv1.Decrypt(rand.Reader, ct, nil, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
}
type testCase struct {
Curve elliptic.Curve
Name string
Expected bool
}
var testCases = []testCase{
testCase{
Curve: elliptic.P224(),
Name: "P224",
Expected: false,
},
testCase{
Curve: elliptic.P256(),
Name: "P256",
Expected: true,
},
testCase{
Curve: elliptic.P384(),
Name: "P384",
Expected: true,
},
testCase{
Curve: elliptic.P521(),
Name: "P521",
Expected: true,
},
}
// Test parameter selection for each curve, and that P224 fails automatic
// parameter selection (see README for a discussion of P224). Ensures that
// selecting a set of parameters automatically for the given curve works.
func TestParamSelection(t *testing.T) {
for _, c := range testCases {
testParamSelection(t, c)
}
}
func testParamSelection(t *testing.T, c testCase) {
params := ParamsFromCurve(c.Curve)
if params == nil && c.Expected {
fmt.Printf("%s (%s)\n", ErrInvalidParams.Error(), c.Name)
t.FailNow()
} else if params != nil && !c.Expected {
fmt.Printf("ecies: parameters should be invalid (%s)\n",
c.Name)
t.FailNow()
}
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Printf("%s (%s)\n", err.Error(), c.Name)
t.FailNow()
}
prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Printf("%s (%s)\n", err.Error(), c.Name)
t.FailNow()
}
message := []byte("Hello, world.")
ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil)
if err != nil {
fmt.Printf("%s (%s)\n", err.Error(), c.Name)
t.FailNow()
}
pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil)
if err != nil {
fmt.Printf("%s (%s)\n", err.Error(), c.Name)
t.FailNow()
}
if !bytes.Equal(pt, message) {
fmt.Printf("ecies: plaintext doesn't match message (%s)\n",
c.Name)
t.FailNow()
}
_, err = prv1.Decrypt(rand.Reader, ct, nil, nil)
if err == nil {
fmt.Printf("ecies: encryption should not have succeeded (%s)\n",
c.Name)
t.FailNow()
}
}
// Ensure that the basic public key validation in the decryption operation
// works.
func TestBasicKeyValidation(t *testing.T) {
badBytes := []byte{0, 1, 5, 6, 7, 8, 9}
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
message := []byte("Hello, world.")
ct, err := Encrypt(rand.Reader, &prv.PublicKey, message, nil, nil)
if err != nil {
fmt.Println(err.Error())
t.FailNow()
}
for _, b := range badBytes {
ct[0] = b
_, err := prv.Decrypt(rand.Reader, ct, nil, nil)
if err != ErrInvalidPublicKey {
fmt.Println("ecies: validated an invalid key")
t.FailNow()
}
}
}

View File

@ -1,187 +0,0 @@
package ecies
// This file contains parameters for ECIES encryption, specifying the
// symmetric encryption and HMAC parameters.
import (
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/elliptic"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
)
// The default curve for this package is the NIST P256 curve, which
// provides security equivalent to AES-128.
var DefaultCurve = elliptic.P256()
var (
ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm")
ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters")
)
type ECIESParams struct {
Hash func() hash.Hash // hash function
hashAlgo crypto.Hash
Cipher func([]byte) (cipher.Block, error) // symmetric cipher
BlockSize int // block size of symmetric cipher
KeyLen int // length of symmetric key
}
// Standard ECIES parameters:
// * ECIES using AES128 and HMAC-SHA-256-16
// * ECIES using AES256 and HMAC-SHA-256-32
// * ECIES using AES256 and HMAC-SHA-384-48
// * ECIES using AES256 and HMAC-SHA-512-64
var (
ECIES_AES128_SHA256 *ECIESParams
ECIES_AES256_SHA256 *ECIESParams
ECIES_AES256_SHA384 *ECIESParams
ECIES_AES256_SHA512 *ECIESParams
)
func init() {
ECIES_AES128_SHA256 = &ECIESParams{
Hash: sha256.New,
hashAlgo: crypto.SHA256,
Cipher: aes.NewCipher,
BlockSize: aes.BlockSize,
KeyLen: 16,
}
ECIES_AES256_SHA256 = &ECIESParams{
Hash: sha256.New,
hashAlgo: crypto.SHA256,
Cipher: aes.NewCipher,
BlockSize: aes.BlockSize,
KeyLen: 32,
}
ECIES_AES256_SHA384 = &ECIESParams{
Hash: sha512.New384,
hashAlgo: crypto.SHA384,
Cipher: aes.NewCipher,
BlockSize: aes.BlockSize,
KeyLen: 32,
}
ECIES_AES256_SHA512 = &ECIESParams{
Hash: sha512.New,
hashAlgo: crypto.SHA512,
Cipher: aes.NewCipher,
BlockSize: aes.BlockSize,
KeyLen: 32,
}
}
var paramsFromCurve = map[elliptic.Curve]*ECIESParams{
elliptic.P256(): ECIES_AES128_SHA256,
elliptic.P384(): ECIES_AES256_SHA384,
elliptic.P521(): ECIES_AES256_SHA512,
}
func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) {
paramsFromCurve[curve] = params
}
// ParamsFromCurve selects parameters optimal for the selected elliptic curve.
// Only the curves P256, P384, and P512 are supported.
func ParamsFromCurve(curve elliptic.Curve) (params *ECIESParams) {
return paramsFromCurve[curve]
/*
switch curve {
case elliptic.P256():
return ECIES_AES128_SHA256
case elliptic.P384():
return ECIES_AES256_SHA384
case elliptic.P521():
return ECIES_AES256_SHA512
default:
return nil
}
*/
}
// ASN.1 encode the ECIES parameters relevant to the encryption operations.
func paramsToASNECIES(params *ECIESParams) (asnParams asnECIESParameters) {
if nil == params {
return
}
asnParams.KDF = asnNISTConcatenationKDF
asnParams.MAC = hmacFull
switch params.KeyLen {
case 16:
asnParams.Sym = aes128CTRinECIES
case 24:
asnParams.Sym = aes192CTRinECIES
case 32:
asnParams.Sym = aes256CTRinECIES
}
return
}
// ASN.1 encode the ECIES parameters relevant to ECDH.
func paramsToASNECDH(params *ECIESParams) (algo asnECDHAlgorithm) {
switch params.hashAlgo {
case crypto.SHA224:
algo = dhSinglePass_stdDH_sha224kdf
case crypto.SHA256:
algo = dhSinglePass_stdDH_sha256kdf
case crypto.SHA384:
algo = dhSinglePass_stdDH_sha384kdf
case crypto.SHA512:
algo = dhSinglePass_stdDH_sha512kdf
}
return
}
// ASN.1 decode the ECIES parameters relevant to the encryption stage.
func asnECIEStoParams(asnParams asnECIESParameters, params *ECIESParams) {
if !asnParams.KDF.Cmp(asnNISTConcatenationKDF) {
params = nil
return
} else if !asnParams.MAC.Cmp(hmacFull) {
params = nil
return
}
switch {
case asnParams.Sym.Cmp(aes128CTRinECIES):
params.KeyLen = 16
params.BlockSize = 16
params.Cipher = aes.NewCipher
case asnParams.Sym.Cmp(aes192CTRinECIES):
params.KeyLen = 24
params.BlockSize = 16
params.Cipher = aes.NewCipher
case asnParams.Sym.Cmp(aes256CTRinECIES):
params.KeyLen = 32
params.BlockSize = 16
params.Cipher = aes.NewCipher
default:
params = nil
}
}
// ASN.1 decode the ECIES parameters relevant to ECDH.
func asnECDHtoParams(asnParams asnECDHAlgorithm, params *ECIESParams) {
if asnParams.Cmp(dhSinglePass_stdDH_sha224kdf) {
params.hashAlgo = crypto.SHA224
params.Hash = sha256.New224
} else if asnParams.Cmp(dhSinglePass_stdDH_sha256kdf) {
params.hashAlgo = crypto.SHA256
params.Hash = sha256.New
} else if asnParams.Cmp(dhSinglePass_stdDH_sha384kdf) {
params.hashAlgo = crypto.SHA384
params.Hash = sha512.New384
} else if asnParams.Cmp(dhSinglePass_stdDH_sha512kdf) {
params.hashAlgo = crypto.SHA512
params.Hash = sha512.New
} else {
params = nil
}
}

View File

@ -64,6 +64,20 @@ func Dial(url_, protocol, origin string) (ws *Conn, err error) {
return DialConfig(config) return DialConfig(config)
} }
var portMap = map[string]string{
"ws": "80",
"wss": "443",
}
func parseAuthority(location *url.URL) string {
if _, ok := portMap[location.Scheme]; ok {
if _, _, err := net.SplitHostPort(location.Host); err != nil {
return net.JoinHostPort(location.Host, portMap[location.Scheme])
}
}
return location.Host
}
// DialConfig opens a new client connection to a WebSocket with a config. // DialConfig opens a new client connection to a WebSocket with a config.
func DialConfig(config *Config) (ws *Conn, err error) { func DialConfig(config *Config) (ws *Conn, err error) {
var client net.Conn var client net.Conn
@ -75,10 +89,10 @@ func DialConfig(config *Config) (ws *Conn, err error) {
} }
switch config.Location.Scheme { switch config.Location.Scheme {
case "ws": case "ws":
client, err = net.Dial("tcp", config.Location.Host) client, err = net.Dial("tcp", parseAuthority(config.Location))
case "wss": case "wss":
client, err = tls.Dial("tcp", config.Location.Host, config.TlsConfig) client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig)
default: default:
err = ErrBadScheme err = ErrBadScheme

View File

@ -8,7 +8,7 @@ import (
"fmt" "fmt"
"log" "log"
"code.google.com/p/go.net/websocket" "golang.org/x/net/websocket"
) )
// This example demonstrates a trivial client. // This example demonstrates a trivial client.

View File

@ -8,7 +8,7 @@ import (
"io" "io"
"net/http" "net/http"
"code.google.com/p/go.net/websocket" "golang.org/x/net/websocket"
) )
// Echo the data received on the WebSocket. // Echo the data received on the WebSocket.

View File

@ -339,3 +339,76 @@ func TestSmallBuffer(t *testing.T) {
} }
conn.Close() conn.Close()
} }
var parseAuthorityTests = []struct {
in *url.URL
out string
}{
{
&url.URL{
Scheme: "ws",
Host: "www.google.com",
},
"www.google.com:80",
},
{
&url.URL{
Scheme: "wss",
Host: "www.google.com",
},
"www.google.com:443",
},
{
&url.URL{
Scheme: "ws",
Host: "www.google.com:80",
},
"www.google.com:80",
},
{
&url.URL{
Scheme: "wss",
Host: "www.google.com:443",
},
"www.google.com:443",
},
// some invalid ones for parseAuthority. parseAuthority doesn't
// concern itself with the scheme unless it actually knows about it
{
&url.URL{
Scheme: "http",
Host: "www.google.com",
},
"www.google.com",
},
{
&url.URL{
Scheme: "http",
Host: "www.google.com:80",
},
"www.google.com:80",
},
{
&url.URL{
Scheme: "asdf",
Host: "127.0.0.1",
},
"127.0.0.1",
},
{
&url.URL{
Scheme: "asdf",
Host: "www.google.com",
},
"www.google.com",
},
}
func TestParseAuthority(t *testing.T) {
for _, tt := range parseAuthorityTests {
out := parseAuthority(tt.in)
if out != tt.out {
t.Errorf("got %v; want %v", out, tt.out)
}
}
}

View File

@ -2,10 +2,10 @@
Ethereum Go Client © 2014 Jeffrey Wilcke. Ethereum Go Client © 2014 Jeffrey Wilcke.
| Linux | OSX | Windows | Linux | OSX | Windows | Tests
----------|---------|-----|-------- ----------|---------|-----|---------|------
develop | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](http://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](http://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | N/A develop | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | N/A | [![Buildr+Status](https://travis-ci.org/ethereum/go-ethereum.svg?branch=develop)](https://travis-ci.org/ethereum/go-ethereum)
master | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](http://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=OSX%20Go%20master%20branch)](http://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | N/A master | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20Go%20master%20branch)](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | N/A | [![Buildr+Status](https://travis-ci.org/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
[![Bugs](https://badge.waffle.io/ethereum/go-ethereum.png?label=bug&title=Bugs)](https://waffle.io/ethereum/go-ethereum) [![Bugs](https://badge.waffle.io/ethereum/go-ethereum.png?label=bug&title=Bugs)](https://waffle.io/ethereum/go-ethereum)
[![Stories in Ready](https://badge.waffle.io/ethereum/go-ethereum.png?label=ready&title=Ready)](https://waffle.io/ethereum/go-ethereum) [![Stories in Ready](https://badge.waffle.io/ethereum/go-ethereum.png?label=ready&title=Ready)](https://waffle.io/ethereum/go-ethereum)

View File

@ -1,8 +1,10 @@
package accounts package accounts
import ( import (
"github.com/ethereum/go-ethereum/crypto"
"testing" "testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/randentropy"
) )
func TestAccountManager(t *testing.T) { func TestAccountManager(t *testing.T) {
@ -10,7 +12,7 @@ func TestAccountManager(t *testing.T) {
am := NewAccountManager(ks) am := NewAccountManager(ks)
pass := "" // not used but required by API pass := "" // not used but required by API
a1, err := am.NewAccount(pass) a1, err := am.NewAccount(pass)
toSign := crypto.GetEntropyCSPRNG(32) toSign := randentropy.GetEntropyCSPRNG(32)
_, err = am.Sign(a1, pass, toSign) _, err = am.Sign(a1, pass, toSign)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View File

@ -132,7 +132,7 @@ func Init() {
natstr = flag.String("nat", "any", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)") natstr = flag.String("nat", "any", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)")
) )
flag.BoolVar(&Dial, "dial", true, "dial out connections (default on)") flag.BoolVar(&Dial, "dial", true, "dial out connections (default on)")
flag.BoolVar(&SHH, "shh", true, "run whisper protocol (default on)") //flag.BoolVar(&SHH, "shh", true, "run whisper protocol (default on)")
flag.StringVar(&OutboundPort, "port", "30303", "listening port") flag.StringVar(&OutboundPort, "port", "30303", "listening port")
flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap") flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap")

View File

@ -37,7 +37,7 @@ import (
const ( const (
ClientIdentifier = "Ethereum(G)" ClientIdentifier = "Ethereum(G)"
Version = "0.8.3" Version = "0.8.5"
) )
var clilogger = logger.NewLogger("CLI") var clilogger = logger.NewLogger("CLI")
@ -67,11 +67,12 @@ func main() {
DataDir: Datadir, DataDir: Datadir,
LogFile: LogFile, LogFile: LogFile,
LogLevel: LogLevel, LogLevel: LogLevel,
LogFormat: LogFormat,
MaxPeers: MaxPeer, MaxPeers: MaxPeer,
Port: OutboundPort, Port: OutboundPort,
NAT: NAT, NAT: NAT,
KeyRing: KeyRing, KeyRing: KeyRing,
Shh: SHH, Shh: true,
Dial: Dial, Dial: Dial,
BootNodes: BootNodes, BootNodes: BootNodes,
NodeKey: NodeKey, NodeKey: NodeKey,

View File

@ -51,8 +51,8 @@ func StateObjectFromAccount(db ethutil.Database, addr string, account Account) *
if ethutil.IsHex(account.Code) { if ethutil.IsHex(account.Code) {
account.Code = account.Code[2:] account.Code = account.Code[2:]
} }
obj.Code = ethutil.Hex2Bytes(account.Code) obj.SetCode(ethutil.Hex2Bytes(account.Code))
obj.Nonce = ethutil.Big(account.Nonce).Uint64() obj.SetNonce(ethutil.Big(account.Nonce).Uint64())
return obj return obj
} }

View File

@ -0,0 +1,22 @@
<html>
<head>
<script src="../ext/bignumber.min.js"></script>
<script src="../ext/ethereum.js/dist/ethereum.js"></script>
<script>
var web3 = require('web3');
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8545'));
var eth = web3.eth;
function bomb() {
for (var i = 0; i < 200; i++) {
eth.transact({})
}
}
</script>
</head>
<body>
<button onclick="bomb();">BOOM!</button>
</body>
</html>

View File

@ -14,8 +14,9 @@
</div> </div>
<div> <div>
<span class="amount">Amount:</span> <span>Address:</span>
<input type="text" id="address" style="width:200px"> <input type="text" id="address" style="width:200px">
<span>Amount:</span>
<input type="text" id="amount" style="width:200px"> <input type="text" id="amount" style="width:200px">
<button onclick="transact()">Send</button> <button onclick="transact()">Send</button>
</div> </div>
@ -58,7 +59,7 @@
}], }],
"outputs": [] "outputs": []
}, { }, {
"name":"received", "name":"Changed",
"type":"event", "type":"event",
"inputs": [ "inputs": [
{"name":"from","type":"address","indexed":true}, {"name":"from","type":"address","indexed":true},
@ -69,23 +70,14 @@
var address = localStorage.getItem("address"); var address = localStorage.getItem("address");
// deploy if not exist // deploy if not exist
if (address == null) { if (address == null) {
var code = "0x60056013565b61012b806100346000396000f35b6103e8600033600160a060020a0316600052602052604060002081905550560060e060020a6000350480637bb98a681461002b578063d0679d3414610039578063e3d670d71461004d57005b610033610126565b60006000f35b610047600435602435610062565b60006000f35b610058600435610104565b8060005260206000f35b80600033600160a060020a0316600052602052604060002054101561008657610100565b80600033600160a060020a0316600052602052604060002090815403908190555080600083600160a060020a0316600052602052604060002090815401908190555033600160a060020a0316600052806020527ff11e547d796cc64acdf758e7cee90439494fd886a19159454aa61e473fdbafef60406000a15b5050565b6000600082600160a060020a03166000526020526040600020549050919050565b5b60008156"; var code = "0x60056013565b61014f8061003a6000396000f35b620f42406000600033600160a060020a0316815260200190815260200160002081905550560060e060020a600035048063d0679d3414610020578063e3d670d71461003457005b61002e600435602435610049565b60006000f35b61003f600435610129565b8060005260206000f35b806000600033600160a060020a03168152602001908152602001600020541061007157610076565b610125565b806000600033600160a060020a03168152602001908152602001600020908154039081905550806000600084600160a060020a031681526020019081526020016000209081540190819055508033600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a38082600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a35b5050565b60006000600083600160a060020a0316815260200190815260200160002054905091905056";
address = web3.eth.transact({data: code}); address = web3.eth.transact({data: code});
localStorage.setItem("address", address); localStorage.setItem("address", address);
} }
document.querySelector("#contract_addr").innerHTML = address.toUpperCase(); document.querySelector("#contract_addr").innerHTML = address;
var contract = web3.eth.contract(address, desc); var contract = web3.eth.contract(address, desc);
contract.received({from: eth.coinbase}).changed(function() { contract.Changed({from: eth.coinbase}).changed(function() {
refresh();
});
var ev = contract.SingleTransact({})
ev.watch(function(log) {
someElement.innerHTML += "tnaheousnthaoeu";
});
eth.watch('chain').changed(function() {
refresh(); refresh();
}); });
@ -98,7 +90,7 @@
var storage = eth.storageAt(address); var storage = eth.storageAt(address);
table.innerHTML = ""; table.innerHTML = "";
for( var item in storage ) { for( var item in storage ) {
table.innerHTML += "<tr><td>"+item.toUpperCase()+"</td><td>"+web3.toDecimal(storage[item])+"</td></tr>"; table.innerHTML += "<tr><td>"+item+"</td><td>"+web3.toDecimal(storage[item])+"</td></tr>";
} }
} }
@ -111,6 +103,7 @@
} }
var value = parseInt( document.querySelector("#amount").value ); var value = parseInt( document.querySelector("#amount").value );
console.log("transact: ", to, " => ", value)
contract.send( to, value ); contract.send( to, value );
} }
@ -126,7 +119,7 @@ contract JevCoin {
balances[msg.sender] = 1000000; balances[msg.sender] = 1000000;
} }
event changed(address indexed from, address indexed to); event Changed(address indexed from, uint indexed amount);
function send(address to, uint value) function send(address to, uint value)
{ {
if( balances[msg.sender] < value ) return; if( balances[msg.sender] < value ) return;
@ -134,7 +127,8 @@ contract JevCoin {
balances[msg.sender] -= value; balances[msg.sender] -= value;
balances[to] += value; balances[to] += value;
changed(msg.sender, to); Changed(msg.sender, value);
Changed(to, value);
} }
function balance(address who) constant returns(uint t) function balance(address who) constant returns(uint t)

View File

@ -71,6 +71,10 @@
document.querySelector("#gas_price").innerHTML = eth.gasPrice; document.querySelector("#gas_price").innerHTML = eth.gasPrice;
document.querySelector("#mining").innerHTML = eth.mining; document.querySelector("#mining").innerHTML = eth.mining;
document.querySelector("#listening").innerHTML = eth.listening; document.querySelector("#listening").innerHTML = eth.listening;
eth.watch('chain').changed(function() {
document.querySelector("#number").innerHTML = eth.number;
});
</script> </script>
</html> </html>

View File

@ -1417,6 +1417,8 @@ var ethMethods = function () {
var methods = [ var methods = [
{ name: 'balanceAt', call: 'eth_balanceAt' }, { name: 'balanceAt', call: 'eth_balanceAt' },
{ name: 'register', call: 'eth_register' },
{ name: 'unregister', call: 'eth_unregister' },
{ name: 'stateAt', call: 'eth_stateAt' }, { name: 'stateAt', call: 'eth_stateAt' },
{ name: 'storageAt', call: 'eth_storageAt' }, { name: 'storageAt', call: 'eth_storageAt' },
{ name: 'countAt', call: 'eth_countAt'}, { name: 'countAt', call: 'eth_countAt'},

View File

@ -256,6 +256,7 @@ ApplicationWindow {
} }
} }
} }
} }
property var blockModel: ListModel { property var blockModel: ListModel {
@ -895,7 +896,7 @@ ApplicationWindow {
} }
function setWalletValue(value) { function setWalletValue(value) {
walletValueLabel.text = value //walletValueLabel.text = value
} }
function loadPlugin(name) { function loadPlugin(name) {
@ -947,7 +948,8 @@ ApplicationWindow {
model: peerModel model: peerModel
TableViewColumn{width: 180; role: "addr" ; title: "Remote Address" } TableViewColumn{width: 180; role: "addr" ; title: "Remote Address" }
TableViewColumn{width: 280; role: "nodeID" ; title: "Node ID" } TableViewColumn{width: 280; role: "nodeID" ; title: "Node ID" }
TableViewColumn{width: 180; role: "caps" ; title: "Capabilities" } TableViewColumn{width: 100; role: "name" ; title: "Name" }
TableViewColumn{width: 40; role: "caps" ; title: "Capabilities" }
} }
} }
} }
@ -978,7 +980,7 @@ ApplicationWindow {
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 30 anchors.topMargin: 30
font.pointSize: 12 font.pointSize: 12
text: "<h2>Mist (0.7.10)</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br>Felix Lange<br>Taylor Gerring<br>Daniel Nagy<br><h3>UX</h3>Alex van de Sande<br>" text: "<h2>Mist (0.8.5)</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br>Felix Lange<br>Taylor Gerring<br>Daniel Nagy<br>Gustav Simonsson<br><h3>UX/UI</h3>Alex van de Sande<br>Fabian Vogelsteller"
} }
} }

View File

@ -41,4 +41,13 @@ Rectangle {
pendingTxModel.insert(0, {hash: tx.hash, to: tx.address, from: tx.sender, value: tx.value, contract: isContract}) pendingTxModel.insert(0, {hash: tx.hash, to: tx.address, from: tx.sender, value: tx.value, contract: isContract})
} }
function removeTx(tx) {
for (var i = 0; i < pendingTxModel.count; i++) {
if (tx.hash === pendingTxModel.get(i).hash) {
pendingTxModel.remove(i);
break;
}
}
}
} }

View File

@ -63,6 +63,7 @@ var (
DebugFile string DebugFile string
LogLevel int LogLevel int
VmType int VmType int
MinerThreads int
) )
// flags specific to gui client // flags specific to gui client
@ -137,6 +138,8 @@ func Init() {
flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap") flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap")
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers") flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
flag.IntVar(&MinerThreads, "minerthreads", runtime.NumCPU(), "number of miner threads")
flag.Parse() flag.Parse()
var err error var err error

View File

@ -131,6 +131,7 @@ func (gui *Gui) Start(assetPath string) {
context.SetVar("gui", gui) context.SetVar("gui", gui)
context.SetVar("eth", gui.uiLib) context.SetVar("eth", gui.uiLib)
context.SetVar("shh", gui.whisper) context.SetVar("shh", gui.whisper)
//clipboard.SetQMLClipboard(context)
win, err := gui.showWallet(context) win, err := gui.showWallet(context)
if err != nil { if err != nil {
@ -386,16 +387,11 @@ func (gui *Gui) update() {
generalUpdateTicker := time.NewTicker(500 * time.Millisecond) generalUpdateTicker := time.NewTicker(500 * time.Millisecond)
statsUpdateTicker := time.NewTicker(5 * time.Second) statsUpdateTicker := time.NewTicker(5 * time.Second)
state := gui.eth.ChainManager().TransState()
gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.address()).Balance())))
lastBlockLabel := gui.getObjectByName("lastBlockLabel") lastBlockLabel := gui.getObjectByName("lastBlockLabel")
miningLabel := gui.getObjectByName("miningLabel") miningLabel := gui.getObjectByName("miningLabel")
events := gui.eth.EventMux().Subscribe( events := gui.eth.EventMux().Subscribe(
//eth.PeerListEvent{}, core.ChainEvent{},
core.NewBlockEvent{},
core.TxPreEvent{}, core.TxPreEvent{},
core.TxPostEvent{}, core.TxPostEvent{},
) )
@ -408,41 +404,13 @@ func (gui *Gui) update() {
return return
} }
switch ev := ev.(type) { switch ev := ev.(type) {
case core.NewBlockEvent: case core.ChainEvent:
gui.processBlock(ev.Block, false) gui.processBlock(ev.Block, false)
//gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
balance := ethutil.CurrencyToString(gui.eth.ChainManager().State().GetBalance(gui.address()))
gui.getObjectByName("balanceLabel").Set("text", fmt.Sprintf("%v", balance))
case core.TxPreEvent: case core.TxPreEvent:
tx := ev.Tx gui.insertTransaction("pre", ev.Tx)
tstate := gui.eth.ChainManager().TransState()
cstate := gui.eth.ChainManager().State()
taccount := tstate.GetAccount(gui.address())
caccount := cstate.GetAccount(gui.address())
unconfirmedFunds := new(big.Int).Sub(taccount.Balance(), caccount.Balance())
gui.setWalletValue(taccount.Balance(), unconfirmedFunds)
gui.insertTransaction("pre", tx)
case core.TxPostEvent: case core.TxPostEvent:
tx := ev.Tx gui.getObjectByName("pendingTxView").Call("removeTx", xeth.NewTx(ev.Tx))
object := state.GetAccount(gui.address())
if bytes.Compare(tx.From(), gui.address()) == 0 {
object.SubAmount(tx.Value())
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
} else if bytes.Compare(tx.To(), gui.address()) == 0 {
object.AddAmount(tx.Value())
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
}
gui.setWalletValue(object.Balance(), nil)
state.UpdateStateObject(object)
} }
case <-peerUpdateTicker.C: case <-peerUpdateTicker.C:
@ -453,19 +421,6 @@ func (gui *Gui) update() {
lastBlockLabel.Set("text", statusText) lastBlockLabel.Set("text", statusText)
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.Miner().HashRate(), 10)+"/Khash") miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.Miner().HashRate(), 10)+"/Khash")
/*
blockLength := gui.eth.BlockPool().BlocksProcessed
chainLength := gui.eth.BlockPool().ChainLength
var (
pct float64 = 1.0 / float64(chainLength) * float64(blockLength)
dlWidget = gui.win.Root().ObjectByName("downloadIndicator")
dlLabel = gui.win.Root().ObjectByName("downloadLabel")
)
dlWidget.Set("value", pct)
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
*/
case <-statsUpdateTicker.C: case <-statsUpdateTicker.C:
gui.setStatsPane() gui.setStatsPane()
} }
@ -498,7 +453,7 @@ NumGC: %d
)) ))
} }
type qmlpeer struct{ Addr, NodeID, Caps string } type qmlpeer struct{ Addr, NodeID, Name, Caps string }
type peersByID []*qmlpeer type peersByID []*qmlpeer
@ -513,6 +468,7 @@ func (gui *Gui) setPeerInfo() {
qpeers[i] = &qmlpeer{ qpeers[i] = &qmlpeer{
NodeID: p.ID().String(), NodeID: p.ID().String(),
Addr: p.RemoteAddr().String(), Addr: p.RemoteAddr().String(),
Name: p.Name(),
Caps: fmt.Sprint(p.Caps()), Caps: fmt.Sprint(p.Caps()),
} }
} }

View File

@ -36,7 +36,7 @@ import (
const ( const (
ClientIdentifier = "Mist" ClientIdentifier = "Mist"
Version = "0.8.3" Version = "0.8.5"
) )
var ethereum *eth.Ethereum var ethereum *eth.Ethereum
@ -52,18 +52,20 @@ func run() error {
config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
ethereum, err := eth.New(&eth.Config{ ethereum, err := eth.New(&eth.Config{
Name: p2p.MakeName(ClientIdentifier, Version), Name: p2p.MakeName(ClientIdentifier, Version),
KeyStore: KeyStore, KeyStore: KeyStore,
DataDir: Datadir, DataDir: Datadir,
LogFile: LogFile, LogFile: LogFile,
LogLevel: LogLevel, LogLevel: LogLevel,
MaxPeers: MaxPeer, MaxPeers: MaxPeer,
Port: OutboundPort, Port: OutboundPort,
NAT: NAT, NAT: NAT,
BootNodes: BootNodes, Shh: true,
NodeKey: NodeKey, BootNodes: BootNodes,
KeyRing: KeyRing, NodeKey: NodeKey,
Dial: true, KeyRing: KeyRing,
Dial: true,
MinerThreads: MinerThreads,
}) })
if err != nil { if err != nil {
mainlogger.Fatalln(err) mainlogger.Fatalln(err)

View File

@ -146,8 +146,8 @@ func (ui *UiLib) AssetPath(p string) string {
func (self *UiLib) StartDbWithContractAndData(contractHash, data string) { func (self *UiLib) StartDbWithContractAndData(contractHash, data string) {
dbWindow := NewDebuggerWindow(self) dbWindow := NewDebuggerWindow(self)
object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash)) object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash))
if len(object.Code) > 0 { if len(object.Code()) > 0 {
dbWindow.SetCode(ethutil.Bytes2Hex(object.Code)) dbWindow.SetCode(ethutil.Bytes2Hex(object.Code()))
} }
dbWindow.SetData(data) dbWindow.SetData(data)

View File

@ -225,7 +225,7 @@ func StartMining(ethereum *eth.Ethereum) bool {
go func() { go func() {
clilogger.Infoln("Start mining") clilogger.Infoln("Start mining")
if gminer == nil { if gminer == nil {
gminer = miner.New(addr, ethereum) gminer = miner.New(addr, ethereum, 4)
} }
gminer.Start() gminer.Start()
}() }()
@ -272,7 +272,7 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
parent := ethereum.ChainManager().GetBlock(block.ParentHash()) parent := ethereum.ChainManager().GetBlock(block.ParentHash())
statedb := state.New(parent.Root(), ethereum.Db()) statedb := state.New(parent.Root(), ethereum.Db())
_, err := ethereum.BlockProcessor().TransitionState(statedb, parent, block) _, err := ethereum.BlockProcessor().TransitionState(statedb, parent, block, true)
if err != nil { if err != nil {
return err return err
} }

View File

@ -60,12 +60,12 @@ func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainM
return sm return sm
} }
func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) { func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase) coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
coinbase.SetGasPool(CalcGasLimit(parent, block)) coinbase.SetGasPool(CalcGasLimit(parent, block))
// Process the transactions on to parent state // Process the transactions on to parent state
receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false) receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -73,38 +73,40 @@ func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block
return receipts, nil return receipts, nil
} }
func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, state *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) { func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
// If we are mining this block and validating we want to set the logs back to 0 // If we are mining this block and validating we want to set the logs back to 0
state.EmptyLogs() statedb.EmptyLogs()
txGas := new(big.Int).Set(tx.Gas()) txGas := new(big.Int).Set(tx.Gas())
cb := state.GetStateObject(coinbase.Address()) cb := statedb.GetStateObject(coinbase.Address())
st := NewStateTransition(NewEnv(state, self.bc, tx, block), tx, cb) st := NewStateTransition(NewEnv(statedb, self.bc, tx, block), tx, cb)
_, err := st.TransitionState() _, err := st.TransitionState()
if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err)) {
return nil, nil, err
}
txGas.Sub(txGas, st.gas) txGas.Sub(txGas, st.gas)
// Update the state with pending changes // Update the state with pending changes
state.Update(txGas) statedb.Update(txGas)
cumulative := new(big.Int).Set(usedGas.Add(usedGas, txGas)) cumulative := new(big.Int).Set(usedGas.Add(usedGas, txGas))
receipt := types.NewReceipt(state.Root(), cumulative) receipt := types.NewReceipt(statedb.Root(), cumulative)
receipt.SetLogs(state.Logs()) receipt.SetLogs(statedb.Logs())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
chainlogger.Debugln(receipt) chainlogger.Debugln(receipt)
// Notify all subscribers // Notify all subscribers
if !transientProcess { if !transientProcess {
go self.eventMux.Post(TxPostEvent{tx}) go self.eventMux.Post(TxPostEvent{tx})
go self.eventMux.Post(statedb.Logs())
} }
go self.eventMux.Post(state.Logs())
return receipt, txGas, err return receipt, txGas, err
} }
func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) { func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) {
var ( var (
receipts types.Receipts receipts types.Receipts
handled, unhandled types.Transactions handled, unhandled types.Transactions
@ -115,12 +117,12 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state
) )
for _, tx := range txs { for _, tx := range txs {
receipt, txGas, err := self.ApplyTransaction(coinbase, state, block, tx, totalUsedGas, transientProcess) receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess)
if err != nil { if err != nil {
switch { switch {
case IsNonceErr(err): case IsNonceErr(err):
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
case IsGasLimitErr(err): case state.IsGasLimitErr(err):
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
default: default:
statelogger.Infoln(err) statelogger.Infoln(err)
@ -176,7 +178,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big
return return
} }
receipts, err := sm.TransitionState(state, parent, block) receipts, err := sm.TransitionState(state, parent, block, false)
if err != nil { if err != nil {
return return
} }
@ -250,7 +252,11 @@ func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error {
} }
if block.Time() > time.Now().Unix() { if block.Time() > time.Now().Unix() {
return fmt.Errorf("block time is in the future") return BlockFutureErr
}
if new(big.Int).Sub(block.Number(), parent.Number()).Cmp(big.NewInt(1)) != 0 {
return BlockNumberErr
} }
// Verify the nonce of the block. Return an error if it's not valid // Verify the nonce of the block. Return an error if it's not valid
@ -289,16 +295,13 @@ func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, paren
r := new(big.Int) r := new(big.Int)
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16)) r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))
uncleAccount := statedb.GetAccount(uncle.Coinbase) statedb.AddBalance(uncle.Coinbase, r)
uncleAccount.AddAmount(r)
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32))) reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
} }
// Get the account associated with the coinbase // Get the account associated with the coinbase
account := statedb.GetAccount(block.Header().Coinbase) statedb.AddBalance(block.Header().Coinbase, reward)
// Reward amount of ether to the coinbase address
account.AddAmount(reward)
return nil return nil
} }
@ -312,13 +315,10 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro
var ( var (
parent = sm.bc.GetBlock(block.Header().ParentHash) parent = sm.bc.GetBlock(block.Header().ParentHash)
//state = state.New(parent.Trie().Copy()) state = state.New(parent.Root(), sm.db)
state = state.New(parent.Root(), sm.db)
) )
defer state.Reset() sm.TransitionState(state, parent, block, true)
sm.TransitionState(state, parent, block)
sm.AccumulateRewards(state, block, parent) sm.AccumulateRewards(state, block, parent)
return state.Logs(), nil return state.Logs(), nil

View File

@ -0,0 +1,34 @@
package core
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
)
func proc() (*BlockProcessor, *ChainManager) {
db, _ := ethdb.NewMemDatabase()
var mux event.TypeMux
chainMan := NewChainManager(db, &mux)
return NewBlockProcessor(db, nil, chainMan, &mux), chainMan
}
func TestNumber(t *testing.T) {
bp, chain := proc()
block1 := chain.NewBlock(nil)
block1.Header().Number = big.NewInt(3)
err := bp.ValidateBlock(block1, chain.Genesis())
if err != BlockNumberErr {
t.Errorf("expected block number error")
}
block1 = chain.NewBlock(nil)
err = bp.ValidateBlock(block1, chain.Genesis())
if err == BlockNumberErr {
t.Errorf("didn't expect block number error")
}
}

View File

@ -79,6 +79,7 @@ type ChainManager struct {
genesisBlock *types.Block genesisBlock *types.Block
// Last known total difficulty // Last known total difficulty
mu sync.RWMutex mu sync.RWMutex
tsmu sync.RWMutex
td *big.Int td *big.Int
currentBlock *types.Block currentBlock *types.Block
lastBlockHash []byte lastBlockHash []byte
@ -86,6 +87,14 @@ type ChainManager struct {
transState *state.StateDB transState *state.StateDB
} }
func NewChainManager(db ethutil.Database, mux *event.TypeMux) *ChainManager {
bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux}
bc.setLastBlock()
bc.transState = bc.State().Copy()
return bc
}
func (self *ChainManager) Td() *big.Int { func (self *ChainManager) Td() *big.Int {
self.mu.RLock() self.mu.RLock()
defer self.mu.RUnlock() defer self.mu.RUnlock()
@ -107,14 +116,6 @@ func (self *ChainManager) CurrentBlock() *types.Block {
return self.currentBlock return self.currentBlock
} }
func NewChainManager(db ethutil.Database, mux *event.TypeMux) *ChainManager {
bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux}
bc.setLastBlock()
bc.transState = bc.State().Copy()
return bc
}
func (self *ChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) { func (self *ChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
self.mu.RLock() self.mu.RLock()
defer self.mu.RUnlock() defer self.mu.RUnlock()
@ -131,9 +132,16 @@ func (self *ChainManager) State() *state.StateDB {
} }
func (self *ChainManager) TransState() *state.StateDB { func (self *ChainManager) TransState() *state.StateDB {
self.tsmu.RLock()
defer self.tsmu.RUnlock()
return self.transState return self.transState
} }
func (self *ChainManager) setTransState(statedb *state.StateDB) {
self.transState = statedb
}
func (bc *ChainManager) setLastBlock() { func (bc *ChainManager) setLastBlock() {
data, _ := bc.db.Get([]byte("LastBlock")) data, _ := bc.db.Get([]byte("LastBlock"))
if len(data) != 0 { if len(data) != 0 {
@ -260,6 +268,7 @@ func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain
break break
} }
} }
fmt.Printf("get hash %x (%d)\n", hash, len(chain))
return return
} }
@ -350,6 +359,9 @@ func (bc *ChainManager) Stop() {
} }
func (self *ChainManager) InsertChain(chain types.Blocks) error { func (self *ChainManager) InsertChain(chain types.Blocks) error {
self.tsmu.Lock()
defer self.tsmu.Unlock()
for _, block := range chain { for _, block := range chain {
td, err := self.processor.Process(block) td, err := self.processor.Process(block)
if err != nil { if err != nil {
@ -365,6 +377,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
} }
block.Td = td block.Td = td
var chain, split bool
self.mu.Lock() self.mu.Lock()
{ {
self.write(block) self.write(block)
@ -372,18 +385,25 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
if td.Cmp(self.td) > 0 { if td.Cmp(self.td) > 0 {
if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 { if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 {
chainlogger.Infof("Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n", block.Header().Number, block.Hash()[:4], td, cblock.Header().Number, cblock.Hash()[:4], self.td) chainlogger.Infof("Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n", block.Header().Number, block.Hash()[:4], td, cblock.Header().Number, cblock.Hash()[:4], self.td)
split = true
} }
self.setTotalDifficulty(td) self.setTotalDifficulty(td)
self.insert(block) self.insert(block)
self.transState = state.New(cblock.Root(), self.db)
self.eventMux.Post(ChainEvent{block, td}) chain = true
} }
} }
self.mu.Unlock() self.mu.Unlock()
self.eventMux.Post(NewBlockEvent{block}) if chain {
self.eventMux.Post(ChainEvent{block, td})
}
if split {
self.setTransState(state.New(block.Root(), self.db))
self.eventMux.Post(ChainSplitEvent{block})
}
} }
return nil return nil

View File

@ -1,10 +1,16 @@
package core package core
import ( import (
"errors"
"fmt" "fmt"
"math/big" "math/big"
) )
var (
BlockNumberErr = errors.New("block number invalid")
BlockFutureErr = errors.New("block time is in the future")
)
// Parent error. In case a parent is unknown this error will be thrown // Parent error. In case a parent is unknown this error will be thrown
// by the block manager // by the block manager
type ParentErr struct { type ParentErr struct {
@ -62,23 +68,6 @@ func IsValidationErr(err error) bool {
return ok return ok
} }
type GasLimitErr struct {
Message string
Is, Max *big.Int
}
func IsGasLimitErr(err error) bool {
_, ok := err.(*GasLimitErr)
return ok
}
func (err *GasLimitErr) Error() string {
return err.Message
}
func GasLimitError(is, max *big.Int) *GasLimitErr {
return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max}
}
type NonceErr struct { type NonceErr struct {
Message string Message string
Is, Exp uint64 Is, Exp uint64

View File

@ -13,3 +13,6 @@ type NewBlockEvent struct{ Block *types.Block }
// NewMinedBlockEvent is posted when a block has been imported. // NewMinedBlockEvent is posted when a block has been imported.
type NewMinedBlockEvent struct{ Block *types.Block } type NewMinedBlockEvent struct{ Block *types.Block }
// ChainSplit is posted when a new head is detected
type ChainSplitEvent struct{ Block *types.Block }

View File

@ -16,7 +16,7 @@ type FilterOptions struct {
Earliest int64 Earliest int64
Latest int64 Latest int64
Address []byte Address [][]byte
Topics [][]byte Topics [][]byte
Skip int Skip int
@ -29,7 +29,7 @@ type Filter struct {
earliest int64 earliest int64
latest int64 latest int64
skip int skip int
address []byte address [][]byte
max int max int
topics [][]byte topics [][]byte
@ -65,7 +65,7 @@ func (self *Filter) SetLatestBlock(latest int64) {
self.latest = latest self.latest = latest
} }
func (self *Filter) SetAddress(addr []byte) { func (self *Filter) SetAddress(addr [][]byte) {
self.address = addr self.address = addr
} }
@ -111,14 +111,14 @@ func (self *Filter) Find() state.Logs {
// current parameters // current parameters
if self.bloomFilter(block) { if self.bloomFilter(block) {
// Get the logs of the block // Get the logs of the block
logs, err := self.eth.BlockProcessor().GetLogs(block) unfiltered, err := self.eth.BlockProcessor().GetLogs(block)
if err != nil { if err != nil {
chainlogger.Warnln("err: filter get logs ", err) chainlogger.Warnln("err: filter get logs ", err)
break break
} }
logs = append(logs, self.FilterLogs(logs)...) logs = append(logs, self.FilterLogs(unfiltered)...)
} }
block = self.eth.ChainManager().GetBlock(block.ParentHash()) block = self.eth.ChainManager().GetBlock(block.ParentHash())
@ -145,7 +145,7 @@ func (self *Filter) FilterLogs(logs state.Logs) state.Logs {
// Filter the logs for interesting stuff // Filter the logs for interesting stuff
Logs: Logs:
for _, log := range logs { for _, log := range logs {
if !bytes.Equal(self.address, log.Address()) { if !includes(self.address, log.Address()) {
continue continue
} }
@ -163,8 +163,18 @@ Logs:
} }
func (self *Filter) bloomFilter(block *types.Block) bool { func (self *Filter) bloomFilter(block *types.Block) bool {
if len(self.address) > 0 && !types.BloomLookup(block.Bloom(), self.address) { if len(self.address) > 0 {
return false var included bool
for _, addr := range self.address {
if types.BloomLookup(block.Bloom(), addr) {
included = true
break
}
}
if !included {
return false
}
} }
for _, topic := range self.topics { for _, topic := range self.topics {

View File

@ -1,7 +1,10 @@
package core package core
import ( import (
"encoding/json"
"fmt"
"math/big" "math/big"
"os"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -31,24 +34,39 @@ func GenesisBlock(db ethutil.Database) *types.Block {
genesis.SetTransactions(types.Transactions{}) genesis.SetTransactions(types.Transactions{})
genesis.SetReceipts(types.Receipts{}) genesis.SetReceipts(types.Receipts{})
var accounts map[string]struct{ Balance string }
err := json.Unmarshal(genesisData, &accounts)
if err != nil {
fmt.Println("enable to decode genesis json data:", err)
os.Exit(1)
}
statedb := state.New(genesis.Root(), db) statedb := state.New(genesis.Root(), db)
for _, addr := range []string{ for addr, account := range accounts {
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6",
"e4157b34ea9615cfbde6b4fda419828124b70c78",
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
"6c386a4b26f73c802f34673f7248bb118f97424a",
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
"e6716f9544a56c530d868e4bfbacb172315bdead",
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
} {
codedAddr := ethutil.Hex2Bytes(addr) codedAddr := ethutil.Hex2Bytes(addr)
account := statedb.GetAccount(codedAddr) accountState := statedb.GetAccount(codedAddr)
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200) accountState.SetBalance(ethutil.Big(account.Balance))
statedb.UpdateStateObject(account) statedb.UpdateStateObject(accountState)
} }
statedb.Sync() statedb.Sync()
genesis.Header().Root = statedb.Root() genesis.Header().Root = statedb.Root()
fmt.Printf("+++ genesis +++\nRoot: %x\nHash: %x\n", genesis.Header().Root, genesis.Hash())
return genesis return genesis
} }
var genesisData = []byte(`{
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"e4157b34ea9615cfbde6b4fda419828124b70c78": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"b9c015918bdaba24b4ff057a92a3873d6eb201be": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"6c386a4b26f73c802f34673f7248bb118f97424a": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"2ef47100e0787b915105fd5e3f4ff6752079d5cb": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"b0afc46d9ce366d06ab4952ca27db1d9557ae9fd": {"balance": "154162184000000000000000"},
"f6b1e9dc460d4d62cc22ec5f987d726929c0f9f0": {"balance": "102774789000000000000000"},
"cc45122d8b7fa0b1eaa6b29e0fb561422a9239d0": {"balance": "51387394000000000000000"},
"b7576e9d314df41ec5506494293afb1bd5d3f65d": {"balance": "69423399000000000000000"}
}`)

View File

@ -138,8 +138,8 @@ func (self *StateTransition) preCheck() (err error) {
) )
// Make sure this transaction's nonce is correct // Make sure this transaction's nonce is correct
if sender.Nonce != msg.Nonce() { if sender.Nonce() != msg.Nonce() {
return NonceError(msg.Nonce(), sender.Nonce) return NonceError(msg.Nonce(), sender.Nonce())
} }
// Pre-pay gas / Buy gas of the coinbase account // Pre-pay gas / Buy gas of the coinbase account
@ -166,7 +166,8 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) {
defer self.RefundGas() defer self.RefundGas()
// Increment the nonce for the next transaction // Increment the nonce for the next transaction
sender.Nonce += 1 self.state.SetNonce(sender.Address(), sender.Nonce()+1)
//sender.Nonce += 1
// Transaction gas // Transaction gas
if err = self.UseGas(vm.GasTx); err != nil { if err = self.UseGas(vm.GasTx); err != nil {
@ -241,7 +242,7 @@ func MakeContract(msg Message, state *state.StateDB) *state.StateObject {
addr := AddressFromMessage(msg) addr := AddressFromMessage(msg)
contract := state.GetOrNewStateObject(addr) contract := state.GetOrNewStateObject(addr)
contract.InitCode = msg.Data() contract.SetInitCode(msg.Data())
return contract return contract
} }

View File

@ -3,6 +3,7 @@ package core
import ( import (
"errors" "errors"
"fmt" "fmt"
"sync"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
@ -35,6 +36,7 @@ type TxProcessor interface {
// guarantee a non blocking pool we use a queue channel which can be // guarantee a non blocking pool we use a queue channel which can be
// independently read without needing access to the actual pool. // independently read without needing access to the actual pool.
type TxPool struct { type TxPool struct {
mu sync.RWMutex
// Queueing channel for reading and writing incoming // Queueing channel for reading and writing incoming
// transactions to // transactions to
queueChan chan *types.Transaction queueChan chan *types.Transaction
@ -97,7 +99,7 @@ func (self *TxPool) addTx(tx *types.Transaction) {
self.txs[string(tx.Hash())] = tx self.txs[string(tx.Hash())] = tx
} }
func (self *TxPool) Add(tx *types.Transaction) error { func (self *TxPool) add(tx *types.Transaction) error {
if self.txs[string(tx.Hash())] != nil { if self.txs[string(tx.Hash())] != nil {
return fmt.Errorf("Known transaction (%x)", tx.Hash()[0:4]) return fmt.Errorf("Known transaction (%x)", tx.Hash()[0:4])
} }
@ -128,17 +130,28 @@ func (self *TxPool) Size() int {
return len(self.txs) return len(self.txs)
} }
func (self *TxPool) Add(tx *types.Transaction) error {
self.mu.Lock()
defer self.mu.Unlock()
return self.add(tx)
}
func (self *TxPool) AddTransactions(txs []*types.Transaction) { func (self *TxPool) AddTransactions(txs []*types.Transaction) {
self.mu.Lock()
defer self.mu.Unlock()
for _, tx := range txs { for _, tx := range txs {
if err := self.Add(tx); err != nil { if err := self.add(tx); err != nil {
txplogger.Infoln(err) txplogger.Debugln(err)
} else { } else {
txplogger.Infof("tx %x\n", tx.Hash()[0:4]) txplogger.Debugf("tx %x\n", tx.Hash()[0:4])
} }
} }
} }
func (self *TxPool) GetTransactions() (txs types.Transactions) { func (self *TxPool) GetTransactions() (txs types.Transactions) {
self.mu.RLock()
defer self.mu.RUnlock()
txs = make(types.Transactions, self.Size()) txs = make(types.Transactions, self.Size())
i := 0 i := 0
for _, tx := range self.txs { for _, tx := range self.txs {
@ -150,30 +163,32 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) {
} }
func (pool *TxPool) RemoveInvalid(query StateQuery) { func (pool *TxPool) RemoveInvalid(query StateQuery) {
pool.mu.Lock()
var removedTxs types.Transactions var removedTxs types.Transactions
for _, tx := range pool.txs { for _, tx := range pool.txs {
sender := query.GetAccount(tx.From()) sender := query.GetAccount(tx.From())
err := pool.ValidateTransaction(tx) err := pool.ValidateTransaction(tx)
fmt.Println(err, sender.Nonce, tx.Nonce()) if err != nil || sender.Nonce() >= tx.Nonce() {
if err != nil || sender.Nonce >= tx.Nonce() {
removedTxs = append(removedTxs, tx) removedTxs = append(removedTxs, tx)
} }
} }
pool.mu.Unlock()
pool.RemoveSet(removedTxs) pool.RemoveSet(removedTxs)
} }
func (self *TxPool) RemoveSet(txs types.Transactions) { func (self *TxPool) RemoveSet(txs types.Transactions) {
self.mu.Lock()
defer self.mu.Unlock()
for _, tx := range txs { for _, tx := range txs {
delete(self.txs, string(tx.Hash())) delete(self.txs, string(tx.Hash()))
} }
} }
func (pool *TxPool) Flush() []*types.Transaction { func (pool *TxPool) Flush() {
txList := pool.GetTransactions()
pool.txs = make(map[string]*types.Transaction) pool.txs = make(map[string]*types.Transaction)
return txList
} }
func (pool *TxPool) Start() { func (pool *TxPool) Start() {

View File

@ -185,6 +185,18 @@ func (self *Block) GasUsed() *big.Int { return self.header.GasUsed }
func (self *Block) Root() []byte { return self.header.Root } func (self *Block) Root() []byte { return self.header.Root }
func (self *Block) SetRoot(root []byte) { self.header.Root = root } func (self *Block) SetRoot(root []byte) { self.header.Root = root }
func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) } func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) }
func (self *Block) GetTransaction(i int) *Transaction {
if len(self.transactions) > i {
return self.transactions[i]
}
return nil
}
func (self *Block) GetUncle(i int) *Header {
if len(self.uncles) > i {
return self.uncles[i]
}
return nil
}
// Implement pow.Block // Implement pow.Block
func (self *Block) Difficulty() *big.Int { return self.header.Difficulty } func (self *Block) Difficulty() *big.Int { return self.header.Difficulty }

View File

@ -16,12 +16,12 @@ import (
"errors" "errors"
"code.google.com/p/go-uuid/uuid" "code.google.com/p/go-uuid/uuid"
"code.google.com/p/go.crypto/pbkdf2"
"code.google.com/p/go.crypto/ripemd160"
"github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/ripemd160"
) )
func init() { func init() {

View File

@ -20,6 +20,7 @@
* @date 2015 * @date 2015
* *
*/ */
/* /*
This key store behaves as KeyStorePlain with the difference that This key store behaves as KeyStorePlain with the difference that
@ -64,17 +65,18 @@ package crypto
import ( import (
"bytes" "bytes"
"code.google.com/p/go-uuid/uuid"
"code.google.com/p/go.crypto/scrypt"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/ethereum/go-ethereum/crypto/randentropy"
"io" "io"
"os" "os"
"path" "path"
"code.google.com/p/go-uuid/uuid"
"github.com/ethereum/go-ethereum/crypto/randentropy"
"golang.org/x/crypto/scrypt"
) )
const ( const (

View File

@ -3,6 +3,8 @@ package eth
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
"io/ioutil"
"path"
"strings" "strings"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@ -25,7 +27,10 @@ var (
jsonlogger = ethlogger.NewJsonLogger() jsonlogger = ethlogger.NewJsonLogger()
defaultBootNodes = []*discover.Node{ defaultBootNodes = []*discover.Node{
// ETH/DEV cmd/bootnode
discover.MustParseNode("enode://6cdd090303f394a1cac34ecc9f7cda18127eafa2a3a06de39f6d920b0e583e062a7362097c7c65ee490a758b442acd5c80c6fce4b148c6a391e946b45131365b@54.169.166.226:30303"), discover.MustParseNode("enode://6cdd090303f394a1cac34ecc9f7cda18127eafa2a3a06de39f6d920b0e583e062a7362097c7c65ee490a758b442acd5c80c6fce4b148c6a391e946b45131365b@54.169.166.226:30303"),
// ETH/DEV cpp-ethereum (poc-8.ethdev.com)
discover.MustParseNode("enode://4a44599974518ea5b0f14c31c4463692ac0329cb84851f3435e6d1b18ee4eae4aa495f846a0fa1219bd58035671881d44423876e57db2abd57254d0197da0ebe@5.1.83.226:30303"),
} }
) )
@ -53,6 +58,8 @@ type Config struct {
Shh bool Shh bool
Dial bool Dial bool
MinerThreads int
KeyManager *crypto.KeyManager KeyManager *crypto.KeyManager
} }
@ -75,6 +82,27 @@ func (cfg *Config) parseBootNodes() []*discover.Node {
return ns return ns
} }
func (cfg *Config) nodeKey() (*ecdsa.PrivateKey, error) {
// use explicit key from command line args if set
if cfg.NodeKey != nil {
return cfg.NodeKey, nil
}
// use persistent key if present
keyfile := path.Join(cfg.DataDir, "nodekey")
key, err := crypto.LoadECDSA(keyfile)
if err == nil {
return key, nil
}
// no persistent key, generate and store a new one
if key, err = crypto.GenerateKey(); err != nil {
return nil, fmt.Errorf("could not generate server key: %v", err)
}
if err := ioutil.WriteFile(keyfile, crypto.FromECDSA(key), 0600); err != nil {
logger.Errorln("could not persist nodekey: ", err)
}
return key, nil
}
type Ethereum struct { type Ethereum struct {
// Channel for shutting down the ethereum // Channel for shutting down the ethereum
shutdownChan chan bool shutdownChan chan bool
@ -153,20 +181,22 @@ func New(config *Config) (*Ethereum, error) {
eth.blockProcessor = core.NewBlockProcessor(db, eth.txPool, eth.chainManager, eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(db, eth.txPool, eth.chainManager, eth.EventMux())
eth.chainManager.SetProcessor(eth.blockProcessor) eth.chainManager.SetProcessor(eth.blockProcessor)
eth.whisper = whisper.New() eth.whisper = whisper.New()
eth.miner = miner.New(keyManager.Address(), eth) eth.miner = miner.New(keyManager.Address(), eth, config.MinerThreads)
hasBlock := eth.chainManager.HasBlock hasBlock := eth.chainManager.HasBlock
insertChain := eth.chainManager.InsertChain insertChain := eth.chainManager.InsertChain
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify) eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool) netprv, err := config.nodeKey()
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()} if err != nil {
netprv := config.NodeKey return nil, err
if netprv == nil {
if netprv, err = crypto.GenerateKey(); err != nil {
return nil, fmt.Errorf("could not generate server key: %v", err)
}
} }
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
protocols := []p2p.Protocol{ethProto}
if config.Shh {
protocols = append(protocols, eth.whisper.Protocol())
}
eth.net = &p2p.Server{ eth.net = &p2p.Server{
PrivateKey: netprv, PrivateKey: netprv,
Name: config.Name, Name: config.Name,
@ -205,9 +235,7 @@ func (s *Ethereum) Coinbase() []byte { return nil } // TODO
func (s *Ethereum) Start() error { func (s *Ethereum) Start() error {
jsonlogger.LogJson(&ethlogger.LogStarting{ jsonlogger.LogJson(&ethlogger.LogStarting{
ClientString: s.net.Name, ClientString: s.net.Name,
Coinbase: ethutil.Bytes2Hex(s.KeyManager().Address()),
ProtocolVersion: ProtocolVersion, ProtocolVersion: ProtocolVersion,
LogEvent: ethlogger.LogEvent{Guid: ethutil.Bytes2Hex(crypto.FromECDSAPub(&s.net.PrivateKey.PublicKey))},
}) })
err := s.net.Start() err := s.net.Start()

View File

@ -13,7 +13,7 @@ import (
) )
const ( const (
ProtocolVersion = 52 ProtocolVersion = 54
NetworkId = 0 NetworkId = 0
ProtocolLength = uint64(8) ProtocolLength = uint64(8)
ProtocolMaxMsgSize = 10 * 1024 * 1024 ProtocolMaxMsgSize = 10 * 1024 * 1024

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"runtime" "runtime"
"time"
) )
func IsWindows() bool { func IsWindows() bool {
@ -86,3 +87,9 @@ var (
Big256 = big.NewInt(0xff) Big256 = big.NewInt(0xff)
Big257 = big.NewInt(257) Big257 = big.NewInt(257)
) )
func Bench(pre string, cb func()) {
start := time.Now()
cb()
fmt.Println(pre, ": took:", time.Since(start))
}

181
ethutil/number/int.go Normal file
View File

@ -0,0 +1,181 @@
package number
import (
"math/big"
"github.com/ethereum/go-ethereum/ethutil"
)
var tt256 = new(big.Int).Lsh(big.NewInt(1), 256)
var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
var tt255 = new(big.Int).Lsh(big.NewInt(1), 255)
func limitUnsigned256(x *Number) *Number {
x.num.And(x.num, tt256m1)
return x
}
func limitSigned256(x *Number) *Number {
if x.num.Cmp(tt255) < 0 {
return x
} else {
x.num.Sub(x.num, tt256)
return x
}
}
// Number function
type Initialiser func(n int64) *Number
// A Number represents a generic integer with a bounding function limiter. Limit is called after each operations
// to give "fake" bounded integers. New types of Number can be created through NewInitialiser returning a lambda
// with the new Initialiser.
type Number struct {
num *big.Int
limit func(n *Number) *Number
}
// Returns a new initialiser for a new *Number without having to expose certain fields
func NewInitialiser(limiter func(*Number) *Number) Initialiser {
return func(n int64) *Number {
return &Number{big.NewInt(n), limiter}
}
}
// Return a Number with a UNSIGNED limiter up to 256 bits
func Uint256(n int64) *Number {
return &Number{big.NewInt(n), limitUnsigned256}
}
// Return a Number with a SIGNED limiter up to 256 bits
func Int256(n int64) *Number {
return &Number{big.NewInt(n), limitSigned256}
}
// Returns a Number with a SIGNED unlimited size
func Big(n int64) *Number {
return &Number{big.NewInt(n), func(x *Number) *Number { return x }}
}
// Sets i to sum of x+y
func (i *Number) Add(x, y *Number) *Number {
i.num.Add(x.num, y.num)
return i.limit(i)
}
// Sets i to difference of x-y
func (i *Number) Sub(x, y *Number) *Number {
i.num.Sub(x.num, y.num)
return i.limit(i)
}
// Sets i to product of x*y
func (i *Number) Mul(x, y *Number) *Number {
i.num.Mul(x.num, y.num)
return i.limit(i)
}
// Sets i to the quotient prodject of x/y
func (i *Number) Div(x, y *Number) *Number {
i.num.Div(x.num, y.num)
return i.limit(i)
}
// Sets i to x % y
func (i *Number) Mod(x, y *Number) *Number {
i.num.Mod(x.num, y.num)
return i.limit(i)
}
// Sets i to x << s
func (i *Number) Lsh(x *Number, s uint) *Number {
i.num.Lsh(x.num, s)
return i.limit(i)
}
// Sets i to x^y
func (i *Number) Pow(x, y *Number) *Number {
i.num.Exp(x.num, y.num, big.NewInt(0))
return i.limit(i)
}
// Setters
// Set x to i
func (i *Number) Set(x *Number) *Number {
i.num.Set(x.num)
return i.limit(i)
}
// Set x bytes to i
func (i *Number) SetBytes(x []byte) *Number {
i.num.SetBytes(x)
return i.limit(i)
}
// Cmp compares x and y and returns:
//
// -1 if x < y
// 0 if x == y
// +1 if x > y
func (i *Number) Cmp(x *Number) int {
return i.num.Cmp(x.num)
}
// Getters
// Returns the string representation of i
func (i *Number) String() string {
return i.num.String()
}
// Returns the byte representation of i
func (i *Number) Bytes() []byte {
return i.num.Bytes()
}
// Uint64 returns the Uint64 representation of x. If x cannot be represented in an int64, the result is undefined.
func (i *Number) Uint64() uint64 {
return i.num.Uint64()
}
// Int64 returns the int64 representation of x. If x cannot be represented in an int64, the result is undefined.
func (i *Number) Int64() int64 {
return i.num.Int64()
}
// Returns the signed version of i
func (i *Number) Int256() *Number {
return Int(0).Set(i)
}
// Returns the unsigned version of i
func (i *Number) Uint256() *Number {
return Uint(0).Set(i)
}
// Returns the index of the first bit that's set to 1
func (i *Number) FirstBitSet() int {
for j := 0; j < i.num.BitLen(); j++ {
if i.num.Bit(j) > 0 {
return j
}
}
return i.num.BitLen()
}
// Variables
var (
Zero = Uint(0)
One = Uint(1)
Two = Uint(2)
MaxUint256 = Uint(0).SetBytes(ethutil.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
MinOne = Int(-1)
// "typedefs"
Uint = Uint256
Int = Int256
)

View File

@ -0,0 +1,92 @@
package number
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/ethutil"
)
func TestSet(t *testing.T) {
a := Uint(0)
b := Uint(10)
a.Set(b)
if a.num.Cmp(b.num) != 0 {
t.Error("didn't compare", a, b)
}
c := Uint(0).SetBytes(ethutil.Hex2Bytes("0a"))
if c.num.Cmp(big.NewInt(10)) != 0 {
t.Error("c set bytes failed.")
}
}
func TestInitialiser(t *testing.T) {
check := false
init := NewInitialiser(func(x *Number) *Number {
check = true
return x
})
a := init(0).Add(init(1), init(2))
if a.Cmp(init(3)) != 0 {
t.Error("expected 3. got", a)
}
if !check {
t.Error("expected limiter to be called")
}
}
func TestGet(t *testing.T) {
a := Uint(10)
if a.Uint64() != 10 {
t.Error("expected to get 10. got", a.Uint64())
}
a = Uint(10)
if a.Int64() != 10 {
t.Error("expected to get 10. got", a.Int64())
}
}
func TestCmp(t *testing.T) {
a := Uint(10)
b := Uint(10)
c := Uint(11)
if a.Cmp(b) != 0 {
t.Error("a b == 0 failed", a, b)
}
if a.Cmp(c) >= 0 {
t.Error("a c < 0 failed", a, c)
}
if c.Cmp(b) <= 0 {
t.Error("c b > 0 failed", c, b)
}
}
func TestMaxArith(t *testing.T) {
a := Uint(0).Add(MaxUint256, One)
if a.Cmp(Zero) != 0 {
t.Error("expected max256 + 1 = 0 got", a)
}
a = Uint(0).Sub(Uint(0), One)
if a.Cmp(MaxUint256) != 0 {
t.Error("expected 0 - 1 = max256 got", a)
}
a = Int(0).Sub(Int(0), One)
if a.Cmp(MinOne) != 0 {
t.Error("expected 0 - 1 = -1 got", a)
}
}
func TestConversion(t *testing.T) {
a := Int(-1)
b := a.Uint256()
if b.Cmp(MaxUint256) != 0 {
t.Error("expected -1 => unsigned to return max. got", b)
}
}

View File

@ -3,6 +3,7 @@ package filter
// TODO make use of the generic filtering system // TODO make use of the generic filtering system
import ( import (
"fmt"
"sync" "sync"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@ -37,17 +38,18 @@ func (self *FilterManager) Stop() {
func (self *FilterManager) InstallFilter(filter *core.Filter) (id int) { func (self *FilterManager) InstallFilter(filter *core.Filter) (id int) {
self.filterMu.Lock() self.filterMu.Lock()
defer self.filterMu.Unlock()
id = self.filterId id = self.filterId
self.filters[id] = filter self.filters[id] = filter
self.filterId++ self.filterId++
self.filterMu.Unlock()
return id return id
} }
func (self *FilterManager) UninstallFilter(id int) { func (self *FilterManager) UninstallFilter(id int) {
self.filterMu.Lock() self.filterMu.Lock()
defer self.filterMu.Unlock()
delete(self.filters, id) delete(self.filters, id)
self.filterMu.Unlock()
} }
// GetFilter retrieves a filter installed using InstallFilter. // GetFilter retrieves a filter installed using InstallFilter.
@ -60,7 +62,10 @@ func (self *FilterManager) GetFilter(id int) *core.Filter {
func (self *FilterManager) filterLoop() { func (self *FilterManager) filterLoop() {
// Subscribe to events // Subscribe to events
events := self.eventMux.Subscribe(core.PendingBlockEvent{}, core.NewBlockEvent{}, state.Logs(nil)) events := self.eventMux.Subscribe(
core.PendingBlockEvent{},
//core.ChainEvent{},
state.Logs(nil))
out: out:
for { for {
@ -69,7 +74,8 @@ out:
break out break out
case event := <-events.Chan(): case event := <-events.Chan():
switch event := event.(type) { switch event := event.(type) {
case core.NewBlockEvent: case core.ChainEvent:
fmt.Println("filter start")
self.filterMu.RLock() self.filterMu.RLock()
for _, filter := range self.filters { for _, filter := range self.filters {
if filter.BlockCallback != nil { if filter.BlockCallback != nil {
@ -77,6 +83,7 @@ out:
} }
} }
self.filterMu.RUnlock() self.filterMu.RUnlock()
fmt.Println("filter stop")
case core.PendingBlockEvent: case core.PendingBlockEvent:
self.filterMu.RLock() self.filterMu.RLock()

View File

@ -1,11 +1,16 @@
#!/bin/bash #!/bin/bash
# The script does automatic checking on a Go package and its sub-packages, including:
# 6. test coverage (http://blog.golang.org/cover)
set -e set -e
# Run test coverage on each subdirectories and merge the coverage profile. # Add godep workspace to GOPATH. We do it manually instead of using
# 'godep go test' or 'godep restore' so godep doesn't need to be installed.
GOPATH="$PWD/Godeps/_workspace:$GOPATH"
# Install packages before testing. Not doing this would cause
# 'go test' to recompile all package dependencies before testing each package.
go install ./...
# Run test coverage on each subdirectories and merge the coverage profile.
echo "mode: count" > profile.cov echo "mode: count" > profile.cov
# Standard go tooling behavior is to ignore dirs with leading underscors # Standard go tooling behavior is to ignore dirs with leading underscors
@ -13,7 +18,7 @@ for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d)
do do
if ls $dir/*.go &> /dev/null; then if ls $dir/*.go &> /dev/null; then
# echo $dir # echo $dir
if [[ $dir != "./tests/vm" ]] if [[ $dir != "./tests/vm" && $dir != "." ]]
then then
go test -covermode=count -coverprofile=$dir/profile.tmp $dir go test -covermode=count -coverprofile=$dir/profile.tmp $dir
fi fi
@ -24,6 +29,3 @@ if ls $dir/*.go &> /dev/null; then
fi fi
fi fi
done done
go tool cover -func profile.cov

View File

@ -7,7 +7,6 @@ import (
type utctime8601 struct{} type utctime8601 struct{}
func (utctime8601) MarshalJSON() ([]byte, error) { func (utctime8601) MarshalJSON() ([]byte, error) {
// FIX This should be re-formated for proper ISO 8601
return []byte(`"` + time.Now().UTC().Format(time.RFC3339Nano)[:26] + `Z"`), nil return []byte(`"` + time.Now().UTC().Format(time.RFC3339Nano)[:26] + `Z"`), nil
} }
@ -16,14 +15,13 @@ type JsonLog interface {
} }
type LogEvent struct { type LogEvent struct {
Guid string `json:"guid"` // Guid string `json:"guid"`
Ts utctime8601 `json:"ts"` Ts utctime8601 `json:"ts"`
// Level string `json:"level"` // Level string `json:"level"`
} }
type LogStarting struct { type LogStarting struct {
ClientString string `json:"version_string"` ClientString string `json:"client_impl"`
Coinbase string `json:"coinbase"`
ProtocolVersion int `json:"eth_version"` ProtocolVersion int `json:"eth_version"`
LogEvent LogEvent
} }
@ -32,20 +30,11 @@ func (l *LogStarting) EventName() string {
return "starting" return "starting"
} }
type P2PConnecting struct {
RemoteId string `json:"remote_id"`
RemoteEndpoint string `json:"remote_endpoint"`
NumConnections int `json:"num_connections"`
LogEvent
}
func (l *P2PConnecting) EventName() string {
return "p2p.connecting"
}
type P2PConnected struct { type P2PConnected struct {
NumConnections int `json:"num_connections"` RemoteId string `json:"remote_id"`
RemoteId string `json:"remote_id"` RemoteAddress string `json:"remote_addr"`
RemoteVersionString string `json:"remote_version_string"`
NumConnections int `json:"num_connections"`
LogEvent LogEvent
} }
@ -53,17 +42,6 @@ func (l *P2PConnected) EventName() string {
return "p2p.connected" return "p2p.connected"
} }
type P2PHandshaked struct {
RemoteCapabilities []string `json:"remote_capabilities"`
RemoteId string `json:"remote_id"`
NumConnections int `json:"num_connections"`
LogEvent
}
func (l *P2PHandshaked) EventName() string {
return "p2p.handshaked"
}
type P2PDisconnected struct { type P2PDisconnected struct {
NumConnections int `json:"num_connections"` NumConnections int `json:"num_connections"`
RemoteId string `json:"remote_id"` RemoteId string `json:"remote_id"`
@ -74,247 +52,46 @@ func (l *P2PDisconnected) EventName() string {
return "p2p.disconnected" return "p2p.disconnected"
} }
type P2PDisconnecting struct { type EthMinerNewBlock struct {
Reason string `json:"reason"` BlockHash string `json:"block_hash"`
RemoteId string `json:"remote_id"` BlockNumber int `json:"block_number"`
NumConnections int `json:"num_connections"` ChainHeadHash string `json:"chain_head_hash"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent LogEvent
} }
func (l *P2PDisconnecting) EventName() string { func (l *EthMinerNewBlock) EventName() string {
return "p2p.disconnecting" return "eth.miner.new_block"
} }
type P2PDisconnectingBadHandshake struct { type EthChainReceivedNewBlock struct {
Reason string `json:"reason"` BlockHash string `json:"block_hash"`
RemoteId string `json:"remote_id"` BlockNumber int `json:"block_number"`
NumConnections int `json:"num_connections"` ChainHeadHash string `json:"chain_head_hash"`
BlockPrevHash string `json:"block_prev_hash"`
RemoteId int `json:"remote_id"`
LogEvent LogEvent
} }
func (l *P2PDisconnectingBadHandshake) EventName() string { func (l *EthChainReceivedNewBlock) EventName() string {
return "p2p.disconnecting.bad_handshake" return "eth.chain.received.new_block"
} }
type P2PDisconnectingBadProtocol struct { type EthChainNewHead struct {
Reason string `json:"reason"` BlockHash string `json:"block_hash"`
RemoteId string `json:"remote_id"` BlockNumber int `json:"block_number"`
NumConnections int `json:"num_connections"` ChainHeadHash string `json:"chain_head_hash"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent LogEvent
} }
func (l *P2PDisconnectingBadProtocol) EventName() string { func (l *EthChainNewHead) EventName() string {
return "p2p.disconnecting.bad_protocol" return "eth.chain.new_head"
}
type P2PDisconnectingReputation struct {
Reason string `json:"reason"`
RemoteId string `json:"remote_id"`
NumConnections int `json:"num_connections"`
LogEvent
}
func (l *P2PDisconnectingReputation) EventName() string {
return "p2p.disconnecting.reputation"
}
type P2PDisconnectingDHT struct {
Reason string `json:"reason"`
RemoteId string `json:"remote_id"`
NumConnections int `json:"num_connections"`
LogEvent
}
func (l *P2PDisconnectingDHT) EventName() string {
return "p2p.disconnecting.dht"
}
type P2PEthDisconnectingBadBlock struct {
Reason string `json:"reason"`
RemoteId string `json:"remote_id"`
NumConnections int `json:"num_connections"`
LogEvent
}
func (l *P2PEthDisconnectingBadBlock) EventName() string {
return "p2p.eth.disconnecting.bad_block"
}
type P2PEthDisconnectingBadTx struct {
Reason string `json:"reason"`
RemoteId string `json:"remote_id"`
NumConnections int `json:"num_connections"`
LogEvent
}
func (l *P2PEthDisconnectingBadTx) EventName() string {
return "p2p.eth.disconnecting.bad_tx"
}
type EthNewBlockMined struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
BlockHash string `json:"block_hash"`
BlockHexRlp string `json:"block_hexrlp"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockMined) EventName() string {
return "eth.newblock.mined"
}
type EthNewBlockBroadcasted struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
BlockHash string `json:"block_hash"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockBroadcasted) EventName() string {
return "eth.newblock.broadcasted"
}
type EthNewBlockReceived struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
BlockHash string `json:"block_hash"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockReceived) EventName() string {
return "eth.newblock.received"
}
type EthNewBlockIsKnown struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
BlockHash string `json:"block_hash"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockIsKnown) EventName() string {
return "eth.newblock.is_known"
}
type EthNewBlockIsNew struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
BlockHash string `json:"block_hash"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockIsNew) EventName() string {
return "eth.newblock.is_new"
}
type EthNewBlockMissingParent struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
BlockHash string `json:"block_hash"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockMissingParent) EventName() string {
return "eth.newblock.missing_parent"
}
type EthNewBlockIsInvalid struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
BlockHash string `json:"block_hash"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockIsInvalid) EventName() string {
return "eth.newblock.is_invalid"
}
type EthNewBlockChainIsOlder struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
BlockHash string `json:"block_hash"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockChainIsOlder) EventName() string {
return "eth.newblock.chain.is_older"
}
type EthNewBlockChainIsCanonical struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
BlockHash string `json:"block_hash"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockChainIsCanonical) EventName() string {
return "eth.newblock.chain.is_cannonical"
}
type EthNewBlockChainNotCanonical struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
BlockHash string `json:"block_hash"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockChainNotCanonical) EventName() string {
return "eth.newblock.chain.not_cannonical"
}
type EthNewBlockChainSwitched struct {
BlockNumber int `json:"block_number"`
HeadHash string `json:"head_hash"`
OldHeadHash string `json:"old_head_hash"`
BlockHash string `json:"block_hash"`
BlockDifficulty int `json:"block_difficulty"`
BlockPrevHash string `json:"block_prev_hash"`
LogEvent
}
func (l *EthNewBlockChainSwitched) EventName() string {
return "eth.newblock.chain.switched"
}
type EthTxCreated struct {
TxHash string `json:"tx_hash"`
TxSender string `json:"tx_sender"`
TxAddress string `json:"tx_address"`
TxHexRLP string `json:"tx_hexrlp"`
TxNonce int `json:"tx_nonce"`
LogEvent
}
func (l *EthTxCreated) EventName() string {
return "eth.tx.created"
} }
type EthTxReceived struct { type EthTxReceived struct {
TxHash string `json:"tx_hash"` TxHash string `json:"tx_hash"`
TxAddress string `json:"tx_address"` RemoteId string `json:"remote_id"`
TxHexRLP string `json:"tx_hexrlp"`
RemoteId string `json:"remote_id"`
TxNonce int `json:"tx_nonce"`
LogEvent LogEvent
} }
@ -322,39 +99,261 @@ func (l *EthTxReceived) EventName() string {
return "eth.tx.received" return "eth.tx.received"
} }
type EthTxBroadcasted struct { //
TxHash string `json:"tx_hash"` //
TxSender string `json:"tx_sender"` // The types below are legacy and need to be converted to new format or deleted
TxAddress string `json:"tx_address"` //
TxNonce int `json:"tx_nonce"` //
LogEvent
}
func (l *EthTxBroadcasted) EventName() string { // type P2PConnecting struct {
return "eth.tx.broadcasted" // RemoteId string `json:"remote_id"`
} // RemoteEndpoint string `json:"remote_endpoint"`
// NumConnections int `json:"num_connections"`
// LogEvent
// }
type EthTxValidated struct { // func (l *P2PConnecting) EventName() string {
TxHash string `json:"tx_hash"` // return "p2p.connecting"
TxSender string `json:"tx_sender"` // }
TxAddress string `json:"tx_address"`
TxNonce int `json:"tx_nonce"`
LogEvent
}
func (l *EthTxValidated) EventName() string { // type P2PHandshaked struct {
return "eth.tx.validated" // RemoteCapabilities []string `json:"remote_capabilities"`
} // RemoteId string `json:"remote_id"`
// NumConnections int `json:"num_connections"`
// LogEvent
// }
type EthTxIsInvalid struct { // func (l *P2PHandshaked) EventName() string {
TxHash string `json:"tx_hash"` // return "p2p.handshaked"
TxSender string `json:"tx_sender"` // }
TxAddress string `json:"tx_address"`
Reason string `json:"reason"`
TxNonce int `json:"tx_nonce"`
LogEvent
}
func (l *EthTxIsInvalid) EventName() string { // type P2PDisconnecting struct {
return "eth.tx.is_invalid" // Reason string `json:"reason"`
} // RemoteId string `json:"remote_id"`
// NumConnections int `json:"num_connections"`
// LogEvent
// }
// func (l *P2PDisconnecting) EventName() string {
// return "p2p.disconnecting"
// }
// type P2PDisconnectingBadHandshake struct {
// Reason string `json:"reason"`
// RemoteId string `json:"remote_id"`
// NumConnections int `json:"num_connections"`
// LogEvent
// }
// func (l *P2PDisconnectingBadHandshake) EventName() string {
// return "p2p.disconnecting.bad_handshake"
// }
// type P2PDisconnectingBadProtocol struct {
// Reason string `json:"reason"`
// RemoteId string `json:"remote_id"`
// NumConnections int `json:"num_connections"`
// LogEvent
// }
// func (l *P2PDisconnectingBadProtocol) EventName() string {
// return "p2p.disconnecting.bad_protocol"
// }
// type P2PDisconnectingReputation struct {
// Reason string `json:"reason"`
// RemoteId string `json:"remote_id"`
// NumConnections int `json:"num_connections"`
// LogEvent
// }
// func (l *P2PDisconnectingReputation) EventName() string {
// return "p2p.disconnecting.reputation"
// }
// type P2PDisconnectingDHT struct {
// Reason string `json:"reason"`
// RemoteId string `json:"remote_id"`
// NumConnections int `json:"num_connections"`
// LogEvent
// }
// func (l *P2PDisconnectingDHT) EventName() string {
// return "p2p.disconnecting.dht"
// }
// type P2PEthDisconnectingBadBlock struct {
// Reason string `json:"reason"`
// RemoteId string `json:"remote_id"`
// NumConnections int `json:"num_connections"`
// LogEvent
// }
// func (l *P2PEthDisconnectingBadBlock) EventName() string {
// return "p2p.eth.disconnecting.bad_block"
// }
// type P2PEthDisconnectingBadTx struct {
// Reason string `json:"reason"`
// RemoteId string `json:"remote_id"`
// NumConnections int `json:"num_connections"`
// LogEvent
// }
// func (l *P2PEthDisconnectingBadTx) EventName() string {
// return "p2p.eth.disconnecting.bad_tx"
// }
// type EthNewBlockBroadcasted struct {
// BlockNumber int `json:"block_number"`
// HeadHash string `json:"head_hash"`
// BlockHash string `json:"block_hash"`
// BlockDifficulty int `json:"block_difficulty"`
// BlockPrevHash string `json:"block_prev_hash"`
// LogEvent
// }
// func (l *EthNewBlockBroadcasted) EventName() string {
// return "eth.newblock.broadcasted"
// }
// type EthNewBlockIsKnown struct {
// BlockNumber int `json:"block_number"`
// HeadHash string `json:"head_hash"`
// BlockHash string `json:"block_hash"`
// BlockDifficulty int `json:"block_difficulty"`
// BlockPrevHash string `json:"block_prev_hash"`
// LogEvent
// }
// func (l *EthNewBlockIsKnown) EventName() string {
// return "eth.newblock.is_known"
// }
// type EthNewBlockIsNew struct {
// BlockNumber int `json:"block_number"`
// HeadHash string `json:"head_hash"`
// BlockHash string `json:"block_hash"`
// BlockDifficulty int `json:"block_difficulty"`
// BlockPrevHash string `json:"block_prev_hash"`
// LogEvent
// }
// func (l *EthNewBlockIsNew) EventName() string {
// return "eth.newblock.is_new"
// }
// type EthNewBlockMissingParent struct {
// BlockNumber int `json:"block_number"`
// HeadHash string `json:"head_hash"`
// BlockHash string `json:"block_hash"`
// BlockDifficulty int `json:"block_difficulty"`
// BlockPrevHash string `json:"block_prev_hash"`
// LogEvent
// }
// func (l *EthNewBlockMissingParent) EventName() string {
// return "eth.newblock.missing_parent"
// }
// type EthNewBlockIsInvalid struct {
// BlockNumber int `json:"block_number"`
// HeadHash string `json:"head_hash"`
// BlockHash string `json:"block_hash"`
// BlockDifficulty int `json:"block_difficulty"`
// BlockPrevHash string `json:"block_prev_hash"`
// LogEvent
// }
// func (l *EthNewBlockIsInvalid) EventName() string {
// return "eth.newblock.is_invalid"
// }
// type EthNewBlockChainIsOlder struct {
// BlockNumber int `json:"block_number"`
// HeadHash string `json:"head_hash"`
// BlockHash string `json:"block_hash"`
// BlockDifficulty int `json:"block_difficulty"`
// BlockPrevHash string `json:"block_prev_hash"`
// LogEvent
// }
// func (l *EthNewBlockChainIsOlder) EventName() string {
// return "eth.newblock.chain.is_older"
// }
// type EthNewBlockChainIsCanonical struct {
// BlockNumber int `json:"block_number"`
// HeadHash string `json:"head_hash"`
// BlockHash string `json:"block_hash"`
// BlockDifficulty int `json:"block_difficulty"`
// BlockPrevHash string `json:"block_prev_hash"`
// LogEvent
// }
// func (l *EthNewBlockChainIsCanonical) EventName() string {
// return "eth.newblock.chain.is_cannonical"
// }
// type EthNewBlockChainNotCanonical struct {
// BlockNumber int `json:"block_number"`
// HeadHash string `json:"head_hash"`
// BlockHash string `json:"block_hash"`
// BlockDifficulty int `json:"block_difficulty"`
// BlockPrevHash string `json:"block_prev_hash"`
// LogEvent
// }
// func (l *EthNewBlockChainNotCanonical) EventName() string {
// return "eth.newblock.chain.not_cannonical"
// }
// type EthTxCreated struct {
// TxHash string `json:"tx_hash"`
// TxSender string `json:"tx_sender"`
// TxAddress string `json:"tx_address"`
// TxHexRLP string `json:"tx_hexrlp"`
// TxNonce int `json:"tx_nonce"`
// LogEvent
// }
// func (l *EthTxCreated) EventName() string {
// return "eth.tx.created"
// }
// type EthTxBroadcasted struct {
// TxHash string `json:"tx_hash"`
// TxSender string `json:"tx_sender"`
// TxAddress string `json:"tx_address"`
// TxNonce int `json:"tx_nonce"`
// LogEvent
// }
// func (l *EthTxBroadcasted) EventName() string {
// return "eth.tx.broadcasted"
// }
// type EthTxValidated struct {
// TxHash string `json:"tx_hash"`
// TxSender string `json:"tx_sender"`
// TxAddress string `json:"tx_address"`
// TxNonce int `json:"tx_nonce"`
// LogEvent
// }
// func (l *EthTxValidated) EventName() string {
// return "eth.tx.validated"
// }
// type EthTxIsInvalid struct {
// TxHash string `json:"tx_hash"`
// TxSender string `json:"tx_sender"`
// TxAddress string `json:"tx_address"`
// Reason string `json:"reason"`
// TxNonce int `json:"tx_nonce"`
// LogEvent
// }
// func (l *EthTxIsInvalid) EventName() string {
// return "eth.tx.is_invalid"
// }

View File

@ -20,13 +20,13 @@ type Miner struct {
mining bool mining bool
} }
func New(coinbase []byte, eth core.Backend) *Miner { func New(coinbase []byte, eth core.Backend, minerThreads int) *Miner {
miner := &Miner{ miner := &Miner{
Coinbase: coinbase, Coinbase: coinbase,
worker: newWorker(coinbase, eth), worker: newWorker(coinbase, eth),
} }
for i := 0; i < 4; i++ { for i := 0; i < minerThreads; i++ {
miner.worker.register(NewCpuMiner(i, ezp.New())) miner.worker.register(NewCpuMiner(i, ezp.New()))
} }

View File

@ -109,14 +109,18 @@ func (self *worker) register(agent Agent) {
} }
func (self *worker) update() { func (self *worker) update() {
events := self.mux.Subscribe(core.ChainEvent{}, core.TxPreEvent{}) events := self.mux.Subscribe(core.ChainEvent{}, core.NewMinedBlockEvent{})
out: out:
for { for {
select { select {
case event := <-events.Chan(): case event := <-events.Chan():
switch event.(type) { switch ev := event.(type) {
case core.ChainEvent, core.TxPreEvent: case core.ChainEvent:
if self.current.block != ev.Block {
self.commitNewWork()
}
case core.NewMinedBlockEvent:
self.commitNewWork() self.commitNewWork()
} }
case <-self.quit: case <-self.quit:
@ -172,17 +176,19 @@ func (self *worker) commitNewWork() {
transactions := self.eth.TxPool().GetTransactions() transactions := self.eth.TxPool().GetTransactions()
sort.Sort(types.TxByNonce{transactions}) sort.Sort(types.TxByNonce{transactions})
minerlogger.Infof("committing new work with %d txs\n", len(transactions))
// Keep track of transactions which return errors so they can be removed // Keep track of transactions which return errors so they can be removed
var remove types.Transactions var remove types.Transactions
gasLimit:
for _, tx := range transactions { for _, tx := range transactions {
err := self.commitTransaction(tx) err := self.commitTransaction(tx)
switch { switch {
case core.IsNonceErr(err): case core.IsNonceErr(err):
// Remove invalid transactions // Remove invalid transactions
remove = append(remove, tx) remove = append(remove, tx)
case core.IsGasLimitErr(err): case state.IsGasLimitErr(err):
// Break on gas limit // Break on gas limit
break break gasLimit
} }
if err != nil { if err != nil {
@ -227,11 +233,9 @@ func (self *worker) commitUncle(uncle *types.Header) error {
} }
func (self *worker) commitTransaction(tx *types.Transaction) error { func (self *worker) commitTransaction(tx *types.Transaction) error {
snapshot := self.current.state.Copy() //fmt.Printf("proc %x %v\n", tx.Hash()[:3], tx.Nonce())
receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true) receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true)
if err != nil && (core.IsNonceErr(err) || core.IsGasLimitErr(err)) { if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err)) {
self.current.state.Set(snapshot)
return err return err
} }

View File

@ -253,7 +253,8 @@ func (t *udp) loop() {
case reply := <-t.replies: case reply := <-t.replies:
// run matching callbacks, remove if they return false. // run matching callbacks, remove if they return false.
for i, p := range pending { for i := 0; i < len(pending); i++ {
p := pending[i]
if reply.from == p.from && reply.ptype == p.ptype && p.callback(reply.data) { if reply.from == p.from && reply.ptype == p.ptype && p.callback(reply.data) {
p.errc <- nil p.errc <- nil
copy(pending[i:], pending[i+1:]) copy(pending[i:], pending[i+1:])

View File

@ -1,21 +1,20 @@
package p2p package p2p
import ( import (
// "binary"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand" "crypto/rand"
"errors"
"fmt" "fmt"
"io" "io"
"net"
"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/crypto/secp256k1" "github.com/ethereum/go-ethereum/crypto/secp256k1"
ethlogger "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/rlp"
) )
var clogger = ethlogger.NewLogger("CRYPTOID")
const ( const (
sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2 sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2
sigLen = 65 // elliptic S256 sigLen = 65 // elliptic S256
@ -30,26 +29,76 @@ const (
rHSLen = authRespLen + eciesBytes // size of the final ECIES payload sent as receiver's handshake rHSLen = authRespLen + eciesBytes // size of the final ECIES payload sent as receiver's handshake
) )
type hexkey []byte type conn struct {
*frameRW
func (self hexkey) String() string { *protoHandshake
return fmt.Sprintf("(%d) %x", len(self), []byte(self))
} }
func encHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, dial *discover.Node) ( func newConn(fd net.Conn, hs *protoHandshake) *conn {
remoteID discover.NodeID, return &conn{newFrameRW(fd, msgWriteTimeout), hs}
sessionToken []byte, }
err error,
) { // encHandshake represents information about the remote end
// of a connection that is negotiated during the encryption handshake.
type encHandshake struct {
ID discover.NodeID
IngressMAC []byte
EgressMAC []byte
Token []byte
}
// protoHandshake is the RLP structure of the protocol handshake.
type protoHandshake struct {
Version uint64
Name string
Caps []Cap
ListenPort uint64
ID discover.NodeID
}
// setupConn starts a protocol session on the given connection.
// It runs the encryption handshake and the protocol handshake.
// If dial is non-nil, the connection the local node is the initiator.
func setupConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, dial *discover.Node) (*conn, error) {
if dial == nil { if dial == nil {
var remotePubkey []byte return setupInboundConn(fd, prv, our)
sessionToken, remotePubkey, err = inboundEncHandshake(conn, prv, nil)
copy(remoteID[:], remotePubkey)
} else { } else {
remoteID = dial.ID return setupOutboundConn(fd, prv, our, dial)
sessionToken, err = outboundEncHandshake(conn, prv, remoteID[:], nil)
} }
return remoteID, sessionToken, err }
func setupInboundConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake) (*conn, error) {
// var remotePubkey []byte
// sessionToken, remotePubkey, err = inboundEncHandshake(fd, prv, nil)
// copy(remoteID[:], remotePubkey)
rw := newFrameRW(fd, msgWriteTimeout)
rhs, err := readProtocolHandshake(rw, our)
if err != nil {
return nil, err
}
if err := writeProtocolHandshake(rw, our); err != nil {
return nil, fmt.Errorf("protocol write error: %v", err)
}
return &conn{rw, rhs}, nil
}
func setupOutboundConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, dial *discover.Node) (*conn, error) {
// remoteID = dial.ID
// sessionToken, err = outboundEncHandshake(fd, prv, remoteID[:], nil)
rw := newFrameRW(fd, msgWriteTimeout)
if err := writeProtocolHandshake(rw, our); err != nil {
return nil, fmt.Errorf("protocol write error: %v", err)
}
rhs, err := readProtocolHandshake(rw, our)
if err != nil {
return nil, fmt.Errorf("protocol handshake read error: %v", err)
}
if rhs.ID != dial.ID {
return nil, errors.New("dialed node id mismatch")
}
return &conn{rw, rhs}, nil
} }
// outboundEncHandshake negotiates a session token on conn. // outboundEncHandshake negotiates a session token on conn.
@ -66,18 +115,9 @@ func outboundEncHandshake(conn io.ReadWriter, prvKey *ecdsa.PrivateKey, remotePu
if err != nil { if err != nil {
return nil, err return nil, err
} }
if sessionToken != nil {
clogger.Debugf("session-token: %v", hexkey(sessionToken))
}
clogger.Debugf("initiator-nonce: %v", hexkey(initNonce))
clogger.Debugf("initiator-random-private-key: %v", hexkey(crypto.FromECDSA(randomPrivKey)))
randomPublicKeyS, _ := exportPublicKey(&randomPrivKey.PublicKey)
clogger.Debugf("initiator-random-public-key: %v", hexkey(randomPublicKeyS))
if _, err = conn.Write(auth); err != nil { if _, err = conn.Write(auth); err != nil {
return nil, err return nil, err
} }
clogger.Debugf("initiator handshake: %v", hexkey(auth))
response := make([]byte, rHSLen) response := make([]byte, rHSLen)
if _, err = io.ReadFull(conn, response); err != nil { if _, err = io.ReadFull(conn, response); err != nil {
@ -88,9 +128,6 @@ func outboundEncHandshake(conn io.ReadWriter, prvKey *ecdsa.PrivateKey, remotePu
return nil, err return nil, err
} }
clogger.Debugf("receiver-nonce: %v", hexkey(recNonce))
remoteRandomPubKeyS, _ := exportPublicKey(remoteRandomPubKey)
clogger.Debugf("receiver-random-public-key: %v", hexkey(remoteRandomPubKeyS))
return newSession(initNonce, recNonce, randomPrivKey, remoteRandomPubKey) return newSession(initNonce, recNonce, randomPrivKey, remoteRandomPubKey)
} }
@ -221,12 +258,9 @@ func inboundEncHandshake(conn io.ReadWriter, prvKey *ecdsa.PrivateKey, sessionTo
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
clogger.Debugf("receiver-nonce: %v", hexkey(recNonce))
clogger.Debugf("receiver-random-priv-key: %v", hexkey(crypto.FromECDSA(randomPrivKey)))
if _, err = conn.Write(response); err != nil { if _, err = conn.Write(response); err != nil {
return nil, nil, err return nil, nil, err
} }
clogger.Debugf("receiver handshake:\n%v", hexkey(response))
token, err = newSession(initNonce, recNonce, randomPrivKey, remoteRandomPubKey) token, err = newSession(initNonce, recNonce, randomPrivKey, remoteRandomPubKey)
return token, remotePubKey, err return token, remotePubKey, err
} }
@ -361,3 +395,40 @@ func xor(one, other []byte) (xor []byte) {
} }
return xor return xor
} }
func writeProtocolHandshake(w MsgWriter, our *protoHandshake) error {
return EncodeMsg(w, handshakeMsg, our.Version, our.Name, our.Caps, our.ListenPort, our.ID[:])
}
func readProtocolHandshake(r MsgReader, our *protoHandshake) (*protoHandshake, error) {
// read and handle remote handshake
msg, err := r.ReadMsg()
if err != nil {
return nil, err
}
if msg.Code == discMsg {
// disconnect before protocol handshake is valid according to the
// spec and we send it ourself if Server.addPeer fails.
var reason DiscReason
rlp.Decode(msg.Payload, &reason)
return nil, discRequestedError(reason)
}
if msg.Code != handshakeMsg {
return nil, fmt.Errorf("expected handshake, got %x", msg.Code)
}
if msg.Size > baseProtocolMaxMsgSize {
return nil, fmt.Errorf("message too big (%d > %d)", msg.Size, baseProtocolMaxMsgSize)
}
var hs protoHandshake
if err := msg.Decode(&hs); err != nil {
return nil, err
}
// validate handshake info
if hs.Version != our.Version {
return nil, newPeerError(errP2PVersionMismatch, "required version %d, received %d\n", baseProtocolVersion, hs.Version)
}
if (hs.ID == discover.NodeID{}) {
return nil, newPeerError(errPubkeyInvalid, "missing")
}
return &hs, nil
}

View File

@ -5,10 +5,12 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand" "crypto/rand"
"net" "net"
"reflect"
"testing" "testing"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/obscuren/ecies" "github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/p2p/discover"
) )
func TestPublicKeyEncoding(t *testing.T) { func TestPublicKeyEncoding(t *testing.T) {
@ -91,14 +93,14 @@ func testCryptoHandshake(prv0, prv1 *ecdsa.PrivateKey, sessionToken []byte, t *t
if err != nil { if err != nil {
t.Errorf("%v", err) t.Errorf("%v", err)
} }
t.Logf("-> %v", hexkey(auth)) // t.Logf("-> %v", hexkey(auth))
// receiver reads auth and responds with response // receiver reads auth and responds with response
response, remoteRecNonce, remoteInitNonce, _, remoteRandomPrivKey, remoteInitRandomPubKey, err := authResp(auth, sessionToken, prv1) response, remoteRecNonce, remoteInitNonce, _, remoteRandomPrivKey, remoteInitRandomPubKey, err := authResp(auth, sessionToken, prv1)
if err != nil { if err != nil {
t.Errorf("%v", err) t.Errorf("%v", err)
} }
t.Logf("<- %v\n", hexkey(response)) // t.Logf("<- %v\n", hexkey(response))
// initiator reads receiver's response and the key exchange completes // initiator reads receiver's response and the key exchange completes
recNonce, remoteRandomPubKey, _, err := completeHandshake(response, prv0) recNonce, remoteRandomPubKey, _, err := completeHandshake(response, prv0)
@ -132,7 +134,7 @@ func testCryptoHandshake(prv0, prv1 *ecdsa.PrivateKey, sessionToken []byte, t *t
} }
} }
func TestHandshake(t *testing.T) { func TestEncHandshake(t *testing.T) {
defer testlog(t).detach() defer testlog(t).detach()
prv0, _ := crypto.GenerateKey() prv0, _ := crypto.GenerateKey()
@ -165,3 +167,58 @@ func TestHandshake(t *testing.T) {
t.Error("session token mismatch") t.Error("session token mismatch")
} }
} }
func TestSetupConn(t *testing.T) {
prv0, _ := crypto.GenerateKey()
prv1, _ := crypto.GenerateKey()
node0 := &discover.Node{
ID: discover.PubkeyID(&prv0.PublicKey),
IP: net.IP{1, 2, 3, 4},
TCPPort: 33,
}
node1 := &discover.Node{
ID: discover.PubkeyID(&prv1.PublicKey),
IP: net.IP{5, 6, 7, 8},
TCPPort: 44,
}
hs0 := &protoHandshake{
Version: baseProtocolVersion,
ID: node0.ID,
Caps: []Cap{{"a", 0}, {"b", 2}},
}
hs1 := &protoHandshake{
Version: baseProtocolVersion,
ID: node1.ID,
Caps: []Cap{{"c", 1}, {"d", 3}},
}
fd0, fd1 := net.Pipe()
done := make(chan struct{})
go func() {
defer close(done)
conn0, err := setupConn(fd0, prv0, hs0, node1)
if err != nil {
t.Errorf("outbound side error: %v", err)
return
}
if conn0.ID != node1.ID {
t.Errorf("outbound conn id mismatch: got %v, want %v", conn0.ID, node1.ID)
}
if !reflect.DeepEqual(conn0.Caps, hs1.Caps) {
t.Errorf("outbound caps mismatch: got %v, want %v", conn0.Caps, hs1.Caps)
}
}()
conn1, err := setupConn(fd1, prv1, hs1, nil)
if err != nil {
t.Fatalf("inbound side error: %v", err)
}
if conn1.ID != node0.ID {
t.Errorf("inbound conn id mismatch: got %v, want %v", conn1.ID, node0.ID)
}
if !reflect.DeepEqual(conn1.Caps, hs0.Caps) {
t.Errorf("inbound caps mismatch: got %v, want %v", conn1.Caps, hs0.Caps)
}
<-done
}

View File

@ -197,7 +197,7 @@ func (rw *frameRW) ReadMsg() (msg Msg, err error) {
return msg, err return msg, err
} }
if !bytes.HasPrefix(start, magicToken) { if !bytes.HasPrefix(start, magicToken) {
return msg, fmt.Errorf("bad magic token %x", start[:4], magicToken) return msg, fmt.Errorf("bad magic token %x", start[:4])
} }
size := binary.BigEndian.Uint32(start[4:]) size := binary.BigEndian.Uint32(start[4:])

View File

@ -7,9 +7,9 @@ import (
"strings" "strings"
"time" "time"
"github.com/fjl/goupnp" "github.com/huin/goupnp"
"github.com/fjl/goupnp/dcps/internetgateway1" "github.com/huin/goupnp/dcps/internetgateway1"
"github.com/fjl/goupnp/dcps/internetgateway2" "github.com/huin/goupnp/dcps/internetgateway2"
) )
type upnp struct { type upnp struct {

View File

@ -21,6 +21,7 @@ const (
baseProtocolMaxMsgSize = 10 * 1024 * 1024 baseProtocolMaxMsgSize = 10 * 1024 * 1024
disconnectGracePeriod = 2 * time.Second disconnectGracePeriod = 2 * time.Second
pingInterval = 15 * time.Second
) )
const ( const (
@ -33,37 +34,14 @@ const (
peersMsg = 0x05 peersMsg = 0x05
) )
// handshake is the RLP structure of the protocol handshake.
type handshake struct {
Version uint64
Name string
Caps []Cap
ListenPort uint64
NodeID discover.NodeID
}
// Peer represents a connected remote node. // Peer represents a connected remote node.
type Peer struct { type Peer struct {
// Peers have all the log methods. // Peers have all the log methods.
// Use them to display messages related to the peer. // Use them to display messages related to the peer.
*logger.Logger *logger.Logger
infoMu sync.Mutex rw *conn
name string running map[string]*protoRW
caps []Cap
ourID, remoteID *discover.NodeID
ourName string
rw *frameRW
// These fields maintain the running protocols.
protocols []Protocol
runlock sync.RWMutex // protects running
running map[string]*proto
// disables protocol handshake, for testing
noHandshake bool
protoWG sync.WaitGroup protoWG sync.WaitGroup
protoErr chan error protoErr chan error
@ -73,36 +51,27 @@ type Peer struct {
// NewPeer returns a peer for testing purposes. // NewPeer returns a peer for testing purposes.
func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer { func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer {
conn, _ := net.Pipe() pipe, _ := net.Pipe()
peer := newPeer(conn, nil, "", nil, &id) conn := newConn(pipe, &protoHandshake{ID: id, Name: name, Caps: caps})
peer.setHandshakeInfo(name, caps) peer := newPeer(conn, nil)
close(peer.closed) // ensures Disconnect doesn't block close(peer.closed) // ensures Disconnect doesn't block
return peer return peer
} }
// ID returns the node's public key. // ID returns the node's public key.
func (p *Peer) ID() discover.NodeID { func (p *Peer) ID() discover.NodeID {
return *p.remoteID return p.rw.ID
} }
// Name returns the node name that the remote node advertised. // Name returns the node name that the remote node advertised.
func (p *Peer) Name() string { func (p *Peer) Name() string {
// this needs a lock because the information is part of the return p.rw.Name
// protocol handshake.
p.infoMu.Lock()
name := p.name
p.infoMu.Unlock()
return name
} }
// Caps returns the capabilities (supported subprotocols) of the remote peer. // Caps returns the capabilities (supported subprotocols) of the remote peer.
func (p *Peer) Caps() []Cap { func (p *Peer) Caps() []Cap {
// this needs a lock because the information is part of the // TODO: maybe return copy
// protocol handshake. return p.rw.Caps
p.infoMu.Lock()
caps := p.caps
p.infoMu.Unlock()
return caps
} }
// RemoteAddr returns the remote address of the network connection. // RemoteAddr returns the remote address of the network connection.
@ -126,30 +95,20 @@ func (p *Peer) Disconnect(reason DiscReason) {
// String implements fmt.Stringer. // String implements fmt.Stringer.
func (p *Peer) String() string { func (p *Peer) String() string {
return fmt.Sprintf("Peer %.8x %v", p.remoteID[:], p.RemoteAddr()) return fmt.Sprintf("Peer %.8x %v", p.rw.ID[:], p.RemoteAddr())
} }
func newPeer(conn net.Conn, protocols []Protocol, ourName string, ourID, remoteID *discover.NodeID) *Peer { func newPeer(conn *conn, protocols []Protocol) *Peer {
logtag := fmt.Sprintf("Peer %.8x %v", remoteID[:], conn.RemoteAddr()) logtag := fmt.Sprintf("Peer %.8x %v", conn.ID[:], conn.RemoteAddr())
return &Peer{ p := &Peer{
Logger: logger.NewLogger(logtag), Logger: logger.NewLogger(logtag),
rw: newFrameRW(conn, msgWriteTimeout), rw: conn,
ourID: ourID, running: matchProtocols(protocols, conn.Caps, conn),
ourName: ourName, disc: make(chan DiscReason),
remoteID: remoteID, protoErr: make(chan error),
protocols: protocols, closed: make(chan struct{}),
running: make(map[string]*proto),
disc: make(chan DiscReason),
protoErr: make(chan error),
closed: make(chan struct{}),
} }
} return p
func (p *Peer) setHandshakeInfo(name string, caps []Cap) {
p.infoMu.Lock()
p.name = name
p.caps = caps
p.infoMu.Unlock()
} }
func (p *Peer) run() DiscReason { func (p *Peer) run() DiscReason {
@ -157,29 +116,36 @@ func (p *Peer) run() DiscReason {
defer p.closeProtocols() defer p.closeProtocols()
defer close(p.closed) defer close(p.closed)
p.startProtocols()
go func() { readErr <- p.readLoop() }() go func() { readErr <- p.readLoop() }()
if !p.noHandshake { ping := time.NewTicker(pingInterval)
if err := writeProtocolHandshake(p.rw, p.ourName, *p.ourID, p.protocols); err != nil { defer ping.Stop()
p.DebugDetailf("Protocol handshake error: %v\n", err)
p.rw.Close()
return DiscProtocolError
}
}
// Wait for an error or disconnect. // Wait for an error or disconnect.
var reason DiscReason var reason DiscReason
select { loop:
case err := <-readErr: for {
// We rely on protocols to abort if there is a write error. It select {
// might be more robust to handle them here as well. case <-ping.C:
p.DebugDetailf("Read error: %v\n", err) go func() {
p.rw.Close() if err := EncodeMsg(p.rw, pingMsg, nil); err != nil {
return DiscNetworkError p.protoErr <- err
return
case err := <-p.protoErr: }
reason = discReasonForError(err) }()
case reason = <-p.disc: case err := <-readErr:
// We rely on protocols to abort if there is a write error. It
// might be more robust to handle them here as well.
p.DebugDetailf("Read error: %v\n", err)
p.rw.Close()
return DiscNetworkError
case err := <-p.protoErr:
reason = discReasonForError(err)
break loop
case reason = <-p.disc:
break loop
}
} }
p.politeDisconnect(reason) p.politeDisconnect(reason)
@ -206,11 +172,6 @@ func (p *Peer) politeDisconnect(reason DiscReason) {
} }
func (p *Peer) readLoop() error { func (p *Peer) readLoop() error {
if !p.noHandshake {
if err := readProtocolHandshake(p, p.rw); err != nil {
return err
}
}
for { for {
msg, err := p.rw.ReadMsg() msg, err := p.rw.ReadMsg()
if err != nil { if err != nil {
@ -249,105 +210,51 @@ func (p *Peer) handle(msg Msg) error {
return nil return nil
} }
func readProtocolHandshake(p *Peer, rw MsgReadWriter) error { // matchProtocols creates structures for matching named subprotocols.
// read and handle remote handshake func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW {
msg, err := rw.ReadMsg()
if err != nil {
return err
}
if msg.Code == discMsg {
// disconnect before protocol handshake is valid according to the
// spec and we send it ourself if Server.addPeer fails.
var reason DiscReason
rlp.Decode(msg.Payload, &reason)
return discRequestedError(reason)
}
if msg.Code != handshakeMsg {
return newPeerError(errProtocolBreach, "expected handshake, got %x", msg.Code)
}
if msg.Size > baseProtocolMaxMsgSize {
return newPeerError(errInvalidMsg, "message too big")
}
var hs handshake
if err := msg.Decode(&hs); err != nil {
return err
}
// validate handshake info
if hs.Version != baseProtocolVersion {
return newPeerError(errP2PVersionMismatch, "required version %d, received %d\n",
baseProtocolVersion, hs.Version)
}
if hs.NodeID == *p.remoteID {
return newPeerError(errPubkeyForbidden, "node ID mismatch")
}
// TODO: remove Caps with empty name
p.setHandshakeInfo(hs.Name, hs.Caps)
p.startSubprotocols(hs.Caps)
return nil
}
func writeProtocolHandshake(w MsgWriter, name string, id discover.NodeID, ps []Protocol) error {
var caps []interface{}
for _, proto := range ps {
caps = append(caps, proto.cap())
}
return EncodeMsg(w, handshakeMsg, baseProtocolVersion, name, caps, 0, id)
}
// startProtocols starts matching named subprotocols.
func (p *Peer) startSubprotocols(caps []Cap) {
sort.Sort(capsByName(caps)) sort.Sort(capsByName(caps))
p.runlock.Lock()
defer p.runlock.Unlock()
offset := baseProtocolLength offset := baseProtocolLength
result := make(map[string]*protoRW)
outer: outer:
for _, cap := range caps { for _, cap := range caps {
for _, proto := range p.protocols { for _, proto := range protocols {
if proto.Name == cap.Name && if proto.Name == cap.Name && proto.Version == cap.Version && result[cap.Name] == nil {
proto.Version == cap.Version && result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw}
p.running[cap.Name] == nil {
p.running[cap.Name] = p.startProto(offset, proto)
offset += proto.Length offset += proto.Length
continue outer continue outer
} }
} }
} }
return result
} }
func (p *Peer) startProto(offset uint64, impl Protocol) *proto { func (p *Peer) startProtocols() {
p.DebugDetailf("Starting protocol %s/%d\n", impl.Name, impl.Version) for _, proto := range p.running {
rw := &proto{ proto := proto
name: impl.Name, p.DebugDetailf("Starting protocol %s/%d\n", proto.Name, proto.Version)
in: make(chan Msg), p.protoWG.Add(1)
offset: offset, go func() {
maxcode: impl.Length, err := proto.Run(p, proto)
w: p.rw, if err == nil {
p.DebugDetailf("Protocol %s/%d returned\n", proto.Name, proto.Version)
err = errors.New("protocol returned")
} else {
p.DebugDetailf("Protocol %s/%d error: %v\n", proto.Name, proto.Version, err)
}
select {
case p.protoErr <- err:
case <-p.closed:
}
p.protoWG.Done()
}()
} }
p.protoWG.Add(1)
go func() {
err := impl.Run(p, rw)
if err == nil {
p.DebugDetailf("Protocol %s/%d returned\n", impl.Name, impl.Version)
err = errors.New("protocol returned")
} else {
p.DebugDetailf("Protocol %s/%d error: %v\n", impl.Name, impl.Version, err)
}
select {
case p.protoErr <- err:
case <-p.closed:
}
p.protoWG.Done()
}()
return rw
} }
// getProto finds the protocol responsible for handling // getProto finds the protocol responsible for handling
// the given message code. // the given message code.
func (p *Peer) getProto(code uint64) (*proto, error) { func (p *Peer) getProto(code uint64) (*protoRW, error) {
p.runlock.RLock()
defer p.runlock.RUnlock()
for _, proto := range p.running { for _, proto := range p.running {
if code >= proto.offset && code < proto.offset+proto.maxcode { if code >= proto.offset && code < proto.offset+proto.Length {
return proto, nil return proto, nil
} }
} }
@ -355,46 +262,43 @@ func (p *Peer) getProto(code uint64) (*proto, error) {
} }
func (p *Peer) closeProtocols() { func (p *Peer) closeProtocols() {
p.runlock.RLock()
for _, p := range p.running { for _, p := range p.running {
close(p.in) close(p.in)
} }
p.runlock.RUnlock()
p.protoWG.Wait() p.protoWG.Wait()
} }
// writeProtoMsg sends the given message on behalf of the given named protocol. // writeProtoMsg sends the given message on behalf of the given named protocol.
// this exists because of Server.Broadcast. // this exists because of Server.Broadcast.
func (p *Peer) writeProtoMsg(protoName string, msg Msg) error { func (p *Peer) writeProtoMsg(protoName string, msg Msg) error {
p.runlock.RLock()
proto, ok := p.running[protoName] proto, ok := p.running[protoName]
p.runlock.RUnlock()
if !ok { if !ok {
return fmt.Errorf("protocol %s not handled by peer", protoName) return fmt.Errorf("protocol %s not handled by peer", protoName)
} }
if msg.Code >= proto.maxcode { if msg.Code >= proto.Length {
return newPeerError(errInvalidMsgCode, "code %x is out of range for protocol %q", msg.Code, protoName) return newPeerError(errInvalidMsgCode, "code %x is out of range for protocol %q", msg.Code, protoName)
} }
msg.Code += proto.offset msg.Code += proto.offset
return p.rw.WriteMsg(msg) return p.rw.WriteMsg(msg)
} }
type proto struct { type protoRW struct {
name string Protocol
in chan Msg
maxcode, offset uint64 in chan Msg
w MsgWriter offset uint64
w MsgWriter
} }
func (rw *proto) WriteMsg(msg Msg) error { func (rw *protoRW) WriteMsg(msg Msg) error {
if msg.Code >= rw.maxcode { if msg.Code >= rw.Length {
return newPeerError(errInvalidMsgCode, "not handled") return newPeerError(errInvalidMsgCode, "not handled")
} }
msg.Code += rw.offset msg.Code += rw.offset
return rw.w.WriteMsg(msg) return rw.w.WriteMsg(msg)
} }
func (rw *proto) ReadMsg() (Msg, error) { func (rw *protoRW) ReadMsg() (Msg, error) {
msg, ok := <-rw.in msg, ok := <-rw.in
if !ok { if !ok {
return msg, io.EOF return msg, io.EOF

View File

@ -6,11 +6,9 @@ import (
"io/ioutil" "io/ioutil"
"net" "net"
"reflect" "reflect"
"sort"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -23,6 +21,7 @@ var discard = Protocol{
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("discarding %d\n", msg.Code)
if err = msg.Discard(); err != nil { if err = msg.Discard(); err != nil {
return err return err
} }
@ -30,13 +29,20 @@ var discard = Protocol{
}, },
} }
func testPeer(noHandshake bool, protos []Protocol) (*frameRW, *Peer, <-chan DiscReason) { func testPeer(protos []Protocol) (*conn, *Peer, <-chan DiscReason) {
conn1, conn2 := net.Pipe() fd1, fd2 := net.Pipe()
peer := newPeer(conn1, protos, "name", &discover.NodeID{}, &discover.NodeID{}) hs1 := &protoHandshake{ID: randomID(), Version: baseProtocolVersion}
peer.noHandshake = noHandshake hs2 := &protoHandshake{ID: randomID(), Version: baseProtocolVersion}
for _, p := range protos {
hs1.Caps = append(hs1.Caps, p.cap())
hs2.Caps = append(hs2.Caps, p.cap())
}
peer := newPeer(newConn(fd1, hs1), protos)
errc := make(chan DiscReason, 1) errc := make(chan DiscReason, 1)
go func() { errc <- peer.run() }() go func() { errc <- peer.run() }()
return newFrameRW(conn2, msgWriteTimeout), peer, errc
return newConn(fd2, hs2), peer, errc
} }
func TestPeerProtoReadMsg(t *testing.T) { func TestPeerProtoReadMsg(t *testing.T) {
@ -61,9 +67,8 @@ func TestPeerProtoReadMsg(t *testing.T) {
}, },
} }
rw, peer, errc := testPeer(true, []Protocol{proto}) rw, _, errc := testPeer([]Protocol{proto})
defer rw.Close() defer rw.Close()
peer.startSubprotocols([]Cap{proto.cap()})
EncodeMsg(rw, baseProtocolLength+2, 1) EncodeMsg(rw, baseProtocolLength+2, 1)
EncodeMsg(rw, baseProtocolLength+3, 2) EncodeMsg(rw, baseProtocolLength+3, 2)
@ -100,9 +105,8 @@ func TestPeerProtoReadLargeMsg(t *testing.T) {
}, },
} }
rw, peer, errc := testPeer(true, []Protocol{proto}) rw, _, errc := testPeer([]Protocol{proto})
defer rw.Close() defer rw.Close()
peer.startSubprotocols([]Cap{proto.cap()})
EncodeMsg(rw, 18, make([]byte, msgsize)) EncodeMsg(rw, 18, make([]byte, msgsize))
select { select {
@ -130,9 +134,8 @@ func TestPeerProtoEncodeMsg(t *testing.T) {
return nil return nil
}, },
} }
rw, peer, _ := testPeer(true, []Protocol{proto}) rw, _, _ := testPeer([]Protocol{proto})
defer rw.Close() defer rw.Close()
peer.startSubprotocols([]Cap{proto.cap()})
if err := expectMsg(rw, 17, []string{"foo", "bar"}); err != nil { if err := expectMsg(rw, 17, []string{"foo", "bar"}); err != nil {
t.Error(err) t.Error(err)
@ -142,9 +145,8 @@ func TestPeerProtoEncodeMsg(t *testing.T) {
func TestPeerWriteForBroadcast(t *testing.T) { func TestPeerWriteForBroadcast(t *testing.T) {
defer testlog(t).detach() defer testlog(t).detach()
rw, peer, peerErr := testPeer(true, []Protocol{discard}) rw, peer, peerErr := testPeer([]Protocol{discard})
defer rw.Close() defer rw.Close()
peer.startSubprotocols([]Cap{discard.cap()})
// test write errors // test write errors
if err := peer.writeProtoMsg("b", NewMsg(3)); err == nil { if err := peer.writeProtoMsg("b", NewMsg(3)); err == nil {
@ -160,7 +162,7 @@ func TestPeerWriteForBroadcast(t *testing.T) {
read := make(chan struct{}) read := make(chan struct{})
go func() { go func() {
if err := expectMsg(rw, 16, nil); err != nil { if err := expectMsg(rw, 16, nil); err != nil {
t.Error() t.Error(err)
} }
close(read) close(read)
}() }()
@ -179,7 +181,7 @@ func TestPeerWriteForBroadcast(t *testing.T) {
func TestPeerPing(t *testing.T) { func TestPeerPing(t *testing.T) {
defer testlog(t).detach() defer testlog(t).detach()
rw, _, _ := testPeer(true, nil) rw, _, _ := testPeer(nil)
defer rw.Close() defer rw.Close()
if err := EncodeMsg(rw, pingMsg); err != nil { if err := EncodeMsg(rw, pingMsg); err != nil {
t.Fatal(err) t.Fatal(err)
@ -192,7 +194,7 @@ func TestPeerPing(t *testing.T) {
func TestPeerDisconnect(t *testing.T) { func TestPeerDisconnect(t *testing.T) {
defer testlog(t).detach() defer testlog(t).detach()
rw, _, disc := testPeer(true, nil) rw, _, disc := testPeer(nil)
defer rw.Close() defer rw.Close()
if err := EncodeMsg(rw, discMsg, DiscQuitting); err != nil { if err := EncodeMsg(rw, discMsg, DiscQuitting); err != nil {
t.Fatal(err) t.Fatal(err)
@ -206,73 +208,6 @@ func TestPeerDisconnect(t *testing.T) {
} }
} }
func TestPeerHandshake(t *testing.T) {
defer testlog(t).detach()
// remote has two matching protocols: a and c
remote := NewPeer(randomID(), "", []Cap{{"a", 1}, {"b", 999}, {"c", 3}})
remoteID := randomID()
remote.ourID = &remoteID
remote.ourName = "remote peer"
start := make(chan string)
stop := make(chan struct{})
run := func(p *Peer, rw MsgReadWriter) error {
name := rw.(*proto).name
if name != "a" && name != "c" {
t.Errorf("protocol %q should not be started", name)
} else {
start <- name
}
<-stop
return nil
}
protocols := []Protocol{
{Name: "a", Version: 1, Length: 1, Run: run},
{Name: "b", Version: 2, Length: 1, Run: run},
{Name: "c", Version: 3, Length: 1, Run: run},
{Name: "d", Version: 4, Length: 1, Run: run},
}
rw, p, disc := testPeer(false, protocols)
p.remoteID = remote.ourID
defer rw.Close()
// run the handshake
remoteProtocols := []Protocol{protocols[0], protocols[2]}
if err := writeProtocolHandshake(rw, "remote peer", remoteID, remoteProtocols); err != nil {
t.Fatalf("handshake write error: %v", err)
}
if err := readProtocolHandshake(remote, rw); err != nil {
t.Fatalf("handshake read error: %v", err)
}
// check that all protocols have been started
var started []string
for i := 0; i < 2; i++ {
select {
case name := <-start:
started = append(started, name)
case <-time.After(100 * time.Millisecond):
}
}
sort.Strings(started)
if !reflect.DeepEqual(started, []string{"a", "c"}) {
t.Errorf("wrong protocols started: %v", started)
}
// check that metadata has been set
if p.ID() != remoteID {
t.Errorf("peer has wrong node ID: got %v, want %v", p.ID(), remoteID)
}
if p.Name() != remote.ourName {
t.Errorf("peer has wrong node name: got %q, want %q", p.Name(), remote.ourName)
}
close(stop)
expectMsg(rw, discMsg, nil)
t.Logf("disc reason: %v", <-disc)
}
func TestNewPeer(t *testing.T) { func TestNewPeer(t *testing.T) {
name := "nodename" name := "nodename"
caps := []Cap{{"foo", 2}, {"bar", 3}} caps := []Cap{{"foo", 2}, {"bar", 3}}

View File

@ -5,7 +5,6 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"errors" "errors"
"fmt" "fmt"
"io"
"net" "net"
"runtime" "runtime"
"sync" "sync"
@ -23,6 +22,7 @@ const (
) )
var srvlog = logger.NewLogger("P2P Server") var srvlog = logger.NewLogger("P2P Server")
var srvjslog = logger.NewJsonLogger()
// MakeName creates a node name that follows the ethereum convention // MakeName creates a node name that follows the ethereum convention
// for such names. It adds the operation system name and Go runtime version // for such names. It adds the operation system name and Go runtime version
@ -83,9 +83,11 @@ type Server struct {
// Hooks for testing. These are useful because we can inhibit // Hooks for testing. These are useful because we can inhibit
// the whole protocol stack. // the whole protocol stack.
handshakeFunc setupFunc
newPeerHook newPeerHook
ourHandshake *protoHandshake
lock sync.RWMutex lock sync.RWMutex
running bool running bool
listener net.Listener listener net.Listener
@ -99,7 +101,7 @@ type Server struct {
peerConnect chan *discover.Node peerConnect chan *discover.Node
} }
type handshakeFunc func(io.ReadWriter, *ecdsa.PrivateKey, *discover.Node) (discover.NodeID, []byte, error) type setupFunc func(net.Conn, *ecdsa.PrivateKey, *protoHandshake, *discover.Node) (*conn, error)
type newPeerHook func(*Peer) type newPeerHook func(*Peer)
// Peers returns all connected peers. // Peers returns all connected peers.
@ -159,7 +161,7 @@ func (srv *Server) Start() (err error) {
} }
srvlog.Infoln("Starting Server") srvlog.Infoln("Starting Server")
// initialize all the fields // static fields
if srv.PrivateKey == nil { if srv.PrivateKey == nil {
return fmt.Errorf("Server.PrivateKey must be set to a non-nil key") return fmt.Errorf("Server.PrivateKey must be set to a non-nil key")
} }
@ -169,25 +171,32 @@ func (srv *Server) Start() (err error) {
srv.quit = make(chan struct{}) srv.quit = make(chan struct{})
srv.peers = make(map[discover.NodeID]*Peer) srv.peers = make(map[discover.NodeID]*Peer)
srv.peerConnect = make(chan *discover.Node) srv.peerConnect = make(chan *discover.Node)
if srv.setupFunc == nil {
if srv.handshakeFunc == nil { srv.setupFunc = setupConn
srv.handshakeFunc = encHandshake
} }
if srv.Blacklist == nil { if srv.Blacklist == nil {
srv.Blacklist = NewBlacklist() srv.Blacklist = NewBlacklist()
} }
// node table
ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT)
if err != nil {
return err
}
srv.ntab = ntab
// handshake
srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: ntab.Self()}
for _, p := range srv.Protocols {
srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap())
}
// listen/dial
if srv.ListenAddr != "" { if srv.ListenAddr != "" {
if err := srv.startListening(); err != nil { if err := srv.startListening(); err != nil {
return err return err
} }
} }
// dial stuff
dt, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT)
if err != nil {
return err
}
srv.ntab = dt
if srv.Dialer == nil { if srv.Dialer == nil {
srv.Dialer = &net.Dialer{Timeout: defaultDialTimeout} srv.Dialer = &net.Dialer{Timeout: defaultDialTimeout}
} }
@ -347,30 +356,41 @@ func (srv *Server) findPeers() {
} }
} }
func (srv *Server) startPeer(conn net.Conn, dest *discover.Node) { func (srv *Server) startPeer(fd net.Conn, dest *discover.Node) {
// TODO: handle/store session token // TODO: handle/store session token
conn.SetDeadline(time.Now().Add(handshakeTimeout)) fd.SetDeadline(time.Now().Add(handshakeTimeout))
remoteID, _, err := srv.handshakeFunc(conn, srv.PrivateKey, dest) conn, err := srv.setupFunc(fd, srv.PrivateKey, srv.ourHandshake, dest)
if err != nil { if err != nil {
conn.Close() fd.Close()
srvlog.Debugf("Encryption Handshake with %v failed: %v", conn.RemoteAddr(), err) srvlog.Debugf("Handshake with %v failed: %v", fd.RemoteAddr(), err)
return return
} }
ourID := srv.ntab.Self() p := newPeer(conn, srv.Protocols)
p := newPeer(conn, srv.Protocols, srv.Name, &ourID, &remoteID) if ok, reason := srv.addPeer(conn.ID, p); !ok {
if ok, reason := srv.addPeer(remoteID, p); !ok {
srvlog.DebugDetailf("Not adding %v (%v)\n", p, reason) srvlog.DebugDetailf("Not adding %v (%v)\n", p, reason)
p.politeDisconnect(reason) p.politeDisconnect(reason)
return return
} }
srvlog.Debugf("Added %v\n", p) srvlog.Debugf("Added %v\n", p)
srvjslog.LogJson(&logger.P2PConnected{
RemoteId: fmt.Sprintf("%x", conn.ID[:]),
RemoteAddress: conn.RemoteAddr().String(),
RemoteVersionString: conn.Name,
NumConnections: srv.PeerCount(),
})
if srv.newPeerHook != nil { if srv.newPeerHook != nil {
srv.newPeerHook(p) srv.newPeerHook(p)
} }
discreason := p.run() discreason := p.run()
srv.removePeer(p) srv.removePeer(p)
srvlog.Debugf("Removed %v (%v)\n", p, discreason) srvlog.Debugf("Removed %v (%v)\n", p, discreason)
srvjslog.LogJson(&logger.P2PDisconnected{
RemoteId: fmt.Sprintf("%x", conn.ID[:]),
NumConnections: srv.PeerCount(),
})
} }
func (srv *Server) addPeer(id discover.NodeID, p *Peer) (bool, DiscReason) { func (srv *Server) addPeer(id discover.NodeID, p *Peer) (bool, DiscReason) {
@ -394,7 +414,7 @@ func (srv *Server) addPeer(id discover.NodeID, p *Peer) (bool, DiscReason) {
func (srv *Server) removePeer(p *Peer) { func (srv *Server) removePeer(p *Peer) {
srv.lock.Lock() srv.lock.Lock()
delete(srv.peers, *p.remoteID) delete(srv.peers, p.ID())
srv.lock.Unlock() srv.lock.Unlock()
srv.peerWG.Done() srv.peerWG.Done()
} }

View File

@ -21,8 +21,12 @@ func startTestServer(t *testing.T, pf newPeerHook) *Server {
ListenAddr: "127.0.0.1:0", ListenAddr: "127.0.0.1:0",
PrivateKey: newkey(), PrivateKey: newkey(),
newPeerHook: pf, newPeerHook: pf,
handshakeFunc: func(io.ReadWriter, *ecdsa.PrivateKey, *discover.Node) (id discover.NodeID, st []byte, err error) { setupFunc: func(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, dial *discover.Node) (*conn, error) {
return randomID(), nil, err id := randomID()
return &conn{
frameRW: newFrameRW(fd, msgWriteTimeout),
protoHandshake: &protoHandshake{ID: id, Version: baseProtocolVersion},
}, nil
}, },
} }
if err := server.Start(); err != nil { if err := server.Start(); err != nil {
@ -116,9 +120,7 @@ func TestServerBroadcast(t *testing.T) {
var connected sync.WaitGroup var connected sync.WaitGroup
srv := startTestServer(t, func(p *Peer) { srv := startTestServer(t, func(p *Peer) {
p.protocols = []Protocol{discard} p.running = matchProtocols([]Protocol{discard}, []Cap{discard.cap()}, p.rw)
p.startSubprotocols([]Cap{discard.cap()})
p.noHandshake = true
connected.Done() connected.Done()
}) })
defer srv.Stop() defer srv.Stop()

View File

@ -1,6 +1,7 @@
package rpc package rpc
import "encoding/json" import "encoding/json"
import "github.com/ethereum/go-ethereum/core" import "github.com/ethereum/go-ethereum/core"
type GetBlockArgs struct { type GetBlockArgs struct {
@ -37,6 +38,35 @@ type NewTxArgs struct {
Data string `json:"data"` Data string `json:"data"`
} }
func (obj *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
// Data can be either specified as "data" or "code" :-/
var ext struct {
From string
To string
Value string
Gas string
GasPrice string
Data string
Code string
}
if err = json.Unmarshal(b, &ext); err == nil {
if len(ext.Data) == 0 {
ext.Data = ext.Code
}
obj.From = ext.From
obj.To = ext.To
obj.Value = ext.Value
obj.Gas = ext.Gas
obj.GasPrice = ext.GasPrice
obj.Data = ext.Data
return
}
return NewErrorResponse(ErrorDecodeArgs)
}
type PushTxArgs struct { type PushTxArgs struct {
Tx string `json:"tx"` Tx string `json:"tx"`
} }
@ -203,7 +233,7 @@ func (obj *Sha3Args) UnmarshalJSON(b []byte) (err error) {
type FilterOptions struct { type FilterOptions struct {
Earliest int64 Earliest int64
Latest int64 Latest int64
Address string Address interface{}
Topic []string Topic []string
Skip int Skip int
Max int Max int
@ -211,9 +241,22 @@ type FilterOptions struct {
func toFilterOptions(options *FilterOptions) core.FilterOptions { func toFilterOptions(options *FilterOptions) core.FilterOptions {
var opts core.FilterOptions var opts core.FilterOptions
// Convert optional address slice/string to byte slice
if str, ok := options.Address.(string); ok {
opts.Address = [][]byte{fromHex(str)}
} else if slice, ok := options.Address.([]interface{}); ok {
bslice := make([][]byte, len(slice))
for i, addr := range slice {
if saddr, ok := addr.(string); ok {
bslice[i] = fromHex(saddr)
}
}
opts.Address = bslice
}
opts.Earliest = options.Earliest opts.Earliest = options.Earliest
opts.Latest = options.Latest opts.Latest = options.Latest
opts.Address = fromHex(options.Address)
opts.Topics = make([][]byte, len(options.Topic)) opts.Topics = make([][]byte, len(options.Topic))
for i, topic := range options.Topic { for i, topic := range options.Topic {
opts.Topics[i] = fromHex(topic) opts.Topics[i] = fromHex(topic)
@ -246,7 +289,7 @@ type WhisperMessageArgs struct {
Payload string Payload string
To string To string
From string From string
Topics []string Topic []string
Priority uint32 Priority uint32
Ttl uint32 Ttl uint32
} }

View File

@ -201,6 +201,36 @@ func (req *RpcRequest) ToGetCodeAtArgs() (*GetCodeAtArgs, error) {
return args, nil return args, nil
} }
func (req *RpcRequest) ToBoolArgs() (bool, error) {
if len(req.Params) < 1 {
return false, NewErrorResponse(ErrorArguments)
}
var args bool
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return false, NewErrorResponse(ErrorDecodeArgs)
}
rpclogger.DebugDetailf("%T %v", args, args)
return args, nil
}
func (req *RpcRequest) ToCompileArgs() (string, error) {
if len(req.Params) < 1 {
return "", NewErrorResponse(ErrorArguments)
}
var args string
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return "", NewErrorResponse(ErrorDecodeArgs)
}
rpclogger.DebugDetailf("%T %v", args, args)
return args, nil
}
func (req *RpcRequest) ToFilterArgs() (*FilterOptions, error) { func (req *RpcRequest) ToFilterArgs() (*FilterOptions, error) {
if len(req.Params) < 1 { if len(req.Params) < 1 {
return nil, NewErrorResponse(ErrorArguments) return nil, NewErrorResponse(ErrorArguments)
@ -231,6 +261,21 @@ func (req *RpcRequest) ToFilterStringArgs() (string, error) {
return args, nil return args, nil
} }
func (req *RpcRequest) ToUninstallFilterArgs() (int, error) {
if len(req.Params) < 1 {
return 0, NewErrorResponse(ErrorArguments)
}
var args int
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return 0, NewErrorResponse(ErrorDecodeArgs)
}
rpclogger.DebugDetailf("%T %v", args, args)
return args, nil
}
func (req *RpcRequest) ToFilterChangedArgs() (int, error) { func (req *RpcRequest) ToFilterChangedArgs() (int, error) {
if len(req.Params) < 1 { if len(req.Params) < 1 {
return 0, NewErrorResponse(ErrorArguments) return 0, NewErrorResponse(ErrorArguments)
@ -301,7 +346,7 @@ func (req *RpcRequest) ToWhisperFilterArgs() (*xeth.Options, error) {
return &args, nil return &args, nil
} }
func (req *RpcRequest) ToWhisperIdArgs() (int, error) { func (req *RpcRequest) ToIdArgs() (int, error) {
if len(req.Params) < 1 { if len(req.Params) < 1 {
return 0, NewErrorResponse(ErrorArguments) return 0, NewErrorResponse(ErrorArguments)
} }
@ -342,3 +387,31 @@ func (req *RpcRequest) ToWhisperHasIdentityArgs() (string, error) {
rpclogger.DebugDetailf("%T %v", args, args) rpclogger.DebugDetailf("%T %v", args, args)
return args, nil return args, nil
} }
func (req *RpcRequest) ToRegisterArgs() (string, error) {
if len(req.Params) < 1 {
return "", NewErrorResponse(ErrorArguments)
}
var args string
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return "", err
}
rpclogger.DebugDetailf("%T %v", args, args)
return args, nil
}
func (req *RpcRequest) ToWatchTxArgs() (string, error) {
if len(req.Params) < 1 {
return "", NewErrorResponse(ErrorArguments)
}
var args string
err := json.Unmarshal(req.Params[0], &args)
if err != nil {
return "", err
}
rpclogger.DebugDetailf("%T %v", args, args)
return args, nil
}

View File

@ -1,21 +1,4 @@
/* /*
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 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 General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/*
For each request type, define the following: For each request type, define the following:
1. RpcRequest "To" method [message.go], which does basic validation and conversion to "Args" type via json.Decoder() 1. RpcRequest "To" method [message.go], which does basic validation and conversion to "Args" type via json.Decoder()
@ -30,6 +13,7 @@ import (
"math/big" "math/big"
"strings" "strings"
"sync" "sync"
"time"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -48,13 +32,17 @@ const (
type EthereumApi struct { type EthereumApi struct {
xeth *xeth.XEth xeth *xeth.XEth
quit chan struct{}
filterManager *filter.FilterManager filterManager *filter.FilterManager
logMut sync.RWMutex logMut sync.RWMutex
logs map[int]state.Logs logs map[int]*logFilter
messagesMut sync.RWMutex messagesMut sync.RWMutex
messages map[int][]xeth.WhisperMessage messages map[int]*whisperFilter
// Register keeps a list of accounts and transaction data
regmut sync.Mutex
register map[string][]*NewTxArgs
db ethutil.Database db ethutil.Database
} }
@ -63,16 +51,48 @@ func NewEthereumApi(eth *xeth.XEth) *EthereumApi {
db, _ := ethdb.NewLDBDatabase("dapps") db, _ := ethdb.NewLDBDatabase("dapps")
api := &EthereumApi{ api := &EthereumApi{
xeth: eth, xeth: eth,
quit: make(chan struct{}),
filterManager: filter.NewFilterManager(eth.Backend().EventMux()), filterManager: filter.NewFilterManager(eth.Backend().EventMux()),
logs: make(map[int]state.Logs), logs: make(map[int]*logFilter),
messages: make(map[int][]xeth.WhisperMessage), messages: make(map[int]*whisperFilter),
db: db, db: db,
} }
go api.filterManager.Start() go api.filterManager.Start()
go api.start()
return api return api
} }
func (self *EthereumApi) Register(args string, reply *interface{}) error {
self.regmut.Lock()
defer self.regmut.Unlock()
if _, ok := self.register[args]; ok {
self.register[args] = nil // register with empty
}
return nil
}
func (self *EthereumApi) Unregister(args string, reply *interface{}) error {
self.regmut.Lock()
defer self.regmut.Unlock()
delete(self.register, args)
return nil
}
func (self *EthereumApi) WatchTx(args string, reply *interface{}) error {
self.regmut.Lock()
defer self.regmut.Unlock()
txs := self.register[args]
self.register[args] = nil
*reply = txs
return nil
}
func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) error { func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) error {
var id int var id int
filter := core.NewFilter(self.xeth.Backend()) filter := core.NewFilter(self.xeth.Backend())
@ -81,20 +101,36 @@ func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) erro
self.logMut.Lock() self.logMut.Lock()
defer self.logMut.Unlock() defer self.logMut.Unlock()
self.logs[id] = append(self.logs[id], logs...) self.logs[id].add(logs...)
} }
id = self.filterManager.InstallFilter(filter) id = self.filterManager.InstallFilter(filter)
self.logs[id] = &logFilter{timeout: time.Now()}
*reply = id *reply = id
return nil return nil
} }
func (self *EthereumApi) UninstallFilter(id int, reply *interface{}) error {
delete(self.logs, id)
self.filterManager.UninstallFilter(id)
*reply = true
return nil
}
func (self *EthereumApi) NewFilterString(args string, reply *interface{}) error { func (self *EthereumApi) NewFilterString(args string, reply *interface{}) error {
var id int var id int
filter := core.NewFilter(self.xeth.Backend()) filter := core.NewFilter(self.xeth.Backend())
callback := func(block *types.Block) { callback := func(block *types.Block) {
self.logs[id] = append(self.logs[id], &state.StateLog{}) self.logMut.Lock()
defer self.logMut.Unlock()
if self.logs[id] == nil {
self.logs[id] = &logFilter{timeout: time.Now()}
}
self.logs[id].add(&state.StateLog{})
} }
if args == "pending" { if args == "pending" {
filter.PendingCallback = callback filter.PendingCallback = callback
@ -112,15 +148,29 @@ func (self *EthereumApi) FilterChanged(id int, reply *interface{}) error {
self.logMut.Lock() self.logMut.Lock()
defer self.logMut.Unlock() defer self.logMut.Unlock()
*reply = toLogs(self.logs[id]) if self.logs[id] != nil {
*reply = toLogs(self.logs[id].get())
self.logs[id] = nil // empty the logs }
return nil return nil
} }
func (self *EthereumApi) Logs(id int, reply *interface{}) error { func (self *EthereumApi) Logs(id int, reply *interface{}) error {
self.logMut.Lock()
defer self.logMut.Unlock()
filter := self.filterManager.GetFilter(id) filter := self.filterManager.GetFilter(id)
if filter != nil {
*reply = toLogs(filter.Find())
}
return nil
}
func (self *EthereumApi) AllLogs(args *FilterOptions, reply *interface{}) error {
filter := core.NewFilter(self.xeth.Backend())
filter.SetOptions(toFilterOptions(args))
*reply = toLogs(filter.Find()) *reply = toLogs(filter.Find())
return nil return nil
@ -149,8 +199,14 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error {
args.GasPrice = defaultGasPrice args.GasPrice = defaultGasPrice
} }
result, _ := p.xeth.Transact( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data) // TODO if no_private_key then
*reply = result if _, exists := p.register[args.From]; exists {
p.register[args.From] = append(p.register[args.From], args)
} else {
result, _ := p.xeth.Transact( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data)
*reply = result
}
return nil return nil
} }
@ -231,6 +287,11 @@ func (p *EthereumApi) GetIsMining(reply *interface{}) error {
return nil return nil
} }
func (p *EthereumApi) SetMining(shouldmine bool, reply *interface{}) error {
*reply = p.xeth.SetMining(shouldmine)
return nil
}
func (p *EthereumApi) BlockNumber(reply *interface{}) error { func (p *EthereumApi) BlockNumber(reply *interface{}) error {
*reply = p.xeth.Backend().ChainManager().CurrentBlock().Number() *reply = p.xeth.Backend().ChainManager().CurrentBlock().Number()
return nil return nil
@ -264,6 +325,21 @@ func (p *EthereumApi) GetCodeAt(args *GetCodeAtArgs, reply *interface{}) error {
return nil return nil
} }
func (p *EthereumApi) GetCompilers(reply *interface{}) error {
c := []string{"serpent"}
*reply = c
return nil
}
func (p *EthereumApi) CompileSerpent(script string, reply *interface{}) error {
res, err := ethutil.Compile(script, false)
if err != nil {
return err
}
*reply = res
return nil
}
func (p *EthereumApi) Sha3(args *Sha3Args, reply *interface{}) error { func (p *EthereumApi) Sha3(args *Sha3Args, reply *interface{}) error {
*reply = toHex(crypto.Sha3(fromHex(args.Data))) *reply = toHex(crypto.Sha3(fromHex(args.Data)))
return nil return nil
@ -301,7 +377,10 @@ func (p *EthereumApi) NewWhisperFilter(args *xeth.Options, reply *interface{}) e
args.Fn = func(msg xeth.WhisperMessage) { args.Fn = func(msg xeth.WhisperMessage) {
p.messagesMut.Lock() p.messagesMut.Lock()
defer p.messagesMut.Unlock() defer p.messagesMut.Unlock()
p.messages[id] = append(p.messages[id], msg) if p.messages[id] == nil {
p.messages[id] = &whisperFilter{timeout: time.Now()}
}
p.messages[id].add(msg) // = append(p.messages[id], msg)
} }
id = p.xeth.Whisper().Watch(args) id = p.xeth.Whisper().Watch(args)
*reply = id *reply = id
@ -312,15 +391,15 @@ func (self *EthereumApi) MessagesChanged(id int, reply *interface{}) error {
self.messagesMut.Lock() self.messagesMut.Lock()
defer self.messagesMut.Unlock() defer self.messagesMut.Unlock()
*reply = self.messages[id] if self.messages[id] != nil {
*reply = self.messages[id].get()
self.messages[id] = nil // empty the messages }
return nil return nil
} }
func (p *EthereumApi) WhisperPost(args *WhisperMessageArgs, reply *interface{}) error { func (p *EthereumApi) WhisperPost(args *WhisperMessageArgs, reply *interface{}) error {
err := p.xeth.Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl) err := p.xeth.Whisper().Post(args.Payload, args.To, args.From, args.Topic, args.Priority, args.Ttl)
if err != nil { if err != nil {
return err return err
} }
@ -349,6 +428,12 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
return p.GetIsListening(reply) return p.GetIsListening(reply)
case "eth_mining": case "eth_mining":
return p.GetIsMining(reply) return p.GetIsMining(reply)
case "eth_setMining":
args, err := req.ToBoolArgs()
if err != nil {
return err
}
return p.SetMining(args, reply)
case "eth_peerCount": case "eth_peerCount":
return p.GetPeerCount(reply) return p.GetPeerCount(reply)
case "eth_number": case "eth_number":
@ -415,15 +500,59 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
return err return err
} }
return p.NewFilterString(args, reply) return p.NewFilterString(args, reply)
case "eth_uninstallFilter":
args, err := req.ToUninstallFilterArgs()
if err != nil {
return err
}
return p.UninstallFilter(args, reply)
case "eth_changed": case "eth_changed":
args, err := req.ToFilterChangedArgs() args, err := req.ToIdArgs()
if err != nil { if err != nil {
return err return err
} }
return p.FilterChanged(args, reply) return p.FilterChanged(args, reply)
case "eth_filterLogs":
args, err := req.ToIdArgs()
if err != nil {
return err
}
return p.Logs(args, reply)
case "eth_logs":
args, err := req.ToFilterArgs()
if err != nil {
return err
}
return p.AllLogs(args, reply)
case "eth_gasPrice": case "eth_gasPrice":
*reply = defaultGasPrice *reply = defaultGasPrice
return nil return nil
case "eth_register":
args, err := req.ToRegisterArgs()
if err != nil {
return err
}
return p.Register(args, reply)
case "eth_unregister":
args, err := req.ToRegisterArgs()
if err != nil {
return err
}
return p.Unregister(args, reply)
case "eth_watchTx":
args, err := req.ToWatchTxArgs()
if err != nil {
return err
}
return p.WatchTx(args, reply)
case "eth_compilers":
return p.GetCompilers(reply)
case "eth_serpent":
args, err := req.ToCompileArgs()
if err != nil {
return err
}
return p.CompileSerpent(args, reply)
case "web3_sha3": case "web3_sha3":
args, err := req.ToSha3Args() args, err := req.ToSha3Args()
if err != nil { if err != nil {
@ -451,7 +580,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
} }
return p.NewWhisperFilter(args, reply) return p.NewWhisperFilter(args, reply)
case "shh_changed": case "shh_changed":
args, err := req.ToWhisperIdArgs() args, err := req.ToIdArgs()
if err != nil { if err != nil {
return err return err
} }
@ -469,7 +598,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
} }
return p.HasWhisperIdentity(args, reply) return p.HasWhisperIdentity(args, reply)
case "shh_getMessages": case "shh_getMessages":
args, err := req.ToWhisperIdArgs() args, err := req.ToIdArgs()
if err != nil { if err != nil {
return err return err
} }
@ -481,3 +610,36 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
rpclogger.DebugDetailf("Reply: %T %s", reply, reply) rpclogger.DebugDetailf("Reply: %T %s", reply, reply)
return nil return nil
} }
var filterTickerTime = 15 * time.Second
func (self *EthereumApi) start() {
timer := time.NewTicker(filterTickerTime)
done:
for {
select {
case <-timer.C:
self.logMut.Lock()
self.messagesMut.Lock()
for id, filter := range self.logs {
if time.Since(filter.timeout) > 20*time.Second {
delete(self.logs, id)
}
}
for id, filter := range self.messages {
if time.Since(filter.timeout) > 20*time.Second {
delete(self.messages, id)
}
}
self.logMut.Unlock()
self.messagesMut.Unlock()
case <-self.quit:
break done
}
}
}
func (self *EthereumApi) stop() {
close(self.quit)
}

37
rpc/packages_test.go Normal file
View File

@ -0,0 +1,37 @@
package rpc
import (
"sync"
"testing"
"time"
)
func TestFilterClose(t *testing.T) {
api := &EthereumApi{
logs: make(map[int]*logFilter),
messages: make(map[int]*whisperFilter),
quit: make(chan struct{}),
}
filterTickerTime = 1
api.logs[0] = &logFilter{}
api.messages[0] = &whisperFilter{}
var wg sync.WaitGroup
wg.Add(1)
go api.start()
go func() {
select {
case <-time.After(500 * time.Millisecond):
api.stop()
wg.Done()
}
}()
wg.Wait()
if len(api.logs) != 0 {
t.Error("expected logs to be empty")
}
if len(api.messages) != 0 {
t.Error("expected messages to be empty")
}
}

View File

@ -20,10 +20,12 @@ import (
"encoding/json" "encoding/json"
"io" "io"
"net/http" "net/http"
"time"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/xeth"
) )
var rpclogger = logger.NewLogger("RPC") var rpclogger = logger.NewLogger("RPC")
@ -80,8 +82,9 @@ type RpcServer interface {
type Log struct { type Log struct {
Address string `json:"address"` Address string `json:"address"`
Topics []string `json:"topics"` Topic []string `json:"topics"`
Data string `json:"data"` Data string `json:"data"`
Number uint64 `json:"number"`
} }
func toLogs(logs state.Logs) (ls []Log) { func toLogs(logs state.Logs) (ls []Log) {
@ -89,14 +92,46 @@ func toLogs(logs state.Logs) (ls []Log) {
for i, log := range logs { for i, log := range logs {
var l Log var l Log
l.Topics = make([]string, len(log.Topics())) l.Topic = make([]string, len(log.Topics()))
l.Address = toHex(log.Address()) l.Address = toHex(log.Address())
l.Data = toHex(log.Data()) l.Data = toHex(log.Data())
l.Number = log.Number()
for j, topic := range log.Topics() { for j, topic := range log.Topics() {
l.Topics[j] = toHex(topic) l.Topic[j] = toHex(topic)
} }
ls[i] = l ls[i] = l
} }
return return
} }
type whisperFilter struct {
messages []xeth.WhisperMessage
timeout time.Time
}
func (w *whisperFilter) add(msgs ...xeth.WhisperMessage) {
w.messages = append(w.messages, msgs...)
}
func (w *whisperFilter) get() []xeth.WhisperMessage {
w.timeout = time.Now()
tmp := w.messages
w.messages = nil
return tmp
}
type logFilter struct {
logs state.Logs
timeout time.Time
}
func (l *logFilter) add(logs ...state.Log) {
l.logs = append(l.logs, logs...)
}
func (l *logFilter) get() state.Logs {
l.timeout = time.Now()
tmp := l.logs
l.logs = nil
return tmp
}

View File

@ -21,10 +21,10 @@ import (
"net" "net"
"net/http" "net/http"
"code.google.com/p/go.net/websocket"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
"golang.org/x/net/websocket"
) )
var wslogger = logger.NewLogger("RPC-WS") var wslogger = logger.NewLogger("RPC-WS")

View File

@ -30,7 +30,7 @@ func (self *StateDB) Dump() []byte {
for it.Next() { for it.Next() {
stateObject := NewStateObjectFromBytes(it.Key, it.Value, self.db) stateObject := NewStateObjectFromBytes(it.Key, it.Value, self.db)
account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.Nonce, Root: ethutil.Bytes2Hex(stateObject.Root()), CodeHash: ethutil.Bytes2Hex(stateObject.codeHash)} account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: ethutil.Bytes2Hex(stateObject.Root()), CodeHash: ethutil.Bytes2Hex(stateObject.codeHash)}
account.Storage = make(map[string]string) account.Storage = make(map[string]string)
storageIt := stateObject.State.trie.Iterator() storageIt := stateObject.State.trie.Iterator()
@ -50,7 +50,7 @@ func (self *StateDB) Dump() []byte {
// Debug stuff // Debug stuff
func (self *StateObject) CreateOutputForDiff() { func (self *StateObject) CreateOutputForDiff() {
fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.balance.Bytes(), self.Nonce) fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.balance.Bytes(), self.nonce)
it := self.State.trie.Iterator() it := self.State.trie.Iterator()
for it.Next() { for it.Next() {
fmt.Printf("%x %x\n", it.Key, it.Value) fmt.Printf("%x %x\n", it.Key, it.Value)

View File

@ -12,16 +12,19 @@ type Log interface {
Address() []byte Address() []byte
Topics() [][]byte Topics() [][]byte
Data() []byte Data() []byte
Number() uint64
} }
type StateLog struct { type StateLog struct {
address []byte address []byte
topics [][]byte topics [][]byte
data []byte data []byte
number uint64
} }
func NewLog(address []byte, topics [][]byte, data []byte) *StateLog { func NewLog(address []byte, topics [][]byte, data []byte, number uint64) *StateLog {
return &StateLog{address, topics, data} return &StateLog{address, topics, data, number}
} }
func (self *StateLog) Address() []byte { func (self *StateLog) Address() []byte {
@ -36,6 +39,10 @@ func (self *StateLog) Data() []byte {
return self.data return self.data
} }
func (self *StateLog) Number() uint64 {
return self.number
}
func NewLogFromValue(decoder *ethutil.Value) *StateLog { func NewLogFromValue(decoder *ethutil.Value) *StateLog {
log := &StateLog{ log := &StateLog{
address: decoder.Get(0).Bytes(), address: decoder.Get(0).Bytes(),

Some files were not shown because too many files have changed in this diff Show More