Merge branch 'develop' into ui
This commit is contained in:
commit
dea6584018
111 files changed
+1987
-2976
No files matched your search
+5
-8
@@ -6,18 +6,15 @@ before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -yqq libgmp3-dev libreadline6-dev qt54quickcontrols qt54webengine
|
||||
install:
|
||||
- go get code.google.com/p/go.tools/cmd/goimports
|
||||
- go get github.com/golang/lint/golint
|
||||
# - go get code.google.com/p/go.tools/cmd/goimports
|
||||
# - go get github.com/golang/lint/golint
|
||||
# - 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
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get gopkg.in/check.v1
|
||||
- go get github.com/tools/godep
|
||||
before_script:
|
||||
- godep restore
|
||||
- gofmt -l -w .
|
||||
- goimports -l -w .
|
||||
- golint .
|
||||
# - gofmt -l -w .
|
||||
# - goimports -l -w .
|
||||
# - golint .
|
||||
# - go vet ./...
|
||||
# - go test -race ./...
|
||||
script:
|
||||
|
||||
Generated
+17
-29
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum",
|
||||
"GoVersion": "go1.4",
|
||||
"GoVersion": "go1.4.1",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
@@ -15,26 +15,6 @@
|
||||
"Comment": "null-12",
|
||||
"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",
|
||||
"Comment": "null-15",
|
||||
@@ -44,22 +24,18 @@
|
||||
"ImportPath": "github.com/ethereum/serpent-go",
|
||||
"Rev": "5767a0dbd759d313df3f404dadb7f98d7ab51443"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/fjl/goupnp",
|
||||
"Rev": "fa95df6feb61e136b499d01711fcd410ccaf20c1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/howeyc/fsnotify",
|
||||
"Comment": "v0.9.0-11-g6b1ef89",
|
||||
"Rev": "6b1ef893dc11e0447abda6da20a5203481878dda"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jackpal/go-nat-pmp",
|
||||
"Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1"
|
||||
"ImportPath": "github.com/huin/goupnp",
|
||||
"Rev": "4191d8a85005844ea202fde52799681971b12dfe"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/obscuren/ecies",
|
||||
"Rev": "d899334bba7bf4a157cab19d8ad836dcb1de0c34"
|
||||
"ImportPath": "github.com/jackpal/go-nat-pmp",
|
||||
"Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/obscuren/otto",
|
||||
@@ -109,6 +85,18 @@
|
||||
"ImportPath": "golang.org/x/crypto/pbkdf2",
|
||||
"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",
|
||||
"Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673"
|
||||
|
||||
-77
@@ -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]
|
||||
}
|
||||
-157
@@ -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)
|
||||
}
|
||||
Generated
Vendored
File renamed without changes.
Generated
Vendored
+1
-1
@@ -3,7 +3,7 @@ goupnp is a UPnP client library for Go
|
||||
Installation
|
||||
------------
|
||||
|
||||
Run `go get -u github.com/fjl/goupnp`.
|
||||
Run `go get -u github.com/huin/goupnp`.
|
||||
|
||||
Regenerating dcps generated source code:
|
||||
----------------------------------------
|
||||
+1
-1
@@ -4,7 +4,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/fjl/goupnp/httpu"
|
||||
"github.com/huin/goupnp/httpu"
|
||||
)
|
||||
|
||||
func main() {
|
||||
+1
-1
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/fjl/goupnp/dcps/internetgateway1"
|
||||
"github.com/huin/goupnp/dcps/internetgateway1"
|
||||
)
|
||||
|
||||
func main() {
|
||||
+2
-2
@@ -11,8 +11,8 @@ package internetgateway1
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/fjl/goupnp"
|
||||
"github.com/fjl/goupnp/soap"
|
||||
"github.com/huin/goupnp"
|
||||
"github.com/huin/goupnp/soap"
|
||||
)
|
||||
|
||||
// Hack to avoid Go complaining if time isn't used.
|
||||
+2
-2
@@ -11,8 +11,8 @@ package internetgateway2
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/fjl/goupnp"
|
||||
"github.com/fjl/goupnp/soap"
|
||||
"github.com/huin/goupnp"
|
||||
"github.com/huin/goupnp/soap"
|
||||
)
|
||||
|
||||
// Hack to avoid Go complaining if time isn't used.
|
||||
Generated
Vendored
+2
-2
@@ -8,8 +8,8 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/fjl/goupnp/scpd"
|
||||
"github.com/fjl/goupnp/soap"
|
||||
"github.com/huin/goupnp/scpd"
|
||||
"github.com/huin/goupnp/soap"
|
||||
)
|
||||
|
||||
const (
|
||||
Generated
Vendored
+1
-1
@@ -2,5 +2,5 @@
|
||||
//
|
||||
// To run examples and see the output for your local network, run the following
|
||||
// command (specifically including the -v flag):
|
||||
// go test -v github.com/fjl/goupnp/example
|
||||
// go test -v github.com/huin/goupnp/example
|
||||
package example
|
||||
Generated
Vendored
+2
-2
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/fjl/goupnp"
|
||||
"github.com/fjl/goupnp/dcps/internetgateway1"
|
||||
"github.com/huin/goupnp"
|
||||
"github.com/huin/goupnp/dcps/internetgateway1"
|
||||
)
|
||||
|
||||
// Use discovered WANPPPConnection1 services to find external IP addresses.
|
||||
Generated
Vendored
+5
-5
@@ -17,8 +17,8 @@ import (
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/fjl/goupnp"
|
||||
"github.com/fjl/goupnp/scpd"
|
||||
"github.com/huin/goupnp"
|
||||
"github.com/huin/goupnp/scpd"
|
||||
"github.com/huin/goutil/codegen"
|
||||
"github.com/jingweno/gotask/tasking"
|
||||
)
|
||||
@@ -38,7 +38,7 @@ var (
|
||||
// -s, --spec_filename=<upnpresources.zip>
|
||||
// Path to the specification file, available from http://upnp.org/resources/upnpresources.zip
|
||||
// -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
|
||||
// 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) {
|
||||
@@ -445,8 +445,8 @@ package {{$name}}
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/fjl/goupnp"
|
||||
"github.com/fjl/goupnp/soap"
|
||||
"github.com/huin/goupnp"
|
||||
"github.com/huin/goupnp/soap"
|
||||
)
|
||||
|
||||
// Hack to avoid Go complaining if time isn't used.
|
||||
Generated
Vendored
+5
-5
@@ -1,11 +1,11 @@
|
||||
// goupnp is an implementation of a client for various UPnP services.
|
||||
//
|
||||
// For most uses, it is recommended to use the code-generated packages under
|
||||
// github.com/fjl/goupnp/dcps. Example use is shown at
|
||||
// http://godoc.org/github.com/fjl/goupnp/example
|
||||
// github.com/huin/goupnp/dcps. Example use is shown at
|
||||
// http://godoc.org/github.com/huin/goupnp/example
|
||||
//
|
||||
// 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
|
||||
// UPnP example XML specifications. Not all methods will work on these clients,
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/fjl/goupnp/httpu"
|
||||
"github.com/fjl/goupnp/ssdp"
|
||||
"github.com/huin/goupnp/httpu"
|
||||
"github.com/huin/goupnp/ssdp"
|
||||
)
|
||||
|
||||
// ContextError is an error that wraps an error with some context information.
|
||||
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
+1
-2
@@ -2,8 +2,7 @@ package goupnp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fjl/goupnp/soap"
|
||||
"github.com/huin/goupnp/soap"
|
||||
)
|
||||
|
||||
// ServiceClient is a SOAP client, root device and the service for the SOAP
|
||||
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
+1
-1
@@ -10,7 +10,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fjl/goupnp/httpu"
|
||||
"github.com/huin/goupnp/httpu"
|
||||
)
|
||||
|
||||
const (
|
||||
Generated
Vendored
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/fjl/goupnp/httpu"
|
||||
"github.com/huin/goupnp/httpu"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -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
|
||||
|
||||
*~
|
||||
-28
@@ -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.
|
||||
-94
@@ -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 Implementer’s 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
|
||||
|
||||
-556
@@ -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
|
||||
}
|
||||
-326
@@ -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
|
||||
}
|
||||
-489
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
-187
@@ -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
|
||||
}
|
||||
}
|
||||
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
+16
-2
@@ -64,6 +64,20 @@ func Dial(url_, protocol, origin string) (ws *Conn, err error) {
|
||||
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.
|
||||
func DialConfig(config *Config) (ws *Conn, err error) {
|
||||
var client net.Conn
|
||||
@@ -75,10 +89,10 @@ func DialConfig(config *Config) (ws *Conn, err error) {
|
||||
}
|
||||
switch config.Location.Scheme {
|
||||
case "ws":
|
||||
client, err = net.Dial("tcp", config.Location.Host)
|
||||
client, err = net.Dial("tcp", parseAuthority(config.Location))
|
||||
|
||||
case "wss":
|
||||
client, err = tls.Dial("tcp", config.Location.Host, config.TlsConfig)
|
||||
client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig)
|
||||
|
||||
default:
|
||||
err = ErrBadScheme
|
||||
Generated
Vendored
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
// This example demonstrates a trivial client.
|
||||
Generated
Vendored
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
// Echo the data received on the WebSocket.
|
||||
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
File renamed without changes.
Generated
Vendored
+73
@@ -339,3 +339,76 @@ func TestSmallBuffer(t *testing.T) {
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
Ethereum Go Client © 2014 Jeffrey Wilcke.
|
||||
|
||||
| Linux | OSX | Windows
|
||||
----------|---------|-----|--------
|
||||
develop | [](http://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [](http://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | N/A
|
||||
master | [](http://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [](http://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | N/A
|
||||
| Linux | OSX | Windows | Tests
|
||||
----------|---------|-----|---------|------
|
||||
develop | [](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | N/A | [](https://travis-ci.org/ethereum/go-ethereum)
|
||||
master | [](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | N/A | [](https://travis-ci.org/ethereum/go-ethereum)
|
||||
|
||||
[](https://waffle.io/ethereum/go-ethereum)
|
||||
[](https://waffle.io/ethereum/go-ethereum)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
||||
)
|
||||
|
||||
func TestAccountManager(t *testing.T) {
|
||||
@@ -10,7 +12,7 @@ func TestAccountManager(t *testing.T) {
|
||||
am := NewAccountManager(ks)
|
||||
pass := "" // not used but required by API
|
||||
a1, err := am.NewAccount(pass)
|
||||
toSign := crypto.GetEntropyCSPRNG(32)
|
||||
toSign := randentropy.GetEntropyCSPRNG(32)
|
||||
_, err = am.Sign(a1, pass, toSign)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -132,7 +132,7 @@ func Init() {
|
||||
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(&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(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap")
|
||||
|
||||
@@ -37,7 +37,7 @@ import (
|
||||
|
||||
const (
|
||||
ClientIdentifier = "Ethereum(G)"
|
||||
Version = "0.8.3"
|
||||
Version = "0.8.5"
|
||||
)
|
||||
|
||||
var clilogger = logger.NewLogger("CLI")
|
||||
@@ -67,11 +67,12 @@ func main() {
|
||||
DataDir: Datadir,
|
||||
LogFile: LogFile,
|
||||
LogLevel: LogLevel,
|
||||
LogFormat: LogFormat,
|
||||
MaxPeers: MaxPeer,
|
||||
Port: OutboundPort,
|
||||
NAT: NAT,
|
||||
KeyRing: KeyRing,
|
||||
Shh: SHH,
|
||||
Shh: true,
|
||||
Dial: Dial,
|
||||
BootNodes: BootNodes,
|
||||
NodeKey: NodeKey,
|
||||
|
||||
+2
-2
@@ -51,8 +51,8 @@ func StateObjectFromAccount(db ethutil.Database, addr string, account Account) *
|
||||
if ethutil.IsHex(account.Code) {
|
||||
account.Code = account.Code[2:]
|
||||
}
|
||||
obj.Code = ethutil.Hex2Bytes(account.Code)
|
||||
obj.Nonce = ethutil.Big(account.Nonce).Uint64()
|
||||
obj.SetCode(ethutil.Hex2Bytes(account.Code))
|
||||
obj.SetNonce(ethutil.Big(account.Nonce).Uint64())
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -14,8 +14,9 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span class="amount">Amount:</span>
|
||||
<span>Address:</span>
|
||||
<input type="text" id="address" style="width:200px">
|
||||
<span>Amount:</span>
|
||||
<input type="text" id="amount" style="width:200px">
|
||||
<button onclick="transact()">Send</button>
|
||||
</div>
|
||||
@@ -58,7 +59,7 @@
|
||||
}],
|
||||
"outputs": []
|
||||
}, {
|
||||
"name":"received",
|
||||
"name":"Changed",
|
||||
"type":"event",
|
||||
"inputs": [
|
||||
{"name":"from","type":"address","indexed":true},
|
||||
@@ -69,23 +70,14 @@
|
||||
var address = localStorage.getItem("address");
|
||||
// deploy if not exist
|
||||
if (address == null) {
|
||||
var code = "0x60056013565b61012b806100346000396000f35b6103e8600033600160a060020a0316600052602052604060002081905550560060e060020a6000350480637bb98a681461002b578063d0679d3414610039578063e3d670d71461004d57005b610033610126565b60006000f35b610047600435602435610062565b60006000f35b610058600435610104565b8060005260206000f35b80600033600160a060020a0316600052602052604060002054101561008657610100565b80600033600160a060020a0316600052602052604060002090815403908190555080600083600160a060020a0316600052602052604060002090815401908190555033600160a060020a0316600052806020527ff11e547d796cc64acdf758e7cee90439494fd886a19159454aa61e473fdbafef60406000a15b5050565b6000600082600160a060020a03166000526020526040600020549050919050565b5b60008156";
|
||||
var code = "0x60056013565b61014f8061003a6000396000f35b620f42406000600033600160a060020a0316815260200190815260200160002081905550560060e060020a600035048063d0679d3414610020578063e3d670d71461003457005b61002e600435602435610049565b60006000f35b61003f600435610129565b8060005260206000f35b806000600033600160a060020a03168152602001908152602001600020541061007157610076565b610125565b806000600033600160a060020a03168152602001908152602001600020908154039081905550806000600084600160a060020a031681526020019081526020016000209081540190819055508033600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a38082600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a35b5050565b60006000600083600160a060020a0316815260200190815260200160002054905091905056";
|
||||
address = web3.eth.transact({data: code});
|
||||
localStorage.setItem("address", address);
|
||||
}
|
||||
document.querySelector("#contract_addr").innerHTML = address.toUpperCase();
|
||||
document.querySelector("#contract_addr").innerHTML = address;
|
||||
|
||||
var contract = web3.eth.contract(address, desc);
|
||||
contract.received({from: eth.coinbase}).changed(function() {
|
||||
refresh();
|
||||
});
|
||||
|
||||
var ev = contract.SingleTransact({})
|
||||
ev.watch(function(log) {
|
||||
someElement.innerHTML += "tnaheousnthaoeu";
|
||||
});
|
||||
|
||||
eth.watch('chain').changed(function() {
|
||||
contract.Changed({from: eth.coinbase}).changed(function() {
|
||||
refresh();
|
||||
});
|
||||
|
||||
@@ -98,7 +90,7 @@
|
||||
var storage = eth.storageAt(address);
|
||||
table.innerHTML = "";
|
||||
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 );
|
||||
console.log("transact: ", to, " => ", value)
|
||||
|
||||
contract.send( to, value );
|
||||
}
|
||||
@@ -126,7 +119,7 @@ contract JevCoin {
|
||||
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)
|
||||
{
|
||||
if( balances[msg.sender] < value ) return;
|
||||
@@ -134,7 +127,8 @@ contract JevCoin {
|
||||
balances[msg.sender] -= value;
|
||||
balances[to] += value;
|
||||
|
||||
changed(msg.sender, to);
|
||||
Changed(msg.sender, value);
|
||||
Changed(to, value);
|
||||
}
|
||||
|
||||
function balance(address who) constant returns(uint t)
|
||||
|
||||
@@ -71,6 +71,10 @@
|
||||
document.querySelector("#gas_price").innerHTML = eth.gasPrice;
|
||||
document.querySelector("#mining").innerHTML = eth.mining;
|
||||
document.querySelector("#listening").innerHTML = eth.listening;
|
||||
eth.watch('chain').changed(function() {
|
||||
document.querySelector("#number").innerHTML = eth.number;
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -1417,6 +1417,8 @@ var ethMethods = function () {
|
||||
|
||||
var methods = [
|
||||
{ name: 'balanceAt', call: 'eth_balanceAt' },
|
||||
{ name: 'register', call: 'eth_register' },
|
||||
{ name: 'unregister', call: 'eth_unregister' },
|
||||
{ name: 'stateAt', call: 'eth_stateAt' },
|
||||
{ name: 'storageAt', call: 'eth_storageAt' },
|
||||
{ name: 'countAt', call: 'eth_countAt'},
|
||||
|
||||
@@ -256,6 +256,7 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
property var blockModel: ListModel {
|
||||
@@ -895,7 +896,7 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
function setWalletValue(value) {
|
||||
walletValueLabel.text = value
|
||||
//walletValueLabel.text = value
|
||||
}
|
||||
|
||||
function loadPlugin(name) {
|
||||
@@ -947,7 +948,8 @@ ApplicationWindow {
|
||||
model: peerModel
|
||||
TableViewColumn{width: 180; role: "addr" ; title: "Remote Address" }
|
||||
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.topMargin: 30
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,4 +41,13 @@ Rectangle {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,7 @@ var (
|
||||
DebugFile string
|
||||
LogLevel int
|
||||
VmType int
|
||||
MinerThreads int
|
||||
)
|
||||
|
||||
// flags specific to gui client
|
||||
@@ -137,6 +138,8 @@ func Init() {
|
||||
flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
||||
|
||||
flag.IntVar(&MinerThreads, "minerthreads", runtime.NumCPU(), "number of miner threads")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
var err error
|
||||
|
||||
+7
-51
@@ -131,6 +131,7 @@ func (gui *Gui) Start(assetPath string) {
|
||||
context.SetVar("gui", gui)
|
||||
context.SetVar("eth", gui.uiLib)
|
||||
context.SetVar("shh", gui.whisper)
|
||||
//clipboard.SetQMLClipboard(context)
|
||||
|
||||
win, err := gui.showWallet(context)
|
||||
if err != nil {
|
||||
@@ -386,16 +387,11 @@ func (gui *Gui) update() {
|
||||
generalUpdateTicker := time.NewTicker(500 * time.Millisecond)
|
||||
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")
|
||||
miningLabel := gui.getObjectByName("miningLabel")
|
||||
|
||||
events := gui.eth.EventMux().Subscribe(
|
||||
//eth.PeerListEvent{},
|
||||
core.NewBlockEvent{},
|
||||
core.ChainEvent{},
|
||||
core.TxPreEvent{},
|
||||
core.TxPostEvent{},
|
||||
)
|
||||
@@ -408,41 +404,13 @@ func (gui *Gui) update() {
|
||||
return
|
||||
}
|
||||
switch ev := ev.(type) {
|
||||
case core.NewBlockEvent:
|
||||
case core.ChainEvent:
|
||||
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:
|
||||
tx := 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)
|
||||
gui.insertTransaction("pre", ev.Tx)
|
||||
|
||||
case core.TxPostEvent:
|
||||
tx := 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)
|
||||
gui.getObjectByName("pendingTxView").Call("removeTx", xeth.NewTx(ev.Tx))
|
||||
}
|
||||
|
||||
case <-peerUpdateTicker.C:
|
||||
@@ -453,19 +421,6 @@ func (gui *Gui) update() {
|
||||
lastBlockLabel.Set("text", statusText)
|
||||
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:
|
||||
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
|
||||
|
||||
@@ -513,6 +468,7 @@ func (gui *Gui) setPeerInfo() {
|
||||
qpeers[i] = &qmlpeer{
|
||||
NodeID: p.ID().String(),
|
||||
Addr: p.RemoteAddr().String(),
|
||||
Name: p.Name(),
|
||||
Caps: fmt.Sprint(p.Caps()),
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -36,7 +36,7 @@ import (
|
||||
|
||||
const (
|
||||
ClientIdentifier = "Mist"
|
||||
Version = "0.8.3"
|
||||
Version = "0.8.5"
|
||||
)
|
||||
|
||||
var ethereum *eth.Ethereum
|
||||
@@ -52,18 +52,20 @@ func run() error {
|
||||
config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
|
||||
|
||||
ethereum, err := eth.New(ð.Config{
|
||||
Name: p2p.MakeName(ClientIdentifier, Version),
|
||||
KeyStore: KeyStore,
|
||||
DataDir: Datadir,
|
||||
LogFile: LogFile,
|
||||
LogLevel: LogLevel,
|
||||
MaxPeers: MaxPeer,
|
||||
Port: OutboundPort,
|
||||
NAT: NAT,
|
||||
BootNodes: BootNodes,
|
||||
NodeKey: NodeKey,
|
||||
KeyRing: KeyRing,
|
||||
Dial: true,
|
||||
Name: p2p.MakeName(ClientIdentifier, Version),
|
||||
KeyStore: KeyStore,
|
||||
DataDir: Datadir,
|
||||
LogFile: LogFile,
|
||||
LogLevel: LogLevel,
|
||||
MaxPeers: MaxPeer,
|
||||
Port: OutboundPort,
|
||||
NAT: NAT,
|
||||
Shh: true,
|
||||
BootNodes: BootNodes,
|
||||
NodeKey: NodeKey,
|
||||
KeyRing: KeyRing,
|
||||
Dial: true,
|
||||
MinerThreads: MinerThreads,
|
||||
})
|
||||
if err != nil {
|
||||
mainlogger.Fatalln(err)
|
||||
|
||||
+2
-2
@@ -146,8 +146,8 @@ func (ui *UiLib) AssetPath(p string) string {
|
||||
func (self *UiLib) StartDbWithContractAndData(contractHash, data string) {
|
||||
dbWindow := NewDebuggerWindow(self)
|
||||
object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash))
|
||||
if len(object.Code) > 0 {
|
||||
dbWindow.SetCode(ethutil.Bytes2Hex(object.Code))
|
||||
if len(object.Code()) > 0 {
|
||||
dbWindow.SetCode(ethutil.Bytes2Hex(object.Code()))
|
||||
}
|
||||
dbWindow.SetData(data)
|
||||
|
||||
|
||||
+2
-2
@@ -225,7 +225,7 @@ func StartMining(ethereum *eth.Ethereum) bool {
|
||||
go func() {
|
||||
clilogger.Infoln("Start mining")
|
||||
if gminer == nil {
|
||||
gminer = miner.New(addr, ethereum)
|
||||
gminer = miner.New(addr, ethereum, 4)
|
||||
}
|
||||
gminer.Start()
|
||||
}()
|
||||
@@ -272,7 +272,7 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
||||
parent := ethereum.ChainManager().GetBlock(block.ParentHash())
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
+26
-26
@@ -60,12 +60,12 @@ func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainM
|
||||
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.SetGasPool(CalcGasLimit(parent, block))
|
||||
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -73,38 +73,40 @@ func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block
|
||||
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
|
||||
state.EmptyLogs()
|
||||
statedb.EmptyLogs()
|
||||
|
||||
txGas := new(big.Int).Set(tx.Gas())
|
||||
|
||||
cb := state.GetStateObject(coinbase.Address())
|
||||
st := NewStateTransition(NewEnv(state, self.bc, tx, block), tx, cb)
|
||||
cb := statedb.GetStateObject(coinbase.Address())
|
||||
st := NewStateTransition(NewEnv(statedb, self.bc, tx, block), tx, cb)
|
||||
_, err := st.TransitionState()
|
||||
if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err)) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
txGas.Sub(txGas, st.gas)
|
||||
|
||||
// Update the state with pending changes
|
||||
state.Update(txGas)
|
||||
statedb.Update(txGas)
|
||||
|
||||
cumulative := new(big.Int).Set(usedGas.Add(usedGas, txGas))
|
||||
receipt := types.NewReceipt(state.Root(), cumulative)
|
||||
receipt.SetLogs(state.Logs())
|
||||
receipt := types.NewReceipt(statedb.Root(), cumulative)
|
||||
receipt.SetLogs(statedb.Logs())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
chainlogger.Debugln(receipt)
|
||||
|
||||
// Notify all subscribers
|
||||
if !transientProcess {
|
||||
go self.eventMux.Post(TxPostEvent{tx})
|
||||
go self.eventMux.Post(statedb.Logs())
|
||||
}
|
||||
|
||||
go self.eventMux.Post(state.Logs())
|
||||
|
||||
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 (
|
||||
receipts types.Receipts
|
||||
handled, unhandled types.Transactions
|
||||
@@ -115,12 +117,12 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state
|
||||
)
|
||||
|
||||
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 {
|
||||
switch {
|
||||
case IsNonceErr(err):
|
||||
return nil, nil, nil, nil, err
|
||||
case IsGasLimitErr(err):
|
||||
case state.IsGasLimitErr(err):
|
||||
return nil, nil, nil, nil, err
|
||||
default:
|
||||
statelogger.Infoln(err)
|
||||
@@ -176,7 +178,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big
|
||||
return
|
||||
}
|
||||
|
||||
receipts, err := sm.TransitionState(state, parent, block)
|
||||
receipts, err := sm.TransitionState(state, parent, block, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -250,7 +252,11 @@ func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -289,16 +295,13 @@ func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, paren
|
||||
r := new(big.Int)
|
||||
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))
|
||||
|
||||
uncleAccount := statedb.GetAccount(uncle.Coinbase)
|
||||
uncleAccount.AddAmount(r)
|
||||
statedb.AddBalance(uncle.Coinbase, r)
|
||||
|
||||
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
|
||||
}
|
||||
|
||||
// Get the account associated with the coinbase
|
||||
account := statedb.GetAccount(block.Header().Coinbase)
|
||||
// Reward amount of ether to the coinbase address
|
||||
account.AddAmount(reward)
|
||||
statedb.AddBalance(block.Header().Coinbase, reward)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -312,13 +315,10 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro
|
||||
|
||||
var (
|
||||
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)
|
||||
sm.TransitionState(state, parent, block, true)
|
||||
sm.AccumulateRewards(state, block, parent)
|
||||
|
||||
return state.Logs(), nil
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
+31
-11
@@ -79,6 +79,7 @@ type ChainManager struct {
|
||||
genesisBlock *types.Block
|
||||
// Last known total difficulty
|
||||
mu sync.RWMutex
|
||||
tsmu sync.RWMutex
|
||||
td *big.Int
|
||||
currentBlock *types.Block
|
||||
lastBlockHash []byte
|
||||
@@ -86,6 +87,14 @@ type ChainManager struct {
|
||||
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 {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
@@ -107,14 +116,6 @@ func (self *ChainManager) CurrentBlock() *types.Block {
|
||||
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) {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
@@ -131,9 +132,16 @@ func (self *ChainManager) State() *state.StateDB {
|
||||
}
|
||||
|
||||
func (self *ChainManager) TransState() *state.StateDB {
|
||||
self.tsmu.RLock()
|
||||
defer self.tsmu.RUnlock()
|
||||
|
||||
return self.transState
|
||||
}
|
||||
|
||||
func (self *ChainManager) setTransState(statedb *state.StateDB) {
|
||||
self.transState = statedb
|
||||
}
|
||||
|
||||
func (bc *ChainManager) setLastBlock() {
|
||||
data, _ := bc.db.Get([]byte("LastBlock"))
|
||||
if len(data) != 0 {
|
||||
@@ -260,6 +268,7 @@ func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Printf("get hash %x (%d)\n", hash, len(chain))
|
||||
|
||||
return
|
||||
}
|
||||
@@ -350,6 +359,9 @@ func (bc *ChainManager) Stop() {
|
||||
}
|
||||
|
||||
func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
||||
self.tsmu.Lock()
|
||||
defer self.tsmu.Unlock()
|
||||
|
||||
for _, block := range chain {
|
||||
td, err := self.processor.Process(block)
|
||||
if err != nil {
|
||||
@@ -365,6 +377,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
||||
}
|
||||
block.Td = td
|
||||
|
||||
var chain, split bool
|
||||
self.mu.Lock()
|
||||
{
|
||||
self.write(block)
|
||||
@@ -372,18 +385,25 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
||||
if td.Cmp(self.td) > 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)
|
||||
split = true
|
||||
}
|
||||
|
||||
self.setTotalDifficulty(td)
|
||||
self.insert(block)
|
||||
self.transState = state.New(cblock.Root(), self.db)
|
||||
|
||||
self.eventMux.Post(ChainEvent{block, td})
|
||||
chain = true
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
+6
-17
@@ -1,10 +1,16 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"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
|
||||
// by the block manager
|
||||
type ParentErr struct {
|
||||
@@ -62,23 +68,6 @@ func IsValidationErr(err error) bool {
|
||||
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 {
|
||||
Message string
|
||||
Is, Exp uint64
|
||||
|
||||
@@ -13,3 +13,6 @@ type NewBlockEvent struct{ Block *types.Block }
|
||||
|
||||
// NewMinedBlockEvent is posted when a block has been imported.
|
||||
type NewMinedBlockEvent struct{ Block *types.Block }
|
||||
|
||||
// ChainSplit is posted when a new head is detected
|
||||
type ChainSplitEvent struct{ Block *types.Block }
|
||||
+18
-8
@@ -16,7 +16,7 @@ type FilterOptions struct {
|
||||
Earliest int64
|
||||
Latest int64
|
||||
|
||||
Address []byte
|
||||
Address [][]byte
|
||||
Topics [][]byte
|
||||
|
||||
Skip int
|
||||
@@ -29,7 +29,7 @@ type Filter struct {
|
||||
earliest int64
|
||||
latest int64
|
||||
skip int
|
||||
address []byte
|
||||
address [][]byte
|
||||
max int
|
||||
topics [][]byte
|
||||
|
||||
@@ -65,7 +65,7 @@ func (self *Filter) SetLatestBlock(latest int64) {
|
||||
self.latest = latest
|
||||
}
|
||||
|
||||
func (self *Filter) SetAddress(addr []byte) {
|
||||
func (self *Filter) SetAddress(addr [][]byte) {
|
||||
self.address = addr
|
||||
}
|
||||
|
||||
@@ -111,14 +111,14 @@ func (self *Filter) Find() state.Logs {
|
||||
// current parameters
|
||||
if self.bloomFilter(block) {
|
||||
// Get the logs of the block
|
||||
logs, err := self.eth.BlockProcessor().GetLogs(block)
|
||||
unfiltered, err := self.eth.BlockProcessor().GetLogs(block)
|
||||
if err != nil {
|
||||
chainlogger.Warnln("err: filter get logs ", err)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
logs = append(logs, self.FilterLogs(logs)...)
|
||||
logs = append(logs, self.FilterLogs(unfiltered)...)
|
||||
}
|
||||
|
||||
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
|
||||
Logs:
|
||||
for _, log := range logs {
|
||||
if !bytes.Equal(self.address, log.Address()) {
|
||||
if !includes(self.address, log.Address()) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -163,8 +163,18 @@ Logs:
|
||||
}
|
||||
|
||||
func (self *Filter) bloomFilter(block *types.Block) bool {
|
||||
if len(self.address) > 0 && !types.BloomLookup(block.Bloom(), self.address) {
|
||||
return false
|
||||
if len(self.address) > 0 {
|
||||
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 {
|
||||
|
||||
+31
-13
@@ -1,7 +1,10 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -31,24 +34,39 @@ func GenesisBlock(db ethutil.Database) *types.Block {
|
||||
genesis.SetTransactions(types.Transactions{})
|
||||
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)
|
||||
for _, addr := range []string{
|
||||
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6",
|
||||
"e4157b34ea9615cfbde6b4fda419828124b70c78",
|
||||
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
|
||||
"6c386a4b26f73c802f34673f7248bb118f97424a",
|
||||
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
|
||||
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
|
||||
"e6716f9544a56c530d868e4bfbacb172315bdead",
|
||||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
|
||||
} {
|
||||
for addr, account := range accounts {
|
||||
codedAddr := ethutil.Hex2Bytes(addr)
|
||||
account := statedb.GetAccount(codedAddr)
|
||||
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
|
||||
statedb.UpdateStateObject(account)
|
||||
accountState := statedb.GetAccount(codedAddr)
|
||||
accountState.SetBalance(ethutil.Big(account.Balance))
|
||||
statedb.UpdateStateObject(accountState)
|
||||
}
|
||||
statedb.Sync()
|
||||
genesis.Header().Root = statedb.Root()
|
||||
|
||||
fmt.Printf("+++ genesis +++\nRoot: %x\nHash: %x\n", genesis.Header().Root, genesis.Hash())
|
||||
|
||||
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"}
|
||||
}`)
|
||||
@@ -138,8 +138,8 @@ func (self *StateTransition) preCheck() (err error) {
|
||||
)
|
||||
|
||||
// Make sure this transaction's nonce is correct
|
||||
if sender.Nonce != msg.Nonce() {
|
||||
return NonceError(msg.Nonce(), sender.Nonce)
|
||||
if sender.Nonce() != msg.Nonce() {
|
||||
return NonceError(msg.Nonce(), sender.Nonce())
|
||||
}
|
||||
|
||||
// Pre-pay gas / Buy gas of the coinbase account
|
||||
@@ -166,7 +166,8 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) {
|
||||
defer self.RefundGas()
|
||||
|
||||
// Increment the nonce for the next transaction
|
||||
sender.Nonce += 1
|
||||
self.state.SetNonce(sender.Address(), sender.Nonce()+1)
|
||||
//sender.Nonce += 1
|
||||
|
||||
// Transaction gas
|
||||
if err = self.UseGas(vm.GasTx); err != nil {
|
||||
@@ -241,7 +242,7 @@ func MakeContract(msg Message, state *state.StateDB) *state.StateObject {
|
||||
addr := AddressFromMessage(msg)
|
||||
|
||||
contract := state.GetOrNewStateObject(addr)
|
||||
contract.InitCode = msg.Data()
|
||||
contract.SetInitCode(msg.Data())
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
+25
-10
@@ -3,6 +3,7 @@ package core
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"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
|
||||
// independently read without needing access to the actual pool.
|
||||
type TxPool struct {
|
||||
mu sync.RWMutex
|
||||
// Queueing channel for reading and writing incoming
|
||||
// transactions to
|
||||
queueChan chan *types.Transaction
|
||||
@@ -97,7 +99,7 @@ func (self *TxPool) addTx(tx *types.Transaction) {
|
||||
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 {
|
||||
return fmt.Errorf("Known transaction (%x)", tx.Hash()[0:4])
|
||||
}
|
||||
@@ -128,17 +130,28 @@ func (self *TxPool) Size() int {
|
||||
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) {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
|
||||
for _, tx := range txs {
|
||||
if err := self.Add(tx); err != nil {
|
||||
txplogger.Infoln(err)
|
||||
if err := self.add(tx); err != nil {
|
||||
txplogger.Debugln(err)
|
||||
} 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) {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
txs = make(types.Transactions, self.Size())
|
||||
i := 0
|
||||
for _, tx := range self.txs {
|
||||
@@ -150,30 +163,32 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) {
|
||||
}
|
||||
|
||||
func (pool *TxPool) RemoveInvalid(query StateQuery) {
|
||||
pool.mu.Lock()
|
||||
|
||||
var removedTxs types.Transactions
|
||||
for _, tx := range pool.txs {
|
||||
sender := query.GetAccount(tx.From())
|
||||
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)
|
||||
}
|
||||
}
|
||||
pool.mu.Unlock()
|
||||
|
||||
pool.RemoveSet(removedTxs)
|
||||
}
|
||||
|
||||
func (self *TxPool) RemoveSet(txs types.Transactions) {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
|
||||
for _, tx := range txs {
|
||||
delete(self.txs, string(tx.Hash()))
|
||||
}
|
||||
}
|
||||
|
||||
func (pool *TxPool) Flush() []*types.Transaction {
|
||||
txList := pool.GetTransactions()
|
||||
func (pool *TxPool) Flush() {
|
||||
pool.txs = make(map[string]*types.Transaction)
|
||||
|
||||
return txList
|
||||
}
|
||||
|
||||
func (pool *TxPool) Start() {
|
||||
|
||||
@@ -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) SetRoot(root []byte) { self.header.Root = root }
|
||||
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
|
||||
func (self *Block) Difficulty() *big.Int { return self.header.Difficulty }
|
||||
|
||||
+2
-2
@@ -16,12 +16,12 @@ import (
|
||||
"errors"
|
||||
|
||||
"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/secp256k1"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
* @date 2015
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This key store behaves as KeyStorePlain with the difference that
|
||||
@@ -64,17 +65,18 @@ package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"code.google.com/p/go.crypto/scrypt"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+38
-10
@@ -3,6 +3,8 @@ package eth
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
@@ -25,7 +27,10 @@ var (
|
||||
jsonlogger = ethlogger.NewJsonLogger()
|
||||
|
||||
defaultBootNodes = []*discover.Node{
|
||||
// ETH/DEV cmd/bootnode
|
||||
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
|
||||
Dial bool
|
||||
|
||||
MinerThreads int
|
||||
|
||||
KeyManager *crypto.KeyManager
|
||||
}
|
||||
|
||||
@@ -75,6 +82,27 @@ func (cfg *Config) parseBootNodes() []*discover.Node {
|
||||
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 {
|
||||
// Channel for shutting down the ethereum
|
||||
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.chainManager.SetProcessor(eth.blockProcessor)
|
||||
eth.whisper = whisper.New()
|
||||
eth.miner = miner.New(keyManager.Address(), eth)
|
||||
eth.miner = miner.New(keyManager.Address(), eth, config.MinerThreads)
|
||||
|
||||
hasBlock := eth.chainManager.HasBlock
|
||||
insertChain := eth.chainManager.InsertChain
|
||||
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
|
||||
|
||||
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
|
||||
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
|
||||
netprv := config.NodeKey
|
||||
if netprv == nil {
|
||||
if netprv, err = crypto.GenerateKey(); err != nil {
|
||||
return nil, fmt.Errorf("could not generate server key: %v", err)
|
||||
}
|
||||
netprv, err := config.nodeKey()
|
||||
if err != nil {
|
||||
return nil, 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{
|
||||
PrivateKey: netprv,
|
||||
Name: config.Name,
|
||||
@@ -205,9 +235,7 @@ func (s *Ethereum) Coinbase() []byte { return nil } // TODO
|
||||
func (s *Ethereum) Start() error {
|
||||
jsonlogger.LogJson(ðlogger.LogStarting{
|
||||
ClientString: s.net.Name,
|
||||
Coinbase: ethutil.Bytes2Hex(s.KeyManager().Address()),
|
||||
ProtocolVersion: ProtocolVersion,
|
||||
LogEvent: ethlogger.LogEvent{Guid: ethutil.Bytes2Hex(crypto.FromECDSAPub(&s.net.PrivateKey.PublicKey))},
|
||||
})
|
||||
|
||||
err := s.net.Start()
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ProtocolVersion = 52
|
||||
ProtocolVersion = 54
|
||||
NetworkId = 0
|
||||
ProtocolLength = uint64(8)
|
||||
ProtocolMaxMsgSize = 10 * 1024 * 1024
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func IsWindows() bool {
|
||||
@@ -86,3 +87,9 @@ var (
|
||||
Big256 = big.NewInt(0xff)
|
||||
Big257 = big.NewInt(257)
|
||||
)
|
||||
|
||||
func Bench(pre string, cb func()) {
|
||||
start := time.Now()
|
||||
cb()
|
||||
fmt.Println(pre, ": took:", time.Since(start))
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package filter
|
||||
// TODO make use of the generic filtering system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
@@ -37,17 +38,18 @@ func (self *FilterManager) Stop() {
|
||||
|
||||
func (self *FilterManager) InstallFilter(filter *core.Filter) (id int) {
|
||||
self.filterMu.Lock()
|
||||
defer self.filterMu.Unlock()
|
||||
id = self.filterId
|
||||
self.filters[id] = filter
|
||||
self.filterId++
|
||||
self.filterMu.Unlock()
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *FilterManager) UninstallFilter(id int) {
|
||||
self.filterMu.Lock()
|
||||
defer self.filterMu.Unlock()
|
||||
delete(self.filters, id)
|
||||
self.filterMu.Unlock()
|
||||
}
|
||||
|
||||
// GetFilter retrieves a filter installed using InstallFilter.
|
||||
@@ -60,7 +62,10 @@ func (self *FilterManager) GetFilter(id int) *core.Filter {
|
||||
|
||||
func (self *FilterManager) filterLoop() {
|
||||
// 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:
|
||||
for {
|
||||
@@ -69,7 +74,8 @@ out:
|
||||
break out
|
||||
case event := <-events.Chan():
|
||||
switch event := event.(type) {
|
||||
case core.NewBlockEvent:
|
||||
case core.ChainEvent:
|
||||
fmt.Println("filter start")
|
||||
self.filterMu.RLock()
|
||||
for _, filter := range self.filters {
|
||||
if filter.BlockCallback != nil {
|
||||
@@ -77,6 +83,7 @@ out:
|
||||
}
|
||||
}
|
||||
self.filterMu.RUnlock()
|
||||
fmt.Println("filter stop")
|
||||
|
||||
case core.PendingBlockEvent:
|
||||
self.filterMu.RLock()
|
||||
|
||||
+9
-7
@@ -1,11 +1,16 @@
|
||||
#!/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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
if ls $dir/*.go &> /dev/null; then
|
||||
# echo $dir
|
||||
if [[ $dir != "./tests/vm" ]]
|
||||
if [[ $dir != "./tests/vm" && $dir != "." ]]
|
||||
then
|
||||
go test -covermode=count -coverprofile=$dir/profile.tmp $dir
|
||||
fi
|
||||
@@ -24,6 +29,3 @@ if ls $dir/*.go &> /dev/null; then
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
go tool cover -func profile.cov
|
||||
|
||||
+284
-285
@@ -7,7 +7,6 @@ import (
|
||||
type utctime8601 struct{}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -16,14 +15,13 @@ type JsonLog interface {
|
||||
}
|
||||
|
||||
type LogEvent struct {
|
||||
Guid string `json:"guid"`
|
||||
Ts utctime8601 `json:"ts"`
|
||||
// Guid string `json:"guid"`
|
||||
Ts utctime8601 `json:"ts"`
|
||||
// Level string `json:"level"`
|
||||
}
|
||||
|
||||
type LogStarting struct {
|
||||
ClientString string `json:"version_string"`
|
||||
Coinbase string `json:"coinbase"`
|
||||
ClientString string `json:"client_impl"`
|
||||
ProtocolVersion int `json:"eth_version"`
|
||||
LogEvent
|
||||
}
|
||||
@@ -32,20 +30,11 @@ func (l *LogStarting) EventName() string {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -53,17 +42,6 @@ func (l *P2PConnected) EventName() string {
|
||||
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 {
|
||||
NumConnections int `json:"num_connections"`
|
||||
RemoteId string `json:"remote_id"`
|
||||
@@ -74,247 +52,46 @@ func (l *P2PDisconnected) EventName() string {
|
||||
return "p2p.disconnected"
|
||||
}
|
||||
|
||||
type P2PDisconnecting struct {
|
||||
Reason string `json:"reason"`
|
||||
RemoteId string `json:"remote_id"`
|
||||
NumConnections int `json:"num_connections"`
|
||||
type EthMinerNewBlock struct {
|
||||
BlockHash string `json:"block_hash"`
|
||||
BlockNumber int `json:"block_number"`
|
||||
ChainHeadHash string `json:"chain_head_hash"`
|
||||
BlockPrevHash string `json:"block_prev_hash"`
|
||||
LogEvent
|
||||
}
|
||||
|
||||
func (l *P2PDisconnecting) EventName() string {
|
||||
return "p2p.disconnecting"
|
||||
func (l *EthMinerNewBlock) EventName() string {
|
||||
return "eth.miner.new_block"
|
||||
}
|
||||
|
||||
type P2PDisconnectingBadHandshake struct {
|
||||
Reason string `json:"reason"`
|
||||
RemoteId string `json:"remote_id"`
|
||||
NumConnections int `json:"num_connections"`
|
||||
type EthChainReceivedNewBlock struct {
|
||||
BlockHash string `json:"block_hash"`
|
||||
BlockNumber int `json:"block_number"`
|
||||
ChainHeadHash string `json:"chain_head_hash"`
|
||||
BlockPrevHash string `json:"block_prev_hash"`
|
||||
RemoteId int `json:"remote_id"`
|
||||
LogEvent
|
||||
}
|
||||
|
||||
func (l *P2PDisconnectingBadHandshake) EventName() string {
|
||||
return "p2p.disconnecting.bad_handshake"
|
||||
func (l *EthChainReceivedNewBlock) EventName() string {
|
||||
return "eth.chain.received.new_block"
|
||||
}
|
||||
|
||||
type P2PDisconnectingBadProtocol struct {
|
||||
Reason string `json:"reason"`
|
||||
RemoteId string `json:"remote_id"`
|
||||
NumConnections int `json:"num_connections"`
|
||||
type EthChainNewHead struct {
|
||||
BlockHash string `json:"block_hash"`
|
||||
BlockNumber int `json:"block_number"`
|
||||
ChainHeadHash string `json:"chain_head_hash"`
|
||||
BlockPrevHash string `json:"block_prev_hash"`
|
||||
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 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"
|
||||
func (l *EthChainNewHead) EventName() string {
|
||||
return "eth.chain.new_head"
|
||||
}
|
||||
|
||||
type EthTxReceived struct {
|
||||
TxHash string `json:"tx_hash"`
|
||||
TxAddress string `json:"tx_address"`
|
||||
TxHexRLP string `json:"tx_hexrlp"`
|
||||
RemoteId string `json:"remote_id"`
|
||||
TxNonce int `json:"tx_nonce"`
|
||||
TxHash string `json:"tx_hash"`
|
||||
RemoteId string `json:"remote_id"`
|
||||
LogEvent
|
||||
}
|
||||
|
||||
@@ -322,39 +99,261 @@ func (l *EthTxReceived) EventName() string {
|
||||
return "eth.tx.received"
|
||||
}
|
||||
|
||||
type EthTxBroadcasted struct {
|
||||
TxHash string `json:"tx_hash"`
|
||||
TxSender string `json:"tx_sender"`
|
||||
TxAddress string `json:"tx_address"`
|
||||
TxNonce int `json:"tx_nonce"`
|
||||
LogEvent
|
||||
}
|
||||
//
|
||||
//
|
||||
// The types below are legacy and need to be converted to new format or deleted
|
||||
//
|
||||
//
|
||||
|
||||
func (l *EthTxBroadcasted) EventName() string {
|
||||
return "eth.tx.broadcasted"
|
||||
}
|
||||
// type P2PConnecting struct {
|
||||
// RemoteId string `json:"remote_id"`
|
||||
// RemoteEndpoint string `json:"remote_endpoint"`
|
||||
// NumConnections int `json:"num_connections"`
|
||||
// LogEvent
|
||||
// }
|
||||
|
||||
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 *P2PConnecting) EventName() string {
|
||||
// return "p2p.connecting"
|
||||
// }
|
||||
|
||||
func (l *EthTxValidated) EventName() string {
|
||||
return "eth.tx.validated"
|
||||
}
|
||||
// type P2PHandshaked struct {
|
||||
// RemoteCapabilities []string `json:"remote_capabilities"`
|
||||
// RemoteId string `json:"remote_id"`
|
||||
// NumConnections int `json:"num_connections"`
|
||||
// LogEvent
|
||||
// }
|
||||
|
||||
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 *P2PHandshaked) EventName() string {
|
||||
// return "p2p.handshaked"
|
||||
// }
|
||||
|
||||
func (l *EthTxIsInvalid) EventName() string {
|
||||
return "eth.tx.is_invalid"
|
||||
}
|
||||
// type P2PDisconnecting struct {
|
||||
// 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"
|
||||
// }
|
||||
+2
-2
@@ -20,13 +20,13 @@ type Miner struct {
|
||||
mining bool
|
||||
}
|
||||
|
||||
func New(coinbase []byte, eth core.Backend) *Miner {
|
||||
func New(coinbase []byte, eth core.Backend, minerThreads int) *Miner {
|
||||
miner := &Miner{
|
||||
Coinbase: coinbase,
|
||||
worker: newWorker(coinbase, eth),
|
||||
}
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := 0; i < minerThreads; i++ {
|
||||
miner.worker.register(NewCpuMiner(i, ezp.New()))
|
||||
}
|
||||
|
||||
|
||||
+13
-9
@@ -109,14 +109,18 @@ func (self *worker) register(agent Agent) {
|
||||
}
|
||||
|
||||
func (self *worker) update() {
|
||||
events := self.mux.Subscribe(core.ChainEvent{}, core.TxPreEvent{})
|
||||
events := self.mux.Subscribe(core.ChainEvent{}, core.NewMinedBlockEvent{})
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case event := <-events.Chan():
|
||||
switch event.(type) {
|
||||
case core.ChainEvent, core.TxPreEvent:
|
||||
switch ev := event.(type) {
|
||||
case core.ChainEvent:
|
||||
if self.current.block != ev.Block {
|
||||
self.commitNewWork()
|
||||
}
|
||||
case core.NewMinedBlockEvent:
|
||||
self.commitNewWork()
|
||||
}
|
||||
case <-self.quit:
|
||||
@@ -172,17 +176,19 @@ func (self *worker) commitNewWork() {
|
||||
transactions := self.eth.TxPool().GetTransactions()
|
||||
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
|
||||
var remove types.Transactions
|
||||
gasLimit:
|
||||
for _, tx := range transactions {
|
||||
err := self.commitTransaction(tx)
|
||||
switch {
|
||||
case core.IsNonceErr(err):
|
||||
// Remove invalid transactions
|
||||
remove = append(remove, tx)
|
||||
case core.IsGasLimitErr(err):
|
||||
case state.IsGasLimitErr(err):
|
||||
// Break on gas limit
|
||||
break
|
||||
break gasLimit
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -227,11 +233,9 @@ func (self *worker) commitUncle(uncle *types.Header) 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)
|
||||
if err != nil && (core.IsNonceErr(err) || core.IsGasLimitErr(err)) {
|
||||
self.current.state.Set(snapshot)
|
||||
|
||||
if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err)) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -253,7 +253,8 @@ func (t *udp) loop() {
|
||||
|
||||
case reply := <-t.replies:
|
||||
// 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) {
|
||||
p.errc <- nil
|
||||
copy(pending[i:], pending[i+1:])
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
// "binary"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||
"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/rlp"
|
||||
)
|
||||
|
||||
var clogger = ethlogger.NewLogger("CRYPTOID")
|
||||
|
||||
const (
|
||||
sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2
|
||||
sigLen = 65 // elliptic S256
|
||||
@@ -30,26 +29,76 @@ const (
|
||||
rHSLen = authRespLen + eciesBytes // size of the final ECIES payload sent as receiver's handshake
|
||||
)
|
||||
|
||||
type hexkey []byte
|
||||
|
||||
func (self hexkey) String() string {
|
||||
return fmt.Sprintf("(%d) %x", len(self), []byte(self))
|
||||
type conn struct {
|
||||
*frameRW
|
||||
*protoHandshake
|
||||
}
|
||||
|
||||
func encHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, dial *discover.Node) (
|
||||
remoteID discover.NodeID,
|
||||
sessionToken []byte,
|
||||
err error,
|
||||
) {
|
||||
func newConn(fd net.Conn, hs *protoHandshake) *conn {
|
||||
return &conn{newFrameRW(fd, msgWriteTimeout), hs}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var remotePubkey []byte
|
||||
sessionToken, remotePubkey, err = inboundEncHandshake(conn, prv, nil)
|
||||
copy(remoteID[:], remotePubkey)
|
||||
return setupInboundConn(fd, prv, our)
|
||||
} else {
|
||||
remoteID = dial.ID
|
||||
sessionToken, err = outboundEncHandshake(conn, prv, remoteID[:], nil)
|
||||
return setupOutboundConn(fd, prv, our, dial)
|
||||
}
|
||||
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.
|
||||
@@ -66,18 +115,9 @@ func outboundEncHandshake(conn io.ReadWriter, prvKey *ecdsa.PrivateKey, remotePu
|
||||
if err != nil {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
clogger.Debugf("initiator handshake: %v", hexkey(auth))
|
||||
|
||||
response := make([]byte, rHSLen)
|
||||
if _, err = io.ReadFull(conn, response); err != nil {
|
||||
@@ -88,9 +128,6 @@ func outboundEncHandshake(conn io.ReadWriter, prvKey *ecdsa.PrivateKey, remotePu
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -221,12 +258,9 @@ func inboundEncHandshake(conn io.ReadWriter, prvKey *ecdsa.PrivateKey, sessionTo
|
||||
if err != nil {
|
||||
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 {
|
||||
return nil, nil, err
|
||||
}
|
||||
clogger.Debugf("receiver handshake:\n%v", hexkey(response))
|
||||
token, err = newSession(initNonce, recNonce, randomPrivKey, remoteRandomPubKey)
|
||||
return token, remotePubKey, err
|
||||
}
|
||||
@@ -361,3 +395,40 @@ func xor(one, other []byte) (xor []byte) {
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -5,10 +5,12 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"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) {
|
||||
@@ -91,14 +93,14 @@ func testCryptoHandshake(prv0, prv1 *ecdsa.PrivateKey, sessionToken []byte, t *t
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
t.Logf("-> %v", hexkey(auth))
|
||||
// t.Logf("-> %v", hexkey(auth))
|
||||
|
||||
// receiver reads auth and responds with response
|
||||
response, remoteRecNonce, remoteInitNonce, _, remoteRandomPrivKey, remoteInitRandomPubKey, err := authResp(auth, sessionToken, prv1)
|
||||
if err != nil {
|
||||
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
|
||||
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()
|
||||
|
||||
prv0, _ := crypto.GenerateKey()
|
||||
@@ -165,3 +167,58 @@ func TestHandshake(t *testing.T) {
|
||||
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
|
||||
}
|
||||
+1
-1
@@ -197,7 +197,7 @@ func (rw *frameRW) ReadMsg() (msg Msg, err error) {
|
||||
return msg, err
|
||||
}
|
||||
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:])
|
||||
|
||||
|
||||
+3
-3
@@ -7,9 +7,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fjl/goupnp"
|
||||
"github.com/fjl/goupnp/dcps/internetgateway1"
|
||||
"github.com/fjl/goupnp/dcps/internetgateway2"
|
||||
"github.com/huin/goupnp"
|
||||
"github.com/huin/goupnp/dcps/internetgateway1"
|
||||
"github.com/huin/goupnp/dcps/internetgateway2"
|
||||
)
|
||||
|
||||
type upnp struct {
|
||||
|
||||
+84
-180
@@ -21,6 +21,7 @@ const (
|
||||
baseProtocolMaxMsgSize = 10 * 1024 * 1024
|
||||
|
||||
disconnectGracePeriod = 2 * time.Second
|
||||
pingInterval = 15 * time.Second
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -33,37 +34,14 @@ const (
|
||||
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.
|
||||
type Peer struct {
|
||||
// Peers have all the log methods.
|
||||
// Use them to display messages related to the peer.
|
||||
*logger.Logger
|
||||
|
||||
infoMu sync.Mutex
|
||||
name string
|
||||
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
|
||||
rw *conn
|
||||
running map[string]*protoRW
|
||||
|
||||
protoWG sync.WaitGroup
|
||||
protoErr chan error
|
||||
@@ -73,36 +51,27 @@ type Peer struct {
|
||||
|
||||
// NewPeer returns a peer for testing purposes.
|
||||
func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer {
|
||||
conn, _ := net.Pipe()
|
||||
peer := newPeer(conn, nil, "", nil, &id)
|
||||
peer.setHandshakeInfo(name, caps)
|
||||
pipe, _ := net.Pipe()
|
||||
conn := newConn(pipe, &protoHandshake{ID: id, Name: name, Caps: caps})
|
||||
peer := newPeer(conn, nil)
|
||||
close(peer.closed) // ensures Disconnect doesn't block
|
||||
return peer
|
||||
}
|
||||
|
||||
// ID returns the node's public key.
|
||||
func (p *Peer) ID() discover.NodeID {
|
||||
return *p.remoteID
|
||||
return p.rw.ID
|
||||
}
|
||||
|
||||
// Name returns the node name that the remote node advertised.
|
||||
func (p *Peer) Name() string {
|
||||
// this needs a lock because the information is part of the
|
||||
// protocol handshake.
|
||||
p.infoMu.Lock()
|
||||
name := p.name
|
||||
p.infoMu.Unlock()
|
||||
return name
|
||||
return p.rw.Name
|
||||
}
|
||||
|
||||
// Caps returns the capabilities (supported subprotocols) of the remote peer.
|
||||
func (p *Peer) Caps() []Cap {
|
||||
// this needs a lock because the information is part of the
|
||||
// protocol handshake.
|
||||
p.infoMu.Lock()
|
||||
caps := p.caps
|
||||
p.infoMu.Unlock()
|
||||
return caps
|
||||
// TODO: maybe return copy
|
||||
return p.rw.Caps
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote address of the network connection.
|
||||
@@ -126,30 +95,20 @@ func (p *Peer) Disconnect(reason DiscReason) {
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
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 {
|
||||
logtag := fmt.Sprintf("Peer %.8x %v", remoteID[:], conn.RemoteAddr())
|
||||
return &Peer{
|
||||
Logger: logger.NewLogger(logtag),
|
||||
rw: newFrameRW(conn, msgWriteTimeout),
|
||||
ourID: ourID,
|
||||
ourName: ourName,
|
||||
remoteID: remoteID,
|
||||
protocols: protocols,
|
||||
running: make(map[string]*proto),
|
||||
disc: make(chan DiscReason),
|
||||
protoErr: make(chan error),
|
||||
closed: make(chan struct{}),
|
||||
func newPeer(conn *conn, protocols []Protocol) *Peer {
|
||||
logtag := fmt.Sprintf("Peer %.8x %v", conn.ID[:], conn.RemoteAddr())
|
||||
p := &Peer{
|
||||
Logger: logger.NewLogger(logtag),
|
||||
rw: conn,
|
||||
running: matchProtocols(protocols, conn.Caps, conn),
|
||||
disc: make(chan DiscReason),
|
||||
protoErr: make(chan error),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Peer) setHandshakeInfo(name string, caps []Cap) {
|
||||
p.infoMu.Lock()
|
||||
p.name = name
|
||||
p.caps = caps
|
||||
p.infoMu.Unlock()
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Peer) run() DiscReason {
|
||||
@@ -157,29 +116,36 @@ func (p *Peer) run() DiscReason {
|
||||
defer p.closeProtocols()
|
||||
defer close(p.closed)
|
||||
|
||||
p.startProtocols()
|
||||
go func() { readErr <- p.readLoop() }()
|
||||
|
||||
if !p.noHandshake {
|
||||
if err := writeProtocolHandshake(p.rw, p.ourName, *p.ourID, p.protocols); err != nil {
|
||||
p.DebugDetailf("Protocol handshake error: %v\n", err)
|
||||
p.rw.Close()
|
||||
return DiscProtocolError
|
||||
}
|
||||
}
|
||||
ping := time.NewTicker(pingInterval)
|
||||
defer ping.Stop()
|
||||
|
||||
// Wait for an error or disconnect.
|
||||
var reason DiscReason
|
||||
select {
|
||||
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)
|
||||
case reason = <-p.disc:
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-ping.C:
|
||||
go func() {
|
||||
if err := EncodeMsg(p.rw, pingMsg, nil); err != nil {
|
||||
p.protoErr <- err
|
||||
return
|
||||
}
|
||||
}()
|
||||
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)
|
||||
|
||||
@@ -206,11 +172,6 @@ func (p *Peer) politeDisconnect(reason DiscReason) {
|
||||
}
|
||||
|
||||
func (p *Peer) readLoop() error {
|
||||
if !p.noHandshake {
|
||||
if err := readProtocolHandshake(p, p.rw); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for {
|
||||
msg, err := p.rw.ReadMsg()
|
||||
if err != nil {
|
||||
@@ -249,105 +210,51 @@ func (p *Peer) handle(msg Msg) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func readProtocolHandshake(p *Peer, rw MsgReadWriter) error {
|
||||
// read and handle remote handshake
|
||||
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) {
|
||||
// matchProtocols creates structures for matching named subprotocols.
|
||||
func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW {
|
||||
sort.Sort(capsByName(caps))
|
||||
p.runlock.Lock()
|
||||
defer p.runlock.Unlock()
|
||||
offset := baseProtocolLength
|
||||
result := make(map[string]*protoRW)
|
||||
outer:
|
||||
for _, cap := range caps {
|
||||
for _, proto := range p.protocols {
|
||||
if proto.Name == cap.Name &&
|
||||
proto.Version == cap.Version &&
|
||||
p.running[cap.Name] == nil {
|
||||
p.running[cap.Name] = p.startProto(offset, proto)
|
||||
for _, proto := range protocols {
|
||||
if proto.Name == cap.Name && proto.Version == cap.Version && result[cap.Name] == nil {
|
||||
result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw}
|
||||
offset += proto.Length
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *Peer) startProto(offset uint64, impl Protocol) *proto {
|
||||
p.DebugDetailf("Starting protocol %s/%d\n", impl.Name, impl.Version)
|
||||
rw := &proto{
|
||||
name: impl.Name,
|
||||
in: make(chan Msg),
|
||||
offset: offset,
|
||||
maxcode: impl.Length,
|
||||
w: p.rw,
|
||||
func (p *Peer) startProtocols() {
|
||||
for _, proto := range p.running {
|
||||
proto := proto
|
||||
p.DebugDetailf("Starting protocol %s/%d\n", proto.Name, proto.Version)
|
||||
p.protoWG.Add(1)
|
||||
go func() {
|
||||
err := proto.Run(p, proto)
|
||||
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
|
||||
// the given message code.
|
||||
func (p *Peer) getProto(code uint64) (*proto, error) {
|
||||
p.runlock.RLock()
|
||||
defer p.runlock.RUnlock()
|
||||
func (p *Peer) getProto(code uint64) (*protoRW, error) {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -355,46 +262,43 @@ func (p *Peer) getProto(code uint64) (*proto, error) {
|
||||
}
|
||||
|
||||
func (p *Peer) closeProtocols() {
|
||||
p.runlock.RLock()
|
||||
for _, p := range p.running {
|
||||
close(p.in)
|
||||
}
|
||||
p.runlock.RUnlock()
|
||||
p.protoWG.Wait()
|
||||
}
|
||||
|
||||
// writeProtoMsg sends the given message on behalf of the given named protocol.
|
||||
// this exists because of Server.Broadcast.
|
||||
func (p *Peer) writeProtoMsg(protoName string, msg Msg) error {
|
||||
p.runlock.RLock()
|
||||
proto, ok := p.running[protoName]
|
||||
p.runlock.RUnlock()
|
||||
if !ok {
|
||||
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)
|
||||
}
|
||||
msg.Code += proto.offset
|
||||
return p.rw.WriteMsg(msg)
|
||||
}
|
||||
|
||||
type proto struct {
|
||||
name string
|
||||
in chan Msg
|
||||
maxcode, offset uint64
|
||||
w MsgWriter
|
||||
type protoRW struct {
|
||||
Protocol
|
||||
|
||||
in chan Msg
|
||||
offset uint64
|
||||
w MsgWriter
|
||||
}
|
||||
|
||||
func (rw *proto) WriteMsg(msg Msg) error {
|
||||
if msg.Code >= rw.maxcode {
|
||||
func (rw *protoRW) WriteMsg(msg Msg) error {
|
||||
if msg.Code >= rw.Length {
|
||||
return newPeerError(errInvalidMsgCode, "not handled")
|
||||
}
|
||||
msg.Code += rw.offset
|
||||
return rw.w.WriteMsg(msg)
|
||||
}
|
||||
|
||||
func (rw *proto) ReadMsg() (Msg, error) {
|
||||
func (rw *protoRW) ReadMsg() (Msg, error) {
|
||||
msg, ok := <-rw.in
|
||||
if !ok {
|
||||
return msg, io.EOF
|
||||
|
||||
+20
-85
@@ -6,11 +6,9 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
@@ -23,6 +21,7 @@ var discard = Protocol{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("discarding %d\n", msg.Code)
|
||||
if err = msg.Discard(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -30,13 +29,20 @@ var discard = Protocol{
|
||||
},
|
||||
}
|
||||
|
||||
func testPeer(noHandshake bool, protos []Protocol) (*frameRW, *Peer, <-chan DiscReason) {
|
||||
conn1, conn2 := net.Pipe()
|
||||
peer := newPeer(conn1, protos, "name", &discover.NodeID{}, &discover.NodeID{})
|
||||
peer.noHandshake = noHandshake
|
||||
func testPeer(protos []Protocol) (*conn, *Peer, <-chan DiscReason) {
|
||||
fd1, fd2 := net.Pipe()
|
||||
hs1 := &protoHandshake{ID: randomID(), Version: baseProtocolVersion}
|
||||
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)
|
||||
go func() { errc <- peer.run() }()
|
||||
return newFrameRW(conn2, msgWriteTimeout), peer, errc
|
||||
|
||||
return newConn(fd2, hs2), peer, errc
|
||||
}
|
||||
|
||||
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()
|
||||
peer.startSubprotocols([]Cap{proto.cap()})
|
||||
|
||||
EncodeMsg(rw, baseProtocolLength+2, 1)
|
||||
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()
|
||||
peer.startSubprotocols([]Cap{proto.cap()})
|
||||
|
||||
EncodeMsg(rw, 18, make([]byte, msgsize))
|
||||
select {
|
||||
@@ -130,9 +134,8 @@ func TestPeerProtoEncodeMsg(t *testing.T) {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
rw, peer, _ := testPeer(true, []Protocol{proto})
|
||||
rw, _, _ := testPeer([]Protocol{proto})
|
||||
defer rw.Close()
|
||||
peer.startSubprotocols([]Cap{proto.cap()})
|
||||
|
||||
if err := expectMsg(rw, 17, []string{"foo", "bar"}); err != nil {
|
||||
t.Error(err)
|
||||
@@ -142,9 +145,8 @@ func TestPeerProtoEncodeMsg(t *testing.T) {
|
||||
func TestPeerWriteForBroadcast(t *testing.T) {
|
||||
defer testlog(t).detach()
|
||||
|
||||
rw, peer, peerErr := testPeer(true, []Protocol{discard})
|
||||
rw, peer, peerErr := testPeer([]Protocol{discard})
|
||||
defer rw.Close()
|
||||
peer.startSubprotocols([]Cap{discard.cap()})
|
||||
|
||||
// test write errors
|
||||
if err := peer.writeProtoMsg("b", NewMsg(3)); err == nil {
|
||||
@@ -160,7 +162,7 @@ func TestPeerWriteForBroadcast(t *testing.T) {
|
||||
read := make(chan struct{})
|
||||
go func() {
|
||||
if err := expectMsg(rw, 16, nil); err != nil {
|
||||
t.Error()
|
||||
t.Error(err)
|
||||
}
|
||||
close(read)
|
||||
}()
|
||||
@@ -179,7 +181,7 @@ func TestPeerWriteForBroadcast(t *testing.T) {
|
||||
func TestPeerPing(t *testing.T) {
|
||||
defer testlog(t).detach()
|
||||
|
||||
rw, _, _ := testPeer(true, nil)
|
||||
rw, _, _ := testPeer(nil)
|
||||
defer rw.Close()
|
||||
if err := EncodeMsg(rw, pingMsg); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -192,7 +194,7 @@ func TestPeerPing(t *testing.T) {
|
||||
func TestPeerDisconnect(t *testing.T) {
|
||||
defer testlog(t).detach()
|
||||
|
||||
rw, _, disc := testPeer(true, nil)
|
||||
rw, _, disc := testPeer(nil)
|
||||
defer rw.Close()
|
||||
if err := EncodeMsg(rw, discMsg, DiscQuitting); err != nil {
|
||||
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) {
|
||||
name := "nodename"
|
||||
caps := []Cap{{"foo", 2}, {"bar", 3}}
|
||||
|
||||
+43
-23
@@ -5,7 +5,6 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
@@ -23,6 +22,7 @@ const (
|
||||
)
|
||||
|
||||
var srvlog = logger.NewLogger("P2P Server")
|
||||
var srvjslog = logger.NewJsonLogger()
|
||||
|
||||
// MakeName creates a node name that follows the ethereum convention
|
||||
// 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
|
||||
// the whole protocol stack.
|
||||
handshakeFunc
|
||||
setupFunc
|
||||
newPeerHook
|
||||
|
||||
ourHandshake *protoHandshake
|
||||
|
||||
lock sync.RWMutex
|
||||
running bool
|
||||
listener net.Listener
|
||||
@@ -99,7 +101,7 @@ type Server struct {
|
||||
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)
|
||||
|
||||
// Peers returns all connected peers.
|
||||
@@ -159,7 +161,7 @@ func (srv *Server) Start() (err error) {
|
||||
}
|
||||
srvlog.Infoln("Starting Server")
|
||||
|
||||
// initialize all the fields
|
||||
// static fields
|
||||
if srv.PrivateKey == nil {
|
||||
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.peers = make(map[discover.NodeID]*Peer)
|
||||
srv.peerConnect = make(chan *discover.Node)
|
||||
|
||||
if srv.handshakeFunc == nil {
|
||||
srv.handshakeFunc = encHandshake
|
||||
if srv.setupFunc == nil {
|
||||
srv.setupFunc = setupConn
|
||||
}
|
||||
if srv.Blacklist == nil {
|
||||
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 err := srv.startListening(); err != nil {
|
||||
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 {
|
||||
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
|
||||
conn.SetDeadline(time.Now().Add(handshakeTimeout))
|
||||
remoteID, _, err := srv.handshakeFunc(conn, srv.PrivateKey, dest)
|
||||
fd.SetDeadline(time.Now().Add(handshakeTimeout))
|
||||
conn, err := srv.setupFunc(fd, srv.PrivateKey, srv.ourHandshake, dest)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
srvlog.Debugf("Encryption Handshake with %v failed: %v", conn.RemoteAddr(), err)
|
||||
fd.Close()
|
||||
srvlog.Debugf("Handshake with %v failed: %v", fd.RemoteAddr(), err)
|
||||
return
|
||||
}
|
||||
ourID := srv.ntab.Self()
|
||||
p := newPeer(conn, srv.Protocols, srv.Name, &ourID, &remoteID)
|
||||
if ok, reason := srv.addPeer(remoteID, p); !ok {
|
||||
p := newPeer(conn, srv.Protocols)
|
||||
if ok, reason := srv.addPeer(conn.ID, p); !ok {
|
||||
srvlog.DebugDetailf("Not adding %v (%v)\n", p, reason)
|
||||
p.politeDisconnect(reason)
|
||||
return
|
||||
}
|
||||
|
||||
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 {
|
||||
srv.newPeerHook(p)
|
||||
}
|
||||
discreason := p.run()
|
||||
srv.removePeer(p)
|
||||
|
||||
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) {
|
||||
@@ -394,7 +414,7 @@ func (srv *Server) addPeer(id discover.NodeID, p *Peer) (bool, DiscReason) {
|
||||
|
||||
func (srv *Server) removePeer(p *Peer) {
|
||||
srv.lock.Lock()
|
||||
delete(srv.peers, *p.remoteID)
|
||||
delete(srv.peers, p.ID())
|
||||
srv.lock.Unlock()
|
||||
srv.peerWG.Done()
|
||||
}
|
||||
|
||||
+7
-5
@@ -21,8 +21,12 @@ func startTestServer(t *testing.T, pf newPeerHook) *Server {
|
||||
ListenAddr: "127.0.0.1:0",
|
||||
PrivateKey: newkey(),
|
||||
newPeerHook: pf,
|
||||
handshakeFunc: func(io.ReadWriter, *ecdsa.PrivateKey, *discover.Node) (id discover.NodeID, st []byte, err error) {
|
||||
return randomID(), nil, err
|
||||
setupFunc: func(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, dial *discover.Node) (*conn, error) {
|
||||
id := randomID()
|
||||
return &conn{
|
||||
frameRW: newFrameRW(fd, msgWriteTimeout),
|
||||
protoHandshake: &protoHandshake{ID: id, Version: baseProtocolVersion},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
if err := server.Start(); err != nil {
|
||||
@@ -116,9 +120,7 @@ func TestServerBroadcast(t *testing.T) {
|
||||
|
||||
var connected sync.WaitGroup
|
||||
srv := startTestServer(t, func(p *Peer) {
|
||||
p.protocols = []Protocol{discard}
|
||||
p.startSubprotocols([]Cap{discard.cap()})
|
||||
p.noHandshake = true
|
||||
p.running = matchProtocols([]Protocol{discard}, []Cap{discard.cap()}, p.rw)
|
||||
connected.Done()
|
||||
})
|
||||
defer srv.Stop()
|
||||
|
||||
+46
-3
@@ -1,6 +1,7 @@
|
||||
package rpc
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
import "github.com/ethereum/go-ethereum/core"
|
||||
|
||||
type GetBlockArgs struct {
|
||||
@@ -37,6 +38,35 @@ type NewTxArgs struct {
|
||||
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 {
|
||||
Tx string `json:"tx"`
|
||||
}
|
||||
@@ -203,7 +233,7 @@ func (obj *Sha3Args) UnmarshalJSON(b []byte) (err error) {
|
||||
type FilterOptions struct {
|
||||
Earliest int64
|
||||
Latest int64
|
||||
Address string
|
||||
Address interface{}
|
||||
Topic []string
|
||||
Skip int
|
||||
Max int
|
||||
@@ -211,9 +241,22 @@ type FilterOptions struct {
|
||||
|
||||
func toFilterOptions(options *FilterOptions) 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.Latest = options.Latest
|
||||
opts.Address = fromHex(options.Address)
|
||||
opts.Topics = make([][]byte, len(options.Topic))
|
||||
for i, topic := range options.Topic {
|
||||
opts.Topics[i] = fromHex(topic)
|
||||
@@ -246,7 +289,7 @@ type WhisperMessageArgs struct {
|
||||
Payload string
|
||||
To string
|
||||
From string
|
||||
Topics []string
|
||||
Topic []string
|
||||
Priority uint32
|
||||
Ttl uint32
|
||||
}
|
||||
+74
-1
@@ -201,6 +201,36 @@ func (req *RpcRequest) ToGetCodeAtArgs() (*GetCodeAtArgs, error) {
|
||||
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) {
|
||||
if len(req.Params) < 1 {
|
||||
return nil, NewErrorResponse(ErrorArguments)
|
||||
@@ -231,6 +261,21 @@ func (req *RpcRequest) ToFilterStringArgs() (string, error) {
|
||||
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) {
|
||||
if len(req.Params) < 1 {
|
||||
return 0, NewErrorResponse(ErrorArguments)
|
||||
@@ -301,7 +346,7 @@ func (req *RpcRequest) ToWhisperFilterArgs() (*xeth.Options, error) {
|
||||
return &args, nil
|
||||
}
|
||||
|
||||
func (req *RpcRequest) ToWhisperIdArgs() (int, error) {
|
||||
func (req *RpcRequest) ToIdArgs() (int, error) {
|
||||
if len(req.Params) < 1 {
|
||||
return 0, NewErrorResponse(ErrorArguments)
|
||||
}
|
||||
@@ -342,3 +387,31 @@ func (req *RpcRequest) ToWhisperHasIdentityArgs() (string, error) {
|
||||
rpclogger.DebugDetailf("%T %v", args, args)
|
||||
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
|
||||
}
|
||||
+198
-36
@@ -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:
|
||||
|
||||
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"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@@ -48,13 +32,17 @@ const (
|
||||
|
||||
type EthereumApi struct {
|
||||
xeth *xeth.XEth
|
||||
quit chan struct{}
|
||||
filterManager *filter.FilterManager
|
||||
|
||||
logMut sync.RWMutex
|
||||
logs map[int]state.Logs
|
||||
logs map[int]*logFilter
|
||||
|
||||
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
|
||||
}
|
||||
@@ -63,16 +51,48 @@ func NewEthereumApi(eth *xeth.XEth) *EthereumApi {
|
||||
db, _ := ethdb.NewLDBDatabase("dapps")
|
||||
api := &EthereumApi{
|
||||
xeth: eth,
|
||||
quit: make(chan struct{}),
|
||||
filterManager: filter.NewFilterManager(eth.Backend().EventMux()),
|
||||
logs: make(map[int]state.Logs),
|
||||
messages: make(map[int][]xeth.WhisperMessage),
|
||||
logs: make(map[int]*logFilter),
|
||||
messages: make(map[int]*whisperFilter),
|
||||
db: db,
|
||||
}
|
||||
go api.filterManager.Start()
|
||||
go api.start()
|
||||
|
||||
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 {
|
||||
var id int
|
||||
filter := core.NewFilter(self.xeth.Backend())
|
||||
@@ -81,20 +101,36 @@ func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) erro
|
||||
self.logMut.Lock()
|
||||
defer self.logMut.Unlock()
|
||||
|
||||
self.logs[id] = append(self.logs[id], logs...)
|
||||
self.logs[id].add(logs...)
|
||||
}
|
||||
id = self.filterManager.InstallFilter(filter)
|
||||
self.logs[id] = &logFilter{timeout: time.Now()}
|
||||
|
||||
*reply = id
|
||||
|
||||
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 {
|
||||
var id int
|
||||
filter := core.NewFilter(self.xeth.Backend())
|
||||
|
||||
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" {
|
||||
filter.PendingCallback = callback
|
||||
@@ -112,15 +148,29 @@ func (self *EthereumApi) FilterChanged(id int, reply *interface{}) error {
|
||||
self.logMut.Lock()
|
||||
defer self.logMut.Unlock()
|
||||
|
||||
*reply = toLogs(self.logs[id])
|
||||
|
||||
self.logs[id] = nil // empty the logs
|
||||
if self.logs[id] != nil {
|
||||
*reply = toLogs(self.logs[id].get())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *EthereumApi) Logs(id int, reply *interface{}) error {
|
||||
self.logMut.Lock()
|
||||
defer self.logMut.Unlock()
|
||||
|
||||
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())
|
||||
|
||||
return nil
|
||||
@@ -149,8 +199,14 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error {
|
||||
args.GasPrice = defaultGasPrice
|
||||
}
|
||||
|
||||
result, _ := p.xeth.Transact( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data)
|
||||
*reply = result
|
||||
// TODO if no_private_key then
|
||||
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
|
||||
}
|
||||
|
||||
@@ -231,6 +287,11 @@ func (p *EthereumApi) GetIsMining(reply *interface{}) error {
|
||||
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 {
|
||||
*reply = p.xeth.Backend().ChainManager().CurrentBlock().Number()
|
||||
return nil
|
||||
@@ -264,6 +325,21 @@ func (p *EthereumApi) GetCodeAt(args *GetCodeAtArgs, reply *interface{}) error {
|
||||
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 {
|
||||
*reply = toHex(crypto.Sha3(fromHex(args.Data)))
|
||||
return nil
|
||||
@@ -301,7 +377,10 @@ func (p *EthereumApi) NewWhisperFilter(args *xeth.Options, reply *interface{}) e
|
||||
args.Fn = func(msg xeth.WhisperMessage) {
|
||||
p.messagesMut.Lock()
|
||||
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)
|
||||
*reply = id
|
||||
@@ -312,15 +391,15 @@ func (self *EthereumApi) MessagesChanged(id int, reply *interface{}) error {
|
||||
self.messagesMut.Lock()
|
||||
defer self.messagesMut.Unlock()
|
||||
|
||||
*reply = self.messages[id]
|
||||
|
||||
self.messages[id] = nil // empty the messages
|
||||
if self.messages[id] != nil {
|
||||
*reply = self.messages[id].get()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@@ -349,6 +428,12 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
|
||||
return p.GetIsListening(reply)
|
||||
case "eth_mining":
|
||||
return p.GetIsMining(reply)
|
||||
case "eth_setMining":
|
||||
args, err := req.ToBoolArgs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.SetMining(args, reply)
|
||||
case "eth_peerCount":
|
||||
return p.GetPeerCount(reply)
|
||||
case "eth_number":
|
||||
@@ -415,15 +500,59 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
|
||||
return err
|
||||
}
|
||||
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":
|
||||
args, err := req.ToFilterChangedArgs()
|
||||
args, err := req.ToIdArgs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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":
|
||||
*reply = defaultGasPrice
|
||||
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":
|
||||
args, err := req.ToSha3Args()
|
||||
if err != nil {
|
||||
@@ -451,7 +580,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
|
||||
}
|
||||
return p.NewWhisperFilter(args, reply)
|
||||
case "shh_changed":
|
||||
args, err := req.ToWhisperIdArgs()
|
||||
args, err := req.ToIdArgs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -469,7 +598,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
|
||||
}
|
||||
return p.HasWhisperIdentity(args, reply)
|
||||
case "shh_getMessages":
|
||||
args, err := req.ToWhisperIdArgs()
|
||||
args, err := req.ToIdArgs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -481,3 +610,36 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
|
||||
rpclogger.DebugDetailf("Reply: %T %s", reply, reply)
|
||||
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)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
+38
-3
@@ -20,10 +20,12 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
var rpclogger = logger.NewLogger("RPC")
|
||||
@@ -80,8 +82,9 @@ type RpcServer interface {
|
||||
|
||||
type Log struct {
|
||||
Address string `json:"address"`
|
||||
Topics []string `json:"topics"`
|
||||
Topic []string `json:"topics"`
|
||||
Data string `json:"data"`
|
||||
Number uint64 `json:"number"`
|
||||
}
|
||||
|
||||
func toLogs(logs state.Logs) (ls []Log) {
|
||||
@@ -89,14 +92,46 @@ func toLogs(logs state.Logs) (ls []Log) {
|
||||
|
||||
for i, log := range logs {
|
||||
var l Log
|
||||
l.Topics = make([]string, len(log.Topics()))
|
||||
l.Topic = make([]string, len(log.Topics()))
|
||||
l.Address = toHex(log.Address())
|
||||
l.Data = toHex(log.Data())
|
||||
l.Number = log.Number()
|
||||
for j, topic := range log.Topics() {
|
||||
l.Topics[j] = toHex(topic)
|
||||
l.Topic[j] = toHex(topic)
|
||||
}
|
||||
ls[i] = l
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+1
-1
@@ -21,10 +21,10 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
var wslogger = logger.NewLogger("RPC-WS")
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ func (self *StateDB) Dump() []byte {
|
||||
for it.Next() {
|
||||
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)
|
||||
|
||||
storageIt := stateObject.State.trie.Iterator()
|
||||
@@ -50,7 +50,7 @@ func (self *StateDB) Dump() []byte {
|
||||
|
||||
// Debug stuff
|
||||
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()
|
||||
for it.Next() {
|
||||
fmt.Printf("%x %x\n", it.Key, it.Value)
|
||||
|
||||
+9
-2
@@ -12,16 +12,19 @@ type Log interface {
|
||||
Address() []byte
|
||||
Topics() [][]byte
|
||||
Data() []byte
|
||||
|
||||
Number() uint64
|
||||
}
|
||||
|
||||
type StateLog struct {
|
||||
address []byte
|
||||
topics [][]byte
|
||||
data []byte
|
||||
number uint64
|
||||
}
|
||||
|
||||
func NewLog(address []byte, topics [][]byte, data []byte) *StateLog {
|
||||
return &StateLog{address, topics, data}
|
||||
func NewLog(address []byte, topics [][]byte, data []byte, number uint64) *StateLog {
|
||||
return &StateLog{address, topics, data, number}
|
||||
}
|
||||
|
||||
func (self *StateLog) Address() []byte {
|
||||
@@ -36,6 +39,10 @@ func (self *StateLog) Data() []byte {
|
||||
return self.data
|
||||
}
|
||||
|
||||
func (self *StateLog) Number() uint64 {
|
||||
return self.number
|
||||
}
|
||||
|
||||
func NewLogFromValue(decoder *ethutil.Value) *StateLog {
|
||||
log := &StateLog{
|
||||
address: decoder.Get(0).Bytes(),
|
||||
|
||||
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user