mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
Merge branch 'master' into localstore
This commit is contained in:
commit
cb8078e53d
155 changed files with 3220 additions and 6671 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
|
@ -2,6 +2,7 @@
|
|||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
accounts/usbwallet @karalabe
|
||||
accounts/abi @gballet
|
||||
consensus @karalabe
|
||||
core/ @karalabe @holiman
|
||||
eth/ @karalabe
|
||||
|
|
|
|||
2
.github/no-response.yml
vendored
2
.github/no-response.yml
vendored
|
|
@ -1,7 +1,7 @@
|
|||
# Number of days of inactivity before an Issue is closed for lack of response
|
||||
daysUntilClose: 30
|
||||
# Label requiring a response
|
||||
responseRequiredLabel: more-information-needed
|
||||
responseRequiredLabel: "need:more-information"
|
||||
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been automatically closed because there has been no response
|
||||
|
|
|
|||
2
.github/stale.yml
vendored
2
.github/stale.yml
vendored
|
|
@ -7,7 +7,7 @@ exemptLabels:
|
|||
- pinned
|
||||
- security
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
staleLabel: "status:inactive"
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ HTTP based JSON-RPC API options:
|
|||
* `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
|
||||
|
||||
You'll need to use your own programming environments' capabilities (libraries, tools, etc) to connect
|
||||
via HTTP, WS or IPC to a Geth node configured with the above flags and you'll need to speak [JSON-RPC](http://www.jsonrpc.org/specification)
|
||||
via HTTP, WS or IPC to a Geth node configured with the above flags and you'll need to speak [JSON-RPC](https://www.jsonrpc.org/specification)
|
||||
on all transports. You can reuse the same connection for multiple requests!
|
||||
|
||||
**Note: Please understand the security implications of opening up an HTTP/WS based transport before
|
||||
|
|
|
|||
|
|
@ -243,11 +243,7 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
|||
// input offset is the bytes offset for packed output
|
||||
inputOffset := 0
|
||||
for _, abiArg := range abiArgs {
|
||||
if abiArg.Type.T == ArrayTy {
|
||||
inputOffset += 32 * abiArg.Type.Size
|
||||
} else {
|
||||
inputOffset += 32
|
||||
}
|
||||
inputOffset += getDynamicTypeOffset(abiArg.Type)
|
||||
}
|
||||
var ret []byte
|
||||
for i, a := range args {
|
||||
|
|
@ -257,14 +253,13 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// check for a slice type (string, bytes, slice)
|
||||
if input.Type.requiresLengthPrefix() {
|
||||
// calculate the offset
|
||||
offset := inputOffset + len(variableInput)
|
||||
// check for dynamic types
|
||||
if isDynamicType(input.Type) {
|
||||
// set the offset
|
||||
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
||||
// Append the packed output to the variable input. The variable input
|
||||
// will be appended at the end of the input.
|
||||
ret = append(ret, packNum(reflect.ValueOf(inputOffset))...)
|
||||
// calculate next offset
|
||||
inputOffset += len(packed)
|
||||
// append to variable input
|
||||
variableInput = append(variableInput, packed...)
|
||||
} else {
|
||||
// append the packed value to the input
|
||||
|
|
|
|||
|
|
@ -36,12 +36,12 @@ type Event struct {
|
|||
func (e Event) String() string {
|
||||
inputs := make([]string, len(e.Inputs))
|
||||
for i, input := range e.Inputs {
|
||||
inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type)
|
||||
inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
||||
if input.Indexed {
|
||||
inputs[i] = fmt.Sprintf("%v indexed %v", input.Name, input.Type)
|
||||
inputs[i] = fmt.Sprintf("%v indexed %v", input.Type, input.Name)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("e %v(%v)", e.Name, strings.Join(inputs, ", "))
|
||||
return fmt.Sprintf("event %v(%v)", e.Name, strings.Join(inputs, ", "))
|
||||
}
|
||||
|
||||
// Id returns the canonical representation of the event's signature used by the
|
||||
|
|
|
|||
|
|
@ -87,12 +87,12 @@ func TestEventId(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
definition: `[
|
||||
{ "type" : "event", "name" : "balance", "inputs": [{ "name" : "in", "type": "uint256" }] },
|
||||
{ "type" : "event", "name" : "check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] }
|
||||
{ "type" : "event", "name" : "Balance", "inputs": [{ "name" : "in", "type": "uint256" }] },
|
||||
{ "type" : "event", "name" : "Check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] }
|
||||
]`,
|
||||
expectations: map[string]common.Hash{
|
||||
"balance": crypto.Keccak256Hash([]byte("balance(uint256)")),
|
||||
"check": crypto.Keccak256Hash([]byte("check(address,uint256)")),
|
||||
"Balance": crypto.Keccak256Hash([]byte("Balance(uint256)")),
|
||||
"Check": crypto.Keccak256Hash([]byte("Check(address,uint256)")),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -111,6 +111,39 @@ func TestEventId(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestEventString(t *testing.T) {
|
||||
var table = []struct {
|
||||
definition string
|
||||
expectations map[string]string
|
||||
}{
|
||||
{
|
||||
definition: `[
|
||||
{ "type" : "event", "name" : "Balance", "inputs": [{ "name" : "in", "type": "uint256" }] },
|
||||
{ "type" : "event", "name" : "Check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] },
|
||||
{ "type" : "event", "name" : "Transfer", "inputs": [{ "name": "from", "type": "address", "indexed": true }, { "name": "to", "type": "address", "indexed": true }, { "name": "value", "type": "uint256" }] }
|
||||
]`,
|
||||
expectations: map[string]string{
|
||||
"Balance": "event Balance(uint256 in)",
|
||||
"Check": "event Check(address t, uint256 b)",
|
||||
"Transfer": "event Transfer(address indexed from, address indexed to, uint256 value)",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range table {
|
||||
abi, err := JSON(strings.NewReader(test.definition))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for name, event := range abi.Events {
|
||||
if event.String() != test.expectations[name] {
|
||||
t.Errorf("expected string to be %s, got %s", test.expectations[name], event.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestEventMultiValueWithArrayUnpack verifies that array fields will be counted after parsing array.
|
||||
func TestEventMultiValueWithArrayUnpack(t *testing.T) {
|
||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
||||
|
|
|
|||
|
|
@ -56,14 +56,14 @@ func (method Method) Sig() string {
|
|||
func (method Method) String() string {
|
||||
inputs := make([]string, len(method.Inputs))
|
||||
for i, input := range method.Inputs {
|
||||
inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type)
|
||||
inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
||||
}
|
||||
outputs := make([]string, len(method.Outputs))
|
||||
for i, output := range method.Outputs {
|
||||
outputs[i] = output.Type.String()
|
||||
if len(output.Name) > 0 {
|
||||
outputs[i] = fmt.Sprintf("%v ", output.Name)
|
||||
outputs[i] += fmt.Sprintf(" %v", output.Name)
|
||||
}
|
||||
outputs[i] += output.Type.String()
|
||||
}
|
||||
constant := ""
|
||||
if method.Const {
|
||||
|
|
|
|||
61
accounts/abi/method_test.go
Normal file
61
accounts/abi/method_test.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const methoddata = `
|
||||
[
|
||||
{ "type" : "function", "name" : "balance", "constant" : true },
|
||||
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
|
||||
{ "type" : "function", "name" : "transfer", "constant" : false, "inputs" : [ { "name" : "from", "type" : "address" }, { "name" : "to", "type" : "address" }, { "name" : "value", "type" : "uint256" } ], "outputs" : [ { "name" : "success", "type" : "bool" } ] }
|
||||
]`
|
||||
|
||||
func TestMethodString(t *testing.T) {
|
||||
var table = []struct {
|
||||
method string
|
||||
expectation string
|
||||
}{
|
||||
{
|
||||
method: "balance",
|
||||
expectation: "function balance() constant returns()",
|
||||
},
|
||||
{
|
||||
method: "send",
|
||||
expectation: "function send(uint256 amount) returns()",
|
||||
},
|
||||
{
|
||||
method: "transfer",
|
||||
expectation: "function transfer(address from, address to, uint256 value) returns(bool success)",
|
||||
},
|
||||
}
|
||||
|
||||
abi, err := JSON(strings.NewReader(methoddata))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, test := range table {
|
||||
got := abi.Methods[test.method].String()
|
||||
if got != test.expectation {
|
||||
t.Errorf("expected string to be %s, got %s", test.expectation, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -324,6 +324,66 @@ func TestPack(t *testing.T) {
|
|||
"foobar",
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"string[]",
|
||||
[]string{"hello", "foobar"},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||
"0000000000000000000000000000000000000000000000000000000000000080" + // offset 128 to i = 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
||||
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
||||
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
||||
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
|
||||
},
|
||||
{
|
||||
"string[2]",
|
||||
[]string{"hello", "foobar"},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0
|
||||
"0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
||||
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
||||
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
||||
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
|
||||
},
|
||||
{
|
||||
"bytes32[][]",
|
||||
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
||||
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
||||
},
|
||||
|
||||
{
|
||||
"bytes32[][2]",
|
||||
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
||||
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
||||
},
|
||||
|
||||
{
|
||||
"bytes32[3][2]",
|
||||
[][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[0][2]
|
||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
||||
},
|
||||
} {
|
||||
typ, err := NewType(test.typ)
|
||||
if err != nil {
|
||||
|
|
@ -336,7 +396,7 @@ func TestPack(t *testing.T) {
|
|||
}
|
||||
|
||||
if !bytes.Equal(output, test.output) {
|
||||
t.Errorf("%d failed. Expected bytes: '%x' Got: '%x'", i, test.output, output)
|
||||
t.Errorf("input %d for typ: %v failed. Expected bytes: '%x' Got: '%x'", i, typ.String(), test.output, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,23 +183,39 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if t.T == SliceTy || t.T == ArrayTy {
|
||||
var packed []byte
|
||||
switch t.T {
|
||||
case SliceTy, ArrayTy:
|
||||
var ret []byte
|
||||
|
||||
if t.requiresLengthPrefix() {
|
||||
// append length
|
||||
ret = append(ret, packNum(reflect.ValueOf(v.Len()))...)
|
||||
}
|
||||
|
||||
// calculate offset if any
|
||||
offset := 0
|
||||
offsetReq := isDynamicType(*t.Elem)
|
||||
if offsetReq {
|
||||
offset = getDynamicTypeOffset(*t.Elem) * v.Len()
|
||||
}
|
||||
var tail []byte
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
val, err := t.Elem.pack(v.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packed = append(packed, val...)
|
||||
}
|
||||
if t.T == SliceTy {
|
||||
return packBytesSlice(packed, v.Len()), nil
|
||||
} else if t.T == ArrayTy {
|
||||
return packed, nil
|
||||
if !offsetReq {
|
||||
ret = append(ret, val...)
|
||||
continue
|
||||
}
|
||||
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
||||
offset += len(val)
|
||||
tail = append(tail, val...)
|
||||
}
|
||||
return append(ret, tail...), nil
|
||||
default:
|
||||
return packElement(t, v), nil
|
||||
}
|
||||
return packElement(t, v), nil
|
||||
}
|
||||
|
||||
// requireLengthPrefix returns whether the type requires any sort of length
|
||||
|
|
@ -207,3 +223,27 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
|||
func (t Type) requiresLengthPrefix() bool {
|
||||
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
|
||||
}
|
||||
|
||||
// isDynamicType returns true if the type is dynamic.
|
||||
// StringTy, BytesTy, and SliceTy(irrespective of slice element type) are dynamic types
|
||||
// ArrayTy is considered dynamic if and only if the Array element is a dynamic type.
|
||||
// This function recursively checks the type for slice and array elements.
|
||||
func isDynamicType(t Type) bool {
|
||||
// dynamic types
|
||||
// array is also a dynamic type if the array type is dynamic
|
||||
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
|
||||
}
|
||||
|
||||
// getDynamicTypeOffset returns the offset for the type.
|
||||
// See `isDynamicType` to know which types are considered dynamic.
|
||||
// If the type t is an array and element type is not a dynamic type, then we consider it a static type and
|
||||
// return 32 * size of array since length prefix is not required.
|
||||
// If t is a dynamic type or element type(for slices and arrays) is dynamic, then we simply return 32 as offset.
|
||||
func getDynamicTypeOffset(t Type) int {
|
||||
// if it is an array and there are no dynamic types
|
||||
// then the array is static type
|
||||
if t.T == ArrayTy && !isDynamicType(*t.Elem) {
|
||||
return 32 * t.Size
|
||||
}
|
||||
return 32
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
|||
// Arrays have packed elements, resulting in longer unpack steps.
|
||||
// Slices have just 32 bytes per element (pointing to the contents).
|
||||
elemSize := 32
|
||||
if t.T == ArrayTy {
|
||||
if t.T == ArrayTy || t.T == SliceTy {
|
||||
elemSize = getFullElemSize(t.Elem)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -191,6 +191,11 @@ var unpackTests = []unpackTest{
|
|||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
want: [][2]uint8{{1, 2}},
|
||||
},
|
||||
{
|
||||
def: `[{"type": "uint8[2][]"}]`,
|
||||
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
want: [][2]uint8{{1, 2}, {1, 2}},
|
||||
},
|
||||
{
|
||||
def: `[{"type": "uint16[]"}]`,
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
|
|
|
|||
|
|
@ -265,7 +265,10 @@ func (ac *accountCache) scanAccounts() error {
|
|||
case (addr == common.Address{}):
|
||||
log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address")
|
||||
default:
|
||||
return &accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}}
|
||||
return &accounts.Account{
|
||||
Address: addr,
|
||||
URL: accounts.URL{Scheme: KeyStoreScheme, Path: path},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,7 +171,10 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
|
|||
if err != nil {
|
||||
return nil, accounts.Account{}, err
|
||||
}
|
||||
a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}}
|
||||
a := accounts.Account{
|
||||
Address: key.Address,
|
||||
URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
|
||||
}
|
||||
if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
|
||||
zeroKey(key.PrivateKey)
|
||||
return nil, a, err
|
||||
|
|
@ -224,5 +227,6 @@ func toISO8601(t time.Time) string {
|
|||
} else {
|
||||
tz = fmt.Sprintf("%03d00", offset/3600)
|
||||
}
|
||||
return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
|
||||
return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s",
|
||||
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
|||
PrivateKey: key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||
if cryptoJson.Cipher != "aes-128-ctr" {
|
||||
return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
|
||||
|
|
@ -38,7 +38,13 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
|
|||
return accounts.Account{}, nil, err
|
||||
}
|
||||
key.Id = uuid.NewRandom()
|
||||
a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: keyStore.JoinPath(keyFileName(key.Address))}}
|
||||
a := accounts.Account{
|
||||
Address: key.Address,
|
||||
URL: accounts.URL{
|
||||
Scheme: KeyStoreScheme,
|
||||
Path: keyStore.JoinPath(keyFileName(key.Address)),
|
||||
},
|
||||
}
|
||||
err = keyStore.StoreKey(a.URL.Path, key, password)
|
||||
return a, key, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -257,7 +257,9 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
|
|||
|
||||
// Decode the hex sting into an Ethereum address and return
|
||||
var address common.Address
|
||||
hex.Decode(address[:], hexstr)
|
||||
if _, err = hex.Decode(address[:], hexstr); err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func runCmd(ctx *cli.Context) error {
|
|||
genesisConfig *core.Genesis
|
||||
)
|
||||
if ctx.GlobalBool(MachineFlag.Name) {
|
||||
tracer = NewJSONLogger(logconfig, os.Stdout)
|
||||
tracer = vm.NewJSONLogger(logconfig, os.Stdout)
|
||||
} else if ctx.GlobalBool(DebugFlag.Name) {
|
||||
debugLogger = vm.NewStructLogger(logconfig)
|
||||
tracer = debugLogger
|
||||
|
|
@ -206,6 +206,7 @@ func runCmd(ctx *cli.Context) error {
|
|||
execTime := time.Since(tstart)
|
||||
|
||||
if ctx.GlobalBool(DumpFlag.Name) {
|
||||
statedb.Commit(true)
|
||||
statedb.IntermediateRoot(true)
|
||||
fmt.Println(string(statedb.Dump()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ func stateTestCmd(ctx *cli.Context) error {
|
|||
)
|
||||
switch {
|
||||
case ctx.GlobalBool(MachineFlag.Name):
|
||||
tracer = NewJSONLogger(config, os.Stderr)
|
||||
tracer = vm.NewJSONLogger(config, os.Stderr)
|
||||
|
||||
case ctx.GlobalBool(DebugFlag.Name):
|
||||
debugger = vm.NewStructLogger(config)
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
|
|||
}
|
||||
for _, boot := range enodes {
|
||||
old, err := enode.ParseV4(boot.String())
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
stack.Server().AddPeer(old)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
"unicode"
|
||||
|
|
@ -152,7 +153,9 @@ func enableWhisper(ctx *cli.Context) bool {
|
|||
|
||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
stack, cfg := makeConfigNode(ctx)
|
||||
|
||||
if ctx.GlobalIsSet(utils.ConstantinopleOverrideFlag.Name) {
|
||||
cfg.Eth.ConstantinopleOverride = new(big.Int).SetUint64(ctx.GlobalUint64(utils.ConstantinopleOverrideFlag.Name))
|
||||
}
|
||||
utils.RegisterEthService(stack, &cfg.Eth)
|
||||
|
||||
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ var (
|
|||
utils.LightServFlag,
|
||||
utils.LightPeersFlag,
|
||||
utils.LightKDFFlag,
|
||||
utils.WhitelistFlag,
|
||||
utils.CacheFlag,
|
||||
utils.CacheDatabaseFlag,
|
||||
utils.CacheTrieFlag,
|
||||
|
|
@ -122,6 +123,7 @@ var (
|
|||
utils.RinkebyFlag,
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.ConstantinopleOverrideFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
utils.RPCVirtualHostsFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ var AppHelpFlagGroups = []flagGroup{
|
|||
utils.LightServFlag,
|
||||
utils.LightPeersFlag,
|
||||
utils.LightKDFFlag,
|
||||
utils.WhitelistFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,35 +20,41 @@ import (
|
|||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
math2 "github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// cppEthereumGenesisSpec represents the genesis specification format used by the
|
||||
// alethGenesisSpec represents the genesis specification format used by the
|
||||
// C++ Ethereum implementation.
|
||||
type cppEthereumGenesisSpec struct {
|
||||
type alethGenesisSpec struct {
|
||||
SealEngine string `json:"sealEngine"`
|
||||
Params struct {
|
||||
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
|
||||
HomesteadForkBlock hexutil.Uint64 `json:"homesteadForkBlock"`
|
||||
EIP150ForkBlock hexutil.Uint64 `json:"EIP150ForkBlock"`
|
||||
EIP158ForkBlock hexutil.Uint64 `json:"EIP158ForkBlock"`
|
||||
ByzantiumForkBlock hexutil.Uint64 `json:"byzantiumForkBlock"`
|
||||
ConstantinopleForkBlock hexutil.Uint64 `json:"constantinopleForkBlock"`
|
||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||
ChainID hexutil.Uint64 `json:"chainID"`
|
||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||
MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"`
|
||||
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
|
||||
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
||||
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
|
||||
DurationLimit *hexutil.Big `json:"durationLimit"`
|
||||
BlockReward *hexutil.Big `json:"blockReward"`
|
||||
AccountStartNonce math2.HexOrDecimal64 `json:"accountStartNonce"`
|
||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||
HomesteadForkBlock hexutil.Uint64 `json:"homesteadForkBlock"`
|
||||
DaoHardforkBlock math2.HexOrDecimal64 `json:"daoHardforkBlock"`
|
||||
EIP150ForkBlock hexutil.Uint64 `json:"EIP150ForkBlock"`
|
||||
EIP158ForkBlock hexutil.Uint64 `json:"EIP158ForkBlock"`
|
||||
ByzantiumForkBlock hexutil.Uint64 `json:"byzantiumForkBlock"`
|
||||
ConstantinopleForkBlock hexutil.Uint64 `json:"constantinopleForkBlock"`
|
||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||
MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"`
|
||||
TieBreakingGas bool `json:"tieBreakingGas"`
|
||||
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
|
||||
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
||||
DifficultyBoundDivisor *math2.HexOrDecimal256 `json:"difficultyBoundDivisor"`
|
||||
DurationLimit *math2.HexOrDecimal256 `json:"durationLimit"`
|
||||
BlockReward *hexutil.Big `json:"blockReward"`
|
||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||
ChainID hexutil.Uint64 `json:"chainID"`
|
||||
AllowFutureBlocks bool `json:"allowFutureBlocks"`
|
||||
} `json:"params"`
|
||||
|
||||
Genesis struct {
|
||||
|
|
@ -62,57 +68,68 @@ type cppEthereumGenesisSpec struct {
|
|||
GasLimit hexutil.Uint64 `json:"gasLimit"`
|
||||
} `json:"genesis"`
|
||||
|
||||
Accounts map[common.Address]*cppEthereumGenesisSpecAccount `json:"accounts"`
|
||||
Accounts map[common.UnprefixedAddress]*alethGenesisSpecAccount `json:"accounts"`
|
||||
}
|
||||
|
||||
// cppEthereumGenesisSpecAccount is the prefunded genesis account and/or precompiled
|
||||
// alethGenesisSpecAccount is the prefunded genesis account and/or precompiled
|
||||
// contract definition.
|
||||
type cppEthereumGenesisSpecAccount struct {
|
||||
Balance *hexutil.Big `json:"balance"`
|
||||
Nonce uint64 `json:"nonce,omitempty"`
|
||||
Precompiled *cppEthereumGenesisSpecBuiltin `json:"precompiled,omitempty"`
|
||||
type alethGenesisSpecAccount struct {
|
||||
Balance *math2.HexOrDecimal256 `json:"balance"`
|
||||
Nonce uint64 `json:"nonce,omitempty"`
|
||||
Precompiled *alethGenesisSpecBuiltin `json:"precompiled,omitempty"`
|
||||
}
|
||||
|
||||
// cppEthereumGenesisSpecBuiltin is the precompiled contract definition.
|
||||
type cppEthereumGenesisSpecBuiltin struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
StartingBlock hexutil.Uint64 `json:"startingBlock,omitempty"`
|
||||
Linear *cppEthereumGenesisSpecLinearPricing `json:"linear,omitempty"`
|
||||
// alethGenesisSpecBuiltin is the precompiled contract definition.
|
||||
type alethGenesisSpecBuiltin struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
StartingBlock hexutil.Uint64 `json:"startingBlock,omitempty"`
|
||||
Linear *alethGenesisSpecLinearPricing `json:"linear,omitempty"`
|
||||
}
|
||||
|
||||
type cppEthereumGenesisSpecLinearPricing struct {
|
||||
type alethGenesisSpecLinearPricing struct {
|
||||
Base uint64 `json:"base"`
|
||||
Word uint64 `json:"word"`
|
||||
}
|
||||
|
||||
// newCppEthereumGenesisSpec converts a go-ethereum genesis block into a Parity specific
|
||||
// newAlethGenesisSpec converts a go-ethereum genesis block into a Aleth-specific
|
||||
// chain specification format.
|
||||
func newCppEthereumGenesisSpec(network string, genesis *core.Genesis) (*cppEthereumGenesisSpec, error) {
|
||||
// Only ethash is currently supported between go-ethereum and cpp-ethereum
|
||||
func newAlethGenesisSpec(network string, genesis *core.Genesis) (*alethGenesisSpec, error) {
|
||||
// Only ethash is currently supported between go-ethereum and aleth
|
||||
if genesis.Config.Ethash == nil {
|
||||
return nil, errors.New("unsupported consensus engine")
|
||||
}
|
||||
// Reconstruct the chain spec in Parity's format
|
||||
spec := &cppEthereumGenesisSpec{
|
||||
// Reconstruct the chain spec in Aleth format
|
||||
spec := &alethGenesisSpec{
|
||||
SealEngine: "Ethash",
|
||||
}
|
||||
// Some defaults
|
||||
spec.Params.AccountStartNonce = 0
|
||||
spec.Params.TieBreakingGas = false
|
||||
spec.Params.AllowFutureBlocks = false
|
||||
spec.Params.DaoHardforkBlock = 0
|
||||
|
||||
spec.Params.HomesteadForkBlock = (hexutil.Uint64)(genesis.Config.HomesteadBlock.Uint64())
|
||||
spec.Params.EIP150ForkBlock = (hexutil.Uint64)(genesis.Config.EIP150Block.Uint64())
|
||||
spec.Params.EIP158ForkBlock = (hexutil.Uint64)(genesis.Config.EIP158Block.Uint64())
|
||||
spec.Params.ByzantiumForkBlock = (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())
|
||||
spec.Params.ConstantinopleForkBlock = (hexutil.Uint64)(math.MaxUint64)
|
||||
|
||||
// Byzantium
|
||||
if num := genesis.Config.ByzantiumBlock; num != nil {
|
||||
spec.setByzantium(num)
|
||||
}
|
||||
// Constantinople
|
||||
if num := genesis.Config.ConstantinopleBlock; num != nil {
|
||||
spec.setConstantinople(num)
|
||||
}
|
||||
|
||||
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||
|
||||
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
|
||||
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
||||
spec.Params.MaxGasLimit = (hexutil.Uint64)(math.MaxUint64)
|
||||
spec.Params.MaxGasLimit = (hexutil.Uint64)(math.MaxInt64)
|
||||
spec.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
|
||||
spec.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
|
||||
spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
|
||||
spec.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
|
||||
spec.Params.DifficultyBoundDivisor = (*math2.HexOrDecimal256)(params.DifficultyBoundDivisor)
|
||||
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
|
||||
spec.Params.DurationLimit = (*math2.HexOrDecimal256)(params.DurationLimit)
|
||||
spec.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
|
||||
|
||||
spec.Genesis.Nonce = (hexutil.Bytes)(make([]byte, 8))
|
||||
|
|
@ -126,77 +143,104 @@ func newCppEthereumGenesisSpec(network string, genesis *core.Genesis) (*cppEther
|
|||
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
|
||||
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
|
||||
|
||||
spec.Accounts = make(map[common.Address]*cppEthereumGenesisSpecAccount)
|
||||
for address, account := range genesis.Alloc {
|
||||
spec.Accounts[address] = &cppEthereumGenesisSpecAccount{
|
||||
Balance: (*hexutil.Big)(account.Balance),
|
||||
Nonce: account.Nonce,
|
||||
}
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{1})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "ecrecover", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 3000},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{2})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "sha256", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 60, Word: 12},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{3})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "ripemd160", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 600, Word: 120},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{4})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "identity", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 15, Word: 3},
|
||||
spec.setAccount(address, account)
|
||||
}
|
||||
|
||||
spec.setPrecompile(1, &alethGenesisSpecBuiltin{Name: "ecrecover",
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 3000}})
|
||||
spec.setPrecompile(2, &alethGenesisSpecBuiltin{Name: "sha256",
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 60, Word: 12}})
|
||||
spec.setPrecompile(3, &alethGenesisSpecBuiltin{Name: "ripemd160",
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 600, Word: 120}})
|
||||
spec.setPrecompile(4, &alethGenesisSpecBuiltin{Name: "identity",
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 15, Word: 3}})
|
||||
if genesis.Config.ByzantiumBlock != nil {
|
||||
spec.Accounts[common.BytesToAddress([]byte{5})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "modexp", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{6})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "alt_bn128_G1_add", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), Linear: &cppEthereumGenesisSpecLinearPricing{Base: 500},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{7})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "alt_bn128_G1_mul", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), Linear: &cppEthereumGenesisSpecLinearPricing{Base: 40000},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{8})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "alt_bn128_pairing_product", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
||||
}
|
||||
spec.setPrecompile(5, &alethGenesisSpecBuiltin{Name: "modexp",
|
||||
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())})
|
||||
spec.setPrecompile(6, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_add",
|
||||
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 500}})
|
||||
spec.setPrecompile(7, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_mul",
|
||||
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 40000}})
|
||||
spec.setPrecompile(8, &alethGenesisSpecBuiltin{Name: "alt_bn128_pairing_product",
|
||||
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())})
|
||||
}
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func (spec *alethGenesisSpec) setPrecompile(address byte, data *alethGenesisSpecBuiltin) {
|
||||
if spec.Accounts == nil {
|
||||
spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount)
|
||||
}
|
||||
spec.Accounts[common.UnprefixedAddress(common.BytesToAddress([]byte{address}))].Precompiled = data
|
||||
}
|
||||
|
||||
func (spec *alethGenesisSpec) setAccount(address common.Address, account core.GenesisAccount) {
|
||||
if spec.Accounts == nil {
|
||||
spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount)
|
||||
}
|
||||
|
||||
a, exist := spec.Accounts[common.UnprefixedAddress(address)]
|
||||
if !exist {
|
||||
a = &alethGenesisSpecAccount{}
|
||||
spec.Accounts[common.UnprefixedAddress(address)] = a
|
||||
}
|
||||
a.Balance = (*math2.HexOrDecimal256)(account.Balance)
|
||||
a.Nonce = account.Nonce
|
||||
|
||||
}
|
||||
|
||||
func (spec *alethGenesisSpec) setByzantium(num *big.Int) {
|
||||
spec.Params.ByzantiumForkBlock = hexutil.Uint64(num.Uint64())
|
||||
}
|
||||
|
||||
func (spec *alethGenesisSpec) setConstantinople(num *big.Int) {
|
||||
spec.Params.ConstantinopleForkBlock = hexutil.Uint64(num.Uint64())
|
||||
}
|
||||
|
||||
// parityChainSpec is the chain specification format used by Parity.
|
||||
type parityChainSpec struct {
|
||||
Name string `json:"name"`
|
||||
Engine struct {
|
||||
Name string `json:"name"`
|
||||
Datadir string `json:"dataDir"`
|
||||
Engine struct {
|
||||
Ethash struct {
|
||||
Params struct {
|
||||
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
||||
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
|
||||
DurationLimit *hexutil.Big `json:"durationLimit"`
|
||||
BlockReward *hexutil.Big `json:"blockReward"`
|
||||
HomesteadTransition uint64 `json:"homesteadTransition"`
|
||||
EIP150Transition uint64 `json:"eip150Transition"`
|
||||
EIP160Transition uint64 `json:"eip160Transition"`
|
||||
EIP161abcTransition uint64 `json:"eip161abcTransition"`
|
||||
EIP161dTransition uint64 `json:"eip161dTransition"`
|
||||
EIP649Reward *hexutil.Big `json:"eip649Reward"`
|
||||
EIP100bTransition uint64 `json:"eip100bTransition"`
|
||||
EIP649Transition uint64 `json:"eip649Transition"`
|
||||
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
||||
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
|
||||
DurationLimit *hexutil.Big `json:"durationLimit"`
|
||||
BlockReward map[string]string `json:"blockReward"`
|
||||
DifficultyBombDelays map[string]string `json:"difficultyBombDelays"`
|
||||
HomesteadTransition hexutil.Uint64 `json:"homesteadTransition"`
|
||||
EIP100bTransition hexutil.Uint64 `json:"eip100bTransition"`
|
||||
} `json:"params"`
|
||||
} `json:"Ethash"`
|
||||
} `json:"engine"`
|
||||
|
||||
Params struct {
|
||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
|
||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||
MaxCodeSize uint64 `json:"maxCodeSize"`
|
||||
EIP155Transition uint64 `json:"eip155Transition"`
|
||||
EIP98Transition uint64 `json:"eip98Transition"`
|
||||
EIP86Transition uint64 `json:"eip86Transition"`
|
||||
EIP140Transition uint64 `json:"eip140Transition"`
|
||||
EIP211Transition uint64 `json:"eip211Transition"`
|
||||
EIP214Transition uint64 `json:"eip214Transition"`
|
||||
EIP658Transition uint64 `json:"eip658Transition"`
|
||||
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
|
||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
|
||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||
ChainID hexutil.Uint64 `json:"chainID"`
|
||||
MaxCodeSize hexutil.Uint64 `json:"maxCodeSize"`
|
||||
MaxCodeSizeTransition hexutil.Uint64 `json:"maxCodeSizeTransition"`
|
||||
EIP98Transition hexutil.Uint64 `json:"eip98Transition"`
|
||||
EIP150Transition hexutil.Uint64 `json:"eip150Transition"`
|
||||
EIP160Transition hexutil.Uint64 `json:"eip160Transition"`
|
||||
EIP161abcTransition hexutil.Uint64 `json:"eip161abcTransition"`
|
||||
EIP161dTransition hexutil.Uint64 `json:"eip161dTransition"`
|
||||
EIP155Transition hexutil.Uint64 `json:"eip155Transition"`
|
||||
EIP140Transition hexutil.Uint64 `json:"eip140Transition"`
|
||||
EIP211Transition hexutil.Uint64 `json:"eip211Transition"`
|
||||
EIP214Transition hexutil.Uint64 `json:"eip214Transition"`
|
||||
EIP658Transition hexutil.Uint64 `json:"eip658Transition"`
|
||||
EIP145Transition hexutil.Uint64 `json:"eip145Transition"`
|
||||
EIP1014Transition hexutil.Uint64 `json:"eip1014Transition"`
|
||||
EIP1052Transition hexutil.Uint64 `json:"eip1052Transition"`
|
||||
EIP1283Transition hexutil.Uint64 `json:"eip1283Transition"`
|
||||
} `json:"params"`
|
||||
|
||||
Genesis struct {
|
||||
|
|
@ -215,22 +259,22 @@ type parityChainSpec struct {
|
|||
GasLimit hexutil.Uint64 `json:"gasLimit"`
|
||||
} `json:"genesis"`
|
||||
|
||||
Nodes []string `json:"nodes"`
|
||||
Accounts map[common.Address]*parityChainSpecAccount `json:"accounts"`
|
||||
Nodes []string `json:"nodes"`
|
||||
Accounts map[common.UnprefixedAddress]*parityChainSpecAccount `json:"accounts"`
|
||||
}
|
||||
|
||||
// parityChainSpecAccount is the prefunded genesis account and/or precompiled
|
||||
// contract definition.
|
||||
type parityChainSpecAccount struct {
|
||||
Balance *hexutil.Big `json:"balance"`
|
||||
Nonce uint64 `json:"nonce,omitempty"`
|
||||
Balance math2.HexOrDecimal256 `json:"balance"`
|
||||
Nonce math2.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||
Builtin *parityChainSpecBuiltin `json:"builtin,omitempty"`
|
||||
}
|
||||
|
||||
// parityChainSpecBuiltin is the precompiled contract definition.
|
||||
type parityChainSpecBuiltin struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
ActivateAt uint64 `json:"activate_at,omitempty"`
|
||||
ActivateAt math2.HexOrDecimal64 `json:"activate_at,omitempty"`
|
||||
Pricing *parityChainSpecPricing `json:"pricing,omitempty"`
|
||||
}
|
||||
|
||||
|
|
@ -265,34 +309,51 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
|
|||
}
|
||||
// Reconstruct the chain spec in Parity's format
|
||||
spec := &parityChainSpec{
|
||||
Name: network,
|
||||
Nodes: bootnodes,
|
||||
Name: network,
|
||||
Nodes: bootnodes,
|
||||
Datadir: strings.ToLower(network),
|
||||
}
|
||||
spec.Engine.Ethash.Params.BlockReward = make(map[string]string)
|
||||
spec.Engine.Ethash.Params.DifficultyBombDelays = make(map[string]string)
|
||||
// Frontier
|
||||
spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
|
||||
spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
|
||||
spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
|
||||
spec.Engine.Ethash.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
|
||||
spec.Engine.Ethash.Params.HomesteadTransition = genesis.Config.HomesteadBlock.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP150Transition = genesis.Config.EIP150Block.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP160Transition = genesis.Config.EIP155Block.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP161abcTransition = genesis.Config.EIP158Block.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP161dTransition = genesis.Config.EIP158Block.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP649Reward = (*hexutil.Big)(ethash.ByzantiumBlockReward)
|
||||
spec.Engine.Ethash.Params.EIP100bTransition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP649Transition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
spec.Engine.Ethash.Params.BlockReward["0x0"] = hexutil.EncodeBig(ethash.FrontierBlockReward)
|
||||
|
||||
// Homestead
|
||||
spec.Engine.Ethash.Params.HomesteadTransition = hexutil.Uint64(genesis.Config.HomesteadBlock.Uint64())
|
||||
|
||||
// Tangerine Whistle : 150
|
||||
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-608.md
|
||||
spec.Params.EIP150Transition = hexutil.Uint64(genesis.Config.EIP150Block.Uint64())
|
||||
|
||||
// Spurious Dragon: 155, 160, 161, 170
|
||||
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-607.md
|
||||
spec.Params.EIP155Transition = hexutil.Uint64(genesis.Config.EIP155Block.Uint64())
|
||||
spec.Params.EIP160Transition = hexutil.Uint64(genesis.Config.EIP155Block.Uint64())
|
||||
spec.Params.EIP161abcTransition = hexutil.Uint64(genesis.Config.EIP158Block.Uint64())
|
||||
spec.Params.EIP161dTransition = hexutil.Uint64(genesis.Config.EIP158Block.Uint64())
|
||||
|
||||
// Byzantium
|
||||
if num := genesis.Config.ByzantiumBlock; num != nil {
|
||||
spec.setByzantium(num)
|
||||
}
|
||||
// Constantinople
|
||||
if num := genesis.Config.ConstantinopleBlock; num != nil {
|
||||
spec.setConstantinople(num)
|
||||
}
|
||||
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
|
||||
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
||||
spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
|
||||
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
|
||||
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||
spec.Params.MaxCodeSize = params.MaxCodeSize
|
||||
spec.Params.EIP155Transition = genesis.Config.EIP155Block.Uint64()
|
||||
spec.Params.EIP98Transition = math.MaxUint64
|
||||
spec.Params.EIP86Transition = math.MaxUint64
|
||||
spec.Params.EIP140Transition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
spec.Params.EIP211Transition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
spec.Params.EIP214Transition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
spec.Params.EIP658Transition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
// geth has it set from zero
|
||||
spec.Params.MaxCodeSizeTransition = 0
|
||||
|
||||
// Disable this one
|
||||
spec.Params.EIP98Transition = math.MaxInt64
|
||||
|
||||
spec.Genesis.Seal.Ethereum.Nonce = (hexutil.Bytes)(make([]byte, 8))
|
||||
binary.LittleEndian.PutUint64(spec.Genesis.Seal.Ethereum.Nonce[:], genesis.Nonce)
|
||||
|
|
@ -305,42 +366,77 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
|
|||
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
|
||||
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
|
||||
|
||||
spec.Accounts = make(map[common.Address]*parityChainSpecAccount)
|
||||
spec.Accounts = make(map[common.UnprefixedAddress]*parityChainSpecAccount)
|
||||
for address, account := range genesis.Alloc {
|
||||
spec.Accounts[address] = &parityChainSpecAccount{
|
||||
Balance: (*hexutil.Big)(account.Balance),
|
||||
Nonce: account.Nonce,
|
||||
bal := math2.HexOrDecimal256(*account.Balance)
|
||||
|
||||
spec.Accounts[common.UnprefixedAddress(address)] = &parityChainSpecAccount{
|
||||
Balance: bal,
|
||||
Nonce: math2.HexOrDecimal64(account.Nonce),
|
||||
}
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{1})].Builtin = &parityChainSpecBuiltin{
|
||||
Name: "ecrecover", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 3000}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{2})].Builtin = &parityChainSpecBuiltin{
|
||||
spec.setPrecompile(1, &parityChainSpecBuiltin{Name: "ecrecover",
|
||||
Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 3000}}})
|
||||
|
||||
spec.setPrecompile(2, &parityChainSpecBuiltin{
|
||||
Name: "sha256", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 60, Word: 12}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{3})].Builtin = &parityChainSpecBuiltin{
|
||||
})
|
||||
spec.setPrecompile(3, &parityChainSpecBuiltin{
|
||||
Name: "ripemd160", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 600, Word: 120}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{4})].Builtin = &parityChainSpecBuiltin{
|
||||
})
|
||||
spec.setPrecompile(4, &parityChainSpecBuiltin{
|
||||
Name: "identity", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 15, Word: 3}},
|
||||
}
|
||||
})
|
||||
if genesis.Config.ByzantiumBlock != nil {
|
||||
spec.Accounts[common.BytesToAddress([]byte{5})].Builtin = &parityChainSpecBuiltin{
|
||||
Name: "modexp", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{6})].Builtin = &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_add", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 500}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{7})].Builtin = &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_mul", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 40000}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{8})].Builtin = &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_pairing", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}},
|
||||
}
|
||||
blnum := math2.HexOrDecimal64(genesis.Config.ByzantiumBlock.Uint64())
|
||||
spec.setPrecompile(5, &parityChainSpecBuiltin{
|
||||
Name: "modexp", ActivateAt: blnum, Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}},
|
||||
})
|
||||
spec.setPrecompile(6, &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_add", ActivateAt: blnum, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 500}},
|
||||
})
|
||||
spec.setPrecompile(7, &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_mul", ActivateAt: blnum, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 40000}},
|
||||
})
|
||||
spec.setPrecompile(8, &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_pairing", ActivateAt: blnum, Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}},
|
||||
})
|
||||
}
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func (spec *parityChainSpec) setPrecompile(address byte, data *parityChainSpecBuiltin) {
|
||||
if spec.Accounts == nil {
|
||||
spec.Accounts = make(map[common.UnprefixedAddress]*parityChainSpecAccount)
|
||||
}
|
||||
a := common.UnprefixedAddress(common.BytesToAddress([]byte{address}))
|
||||
if _, exist := spec.Accounts[a]; !exist {
|
||||
spec.Accounts[a] = &parityChainSpecAccount{}
|
||||
}
|
||||
spec.Accounts[a].Builtin = data
|
||||
}
|
||||
|
||||
func (spec *parityChainSpec) setByzantium(num *big.Int) {
|
||||
spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ByzantiumBlockReward)
|
||||
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(3000000)
|
||||
n := hexutil.Uint64(num.Uint64())
|
||||
spec.Engine.Ethash.Params.EIP100bTransition = n
|
||||
spec.Params.EIP140Transition = n
|
||||
spec.Params.EIP211Transition = n
|
||||
spec.Params.EIP214Transition = n
|
||||
spec.Params.EIP658Transition = n
|
||||
}
|
||||
|
||||
func (spec *parityChainSpec) setConstantinople(num *big.Int) {
|
||||
spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ConstantinopleBlockReward)
|
||||
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(2000000)
|
||||
n := hexutil.Uint64(num.Uint64())
|
||||
spec.Params.EIP145Transition = n
|
||||
spec.Params.EIP1014Transition = n
|
||||
spec.Params.EIP1052Transition = n
|
||||
spec.Params.EIP1283Transition = n
|
||||
}
|
||||
|
||||
// pyEthereumGenesisSpec represents the genesis specification format used by the
|
||||
// Python Ethereum implementation.
|
||||
type pyEthereumGenesisSpec struct {
|
||||
|
|
|
|||
109
cmd/puppeth/genesis_test.go
Normal file
109
cmd/puppeth/genesis_test.go
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// 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/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
)
|
||||
|
||||
// Tests the go-ethereum to Aleth chainspec conversion for the Stureby testnet.
|
||||
func TestAlethSturebyConverter(t *testing.T) {
|
||||
blob, err := ioutil.ReadFile("testdata/stureby_geth.json")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file: %v", err)
|
||||
}
|
||||
var genesis core.Genesis
|
||||
if err := json.Unmarshal(blob, &genesis); err != nil {
|
||||
t.Fatalf("failed parsing genesis: %v", err)
|
||||
}
|
||||
spec, err := newAlethGenesisSpec("stureby", &genesis)
|
||||
if err != nil {
|
||||
t.Fatalf("failed creating chainspec: %v", err)
|
||||
}
|
||||
|
||||
expBlob, err := ioutil.ReadFile("testdata/stureby_aleth.json")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file: %v", err)
|
||||
}
|
||||
expspec := &alethGenesisSpec{}
|
||||
if err := json.Unmarshal(expBlob, expspec); err != nil {
|
||||
t.Fatalf("failed parsing genesis: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(expspec, spec) {
|
||||
t.Errorf("chainspec mismatch")
|
||||
c := spew.ConfigState{
|
||||
DisablePointerAddresses: true,
|
||||
SortKeys: true,
|
||||
}
|
||||
exp := strings.Split(c.Sdump(expspec), "\n")
|
||||
got := strings.Split(c.Sdump(spec), "\n")
|
||||
for i := 0; i < len(exp) && i < len(got); i++ {
|
||||
if exp[i] != got[i] {
|
||||
fmt.Printf("got: %v\nexp: %v\n", exp[i], got[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests the go-ethereum to Parity chainspec conversion for the Stureby testnet.
|
||||
func TestParitySturebyConverter(t *testing.T) {
|
||||
blob, err := ioutil.ReadFile("testdata/stureby_geth.json")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file: %v", err)
|
||||
}
|
||||
var genesis core.Genesis
|
||||
if err := json.Unmarshal(blob, &genesis); err != nil {
|
||||
t.Fatalf("failed parsing genesis: %v", err)
|
||||
}
|
||||
spec, err := newParityChainSpec("Stureby", &genesis, []string{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed creating chainspec: %v", err)
|
||||
}
|
||||
|
||||
expBlob, err := ioutil.ReadFile("testdata/stureby_parity.json")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file: %v", err)
|
||||
}
|
||||
expspec := &parityChainSpec{}
|
||||
if err := json.Unmarshal(expBlob, expspec); err != nil {
|
||||
t.Fatalf("failed parsing genesis: %v", err)
|
||||
}
|
||||
expspec.Nodes = []string{}
|
||||
|
||||
if !reflect.DeepEqual(expspec, spec) {
|
||||
t.Errorf("chainspec mismatch")
|
||||
c := spew.ConfigState{
|
||||
DisablePointerAddresses: true,
|
||||
SortKeys: true,
|
||||
}
|
||||
exp := strings.Split(c.Sdump(expspec), "\n")
|
||||
got := strings.Split(c.Sdump(spec), "\n")
|
||||
for i := 0; i < len(exp) && i < len(got); i++ {
|
||||
if exp[i] != got[i] {
|
||||
fmt.Printf("got: %v\nexp: %v\n", exp[i], got[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -640,7 +640,7 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
|
|||
files[filepath.Join(workdir, network+".json")] = genesis
|
||||
|
||||
if conf.Genesis.Config.Ethash != nil {
|
||||
cppSpec, err := newCppEthereumGenesisSpec(network, conf.Genesis)
|
||||
cppSpec, err := newAlethGenesisSpec(network, conf.Genesis)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ version: '2'
|
|||
services:
|
||||
ethstats:
|
||||
build: .
|
||||
image: {{.Network}}/ethstats{{if not .VHost}}
|
||||
image: {{.Network}}/ethstats
|
||||
container_name: {{.Network}}_ethstats_1{{if not .VHost}}
|
||||
ports:
|
||||
- "{{.Port}}:3000"{{end}}
|
||||
environment:
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ services:
|
|||
explorer:
|
||||
build: .
|
||||
image: {{.Network}}/explorer
|
||||
container_name: {{.Network}}_explorer_1
|
||||
ports:
|
||||
- "{{.NodePort}}:{{.NodePort}}"
|
||||
- "{{.NodePort}}:{{.NodePort}}/udp"{{if not .VHost}}
|
||||
|
|
|
|||
|
|
@ -56,8 +56,10 @@ services:
|
|||
faucet:
|
||||
build: .
|
||||
image: {{.Network}}/faucet
|
||||
container_name: {{.Network}}_faucet_1
|
||||
ports:
|
||||
- "{{.EthPort}}:{{.EthPort}}"{{if not .VHost}}
|
||||
- "{{.EthPort}}:{{.EthPort}}"
|
||||
- "{{.EthPort}}:{{.EthPort}}/udp"{{if not .VHost}}
|
||||
- "{{.ApiPort}}:8080"{{end}}
|
||||
volumes:
|
||||
- {{.Datadir}}:/root/.faucet
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ services:
|
|||
nginx:
|
||||
build: .
|
||||
image: {{.Network}}/nginx
|
||||
container_name: {{.Network}}_nginx_1
|
||||
ports:
|
||||
- "{{.Port}}:80"
|
||||
volumes:
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ services:
|
|||
{{.Type}}:
|
||||
build: .
|
||||
image: {{.Network}}/{{.Type}}
|
||||
container_name: {{.Network}}_{{.Type}}_1
|
||||
ports:
|
||||
- "{{.Port}}:{{.Port}}"
|
||||
- "{{.Port}}:{{.Port}}/udp"
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ services:
|
|||
wallet:
|
||||
build: .
|
||||
image: {{.Network}}/wallet
|
||||
container_name: {{.Network}}_wallet_1
|
||||
ports:
|
||||
- "{{.NodePort}}:{{.NodePort}}"
|
||||
- "{{.NodePort}}:{{.NodePort}}/udp"
|
||||
|
|
|
|||
|
|
@ -43,18 +43,23 @@ func main() {
|
|||
Usage: "log level to emit to the screen",
|
||||
},
|
||||
}
|
||||
app.Action = func(c *cli.Context) error {
|
||||
app.Before = func(c *cli.Context) error {
|
||||
// Set up the logger to print everything and the random generator
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
network := c.String("network")
|
||||
if strings.Contains(network, " ") || strings.Contains(network, "-") {
|
||||
log.Crit("No spaces or hyphens allowed in network name")
|
||||
}
|
||||
// Start the wizard and relinquish control
|
||||
makeWizard(c.String("network")).run()
|
||||
return nil
|
||||
}
|
||||
app.Action = runWizard
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
// runWizard start the wizard and relinquish control to it.
|
||||
func runWizard(c *cli.Context) error {
|
||||
network := c.String("network")
|
||||
if strings.Contains(network, " ") || strings.Contains(network, "-") || strings.ToLower(network) != network {
|
||||
log.Crit("No spaces, hyphens or capital letters allowed in network name")
|
||||
}
|
||||
makeWizard(c.String("network")).run()
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
112
cmd/puppeth/testdata/stureby_aleth.json
vendored
Normal file
112
cmd/puppeth/testdata/stureby_aleth.json
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
{
|
||||
"sealEngine":"Ethash",
|
||||
"params":{
|
||||
"accountStartNonce":"0x00",
|
||||
"maximumExtraDataSize":"0x20",
|
||||
"homesteadForkBlock":"0x2710",
|
||||
"daoHardforkBlock":"0x00",
|
||||
"EIP150ForkBlock":"0x3a98",
|
||||
"EIP158ForkBlock":"0x59d8",
|
||||
"byzantiumForkBlock":"0x7530",
|
||||
"constantinopleForkBlock":"0x9c40",
|
||||
"minGasLimit":"0x1388",
|
||||
"maxGasLimit":"0x7fffffffffffffff",
|
||||
"tieBreakingGas":false,
|
||||
"gasLimitBoundDivisor":"0x0400",
|
||||
"minimumDifficulty":"0x20000",
|
||||
"difficultyBoundDivisor":"0x0800",
|
||||
"durationLimit":"0x0d",
|
||||
"blockReward":"0x4563918244F40000",
|
||||
"networkID":"0x4cb2e",
|
||||
"chainID":"0x4cb2e",
|
||||
"allowFutureBlocks":false
|
||||
},
|
||||
"genesis":{
|
||||
"nonce":"0x0000000000000000",
|
||||
"difficulty":"0x20000",
|
||||
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author":"0x0000000000000000000000000000000000000000",
|
||||
"timestamp":"0x59a4e76d",
|
||||
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData":"0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
|
||||
"gasLimit":"0x47b760"
|
||||
},
|
||||
"accounts":{
|
||||
"0000000000000000000000000000000000000001":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"ecrecover",
|
||||
"linear":{
|
||||
"base":3000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000002":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"sha256",
|
||||
"linear":{
|
||||
"base":60,
|
||||
"word":12
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000003":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"ripemd160",
|
||||
"linear":{
|
||||
"base":600,
|
||||
"word":120
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000004":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"identity",
|
||||
"linear":{
|
||||
"base":15,
|
||||
"word":3
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000005":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"modexp",
|
||||
"startingBlock":"0x7530"
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000006":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"alt_bn128_G1_add",
|
||||
"startingBlock":"0x7530",
|
||||
"linear":{
|
||||
"base":500,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000007":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"alt_bn128_G1_mul",
|
||||
"startingBlock":"0x7530",
|
||||
"linear":{
|
||||
"base":40000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000008":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"alt_bn128_pairing_product",
|
||||
"startingBlock":"0x7530"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
cmd/puppeth/testdata/stureby_geth.json
vendored
Normal file
47
cmd/puppeth/testdata/stureby_geth.json
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"config": {
|
||||
"ethash":{},
|
||||
"chainId": 314158,
|
||||
"homesteadBlock": 10000,
|
||||
"eip150Block": 15000,
|
||||
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"eip155Block": 23000,
|
||||
"eip158Block": 23000,
|
||||
"byzantiumBlock": 30000,
|
||||
"constantinopleBlock": 40000
|
||||
},
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x59a4e76d",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
|
||||
"gasLimit": "0x47b760",
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"alloc": {
|
||||
"0000000000000000000000000000000000000001": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000002": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000003": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000004": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000005": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000006": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000007": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000008": {
|
||||
"balance": "0x01"
|
||||
}
|
||||
}
|
||||
}
|
||||
181
cmd/puppeth/testdata/stureby_parity.json
vendored
Normal file
181
cmd/puppeth/testdata/stureby_parity.json
vendored
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
{
|
||||
"name":"Stureby",
|
||||
"dataDir":"stureby",
|
||||
"engine":{
|
||||
"Ethash":{
|
||||
"params":{
|
||||
"minimumDifficulty":"0x20000",
|
||||
"difficultyBoundDivisor":"0x800",
|
||||
"durationLimit":"0xd",
|
||||
"blockReward":{
|
||||
"0x0":"0x4563918244f40000",
|
||||
"0x7530":"0x29a2241af62c0000",
|
||||
"0x9c40":"0x1bc16d674ec80000"
|
||||
},
|
||||
"homesteadTransition":"0x2710",
|
||||
"eip100bTransition":"0x7530",
|
||||
"difficultyBombDelays":{
|
||||
"0x7530":"0x2dc6c0",
|
||||
"0x9c40":"0x1e8480"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"params":{
|
||||
"accountStartNonce":"0x0",
|
||||
"maximumExtraDataSize":"0x20",
|
||||
"gasLimitBoundDivisor":"0x400",
|
||||
"minGasLimit":"0x1388",
|
||||
"networkID":"0x4cb2e",
|
||||
"chainID":"0x4cb2e",
|
||||
"maxCodeSize":"0x6000",
|
||||
"maxCodeSizeTransition":"0x0",
|
||||
"eip98Transition": "0x7fffffffffffffff",
|
||||
"eip150Transition":"0x3a98",
|
||||
"eip160Transition":"0x59d8",
|
||||
"eip161abcTransition":"0x59d8",
|
||||
"eip161dTransition":"0x59d8",
|
||||
"eip155Transition":"0x59d8",
|
||||
"eip140Transition":"0x7530",
|
||||
"eip211Transition":"0x7530",
|
||||
"eip214Transition":"0x7530",
|
||||
"eip658Transition":"0x7530",
|
||||
"eip145Transition":"0x9c40",
|
||||
"eip1014Transition":"0x9c40",
|
||||
"eip1052Transition":"0x9c40",
|
||||
"eip1283Transition":"0x9c40"
|
||||
},
|
||||
"genesis":{
|
||||
"seal":{
|
||||
"ethereum":{
|
||||
"nonce":"0x0000000000000000",
|
||||
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty":"0x20000",
|
||||
"author":"0x0000000000000000000000000000000000000000",
|
||||
"timestamp":"0x59a4e76d",
|
||||
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData":"0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
|
||||
"gasLimit":"0x47b760"
|
||||
},
|
||||
"nodes":[
|
||||
"enode://dfa7aca3f5b635fbfe7d0b20575f25e40d9e27b4bfbb3cf74364a42023ad9f25c1a4383bcc8cced86ee511a7d03415345a4df05be37f1dff040e4c780699f1c0@168.61.153.255:31303",
|
||||
"enode://ef441b20dd70aeabf0eac35c3b8a2854e5ce04db0e30be9152ea9fd129359dcbb3f803993303ff5781c755dfd7223f3fe43505f583cccb740949407677412ba9@40.74.91.252:31303",
|
||||
"enode://953b5ea1c8987cf46008232a0160324fd00d41320ecf00e23af86ec8f5396b19eb57ddab37c78141be56f62e9077de4f4dfa0747fa768ed8c8531bbfb1046237@40.70.214.166:31303",
|
||||
"enode://276e613dd4b277a66591e565711e6c8bb107f0905248a9f8f8228c1a87992e156e5114bb9937c02824a9d9d25f76340442cf86e2028bf5293cae19904fb2b98e@35.178.251.52:30303",
|
||||
"enode://064c820d41e52ed7d426ac64b60506c2998235bedc7e67cb497c6faf7bb4fc54fe56fc82d0add3180b747c0c4f40a1108a6f84d7d0629ed606d504528e61cc57@3.8.5.3:30303",
|
||||
"enode://90069fdabcc5e684fa5d59430bebbb12755d9362dfe5006a1485b13d71a78a3812d36e74dd7d88e50b51add01e097ea80f16263aeaa4f0230db6c79e2a97e7ca@217.29.191.142:30303",
|
||||
"enode://0aac74b7fd28726275e466acb5e03bc88a95927e9951eb66b5efb239b2f798ada0690853b2f2823fe4efa408f0f3d4dd258430bc952a5ff70677b8625b3e3b14@40.115.33.57:40404",
|
||||
"enode://0b96415a10f835106d83e090a0528eed5e7887e5c802a6d084e9f1993a9d0fc713781e6e4101f6365e9b91259712f291acc0a9e6e667e22023050d602c36fbe2@40.115.33.57:40414"
|
||||
],
|
||||
"accounts":{
|
||||
"0000000000000000000000000000000000000001":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"ecrecover",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":3000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000002":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"sha256",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":60,
|
||||
"word":12
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000003":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"ripemd160",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":600,
|
||||
"word":120
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000004":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"identity",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":15,
|
||||
"word":3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000005":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"modexp",
|
||||
"activate_at":"0x7530",
|
||||
"pricing":{
|
||||
"modexp":{
|
||||
"divisor":20
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000006":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"alt_bn128_add",
|
||||
"activate_at":"0x7530",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":500,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000007":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"alt_bn128_mul",
|
||||
"activate_at":"0x7530",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":40000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000008":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"alt_bn128_pairing",
|
||||
"activate_at":"0x7530",
|
||||
"pricing":{
|
||||
"alt_bn128_pairing":{
|
||||
"base":100000,
|
||||
"pair":80000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
|
@ -118,6 +119,47 @@ func (w *wizard) readDefaultString(def string) string {
|
|||
return def
|
||||
}
|
||||
|
||||
// readDefaultYesNo reads a single line from stdin, trimming if from spaces and
|
||||
// interpreting it as a 'yes' or a 'no'. If an empty line is entered, the default
|
||||
// value is returned.
|
||||
func (w *wizard) readDefaultYesNo(def bool) bool {
|
||||
for {
|
||||
fmt.Printf("> ")
|
||||
text, err := w.in.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Crit("Failed to read user input", "err", err)
|
||||
}
|
||||
if text = strings.ToLower(strings.TrimSpace(text)); text == "" {
|
||||
return def
|
||||
}
|
||||
if text == "y" || text == "yes" {
|
||||
return true
|
||||
}
|
||||
if text == "n" || text == "no" {
|
||||
return false
|
||||
}
|
||||
log.Error("Invalid input, expected 'y', 'yes', 'n', 'no' or empty")
|
||||
}
|
||||
}
|
||||
|
||||
// readURL reads a single line from stdin, trimming if from spaces and trying to
|
||||
// interpret it as a URL (http, https or file).
|
||||
func (w *wizard) readURL() *url.URL {
|
||||
for {
|
||||
fmt.Printf("> ")
|
||||
text, err := w.in.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Crit("Failed to read user input", "err", err)
|
||||
}
|
||||
uri, err := url.Parse(strings.TrimSpace(text))
|
||||
if err != nil {
|
||||
log.Error("Invalid input, expected URL", "err", err)
|
||||
continue
|
||||
}
|
||||
return uri
|
||||
}
|
||||
}
|
||||
|
||||
// readInt reads a single line from stdin, trimming if from spaces, enforcing it
|
||||
// to parse into an integer.
|
||||
func (w *wizard) readInt() int {
|
||||
|
|
|
|||
|
|
@ -137,14 +137,14 @@ func (w *wizard) deployDashboard() {
|
|||
if w.conf.ethstats != "" {
|
||||
fmt.Println()
|
||||
fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)")
|
||||
infos.trusted = w.readDefaultString("y") == "y"
|
||||
infos.trusted = w.readDefaultYesNo(true)
|
||||
}
|
||||
// Try to deploy the dashboard container on the host
|
||||
nocache := false
|
||||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the dashboard be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployDashboard(client, w.network, &w.conf, infos, nocache); err != nil {
|
||||
log.Error("Failed to deploy dashboard container", "err", err)
|
||||
|
|
|
|||
|
|
@ -67,11 +67,11 @@ func (w *wizard) deployEthstats() {
|
|||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Keep existing IP %v blacklist (y/n)? (default = yes)\n", infos.banned)
|
||||
if w.readDefaultString("y") != "y" {
|
||||
if !w.readDefaultYesNo(true) {
|
||||
// The user might want to clear the entire list, although generally probably not
|
||||
fmt.Println()
|
||||
fmt.Printf("Clear out blacklist and start over (y/n)? (default = no)\n")
|
||||
if w.readDefaultString("n") != "n" {
|
||||
if w.readDefaultYesNo(false) {
|
||||
infos.banned = nil
|
||||
}
|
||||
// Offer the user to explicitly add/remove certain IP addresses
|
||||
|
|
@ -106,7 +106,7 @@ func (w *wizard) deployEthstats() {
|
|||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the ethstats be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
trusted := make([]string, 0, len(w.servers))
|
||||
for _, client := range w.servers {
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ func (w *wizard) deployExplorer() {
|
|||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the explorer be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployExplorer(client, w.network, chain, infos, nocache); err != nil {
|
||||
log.Error("Failed to deploy explorer container", "err", err)
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ func (w *wizard) deployFaucet() {
|
|||
if infos.captchaToken != "" {
|
||||
fmt.Println()
|
||||
fmt.Println("Reuse previous reCaptcha API authorization (y/n)? (default = yes)")
|
||||
if w.readDefaultString("y") != "y" {
|
||||
if !w.readDefaultYesNo(true) {
|
||||
infos.captchaToken, infos.captchaSecret = "", ""
|
||||
}
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ func (w *wizard) deployFaucet() {
|
|||
// No previous authorization (or old one discarded)
|
||||
fmt.Println()
|
||||
fmt.Println("Enable reCaptcha protection against robots (y/n)? (default = no)")
|
||||
if w.readDefaultString("n") == "n" {
|
||||
if !w.readDefaultYesNo(false) {
|
||||
log.Warn("Users will be able to requests funds via automated scripts")
|
||||
} else {
|
||||
// Captcha protection explicitly requested, read the site and secret keys
|
||||
|
|
@ -132,7 +132,7 @@ func (w *wizard) deployFaucet() {
|
|||
} else {
|
||||
fmt.Println()
|
||||
fmt.Printf("Reuse previous (%s) funding account (y/n)? (default = yes)\n", key.Address.Hex())
|
||||
if w.readDefaultString("y") != "y" {
|
||||
if !w.readDefaultYesNo(true) {
|
||||
infos.node.keyJSON, infos.node.keyPass = "", ""
|
||||
}
|
||||
}
|
||||
|
|
@ -166,7 +166,7 @@ func (w *wizard) deployFaucet() {
|
|||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployFaucet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
||||
log.Error("Failed to deploy faucet container", "err", err)
|
||||
|
|
|
|||
|
|
@ -20,9 +20,13 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -40,11 +44,12 @@ func (w *wizard) makeGenesis() {
|
|||
Difficulty: big.NewInt(524288),
|
||||
Alloc: make(core.GenesisAlloc),
|
||||
Config: ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(1),
|
||||
EIP150Block: big.NewInt(2),
|
||||
EIP155Block: big.NewInt(3),
|
||||
EIP158Block: big.NewInt(3),
|
||||
ByzantiumBlock: big.NewInt(4),
|
||||
HomesteadBlock: big.NewInt(1),
|
||||
EIP150Block: big.NewInt(2),
|
||||
EIP155Block: big.NewInt(3),
|
||||
EIP158Block: big.NewInt(3),
|
||||
ByzantiumBlock: big.NewInt(4),
|
||||
ConstantinopleBlock: big.NewInt(5),
|
||||
},
|
||||
}
|
||||
// Figure out which consensus engine to choose
|
||||
|
|
@ -114,9 +119,13 @@ func (w *wizard) makeGenesis() {
|
|||
}
|
||||
break
|
||||
}
|
||||
// Add a batch of precompile balances to avoid them getting deleted
|
||||
for i := int64(0); i < 256; i++ {
|
||||
genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)}
|
||||
fmt.Println()
|
||||
fmt.Println("Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes)")
|
||||
if w.readDefaultYesNo(true) {
|
||||
// Add a batch of precompile balances to avoid them getting deleted
|
||||
for i := int64(0); i < 256; i++ {
|
||||
genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)}
|
||||
}
|
||||
}
|
||||
// Query the user for some custom extras
|
||||
fmt.Println()
|
||||
|
|
@ -130,53 +139,130 @@ func (w *wizard) makeGenesis() {
|
|||
w.conf.flush()
|
||||
}
|
||||
|
||||
// importGenesis imports a Geth genesis spec into puppeth.
|
||||
func (w *wizard) importGenesis() {
|
||||
// Request the genesis JSON spec URL from the user
|
||||
fmt.Println()
|
||||
fmt.Println("Where's the genesis file? (local file or http/https url)")
|
||||
url := w.readURL()
|
||||
|
||||
// Convert the various allowed URLs to a reader stream
|
||||
var reader io.Reader
|
||||
|
||||
switch url.Scheme {
|
||||
case "http", "https":
|
||||
// Remote web URL, retrieve it via an HTTP client
|
||||
res, err := http.Get(url.String())
|
||||
if err != nil {
|
||||
log.Error("Failed to retrieve remote genesis", "err", err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
reader = res.Body
|
||||
|
||||
case "":
|
||||
// Schemaless URL, interpret as a local file
|
||||
file, err := os.Open(url.String())
|
||||
if err != nil {
|
||||
log.Error("Failed to open local genesis", "err", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
reader = file
|
||||
|
||||
default:
|
||||
log.Error("Unsupported genesis URL scheme", "scheme", url.Scheme)
|
||||
return
|
||||
}
|
||||
// Parse the genesis file and inject it successful
|
||||
var genesis core.Genesis
|
||||
if err := json.NewDecoder(reader).Decode(&genesis); err != nil {
|
||||
log.Error("Invalid genesis spec: %v", err)
|
||||
return
|
||||
}
|
||||
log.Info("Imported genesis block")
|
||||
|
||||
w.conf.Genesis = &genesis
|
||||
w.conf.flush()
|
||||
}
|
||||
|
||||
// manageGenesis permits the modification of chain configuration parameters in
|
||||
// a genesis config and the export of the entire genesis spec.
|
||||
func (w *wizard) manageGenesis() {
|
||||
// Figure out whether to modify or export the genesis
|
||||
fmt.Println()
|
||||
fmt.Println(" 1. Modify existing fork rules")
|
||||
fmt.Println(" 2. Export genesis configuration")
|
||||
fmt.Println(" 2. Export genesis configurations")
|
||||
fmt.Println(" 3. Remove genesis configuration")
|
||||
|
||||
choice := w.read()
|
||||
switch {
|
||||
case choice == "1":
|
||||
switch choice {
|
||||
case "1":
|
||||
// Fork rule updating requested, iterate over each fork
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should Homestead come into effect? (default = %v)\n", w.conf.Genesis.Config.HomesteadBlock)
|
||||
w.conf.Genesis.Config.HomesteadBlock = w.readDefaultBigInt(w.conf.Genesis.Config.HomesteadBlock)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should EIP150 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block)
|
||||
fmt.Printf("Which block should EIP150 (Tangerine Whistle) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block)
|
||||
w.conf.Genesis.Config.EIP150Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP150Block)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should EIP155 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block)
|
||||
fmt.Printf("Which block should EIP155 (Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block)
|
||||
w.conf.Genesis.Config.EIP155Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP155Block)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should EIP158 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block)
|
||||
fmt.Printf("Which block should EIP158/161 (also Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block)
|
||||
w.conf.Genesis.Config.EIP158Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP158Block)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should Byzantium come into effect? (default = %v)\n", w.conf.Genesis.Config.ByzantiumBlock)
|
||||
w.conf.Genesis.Config.ByzantiumBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ByzantiumBlock)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should Constantinople come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock)
|
||||
w.conf.Genesis.Config.ConstantinopleBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock)
|
||||
|
||||
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
|
||||
fmt.Printf("Chain configuration updated:\n\n%s\n", out)
|
||||
|
||||
case choice == "2":
|
||||
case "2":
|
||||
// Save whatever genesis configuration we currently have
|
||||
fmt.Println()
|
||||
fmt.Printf("Which file to save the genesis into? (default = %s.json)\n", w.network)
|
||||
out, _ := json.MarshalIndent(w.conf.Genesis, "", " ")
|
||||
if err := ioutil.WriteFile(w.readDefaultString(fmt.Sprintf("%s.json", w.network)), out, 0644); err != nil {
|
||||
log.Error("Failed to save genesis file", "err", err)
|
||||
}
|
||||
log.Info("Exported existing genesis block")
|
||||
fmt.Printf("Which folder to save the genesis specs into? (default = current)\n")
|
||||
fmt.Printf(" Will create %s.json, %s-aleth.json, %s-harmony.json, %s-parity.json\n", w.network, w.network, w.network, w.network)
|
||||
|
||||
case choice == "3":
|
||||
folder := w.readDefaultString(".")
|
||||
if err := os.MkdirAll(folder, 0755); err != nil {
|
||||
log.Error("Failed to create spec folder", "folder", folder, "err", err)
|
||||
return
|
||||
}
|
||||
out, _ := json.MarshalIndent(w.conf.Genesis, "", " ")
|
||||
|
||||
// Export the native genesis spec used by puppeth and Geth
|
||||
gethJson := filepath.Join(folder, fmt.Sprintf("%s.json", w.network))
|
||||
if err := ioutil.WriteFile((gethJson), out, 0644); err != nil {
|
||||
log.Error("Failed to save genesis file", "err", err)
|
||||
return
|
||||
}
|
||||
log.Info("Saved native genesis chain spec", "path", gethJson)
|
||||
|
||||
// Export the genesis spec used by Aleth (formerly C++ Ethereum)
|
||||
if spec, err := newAlethGenesisSpec(w.network, w.conf.Genesis); err != nil {
|
||||
log.Error("Failed to create Aleth chain spec", "err", err)
|
||||
} else {
|
||||
saveGenesis(folder, w.network, "aleth", spec)
|
||||
}
|
||||
// Export the genesis spec used by Parity
|
||||
if spec, err := newParityChainSpec(w.network, w.conf.Genesis, []string{}); err != nil {
|
||||
log.Error("Failed to create Parity chain spec", "err", err)
|
||||
} else {
|
||||
saveGenesis(folder, w.network, "parity", spec)
|
||||
}
|
||||
// Export the genesis spec used by Harmony (formerly EthereumJ
|
||||
saveGenesis(folder, w.network, "harmony", w.conf.Genesis)
|
||||
|
||||
case "3":
|
||||
// Make sure we don't have any services running
|
||||
if len(w.conf.servers()) > 0 {
|
||||
log.Error("Genesis reset requires all services and servers torn down")
|
||||
|
|
@ -186,8 +272,20 @@ func (w *wizard) manageGenesis() {
|
|||
|
||||
w.conf.Genesis = nil
|
||||
w.conf.flush()
|
||||
|
||||
default:
|
||||
log.Error("That's not something I can do")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// saveGenesis JSON encodes an arbitrary genesis spec into a pre-defined file.
|
||||
func saveGenesis(folder, network, client string, spec interface{}) {
|
||||
path := filepath.Join(folder, fmt.Sprintf("%s-%s.json", network, client))
|
||||
|
||||
out, _ := json.Marshal(spec)
|
||||
if err := ioutil.WriteFile(path, out, 0644); err != nil {
|
||||
log.Error("Failed to save genesis file", "client", client, "err", err)
|
||||
return
|
||||
}
|
||||
log.Info("Saved genesis chain spec", "client", client, "path", path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,14 +61,14 @@ func (w *wizard) run() {
|
|||
// Make sure we have a good network name to work with fmt.Println()
|
||||
// Docker accepts hyphens in image names, but doesn't like it for container names
|
||||
if w.network == "" {
|
||||
fmt.Println("Please specify a network name to administer (no spaces or hyphens, please)")
|
||||
fmt.Println("Please specify a network name to administer (no spaces, hyphens or capital letters please)")
|
||||
for {
|
||||
w.network = w.readString()
|
||||
if !strings.Contains(w.network, " ") && !strings.Contains(w.network, "-") {
|
||||
if !strings.Contains(w.network, " ") && !strings.Contains(w.network, "-") && strings.ToLower(w.network) == w.network {
|
||||
fmt.Printf("\nSweet, you can set this via --network=%s next time!\n\n", w.network)
|
||||
break
|
||||
}
|
||||
log.Error("I also like to live dangerously, still no spaces or hyphens")
|
||||
log.Error("I also like to live dangerously, still no spaces, hyphens or capital letters")
|
||||
}
|
||||
}
|
||||
log.Info("Administering Ethereum network", "name", w.network)
|
||||
|
|
@ -131,7 +131,20 @@ func (w *wizard) run() {
|
|||
|
||||
case choice == "2":
|
||||
if w.conf.Genesis == nil {
|
||||
w.makeGenesis()
|
||||
fmt.Println()
|
||||
fmt.Println("What would you like to do? (default = create)")
|
||||
fmt.Println(" 1. Create new genesis from scratch")
|
||||
fmt.Println(" 2. Import already existing genesis")
|
||||
|
||||
choice := w.read()
|
||||
switch {
|
||||
case choice == "" || choice == "1":
|
||||
w.makeGenesis()
|
||||
case choice == "2":
|
||||
w.importGenesis()
|
||||
default:
|
||||
log.Error("That's not something I can do")
|
||||
}
|
||||
} else {
|
||||
w.manageGenesis()
|
||||
}
|
||||
|
|
@ -149,7 +162,6 @@ func (w *wizard) run() {
|
|||
} else {
|
||||
w.manageComponents()
|
||||
}
|
||||
|
||||
default:
|
||||
log.Error("That's not something I can do")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,12 +41,12 @@ func (w *wizard) ensureVirtualHost(client *sshClient, port int, def string) (str
|
|||
// Reverse proxy is not running, offer to deploy a new one
|
||||
fmt.Println()
|
||||
fmt.Println("Allow sharing the port with other services (y/n)? (default = yes)")
|
||||
if w.readDefaultString("y") == "y" {
|
||||
if w.readDefaultYesNo(true) {
|
||||
nocache := false
|
||||
if proxy != nil {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the reverse-proxy be rebuilt from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployNginx(client, w.network, port, nocache); err != nil {
|
||||
log.Error("Failed to deploy reverse-proxy", "err", err)
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ func (w *wizard) deployNode(boot bool) {
|
|||
} else {
|
||||
fmt.Println()
|
||||
fmt.Printf("Reuse previous (%s) signing account (y/n)? (default = yes)\n", key.Address.Hex())
|
||||
if w.readDefaultString("y") != "y" {
|
||||
if !w.readDefaultYesNo(true) {
|
||||
infos.keyJSON, infos.keyPass = "", ""
|
||||
}
|
||||
}
|
||||
|
|
@ -165,7 +165,7 @@ func (w *wizard) deployNode(boot bool) {
|
|||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
||||
log.Error("Failed to deploy Ethereum node container", "err", err)
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ func (w *wizard) deployWallet() {
|
|||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the wallet be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployWallet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
||||
log.Error("Failed to deploy wallet container", "err", err)
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
|
@ -16,8 +18,13 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/swarm/api/client"
|
||||
"github.com/ethereum/go-ethereum/swarm/spancontext"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/pborman/uuid"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
|
@ -26,14 +33,32 @@ const (
|
|||
feedRandomDataLength = 8
|
||||
)
|
||||
|
||||
// TODO: retrieve with manifest + extract repeating code
|
||||
func cliFeedUploadAndSync(c *cli.Context) error {
|
||||
|
||||
metrics.GetOrRegisterCounter("feed-and-sync", nil).Inc(1)
|
||||
log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))))
|
||||
|
||||
errc := make(chan error)
|
||||
go func() {
|
||||
errc <- feedUploadAndSync(c)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errc:
|
||||
if err != nil {
|
||||
metrics.GetOrRegisterCounter("feed-and-sync.fail", nil).Inc(1)
|
||||
}
|
||||
return err
|
||||
case <-time.After(time.Duration(timeout) * time.Second):
|
||||
metrics.GetOrRegisterCounter("feed-and-sync.timeout", nil).Inc(1)
|
||||
return fmt.Errorf("timeout after %v sec", timeout)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: retrieve with manifest + extract repeating code
|
||||
func feedUploadAndSync(c *cli.Context) error {
|
||||
defer func(now time.Time) { log.Info("total time", "time", time.Since(now), "size (kb)", filesize) }(time.Now())
|
||||
|
||||
generateEndpoints(scheme, cluster, from, to)
|
||||
generateEndpoints(scheme, cluster, appName, from, to)
|
||||
|
||||
log.Info("generating and uploading feeds to " + endpoints[0] + " and syncing")
|
||||
|
||||
|
|
@ -204,12 +229,12 @@ func cliFeedUploadAndSync(c *cli.Context) error {
|
|||
log.Info("all endpoints synced random data successfully")
|
||||
|
||||
// upload test file
|
||||
log.Info("uploading to " + endpoints[0] + " and syncing")
|
||||
seed := int(time.Now().UnixNano() / 1e6)
|
||||
log.Info("feed uploading to "+endpoints[0]+" and syncing", "seed", seed)
|
||||
|
||||
f, cleanup := generateRandomFile(filesize * 1000)
|
||||
defer cleanup()
|
||||
randomBytes := testutil.RandomBytes(seed, filesize*1000)
|
||||
|
||||
hash, err := upload(f, endpoints[0])
|
||||
hash, err := upload(&randomBytes, endpoints[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -218,7 +243,7 @@ func cliFeedUploadAndSync(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
multihashHex := hexutil.Encode(hashBytes)
|
||||
fileHash, err := digest(f)
|
||||
fileHash, err := digest(bytes.NewReader(randomBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -284,14 +309,37 @@ func cliFeedUploadAndSync(c *cli.Context) error {
|
|||
}
|
||||
|
||||
func fetchFeed(topic string, user string, endpoint string, original []byte, ruid string) error {
|
||||
ctx, sp := spancontext.StartSpan(context.Background(), "feed-and-sync.fetch")
|
||||
defer sp.Finish()
|
||||
|
||||
log.Trace("sleeping", "ruid", ruid)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
log.Trace("http get request (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user)
|
||||
res, err := http.Get(endpoint + "/bzz-feed:/?topic=" + topic + "&user=" + user)
|
||||
|
||||
var tn time.Time
|
||||
reqUri := endpoint + "/bzz-feed:/?topic=" + topic + "&user=" + user
|
||||
req, _ := http.NewRequest("GET", reqUri, nil)
|
||||
|
||||
opentracing.GlobalTracer().Inject(
|
||||
sp.Context(),
|
||||
opentracing.HTTPHeaders,
|
||||
opentracing.HTTPHeadersCarrier(req.Header))
|
||||
|
||||
trace := client.GetClientTrace("feed-and-sync - http get", "feed-and-sync", ruid, &tn)
|
||||
|
||||
req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
|
||||
transport := http.DefaultTransport
|
||||
|
||||
//transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
tn = time.Now()
|
||||
res, err := transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
log.Error(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("http get response (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user, "code", res.StatusCode, "len", res.ContentLength)
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
|
|
|
|||
|
|
@ -17,23 +17,38 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
gethmetrics "github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/metrics/influxdb"
|
||||
swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
|
||||
"github.com/ethereum/go-ethereum/swarm/tracing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
|
||||
)
|
||||
|
||||
var (
|
||||
endpoints []string
|
||||
includeLocalhost bool
|
||||
cluster string
|
||||
appName string
|
||||
scheme string
|
||||
filesize int
|
||||
syncDelay int
|
||||
from int
|
||||
to int
|
||||
verbosity int
|
||||
timeout int
|
||||
single bool
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -49,6 +64,12 @@ func main() {
|
|||
Usage: "cluster to point to (prod or a given namespace)",
|
||||
Destination: &cluster,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "app",
|
||||
Value: "swarm",
|
||||
Usage: "application to point to (swarm or swarm-private)",
|
||||
Destination: &appName,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "cluster-from",
|
||||
Value: 8501,
|
||||
|
|
@ -78,14 +99,42 @@ func main() {
|
|||
Usage: "file size for generated random file in KB",
|
||||
Destination: &filesize,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sync-delay",
|
||||
Value: 5,
|
||||
Usage: "duration of delay in seconds to wait for content to be synced",
|
||||
Destination: &syncDelay,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "verbosity",
|
||||
Value: 1,
|
||||
Usage: "verbosity",
|
||||
Destination: &verbosity,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "timeout",
|
||||
Value: 120,
|
||||
Usage: "timeout in seconds after which kill the process",
|
||||
Destination: &timeout,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "single",
|
||||
Usage: "whether to fetch content from a single node or from all nodes",
|
||||
Destination: &single,
|
||||
},
|
||||
}
|
||||
|
||||
app.Flags = append(app.Flags, []cli.Flag{
|
||||
utils.MetricsEnabledFlag,
|
||||
swarmmetrics.MetricsInfluxDBEndpointFlag,
|
||||
swarmmetrics.MetricsInfluxDBDatabaseFlag,
|
||||
swarmmetrics.MetricsInfluxDBUsernameFlag,
|
||||
swarmmetrics.MetricsInfluxDBPasswordFlag,
|
||||
swarmmetrics.MetricsInfluxDBHostTagFlag,
|
||||
}...)
|
||||
|
||||
app.Flags = append(app.Flags, tracing.Flags...)
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "upload_and_sync",
|
||||
|
|
@ -104,8 +153,38 @@ func main() {
|
|||
sort.Sort(cli.FlagsByName(app.Flags))
|
||||
sort.Sort(cli.CommandsByName(app.Commands))
|
||||
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
tracing.Setup(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
app.After = func(ctx *cli.Context) error {
|
||||
return emitMetrics(ctx)
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func emitMetrics(ctx *cli.Context) error {
|
||||
if gethmetrics.Enabled {
|
||||
var (
|
||||
endpoint = ctx.GlobalString(swarmmetrics.MetricsInfluxDBEndpointFlag.Name)
|
||||
database = ctx.GlobalString(swarmmetrics.MetricsInfluxDBDatabaseFlag.Name)
|
||||
username = ctx.GlobalString(swarmmetrics.MetricsInfluxDBUsernameFlag.Name)
|
||||
password = ctx.GlobalString(swarmmetrics.MetricsInfluxDBPasswordFlag.Name)
|
||||
hosttag = ctx.GlobalString(swarmmetrics.MetricsInfluxDBHostTagFlag.Name)
|
||||
)
|
||||
return influxdb.InfluxDBWithTagsOnce(gethmetrics.DefaultRegistry, endpoint, database, username, password, "swarm-smoke.", map[string]string{
|
||||
"host": hosttag,
|
||||
"version": gitCommit,
|
||||
"filesize": fmt.Sprintf("%v", filesize),
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,35 +18,40 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
crand "crypto/rand"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/swarm/api"
|
||||
"github.com/ethereum/go-ethereum/swarm/api/client"
|
||||
"github.com/ethereum/go-ethereum/swarm/spancontext"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
func generateEndpoints(scheme string, cluster string, from int, to int) {
|
||||
func generateEndpoints(scheme string, cluster string, app string, from int, to int) {
|
||||
if cluster == "prod" {
|
||||
for port := from; port <= to; port++ {
|
||||
for port := from; port < to; port++ {
|
||||
endpoints = append(endpoints, fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, port))
|
||||
}
|
||||
} else {
|
||||
for port := from; port <= to; port++ {
|
||||
endpoints = append(endpoints, fmt.Sprintf("%s://swarm-%v-%s.stg.swarm-gateways.net", scheme, port, cluster))
|
||||
for port := from; port < to; port++ {
|
||||
endpoints = append(endpoints, fmt.Sprintf("%s://%s-%v-%s.stg.swarm-gateways.net", scheme, app, port, cluster))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,24 +62,50 @@ func generateEndpoints(scheme string, cluster string, from int, to int) {
|
|||
|
||||
func cliUploadAndSync(c *cli.Context) error {
|
||||
log.PrintOrigins(true)
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
|
||||
|
||||
defer func(now time.Time) { log.Info("total time", "time", time.Since(now), "size (kb)", filesize) }(time.Now())
|
||||
metrics.GetOrRegisterCounter("upload-and-sync", nil).Inc(1)
|
||||
|
||||
generateEndpoints(scheme, cluster, from, to)
|
||||
errc := make(chan error)
|
||||
go func() {
|
||||
errc <- uploadAndSync(c)
|
||||
}()
|
||||
|
||||
log.Info("uploading to " + endpoints[0] + " and syncing")
|
||||
select {
|
||||
case err := <-errc:
|
||||
if err != nil {
|
||||
metrics.GetOrRegisterCounter("upload-and-sync.fail", nil).Inc(1)
|
||||
}
|
||||
return err
|
||||
case <-time.After(time.Duration(timeout) * time.Second):
|
||||
metrics.GetOrRegisterCounter("upload-and-sync.timeout", nil).Inc(1)
|
||||
return fmt.Errorf("timeout after %v sec", timeout)
|
||||
}
|
||||
}
|
||||
|
||||
f, cleanup := generateRandomFile(filesize * 1000)
|
||||
defer cleanup()
|
||||
func uploadAndSync(c *cli.Context) error {
|
||||
defer func(now time.Time) {
|
||||
totalTime := time.Since(now)
|
||||
|
||||
hash, err := upload(f, endpoints[0])
|
||||
log.Info("total time", "time", totalTime, "kb", filesize)
|
||||
metrics.GetOrRegisterCounter("upload-and-sync.total-time", nil).Inc(int64(totalTime))
|
||||
}(time.Now())
|
||||
|
||||
generateEndpoints(scheme, cluster, appName, from, to)
|
||||
seed := int(time.Now().UnixNano() / 1e6)
|
||||
log.Info("uploading to "+endpoints[0]+" and syncing", "seed", seed)
|
||||
|
||||
randomBytes := testutil.RandomBytes(seed, filesize*1000)
|
||||
|
||||
t1 := time.Now()
|
||||
hash, err := upload(&randomBytes, endpoints[0])
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
metrics.GetOrRegisterCounter("upload-and-sync.upload-time", nil).Inc(int64(time.Since(t1)))
|
||||
|
||||
fhash, err := digest(f)
|
||||
fhash, err := digest(bytes.NewReader(randomBytes))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return err
|
||||
|
|
@ -82,23 +113,47 @@ func cliUploadAndSync(c *cli.Context) error {
|
|||
|
||||
log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash))
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
time.Sleep(time.Duration(syncDelay) * time.Second)
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
for _, endpoint := range endpoints {
|
||||
if single {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
randIndex := 1 + rand.Intn(len(endpoints)-1)
|
||||
ruid := uuid.New()[:8]
|
||||
wg.Add(1)
|
||||
go func(endpoint string, ruid string) {
|
||||
for {
|
||||
start := time.Now()
|
||||
err := fetch(hash, endpoint, fhash, ruid)
|
||||
fetchTime := time.Since(start)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
metrics.GetOrRegisterMeter("upload-and-sync.single.fetch-time", nil).Mark(int64(fetchTime))
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}(endpoint, ruid)
|
||||
}(endpoints[randIndex], ruid)
|
||||
} else {
|
||||
for _, endpoint := range endpoints {
|
||||
ruid := uuid.New()[:8]
|
||||
wg.Add(1)
|
||||
go func(endpoint string, ruid string) {
|
||||
for {
|
||||
start := time.Now()
|
||||
err := fetch(hash, endpoint, fhash, ruid)
|
||||
fetchTime := time.Since(start)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
metrics.GetOrRegisterMeter("upload-and-sync.each.fetch-time", nil).Mark(int64(fetchTime))
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}(endpoint, ruid)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
log.Info("all endpoints synced random file successfully")
|
||||
|
|
@ -108,16 +163,33 @@ func cliUploadAndSync(c *cli.Context) error {
|
|||
|
||||
// fetch is getting the requested `hash` from the `endpoint` and compares it with the `original` file
|
||||
func fetch(hash string, endpoint string, original []byte, ruid string) error {
|
||||
ctx, sp := spancontext.StartSpan(context.Background(), "upload-and-sync.fetch")
|
||||
defer sp.Finish()
|
||||
|
||||
log.Trace("sleeping", "ruid", ruid)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash)
|
||||
client := &http.Client{Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}}
|
||||
res, err := client.Get(endpoint + "/bzz:/" + hash + "/")
|
||||
|
||||
var tn time.Time
|
||||
reqUri := endpoint + "/bzz:/" + hash + "/"
|
||||
req, _ := http.NewRequest("GET", reqUri, nil)
|
||||
|
||||
opentracing.GlobalTracer().Inject(
|
||||
sp.Context(),
|
||||
opentracing.HTTPHeaders,
|
||||
opentracing.HTTPHeadersCarrier(req.Header))
|
||||
|
||||
trace := client.GetClientTrace("upload-and-sync - http get", "upload-and-sync", ruid, &tn)
|
||||
|
||||
req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
|
||||
transport := http.DefaultTransport
|
||||
|
||||
//transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
tn = time.Now()
|
||||
res, err := transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
log.Error(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
log.Trace("http get response", "ruid", ruid, "api", endpoint, "hash", hash, "code", res.StatusCode, "len", res.ContentLength)
|
||||
|
|
@ -148,16 +220,19 @@ func fetch(hash string, endpoint string, original []byte, ruid string) error {
|
|||
}
|
||||
|
||||
// upload is uploading a file `f` to `endpoint` via the `swarm up` cmd
|
||||
func upload(f *os.File, endpoint string) (string, error) {
|
||||
var out bytes.Buffer
|
||||
cmd := exec.Command("swarm", "--bzzapi", endpoint, "up", f.Name())
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
func upload(dataBytes *[]byte, endpoint string) (string, error) {
|
||||
swarm := client.NewClient(endpoint)
|
||||
f := &client.File{
|
||||
ReadCloser: ioutil.NopCloser(bytes.NewReader(*dataBytes)),
|
||||
ManifestEntry: api.ManifestEntry{
|
||||
ContentType: "text/plain",
|
||||
Mode: 0660,
|
||||
Size: int64(len(*dataBytes)),
|
||||
},
|
||||
}
|
||||
hash := strings.TrimRight(out.String(), "\r\n")
|
||||
return hash, nil
|
||||
|
||||
// upload data to bzz:// and retrieve the content-addressed manifest hash, hex-encoded.
|
||||
return swarm.Upload(f, "", false)
|
||||
}
|
||||
|
||||
func digest(r io.Reader) ([]byte, error) {
|
||||
|
|
@ -180,27 +255,3 @@ func generateRandomData(datasize int) ([]byte, error) {
|
|||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// generateRandomFile is creating a temporary file with the requested byte size
|
||||
func generateRandomFile(size int) (f *os.File, teardown func()) {
|
||||
// create a tmp file
|
||||
tmp, err := ioutil.TempFile("", "swarm-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// callback for tmp file cleanup
|
||||
teardown = func() {
|
||||
tmp.Close()
|
||||
os.Remove(tmp.Name())
|
||||
}
|
||||
|
||||
buf := make([]byte, size)
|
||||
_, err = crand.Read(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ioutil.WriteFile(tmp.Name(), buf, 0755)
|
||||
|
||||
return tmp, teardown
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,6 +140,10 @@ var (
|
|||
Name: "rinkeby",
|
||||
Usage: "Rinkeby network: pre-configured proof-of-authority test network",
|
||||
}
|
||||
ConstantinopleOverrideFlag = cli.Uint64Flag{
|
||||
Name: "override.constantinople",
|
||||
Usage: "Manually specify constantinople fork-block, overriding the bundled setting",
|
||||
}
|
||||
DeveloperFlag = cli.BoolFlag{
|
||||
Name: "dev",
|
||||
Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled",
|
||||
|
|
@ -182,6 +186,10 @@ var (
|
|||
Name: "lightkdf",
|
||||
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
|
||||
}
|
||||
WhitelistFlag = cli.StringFlag{
|
||||
Name: "whitelist",
|
||||
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
|
||||
}
|
||||
// Dashboard settings
|
||||
DashboardEnabledFlag = cli.BoolFlag{
|
||||
Name: metrics.DashboardEnabledFlag,
|
||||
|
|
@ -824,17 +832,12 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
|
|||
// makeDatabaseHandles raises out the number of allowed file handles per process
|
||||
// for Geth and returns half of the allowance to assign to the database.
|
||||
func makeDatabaseHandles() int {
|
||||
limit, err := fdlimit.Current()
|
||||
limit, err := fdlimit.Maximum()
|
||||
if err != nil {
|
||||
Fatalf("Failed to retrieve file descriptor allowance: %v", err)
|
||||
}
|
||||
if limit < 2048 {
|
||||
if err := fdlimit.Raise(2048); err != nil {
|
||||
Fatalf("Failed to raise file descriptor allowance: %v", err)
|
||||
}
|
||||
}
|
||||
if limit > 2048 { // cap database file descriptors even if more is available
|
||||
limit = 2048
|
||||
if err := fdlimit.Raise(uint64(limit)); err != nil {
|
||||
Fatalf("Failed to raise file descriptor allowance: %v", err)
|
||||
}
|
||||
return limit / 2 // Leave half for networking and other stuff
|
||||
}
|
||||
|
|
@ -1077,6 +1080,29 @@ func setEthash(ctx *cli.Context, cfg *eth.Config) {
|
|||
}
|
||||
}
|
||||
|
||||
func setWhitelist(ctx *cli.Context, cfg *eth.Config) {
|
||||
whitelist := ctx.GlobalString(WhitelistFlag.Name)
|
||||
if whitelist == "" {
|
||||
return
|
||||
}
|
||||
cfg.Whitelist = make(map[uint64]common.Hash)
|
||||
for _, entry := range strings.Split(whitelist, ",") {
|
||||
parts := strings.Split(entry, "=")
|
||||
if len(parts) != 2 {
|
||||
Fatalf("Invalid whitelist entry: %s", entry)
|
||||
}
|
||||
number, err := strconv.ParseUint(parts[0], 0, 64)
|
||||
if err != nil {
|
||||
Fatalf("Invalid whitelist block number %s: %v", parts[0], err)
|
||||
}
|
||||
var hash common.Hash
|
||||
if err = hash.UnmarshalText([]byte(parts[1])); err != nil {
|
||||
Fatalf("Invalid whitelist hash %s: %v", parts[1], err)
|
||||
}
|
||||
cfg.Whitelist[number] = hash
|
||||
}
|
||||
}
|
||||
|
||||
// checkExclusive verifies that only a single instance of the provided flags was
|
||||
// set by the user. Each flag might optionally be followed by a string type to
|
||||
// specialize it further.
|
||||
|
|
@ -1142,6 +1168,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
|||
setGPO(ctx, &cfg.GPO)
|
||||
setTxPool(ctx, &cfg.TxPool)
|
||||
setEthash(ctx, cfg)
|
||||
setWhitelist(ctx, cfg)
|
||||
|
||||
if ctx.GlobalIsSet(SyncModeFlag.Name) {
|
||||
cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode)
|
||||
|
|
@ -1155,7 +1182,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
|||
if ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
|
||||
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
|
||||
}
|
||||
|
|
@ -1380,7 +1406,6 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
|||
func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) {
|
||||
var err error
|
||||
chainDb = MakeChainDatabase(ctx, stack)
|
||||
|
||||
config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx))
|
||||
if err != nil {
|
||||
Fatalf("%v", err)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,10 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
|
||||
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
|
||||
blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil)
|
||||
blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil)
|
||||
blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil)
|
||||
|
||||
ErrNoGenesis = errors.New("Genesis not found in chain")
|
||||
)
|
||||
|
|
@ -207,6 +210,11 @@ func (bc *BlockChain) getProcInterrupt() bool {
|
|||
return atomic.LoadInt32(&bc.procInterrupt) == 1
|
||||
}
|
||||
|
||||
// GetVMConfig returns the block chain VM config.
|
||||
func (bc *BlockChain) GetVMConfig() *vm.Config {
|
||||
return &bc.vmConfig
|
||||
}
|
||||
|
||||
// loadLastState loads the last known chain state from the database. This method
|
||||
// assumes that the chain manager mutex is held.
|
||||
func (bc *BlockChain) loadLastState() error {
|
||||
|
|
@ -1188,7 +1196,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
|||
return it.index, events, coalescedLogs, err
|
||||
}
|
||||
// Process block using the parent state as reference point.
|
||||
t0 := time.Now()
|
||||
receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig)
|
||||
t1 := time.Now()
|
||||
if err != nil {
|
||||
bc.reportBlock(block, receipts, err)
|
||||
return it.index, events, coalescedLogs, err
|
||||
|
|
@ -1198,13 +1208,19 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
|||
bc.reportBlock(block, receipts, err)
|
||||
return it.index, events, coalescedLogs, err
|
||||
}
|
||||
t2 := time.Now()
|
||||
proctime := time.Since(start)
|
||||
|
||||
// Write the block to the chain and get the status.
|
||||
status, err := bc.WriteBlockWithState(block, receipts, state)
|
||||
t3 := time.Now()
|
||||
if err != nil {
|
||||
return it.index, events, coalescedLogs, err
|
||||
}
|
||||
blockInsertTimer.UpdateSince(start)
|
||||
blockExecutionTimer.Update(t1.Sub(t0))
|
||||
blockValidationTimer.Update(t2.Sub(t1))
|
||||
blockWriteTimer.Update(t3.Sub(t2))
|
||||
switch status {
|
||||
case CanonStatTy:
|
||||
log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(),
|
||||
|
|
|
|||
|
|
@ -151,6 +151,9 @@ func (e *GenesisMismatchError) Error() string {
|
|||
//
|
||||
// The returned chain configuration is never nil.
|
||||
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
|
||||
return SetupGenesisBlockWithOverride(db, genesis, nil)
|
||||
}
|
||||
func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, constantinopleOverride *big.Int) (*params.ChainConfig, common.Hash, error) {
|
||||
if genesis != nil && genesis.Config == nil {
|
||||
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
|
||||
}
|
||||
|
|
@ -178,6 +181,9 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
|
|||
|
||||
// Get the existing chain configuration.
|
||||
newcfg := genesis.configOrDefault(stored)
|
||||
if constantinopleOverride != nil {
|
||||
newcfg.ConstantinopleBlock = constantinopleOverride
|
||||
}
|
||||
storedcfg := rawdb.ReadChainConfig(db, stored)
|
||||
if storedcfg == nil {
|
||||
log.Warn("Found genesis block without chain config")
|
||||
|
|
|
|||
|
|
@ -468,9 +468,9 @@ func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObjec
|
|||
//
|
||||
// Carrying over the balance ensures that Ether doesn't disappear.
|
||||
func (self *StateDB) CreateAccount(addr common.Address) {
|
||||
new, prev := self.createObject(addr)
|
||||
newObj, prev := self.createObject(addr)
|
||||
if prev != nil {
|
||||
new.setBalance(prev.data.Balance)
|
||||
newObj.setBalance(prev.data.Balance)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// senderCacher is a concurrent transaction sender recoverer anc cacher.
|
||||
// senderCacher is a concurrent transaction sender recoverer and cacher.
|
||||
var senderCacher = newTxSenderCacher(runtime.NumCPU())
|
||||
|
||||
// txSenderCacherRequest is a request for recovering transaction senders with a
|
||||
|
|
|
|||
|
|
@ -339,6 +339,12 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|||
contract := NewContract(caller, to, new(big.Int), gas)
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
|
||||
// We do an AddBalance of zero here, just in order to trigger a touch.
|
||||
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
||||
// but is the correct thing to do and matters on other networks, in tests, and potential
|
||||
// future scenarios
|
||||
evm.StateDB.AddBalance(addr, bigZero)
|
||||
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||
// when we're in Homestead this also counts for code storage gas errors.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
package vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
@ -24,17 +24,16 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
type JSONLogger struct {
|
||||
encoder *json.Encoder
|
||||
cfg *vm.LogConfig
|
||||
cfg *LogConfig
|
||||
}
|
||||
|
||||
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
|
||||
// into the provided stream.
|
||||
func NewJSONLogger(cfg *vm.LogConfig, writer io.Writer) *JSONLogger {
|
||||
func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
|
||||
return &JSONLogger{json.NewEncoder(writer), cfg}
|
||||
}
|
||||
|
||||
|
|
@ -43,8 +42,8 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create
|
|||
}
|
||||
|
||||
// CaptureState outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
log := vm.StructLog{
|
||||
func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
log := StructLog{
|
||||
Pc: pc,
|
||||
Op: op,
|
||||
Gas: gas,
|
||||
|
|
@ -65,7 +64,7 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
|
|||
}
|
||||
|
||||
// CaptureFault outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +310,7 @@ var theCurve = new(BitCurve)
|
|||
func init() {
|
||||
// See SEC 2 section 2.7.1
|
||||
// curve parameters taken from:
|
||||
// http://www.secg.org/collateral/sec2_final.pdf
|
||||
// http://www.secg.org/sec2-v2.pdf
|
||||
theCurve.P, _ = new(big.Int).SetString("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 0)
|
||||
theCurve.N, _ = new(big.Int).SetString("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 0)
|
||||
theCurve.B, _ = new(big.Int).SetString("0x0000000000000000000000000000000000000000000000000000000000000007", 0)
|
||||
|
|
|
|||
|
|
@ -125,12 +125,12 @@ func (b *EthAPIBackend) GetTd(blockHash common.Hash) *big.Int {
|
|||
return b.eth.blockchain.GetTdByHash(blockHash)
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
|
||||
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
|
||||
state.SetBalance(msg.From(), math.MaxBig256)
|
||||
vmError := func() error { return nil }
|
||||
|
||||
context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil)
|
||||
return vm.NewEVM(context, state, b.eth.chainConfig, vmCfg), vmError, nil
|
||||
return vm.NewEVM(context, state, b.eth.chainConfig, *b.eth.blockchain.GetVMConfig()), vmError, nil
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -60,6 +62,13 @@ type TraceConfig struct {
|
|||
Reexec *uint64
|
||||
}
|
||||
|
||||
// StdTraceConfig holds extra parameters to standard-json trace functions.
|
||||
type StdTraceConfig struct {
|
||||
*vm.LogConfig
|
||||
Reexec *uint64
|
||||
TxHash common.Hash
|
||||
}
|
||||
|
||||
// txTraceResult is the result of a single transaction trace.
|
||||
type txTraceResult struct {
|
||||
Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer
|
||||
|
|
@ -366,7 +375,7 @@ func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.B
|
|||
func (api *PrivateDebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
|
||||
block := api.eth.blockchain.GetBlockByHash(hash)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("block #%x not found", hash)
|
||||
return nil, fmt.Errorf("block %#x not found", hash)
|
||||
}
|
||||
return api.traceBlock(ctx, block, config)
|
||||
}
|
||||
|
|
@ -391,13 +400,41 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string,
|
|||
return api.TraceBlock(ctx, blob, config)
|
||||
}
|
||||
|
||||
// TraceBadBlock returns the structured logs created during the execution of a block
|
||||
// within the blockchain 'badblocks' cache
|
||||
func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, index int, config *TraceConfig) ([]*txTraceResult, error) {
|
||||
if blocks := api.eth.blockchain.BadBlocks(); index < len(blocks) {
|
||||
return api.traceBlock(ctx, blocks[index], config)
|
||||
// TraceBadBlockByHash returns the structured logs created during the execution of
|
||||
// EVM against a block pulled from the pool of bad ones and returns them as a JSON
|
||||
// object.
|
||||
func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
|
||||
blocks := api.eth.blockchain.BadBlocks()
|
||||
for _, block := range blocks {
|
||||
if block.Hash() == hash {
|
||||
return api.traceBlock(ctx, block, config)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("index out of range")
|
||||
return nil, fmt.Errorf("bad block %#x not found", hash)
|
||||
}
|
||||
|
||||
// StandardTraceBlockToFile dumps the structured logs created during the
|
||||
// execution of EVM to the local file system and returns a list of files
|
||||
// to the caller.
|
||||
func (api *PrivateDebugAPI) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
|
||||
block := api.eth.blockchain.GetBlockByHash(hash)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("block %#x not found", hash)
|
||||
}
|
||||
return api.standardTraceBlockToFile(ctx, block, config)
|
||||
}
|
||||
|
||||
// StandardTraceBadBlockToFile dumps the structured logs created during the
|
||||
// execution of EVM against a block pulled from the pool of bad ones to the
|
||||
// local file system and returns a list of files to the caller.
|
||||
func (api *PrivateDebugAPI) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
|
||||
blocks := api.eth.blockchain.BadBlocks()
|
||||
for _, block := range blocks {
|
||||
if block.Hash() == hash {
|
||||
return api.standardTraceBlockToFile(ctx, block, config)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("bad block %#x not found", hash)
|
||||
}
|
||||
|
||||
// traceBlock configures a new tracer according to the provided configuration, and
|
||||
|
|
@ -410,7 +447,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
|||
}
|
||||
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
return nil, fmt.Errorf("parent %x not found", block.ParentHash())
|
||||
return nil, fmt.Errorf("parent %#x not found", block.ParentHash())
|
||||
}
|
||||
reexec := defaultTraceReexec
|
||||
if config != nil && config.Reexec != nil {
|
||||
|
|
@ -481,6 +518,106 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
|||
return results, nil
|
||||
}
|
||||
|
||||
// standardTraceBlockToFile configures a new tracer which uses standard JSON output,
|
||||
// and traces either a full block or an individual transaction. The return value will
|
||||
// be one filename per transaction traced.
|
||||
func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) {
|
||||
// If we're tracing a single transaction, make sure it's present
|
||||
if config != nil && config.TxHash != (common.Hash{}) {
|
||||
var exists bool
|
||||
for _, tx := range block.Transactions() {
|
||||
if exists = (tx.Hash() == config.TxHash); exists {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash)
|
||||
}
|
||||
}
|
||||
// Create the parent state database
|
||||
if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
return nil, fmt.Errorf("parent %#x not found", block.ParentHash())
|
||||
}
|
||||
reexec := defaultTraceReexec
|
||||
if config != nil && config.Reexec != nil {
|
||||
reexec = *config.Reexec
|
||||
}
|
||||
statedb, err := api.computeStateDB(parent, reexec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Retrieve the tracing configurations, or use default values
|
||||
var (
|
||||
logConfig vm.LogConfig
|
||||
txHash common.Hash
|
||||
)
|
||||
if config != nil {
|
||||
if config.LogConfig != nil {
|
||||
logConfig = *config.LogConfig
|
||||
}
|
||||
txHash = config.TxHash
|
||||
}
|
||||
logConfig.Debug = true
|
||||
|
||||
// Execute transaction, either tracing all or just the requested one
|
||||
var (
|
||||
signer = types.MakeSigner(api.config, block.Number())
|
||||
dumps []string
|
||||
)
|
||||
for i, tx := range block.Transactions() {
|
||||
// Prepare the trasaction for un-traced execution
|
||||
var (
|
||||
msg, _ = tx.AsMessage(signer)
|
||||
vmctx = core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
|
||||
|
||||
vmConf vm.Config
|
||||
dump *os.File
|
||||
err error
|
||||
)
|
||||
// If the transaction needs tracing, swap out the configs
|
||||
if tx.Hash() == txHash || txHash == (common.Hash{}) {
|
||||
// Generate a unique temporary file to dump it into
|
||||
prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4])
|
||||
|
||||
dump, err = ioutil.TempFile(os.TempDir(), prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dumps = append(dumps, dump.Name())
|
||||
|
||||
// Swap out the noop logger to the standard tracer
|
||||
vmConf = vm.Config{
|
||||
Debug: true,
|
||||
Tracer: vm.NewJSONLogger(&logConfig, bufio.NewWriter(dump)),
|
||||
EnablePreimageRecording: true,
|
||||
}
|
||||
}
|
||||
// Execute the transaction and flush any traces to disk
|
||||
vmenv := vm.NewEVM(vmctx, statedb, api.config, vmConf)
|
||||
_, _, _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
||||
|
||||
if dump != nil {
|
||||
dump.Close()
|
||||
log.Info("Wrote standard trace", "file", dump.Name())
|
||||
}
|
||||
if err != nil {
|
||||
return dumps, err
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
statedb.Finalise(true)
|
||||
|
||||
// If we've traced the transaction we were looking for, abort
|
||||
if tx.Hash() == txHash {
|
||||
break
|
||||
}
|
||||
}
|
||||
return dumps, nil
|
||||
}
|
||||
|
||||
// computeStateDB retrieves the state database associated with a certain block.
|
||||
// If no state is locally available for the given block, a number of blocks are
|
||||
// attempted to be reexecuted to generate the desired state.
|
||||
|
|
@ -506,7 +643,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
|
|||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *trie.MissingNodeError:
|
||||
return nil, errors.New("required historical state unavailable")
|
||||
return nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec)
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -520,7 +657,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
|
|||
for block.NumberU64() < origin {
|
||||
// Print progress logs if long enough time elapsed
|
||||
if time.Since(logged) > 8*time.Second {
|
||||
log.Info("Regenerating historical state", "block", block.NumberU64()+1, "target", origin, "elapsed", time.Since(start))
|
||||
log.Info("Regenerating historical state", "block", block.NumberU64()+1, "target", origin, "remaining", origin-block.NumberU64()-1, "elapsed", time.Since(start))
|
||||
logged = time.Now()
|
||||
}
|
||||
// Retrieve the next block to regenerate and process it
|
||||
|
|
@ -529,15 +666,15 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
|
|||
}
|
||||
_, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err)
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
root, err := statedb.Commit(true)
|
||||
root, err := statedb.Commit(api.eth.blockchain.Config().IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := statedb.Reset(root); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("state reset after block %d failed: %v", block.NumberU64(), err)
|
||||
}
|
||||
database.TrieDB().Reference(root, common.Hash{})
|
||||
if proot != (common.Hash{}) {
|
||||
|
|
@ -556,7 +693,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha
|
|||
// Retrieve the transaction and assemble its EVM context
|
||||
tx, blockHash, _, index := rawdb.ReadTransaction(api.eth.ChainDb(), hash)
|
||||
if tx == nil {
|
||||
return nil, fmt.Errorf("transaction %x not found", hash)
|
||||
return nil, fmt.Errorf("transaction %#x not found", hash)
|
||||
}
|
||||
reexec := defaultTraceReexec
|
||||
if config != nil && config.Reexec != nil {
|
||||
|
|
@ -636,11 +773,11 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
|
|||
// Create the parent state database
|
||||
block := api.eth.blockchain.GetBlockByHash(blockHash)
|
||||
if block == nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash)
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("block %#x not found", blockHash)
|
||||
}
|
||||
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash())
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
|
||||
}
|
||||
statedb, err := api.computeStateDB(parent, reexec)
|
||||
if err != nil {
|
||||
|
|
@ -659,10 +796,10 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
|
|||
// Not yet the searched for transaction, execute on top of the current state
|
||||
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{})
|
||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||
}
|
||||
// Ensure any modifications are committed to the state
|
||||
statedb.Finalise(true)
|
||||
}
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, blockHash)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
|
||||
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.ConstantinopleOverride)
|
||||
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
|
||||
return nil, genesisErr
|
||||
}
|
||||
|
|
@ -173,7 +173,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
}
|
||||
eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)
|
||||
|
||||
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil {
|
||||
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, config.Whitelist); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,9 @@ type Config struct {
|
|||
SyncMode downloader.SyncMode
|
||||
NoPruning bool
|
||||
|
||||
// Whitelist of required block number -> hash values to accept
|
||||
Whitelist map[uint64]common.Hash `toml:"-"`
|
||||
|
||||
// Light client options
|
||||
LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
|
||||
LightPeers int `toml:",omitempty"` // Maximum number of LES client peers
|
||||
|
|
@ -126,8 +129,12 @@ type Config struct {
|
|||
|
||||
// Type of the EWASM interpreter ("" for default)
|
||||
EWASMInterpreter string
|
||||
|
||||
// Type of the EVM interpreter ("" for default)
|
||||
EVMInterpreter string
|
||||
|
||||
// Constantinople block override (TODO: remove after the fork)
|
||||
ConstantinopleOverride *big.Int
|
||||
}
|
||||
|
||||
type configMarshaling struct {
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ type Downloader struct {
|
|||
mode SyncMode // Synchronisation mode defining the strategy used (per sync cycle)
|
||||
mux *event.TypeMux // Event multiplexer to announce sync operation events
|
||||
|
||||
genesis uint64 // Genesis block number to limit sync to (e.g. light client CHT)
|
||||
queue *queue // Scheduler for selecting the hashes to download
|
||||
peers *peerSet // Set of active peers from which download can proceed
|
||||
stateDB ethdb.Database
|
||||
|
|
@ -664,7 +665,28 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header)
|
|||
}
|
||||
p.log.Debug("Looking for common ancestor", "local", localHeight, "remote", remoteHeight)
|
||||
if localHeight >= MaxForkAncestry {
|
||||
// We're above the max reorg threshold, find the earliest fork point
|
||||
floor = int64(localHeight - MaxForkAncestry)
|
||||
|
||||
// If we're doing a light sync, ensure the floor doesn't go below the CHT, as
|
||||
// all headers before that point will be missing.
|
||||
if d.mode == LightSync {
|
||||
// If we dont know the current CHT position, find it
|
||||
if d.genesis == 0 {
|
||||
header := d.lightchain.CurrentHeader()
|
||||
for header != nil {
|
||||
d.genesis = header.Number.Uint64()
|
||||
if floor >= int64(d.genesis)-1 {
|
||||
break
|
||||
}
|
||||
header = d.lightchain.GetHeaderByHash(header.ParentHash)
|
||||
}
|
||||
}
|
||||
// We already know the "genesis" block number, cap floor to that
|
||||
if floor < int64(d.genesis)-1 {
|
||||
floor = int64(d.genesis) - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
from, count, skip, max := calculateRequestSpan(remoteHeight, localHeight)
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ type ProtocolManager struct {
|
|||
txsSub event.Subscription
|
||||
minedBlockSub *event.TypeMuxSubscription
|
||||
|
||||
whitelist map[uint64]common.Hash
|
||||
|
||||
// channels for fetcher, syncer, txsyncLoop
|
||||
newPeerCh chan *peer
|
||||
txsyncCh chan *txsync
|
||||
|
|
@ -101,7 +103,7 @@ type ProtocolManager struct {
|
|||
|
||||
// NewProtocolManager returns a new Ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
||||
// with the Ethereum network.
|
||||
func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
|
||||
func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database, whitelist map[uint64]common.Hash) (*ProtocolManager, error) {
|
||||
// Create the protocol manager with the base fields
|
||||
manager := &ProtocolManager{
|
||||
networkID: networkID,
|
||||
|
|
@ -110,6 +112,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
|||
blockchain: blockchain,
|
||||
chainconfig: config,
|
||||
peers: newPeerSet(),
|
||||
whitelist: whitelist,
|
||||
newPeerCh: make(chan *peer),
|
||||
noMorePeers: make(chan struct{}),
|
||||
txsyncCh: make(chan *txsync),
|
||||
|
|
@ -307,7 +310,13 @@ func (pm *ProtocolManager) handle(p *peer) error {
|
|||
}
|
||||
}()
|
||||
}
|
||||
// main loop. handle incoming messages.
|
||||
// If we have any explicit whitelist block hashes, request them
|
||||
for number := range pm.whitelist {
|
||||
if err := p.RequestHeadersByNumber(number, 1, 0, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Handle incoming messages until the connection is torn down
|
||||
for {
|
||||
if err := pm.handleMsg(p); err != nil {
|
||||
p.Log().Debug("Ethereum message handling failed", "err", err)
|
||||
|
|
@ -466,6 +475,14 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
p.Log().Debug("Verified to be on the same side of the DAO fork")
|
||||
return nil
|
||||
}
|
||||
// Otherwise if it's a whitelisted block, validate against the set
|
||||
if want, ok := pm.whitelist[headers[0].Number.Uint64()]; ok {
|
||||
if hash := headers[0].Hash(); want != hash {
|
||||
p.Log().Info("Whitelist mismatch, dropping peer", "number", headers[0].Number.Uint64(), "hash", hash, "want", want)
|
||||
return errors.New("whitelist block mismatch")
|
||||
}
|
||||
p.Log().Debug("Whitelist block verified", "number", headers[0].Number.Uint64(), "hash", want)
|
||||
}
|
||||
// Irrelevant of the fork checks, send the header to the fetcher just in case
|
||||
headers = pm.fetcher.FilterHeaders(p.id, headers, time.Now())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -478,7 +478,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool
|
|||
if err != nil {
|
||||
t.Fatalf("failed to create new blockchain: %v", err)
|
||||
}
|
||||
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db)
|
||||
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to start test protocol manager: %v", err)
|
||||
}
|
||||
|
|
@ -559,7 +559,7 @@ func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to create new blockchain: %v", err)
|
||||
}
|
||||
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db)
|
||||
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to start test protocol manager: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
|
|||
panic(err)
|
||||
}
|
||||
|
||||
pm, err := NewProtocolManager(gspec.Config, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx}, engine, blockchain, db)
|
||||
pm, err := NewProtocolManager(gspec.Config, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx}, engine, blockchain, db, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -40,10 +40,7 @@
|
|||
var idx = toHex(key);
|
||||
|
||||
if (this.prestate[acc].storage[idx] === undefined) {
|
||||
var val = toHex(db.getState(addr, key));
|
||||
if (val != "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
||||
this.prestate[acc].storage[idx] = toHex(db.getState(addr, key));
|
||||
}
|
||||
this.prestate[acc].storage[idx] = toHex(db.getState(addr, key));
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ type FilterQuery struct {
|
|||
// {{A}} matches topic A in first position
|
||||
// {{}, {B}} matches any topic in first position, B in second position
|
||||
// {{A}, {B}} matches topic A in first position, B in second position
|
||||
// {{A, B}}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position
|
||||
// {{A, B}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position
|
||||
Topics [][]common.Hash
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -683,7 +683,7 @@ type CallArgs struct {
|
|||
Data hexutil.Bytes `json:"data"`
|
||||
}
|
||||
|
||||
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error) {
|
||||
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, timeout time.Duration) ([]byte, uint64, bool, error) {
|
||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||
|
||||
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
||||
|
|
@ -724,7 +724,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
|
|||
defer cancel()
|
||||
|
||||
// Get a new instance of the EVM.
|
||||
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)
|
||||
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header)
|
||||
if err != nil {
|
||||
return nil, 0, false, err
|
||||
}
|
||||
|
|
@ -748,7 +748,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
|
|||
// Call executes the given transaction on the state for the given block number.
|
||||
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
||||
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||
result, _, _, err := s.doCall(ctx, args, blockNr, vm.Config{}, 5*time.Second)
|
||||
result, _, _, err := s.doCall(ctx, args, blockNr, 5*time.Second)
|
||||
return (hexutil.Bytes)(result), err
|
||||
}
|
||||
|
||||
|
|
@ -777,7 +777,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
|
|||
executable := func(gas uint64) bool {
|
||||
args.Gas = hexutil.Uint64(gas)
|
||||
|
||||
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}, 0)
|
||||
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, 0)
|
||||
if err != nil || failed {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ type Backend interface {
|
|||
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
|
||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
||||
GetTd(blockHash common.Hash) *big.Int
|
||||
GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error)
|
||||
GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error)
|
||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
||||
|
|
|
|||
|
|
@ -384,6 +384,18 @@ web3._extend({
|
|||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'standardTraceBadBlockToFile',
|
||||
call: 'debug_standardTraceBadBlockToFile',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'standardTraceBlockToFile',
|
||||
call: 'debug_standardTraceBlockToFile',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlockByNumber',
|
||||
call: 'debug_traceBlockByNumber',
|
||||
|
|
|
|||
|
|
@ -105,10 +105,10 @@ func (b *LesApiBackend) GetTd(hash common.Hash) *big.Int {
|
|||
return b.eth.blockchain.GetTdByHash(hash)
|
||||
}
|
||||
|
||||
func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
|
||||
func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
|
||||
state.SetBalance(msg.From(), math.MaxBig256)
|
||||
context := core.NewEVMContext(msg, header, b.eth.blockchain, nil)
|
||||
return vm.NewEVM(context, state, b.eth.chainConfig, vmCfg), state.Error, nil
|
||||
return vm.NewEVM(context, state, b.eth.chainConfig, vm.Config{}), state.Error, nil
|
||||
}
|
||||
|
||||
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
|
||||
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.ConstantinopleOverride)
|
||||
if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat {
|
||||
return nil, genesisErr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,6 @@ func (peer *ClientNode) RequestProcessed(cost uint64) (bv, realCost uint64) {
|
|||
time := mclock.Now()
|
||||
peer.recalcBV(time)
|
||||
peer.bufValue -= cost
|
||||
peer.recalcBV(time)
|
||||
rcValue, rcost := peer.cm.processed(peer.cmNode, time)
|
||||
if rcValue < peer.params.BufLimit {
|
||||
bv := peer.params.BufLimit - rcValue
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ func diffTries(t1, t2 state.Trie) error {
|
|||
spew.Dump(i2)
|
||||
return fmt.Errorf("tries have different keys %x, %x", i1.Key, i2.Key)
|
||||
}
|
||||
if !bytes.Equal(i2.Value, i2.Value) {
|
||||
if !bytes.Equal(i1.Value, i2.Value) {
|
||||
return fmt.Errorf("tries differ at key %x", i1.Key)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,34 @@ func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, userna
|
|||
rep.run()
|
||||
}
|
||||
|
||||
// InfluxDBWithTagsOnce runs once an InfluxDB reporter and post the given metrics.Registry with the specified tags
|
||||
func InfluxDBWithTagsOnce(r metrics.Registry, url, database, username, password, namespace string, tags map[string]string) error {
|
||||
u, err := uurl.Parse(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to parse InfluxDB. url: %s, err: %v", url, err)
|
||||
}
|
||||
|
||||
rep := &reporter{
|
||||
reg: r,
|
||||
url: *u,
|
||||
database: database,
|
||||
username: username,
|
||||
password: password,
|
||||
namespace: namespace,
|
||||
tags: tags,
|
||||
cache: make(map[string]int64),
|
||||
}
|
||||
if err := rep.makeClient(); err != nil {
|
||||
return fmt.Errorf("Unable to make InfluxDB client. err: %v", err)
|
||||
}
|
||||
|
||||
if err := rep.send(); err != nil {
|
||||
return fmt.Errorf("Unable to send to InfluxDB. err: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reporter) makeClient() (err error) {
|
||||
r.client, err = client.NewClient(client.Config{
|
||||
URL: r.url,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -692,7 +691,7 @@ func (w *worker) updateSnapshot() {
|
|||
func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) {
|
||||
snap := w.current.state.Snapshot()
|
||||
|
||||
receipt, _, err := core.ApplyTransaction(w.config, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, vm.Config{})
|
||||
receipt, _, err := core.ApplyTransaction(w.config, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig())
|
||||
if err != nil {
|
||||
w.current.state.RevertToSnapshot(snap)
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -84,6 +84,13 @@ func (bi *BigInt) SetString(x string, base int) {
|
|||
// BigInts represents a slice of big ints.
|
||||
type BigInts struct{ bigints []*big.Int }
|
||||
|
||||
// NewBigInts creates a slice of uninitialized big numbers.
|
||||
func NewBigInts(size int) *BigInts {
|
||||
return &BigInts{
|
||||
bigints: make([]*big.Int, size),
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the number of big ints in the slice.
|
||||
func (bi *BigInts) Size() int {
|
||||
return len(bi.bigints)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
|
|
@ -152,6 +153,10 @@ type Config struct {
|
|||
|
||||
// Logger is a custom logger to use with the p2p.Server.
|
||||
Logger log.Logger `toml:",omitempty"`
|
||||
|
||||
staticNodesWarning bool
|
||||
trustedNodesWarning bool
|
||||
oldGethResourceWarning bool
|
||||
}
|
||||
|
||||
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into
|
||||
|
|
@ -263,8 +268,8 @@ var isOldGethResource = map[string]bool{
|
|||
"chaindata": true,
|
||||
"nodes": true,
|
||||
"nodekey": true,
|
||||
"static-nodes.json": true,
|
||||
"trusted-nodes.json": true,
|
||||
"static-nodes.json": false, // no warning for these because they have their
|
||||
"trusted-nodes.json": false, // own separate warning.
|
||||
}
|
||||
|
||||
// ResolvePath resolves path in the instance directory.
|
||||
|
|
@ -277,13 +282,15 @@ func (c *Config) ResolvePath(path string) string {
|
|||
}
|
||||
// Backwards-compatibility: ensure that data directory files created
|
||||
// by geth 1.4 are used if they exist.
|
||||
if c.name() == "geth" && isOldGethResource[path] {
|
||||
if warn, isOld := isOldGethResource[path]; isOld {
|
||||
oldpath := ""
|
||||
if c.Name == "geth" {
|
||||
if c.name() == "geth" {
|
||||
oldpath = filepath.Join(c.DataDir, path)
|
||||
}
|
||||
if oldpath != "" && common.FileExist(oldpath) {
|
||||
// TODO: print warning
|
||||
if warn {
|
||||
c.warnOnce(&c.oldGethResourceWarning, "Using deprecated resource file %s, please move this file to the 'geth' subdirectory of datadir.", oldpath)
|
||||
}
|
||||
return oldpath
|
||||
}
|
||||
}
|
||||
|
|
@ -337,17 +344,17 @@ func (c *Config) NodeKey() *ecdsa.PrivateKey {
|
|||
|
||||
// StaticNodes returns a list of node enode URLs configured as static nodes.
|
||||
func (c *Config) StaticNodes() []*enode.Node {
|
||||
return c.parsePersistentNodes(c.ResolvePath(datadirStaticNodes))
|
||||
return c.parsePersistentNodes(&c.staticNodesWarning, c.ResolvePath(datadirStaticNodes))
|
||||
}
|
||||
|
||||
// TrustedNodes returns a list of node enode URLs configured as trusted nodes.
|
||||
func (c *Config) TrustedNodes() []*enode.Node {
|
||||
return c.parsePersistentNodes(c.ResolvePath(datadirTrustedNodes))
|
||||
return c.parsePersistentNodes(&c.trustedNodesWarning, c.ResolvePath(datadirTrustedNodes))
|
||||
}
|
||||
|
||||
// parsePersistentNodes parses a list of discovery node URLs loaded from a .json
|
||||
// file from within the data directory.
|
||||
func (c *Config) parsePersistentNodes(path string) []*enode.Node {
|
||||
func (c *Config) parsePersistentNodes(w *bool, path string) []*enode.Node {
|
||||
// Short circuit if no node config is present
|
||||
if c.DataDir == "" {
|
||||
return nil
|
||||
|
|
@ -355,10 +362,12 @@ func (c *Config) parsePersistentNodes(path string) []*enode.Node {
|
|||
if _, err := os.Stat(path); err != nil {
|
||||
return nil
|
||||
}
|
||||
c.warnOnce(w, "Found deprecated node list file %s, please use the TOML config file instead.", path)
|
||||
|
||||
// Load the nodes from the config file.
|
||||
var nodelist []string
|
||||
if err := common.LoadJSON(path, &nodelist); err != nil {
|
||||
log.Error(fmt.Sprintf("Can't load node file %s: %v", path, err))
|
||||
log.Error(fmt.Sprintf("Can't load node list file: %v", err))
|
||||
return nil
|
||||
}
|
||||
// Interpret the list as a discovery node array
|
||||
|
|
@ -440,3 +449,20 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
|
|||
}
|
||||
return accounts.NewManager(backends...), ephemeral, nil
|
||||
}
|
||||
|
||||
var warnLock sync.Mutex
|
||||
|
||||
func (c *Config) warnOnce(w *bool, format string, args ...interface{}) {
|
||||
warnLock.Lock()
|
||||
defer warnLock.Unlock()
|
||||
|
||||
if *w {
|
||||
return
|
||||
}
|
||||
l := c.Logger
|
||||
if l == nil {
|
||||
l = log.Root()
|
||||
}
|
||||
l.Warn(fmt.Sprintf(format, args...))
|
||||
*w = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ func (n *Node) stopIPC() {
|
|||
n.ipcListener.Close()
|
||||
n.ipcListener = nil
|
||||
|
||||
n.log.Info("IPC endpoint closed", "endpoint", n.ipcEndpoint)
|
||||
n.log.Info("IPC endpoint closed", "url", n.ipcEndpoint)
|
||||
}
|
||||
if n.ipcHandler != nil {
|
||||
n.ipcHandler.Stop()
|
||||
|
|
|
|||
|
|
@ -567,12 +567,11 @@ loop:
|
|||
net.ticketStore.searchLookupDone(res.target, res.nodes, func(n *Node, topic Topic) []byte {
|
||||
if n.state != nil && n.state.canQuery {
|
||||
return net.conn.send(n, topicQueryPacket, topicQuery{Topic: topic}) // TODO: set expiration
|
||||
} else {
|
||||
if n.state == unknown {
|
||||
net.ping(n, n.addr())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if n.state == unknown {
|
||||
net.ping(n, n.addr())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
case <-statsDump.C:
|
||||
|
|
@ -801,7 +800,7 @@ func (n *nodeNetGuts) startNextQuery(net *Network) {
|
|||
func (q *findnodeQuery) start(net *Network) bool {
|
||||
// Satisfy queries against the local node directly.
|
||||
if q.remote == net.tab.self {
|
||||
closest := net.tab.closest(crypto.Keccak256Hash(q.target[:]), bucketSize)
|
||||
closest := net.tab.closest(q.target, bucketSize)
|
||||
q.reply <- closest.entries
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ func (p *Peer) handleIncoming(handle func(ctx context.Context, msg interface{})
|
|||
// * arguments
|
||||
// * context
|
||||
// * the local handshake to be sent to the remote peer
|
||||
// * funcion to be called on the remote handshake (can be nil)
|
||||
// * function to be called on the remote handshake (can be nil)
|
||||
// * expects a remote handshake back of the same type
|
||||
// * the dialing peer needs to send the handshake first and then waits for remote
|
||||
// * the listening peer waits for the remote handshake and then sends it
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"sync"
|
||||
|
|
@ -391,7 +390,7 @@ type sharedUDPConn struct {
|
|||
func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
|
||||
packet, ok := <-s.unhandled
|
||||
if !ok {
|
||||
return 0, nil, fmt.Errorf("Connection was closed")
|
||||
return 0, nil, errors.New("Connection was closed")
|
||||
}
|
||||
l := len(packet.Data)
|
||||
if l > len(b) {
|
||||
|
|
@ -425,7 +424,7 @@ func (srv *Server) Start() (err error) {
|
|||
|
||||
// static fields
|
||||
if srv.PrivateKey == nil {
|
||||
return fmt.Errorf("Server.PrivateKey must be set to a non-nil key")
|
||||
return errors.New("Server.PrivateKey must be set to a non-nil key")
|
||||
}
|
||||
if srv.newTransport == nil {
|
||||
srv.newTransport = newRLPX
|
||||
|
|
@ -903,7 +902,7 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *enode.Node) erro
|
|||
if dialDest != nil {
|
||||
dialPubkey = new(ecdsa.PublicKey)
|
||||
if err := dialDest.Load((*enode.Secp256k1)(dialPubkey)); err != nil {
|
||||
return fmt.Errorf("dial destination doesn't have a secp256k1 public key")
|
||||
return errors.New("dial destination doesn't have a secp256k1 public key")
|
||||
}
|
||||
}
|
||||
// Run the encryption handshake.
|
||||
|
|
@ -937,7 +936,7 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *enode.Node) erro
|
|||
return err
|
||||
}
|
||||
if id := c.node.ID(); !bytes.Equal(crypto.Keccak256(phs.ID), id[:]) {
|
||||
clog.Trace("Wrong devp2p handshake identity", "phsid", fmt.Sprintf("%x", phs.ID))
|
||||
clog.Trace("Wrong devp2p handshake identity", "phsid", hex.EncodeToString(phs.ID))
|
||||
return DiscUnexpectedIdentity
|
||||
}
|
||||
c.caps, c.name = phs.Caps, phs.Name
|
||||
|
|
|
|||
|
|
@ -14,65 +14,69 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package simulation
|
||||
package simulations
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNodeNotFound = errors.New("node not found")
|
||||
ErrNoPivotNode = errors.New("no pivot node set")
|
||||
)
|
||||
|
||||
// ConnectToPivotNode connects the node with provided NodeID
|
||||
// to the pivot node, already set by Simulation.SetPivotNode method.
|
||||
// to the pivot node, already set by Network.SetPivotNode method.
|
||||
// It is useful when constructing a star network topology
|
||||
// when simulation adds and removes nodes dynamically.
|
||||
func (s *Simulation) ConnectToPivotNode(id enode.ID) (err error) {
|
||||
pid := s.PivotNodeID()
|
||||
if pid == nil {
|
||||
// when Network adds and removes nodes dynamically.
|
||||
func (net *Network) ConnectToPivotNode(id enode.ID) (err error) {
|
||||
pivot := net.GetPivotNode()
|
||||
if pivot == nil {
|
||||
return ErrNoPivotNode
|
||||
}
|
||||
return s.connect(*pid, id)
|
||||
return net.connect(pivot.ID(), id)
|
||||
}
|
||||
|
||||
// ConnectToLastNode connects the node with provided NodeID
|
||||
// to the last node that is up, and avoiding connection to self.
|
||||
// It is useful when constructing a chain network topology
|
||||
// when simulation adds and removes nodes dynamically.
|
||||
func (s *Simulation) ConnectToLastNode(id enode.ID) (err error) {
|
||||
ids := s.UpNodeIDs()
|
||||
// when Network adds and removes nodes dynamically.
|
||||
func (net *Network) ConnectToLastNode(id enode.ID) (err error) {
|
||||
ids := net.getUpNodeIDs()
|
||||
l := len(ids)
|
||||
if l < 2 {
|
||||
return nil
|
||||
}
|
||||
lid := ids[l-1]
|
||||
if lid == id {
|
||||
lid = ids[l-2]
|
||||
last := ids[l-1]
|
||||
if last == id {
|
||||
last = ids[l-2]
|
||||
}
|
||||
return s.connect(lid, id)
|
||||
return net.connect(last, id)
|
||||
}
|
||||
|
||||
// ConnectToRandomNode connects the node with provieded NodeID
|
||||
// ConnectToRandomNode connects the node with provided NodeID
|
||||
// to a random node that is up.
|
||||
func (s *Simulation) ConnectToRandomNode(id enode.ID) (err error) {
|
||||
n := s.RandomUpNode(id)
|
||||
if n == nil {
|
||||
func (net *Network) ConnectToRandomNode(id enode.ID) (err error) {
|
||||
selected := net.GetRandomUpNode(id)
|
||||
if selected == nil {
|
||||
return ErrNodeNotFound
|
||||
}
|
||||
return s.connect(n.ID, id)
|
||||
return net.connect(selected.ID(), id)
|
||||
}
|
||||
|
||||
// ConnectNodesFull connects all nodes one to another.
|
||||
// It provides a complete connectivity in the network
|
||||
// which should be rarely needed.
|
||||
func (s *Simulation) ConnectNodesFull(ids []enode.ID) (err error) {
|
||||
func (net *Network) ConnectNodesFull(ids []enode.ID) (err error) {
|
||||
if ids == nil {
|
||||
ids = s.UpNodeIDs()
|
||||
ids = net.getUpNodeIDs()
|
||||
}
|
||||
l := len(ids)
|
||||
for i := 0; i < l; i++ {
|
||||
for j := i + 1; j < l; j++ {
|
||||
err = s.connect(ids[i], ids[j])
|
||||
if err != nil {
|
||||
for i, lid := range ids {
|
||||
for _, rid := range ids[i+1:] {
|
||||
if err = net.connect(lid, rid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -82,14 +86,13 @@ func (s *Simulation) ConnectNodesFull(ids []enode.ID) (err error) {
|
|||
|
||||
// ConnectNodesChain connects all nodes in a chain topology.
|
||||
// If ids argument is nil, all nodes that are up will be connected.
|
||||
func (s *Simulation) ConnectNodesChain(ids []enode.ID) (err error) {
|
||||
func (net *Network) ConnectNodesChain(ids []enode.ID) (err error) {
|
||||
if ids == nil {
|
||||
ids = s.UpNodeIDs()
|
||||
ids = net.getUpNodeIDs()
|
||||
}
|
||||
l := len(ids)
|
||||
for i := 0; i < l-1; i++ {
|
||||
err = s.connect(ids[i], ids[i+1])
|
||||
if err != nil {
|
||||
if err := net.connect(ids[i], ids[i+1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -98,37 +101,32 @@ func (s *Simulation) ConnectNodesChain(ids []enode.ID) (err error) {
|
|||
|
||||
// ConnectNodesRing connects all nodes in a ring topology.
|
||||
// If ids argument is nil, all nodes that are up will be connected.
|
||||
func (s *Simulation) ConnectNodesRing(ids []enode.ID) (err error) {
|
||||
func (net *Network) ConnectNodesRing(ids []enode.ID) (err error) {
|
||||
if ids == nil {
|
||||
ids = s.UpNodeIDs()
|
||||
ids = net.getUpNodeIDs()
|
||||
}
|
||||
l := len(ids)
|
||||
if l < 2 {
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < l-1; i++ {
|
||||
err = s.connect(ids[i], ids[i+1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := net.ConnectNodesChain(ids); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.connect(ids[l-1], ids[0])
|
||||
return net.connect(ids[l-1], ids[0])
|
||||
}
|
||||
|
||||
// ConnectNodesStar connects all nodes in a star topology
|
||||
// with the center at provided NodeID.
|
||||
// If ids argument is nil, all nodes that are up will be connected.
|
||||
func (s *Simulation) ConnectNodesStar(id enode.ID, ids []enode.ID) (err error) {
|
||||
func (net *Network) ConnectNodesStar(pivot enode.ID, ids []enode.ID) (err error) {
|
||||
if ids == nil {
|
||||
ids = s.UpNodeIDs()
|
||||
ids = net.getUpNodeIDs()
|
||||
}
|
||||
l := len(ids)
|
||||
for i := 0; i < l; i++ {
|
||||
if id == ids[i] {
|
||||
for _, id := range ids {
|
||||
if pivot == id {
|
||||
continue
|
||||
}
|
||||
err = s.connect(id, ids[i])
|
||||
if err != nil {
|
||||
if err := net.connect(pivot, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -138,17 +136,17 @@ func (s *Simulation) ConnectNodesStar(id enode.ID, ids []enode.ID) (err error) {
|
|||
// ConnectNodesStarPivot connects all nodes in a star topology
|
||||
// with the center at already set pivot node.
|
||||
// If ids argument is nil, all nodes that are up will be connected.
|
||||
func (s *Simulation) ConnectNodesStarPivot(ids []enode.ID) (err error) {
|
||||
id := s.PivotNodeID()
|
||||
if id == nil {
|
||||
func (net *Network) ConnectNodesStarPivot(ids []enode.ID) (err error) {
|
||||
pivot := net.GetPivotNode()
|
||||
if pivot == nil {
|
||||
return ErrNoPivotNode
|
||||
}
|
||||
return s.ConnectNodesStar(*id, ids)
|
||||
return net.ConnectNodesStar(pivot.ID(), ids)
|
||||
}
|
||||
|
||||
// connect connects two nodes but ignores already connected error.
|
||||
func (s *Simulation) connect(oneID, otherID enode.ID) error {
|
||||
return ignoreAlreadyConnectedErr(s.Net.Connect(oneID, otherID))
|
||||
func (net *Network) connect(oneID, otherID enode.ID) error {
|
||||
return ignoreAlreadyConnectedErr(net.Connect(oneID, otherID))
|
||||
}
|
||||
|
||||
func ignoreAlreadyConnectedErr(err error) error {
|
||||
|
|
@ -157,3 +155,22 @@ func ignoreAlreadyConnectedErr(err error) error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SetPivotNode sets the NodeID of the network's pivot node.
|
||||
// Pivot node is just a specific node that should be treated
|
||||
// differently then other nodes in test. SetPivotNode and
|
||||
// GetPivotNode are just a convenient functions to set and
|
||||
// retrieve it.
|
||||
func (net *Network) SetPivotNode(id enode.ID) {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
net.pivotNodeID = id
|
||||
}
|
||||
|
||||
// GetPivotNode returns NodeID of the pivot node set by
|
||||
// Network.SetPivotNode method.
|
||||
func (net *Network) GetPivotNode() (node *Node) {
|
||||
net.lock.RLock()
|
||||
defer net.lock.RUnlock()
|
||||
return net.getNode(net.pivotNodeID)
|
||||
}
|
||||
190
p2p/simulations/connect_test.go
Normal file
190
p2p/simulations/connect_test.go
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package simulations
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
)
|
||||
|
||||
func newTestNetwork(t *testing.T, nodeCount int) (*Network, []enode.ID) {
|
||||
adapter := adapters.NewSimAdapter(adapters.Services{
|
||||
"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
|
||||
return NewNoopService(nil), nil
|
||||
},
|
||||
})
|
||||
|
||||
// create network
|
||||
network := NewNetwork(adapter, &NetworkConfig{
|
||||
DefaultService: "noopwoop",
|
||||
})
|
||||
|
||||
// create and start nodes
|
||||
ids := make([]enode.ID, nodeCount)
|
||||
for i := range ids {
|
||||
conf := adapters.RandomNodeConfig()
|
||||
node, err := network.NewNodeWithConfig(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating node: %s", err)
|
||||
}
|
||||
if err := network.Start(node.ID()); err != nil {
|
||||
t.Fatalf("error starting node: %s", err)
|
||||
}
|
||||
ids[i] = node.ID()
|
||||
}
|
||||
|
||||
if len(network.Conns) > 0 {
|
||||
t.Fatal("no connections should exist after just adding nodes")
|
||||
}
|
||||
|
||||
return network, ids
|
||||
}
|
||||
|
||||
func TestConnectToPivotNode(t *testing.T) {
|
||||
net, ids := newTestNetwork(t, 2)
|
||||
defer net.Shutdown()
|
||||
|
||||
pivot := ids[0]
|
||||
net.SetPivotNode(pivot)
|
||||
|
||||
other := ids[1]
|
||||
err := net.ConnectToPivotNode(other)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if net.GetConn(pivot, other) == nil {
|
||||
t.Error("pivot and the other node are not connected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectToLastNode(t *testing.T) {
|
||||
net, ids := newTestNetwork(t, 10)
|
||||
defer net.Shutdown()
|
||||
|
||||
first := ids[0]
|
||||
if err := net.ConnectToLastNode(first); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
last := ids[len(ids)-1]
|
||||
for i, id := range ids {
|
||||
if id == first || id == last {
|
||||
continue
|
||||
}
|
||||
|
||||
if net.GetConn(first, id) != nil {
|
||||
t.Errorf("connection must not exist with node(ind: %v, id: %v)", i, id)
|
||||
}
|
||||
}
|
||||
|
||||
if net.GetConn(first, last) == nil {
|
||||
t.Error("first and last node must be connected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectToRandomNode(t *testing.T) {
|
||||
net, ids := newTestNetwork(t, 10)
|
||||
defer net.Shutdown()
|
||||
|
||||
err := net.ConnectToRandomNode(ids[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var cc int
|
||||
for i, a := range ids {
|
||||
for _, b := range ids[i:] {
|
||||
if net.GetConn(a, b) != nil {
|
||||
cc++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cc != 1 {
|
||||
t.Errorf("expected one connection, got %v", cc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectNodesFull(t *testing.T) {
|
||||
net, ids := newTestNetwork(t, 12)
|
||||
defer net.Shutdown()
|
||||
|
||||
err := net.ConnectNodesFull(ids)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
VerifyFull(t, net, ids)
|
||||
}
|
||||
|
||||
func TestConnectNodesChain(t *testing.T) {
|
||||
net, ids := newTestNetwork(t, 10)
|
||||
defer net.Shutdown()
|
||||
|
||||
err := net.ConnectNodesChain(ids)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
VerifyChain(t, net, ids)
|
||||
}
|
||||
|
||||
func TestConnectNodesRing(t *testing.T) {
|
||||
net, ids := newTestNetwork(t, 10)
|
||||
defer net.Shutdown()
|
||||
|
||||
err := net.ConnectNodesRing(ids)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
VerifyRing(t, net, ids)
|
||||
}
|
||||
|
||||
func TestConnectNodesStar(t *testing.T) {
|
||||
net, ids := newTestNetwork(t, 10)
|
||||
defer net.Shutdown()
|
||||
|
||||
pivotIndex := 2
|
||||
|
||||
err := net.ConnectNodesStar(ids[pivotIndex], ids)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
VerifyStar(t, net, ids, pivotIndex)
|
||||
}
|
||||
|
||||
func TestConnectNodesStarPivot(t *testing.T) {
|
||||
net, ids := newTestNetwork(t, 10)
|
||||
defer net.Shutdown()
|
||||
|
||||
pivotIndex := 4
|
||||
|
||||
net.SetPivotNode(ids[pivotIndex])
|
||||
|
||||
err := net.ConnectNodesStarPivot(ids)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
VerifyStar(t, net, ids, pivotIndex)
|
||||
}
|
||||
|
|
@ -20,7 +20,9 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -56,6 +58,8 @@ type Network struct {
|
|||
Conns []*Conn `json:"conns"`
|
||||
connMap map[string]int
|
||||
|
||||
pivotNodeID enode.ID
|
||||
|
||||
nodeAdapter adapters.NodeAdapter
|
||||
events event.Feed
|
||||
lock sync.RWMutex
|
||||
|
|
@ -369,23 +373,32 @@ func (net *Network) DidReceive(sender, receiver enode.ID, proto string, code uin
|
|||
// GetNode gets the node with the given ID, returning nil if the node does not
|
||||
// exist
|
||||
func (net *Network) GetNode(id enode.ID) *Node {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
net.lock.RLock()
|
||||
defer net.lock.RUnlock()
|
||||
return net.getNode(id)
|
||||
}
|
||||
|
||||
// GetNode gets the node with the given name, returning nil if the node does
|
||||
// not exist
|
||||
func (net *Network) GetNodeByName(name string) *Node {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
net.lock.RLock()
|
||||
defer net.lock.RUnlock()
|
||||
return net.getNodeByName(name)
|
||||
}
|
||||
|
||||
func (net *Network) getNodeByName(name string) *Node {
|
||||
for _, node := range net.Nodes {
|
||||
if node.Config.Name == name {
|
||||
return node
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNodes returns the existing nodes
|
||||
func (net *Network) GetNodes() (nodes []*Node) {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
net.lock.RLock()
|
||||
defer net.lock.RUnlock()
|
||||
|
||||
nodes = append(nodes, net.Nodes...)
|
||||
return nodes
|
||||
|
|
@ -399,20 +412,67 @@ func (net *Network) getNode(id enode.ID) *Node {
|
|||
return net.Nodes[i]
|
||||
}
|
||||
|
||||
func (net *Network) getNodeByName(name string) *Node {
|
||||
// GetRandomUpNode returns a random node on the network, which is running.
|
||||
func (net *Network) GetRandomUpNode(excludeIDs ...enode.ID) *Node {
|
||||
net.lock.RLock()
|
||||
defer net.lock.RUnlock()
|
||||
return net.getRandomNode(net.getUpNodeIDs(), excludeIDs)
|
||||
}
|
||||
|
||||
func (net *Network) getUpNodeIDs() (ids []enode.ID) {
|
||||
for _, node := range net.Nodes {
|
||||
if node.Config.Name == name {
|
||||
return node
|
||||
if node.Up {
|
||||
ids = append(ids, node.ID())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return ids
|
||||
}
|
||||
|
||||
// GetRandomDownNode returns a random node on the network, which is stopped.
|
||||
func (net *Network) GetRandomDownNode(excludeIDs ...enode.ID) *Node {
|
||||
net.lock.RLock()
|
||||
defer net.lock.RUnlock()
|
||||
return net.getRandomNode(net.getDownNodeIDs(), excludeIDs)
|
||||
}
|
||||
|
||||
func (net *Network) getDownNodeIDs() (ids []enode.ID) {
|
||||
for _, node := range net.GetNodes() {
|
||||
if !node.Up {
|
||||
ids = append(ids, node.ID())
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func (net *Network) getRandomNode(ids []enode.ID, excludeIDs []enode.ID) *Node {
|
||||
filtered := filterIDs(ids, excludeIDs)
|
||||
|
||||
l := len(filtered)
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
return net.GetNode(filtered[rand.Intn(l)])
|
||||
}
|
||||
|
||||
func filterIDs(ids []enode.ID, excludeIDs []enode.ID) []enode.ID {
|
||||
exclude := make(map[enode.ID]bool)
|
||||
for _, id := range excludeIDs {
|
||||
exclude[id] = true
|
||||
}
|
||||
var filtered []enode.ID
|
||||
for _, id := range ids {
|
||||
if _, found := exclude[id]; !found {
|
||||
filtered = append(filtered, id)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// GetConn returns the connection which exists between "one" and "other"
|
||||
// regardless of which node initiated the connection
|
||||
func (net *Network) GetConn(oneID, otherID enode.ID) *Conn {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
net.lock.RLock()
|
||||
defer net.lock.RUnlock()
|
||||
return net.getConn(oneID, otherID)
|
||||
}
|
||||
|
||||
|
|
@ -705,8 +765,11 @@ func (net *Network) snapshot(addServices []string, removeServices []string) (*Sn
|
|||
return snap, nil
|
||||
}
|
||||
|
||||
var snapshotLoadTimeout = 120 * time.Second
|
||||
|
||||
// Load loads a network snapshot
|
||||
func (net *Network) Load(snap *Snapshot) error {
|
||||
// Start nodes.
|
||||
for _, n := range snap.Nodes {
|
||||
if _, err := net.NewNodeWithConfig(n.Node.Config); err != nil {
|
||||
return err
|
||||
|
|
@ -718,6 +781,69 @@ func (net *Network) Load(snap *Snapshot) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare connection events counter.
|
||||
allConnected := make(chan struct{}) // closed when all connections are established
|
||||
done := make(chan struct{}) // ensures that the event loop goroutine is terminated
|
||||
defer close(done)
|
||||
|
||||
// Subscribe to event channel.
|
||||
// It needs to be done outside of the event loop goroutine (created below)
|
||||
// to ensure that the event channel is blocking before connect calls are made.
|
||||
events := make(chan *Event)
|
||||
sub := net.Events().Subscribe(events)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
go func() {
|
||||
// Expected number of connections.
|
||||
total := len(snap.Conns)
|
||||
// Set of all established connections from the snapshot, not other connections.
|
||||
// Key array element 0 is the connection One field value, and element 1 connection Other field.
|
||||
connections := make(map[[2]enode.ID]struct{}, total)
|
||||
|
||||
for {
|
||||
select {
|
||||
case e := <-events:
|
||||
// Ignore control events as they do not represent
|
||||
// connect or disconnect (Up) state change.
|
||||
if e.Control {
|
||||
continue
|
||||
}
|
||||
// Detect only connection events.
|
||||
if e.Type != EventTypeConn {
|
||||
continue
|
||||
}
|
||||
connection := [2]enode.ID{e.Conn.One, e.Conn.Other}
|
||||
// Nodes are still not connected or have been disconnected.
|
||||
if !e.Conn.Up {
|
||||
// Delete the connection from the set of established connections.
|
||||
// This will prevent false positive in case disconnections happen.
|
||||
delete(connections, connection)
|
||||
log.Warn("load snapshot: unexpected disconnection", "one", e.Conn.One, "other", e.Conn.Other)
|
||||
continue
|
||||
}
|
||||
// Check that the connection is from the snapshot.
|
||||
for _, conn := range snap.Conns {
|
||||
if conn.One == e.Conn.One && conn.Other == e.Conn.Other {
|
||||
// Add the connection to the set of established connections.
|
||||
connections[connection] = struct{}{}
|
||||
if len(connections) == total {
|
||||
// Signal that all nodes are connected.
|
||||
close(allConnected)
|
||||
return
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
case <-done:
|
||||
// Load function returned, terminate this goroutine.
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Start connecting.
|
||||
for _, conn := range snap.Conns {
|
||||
|
||||
if !net.GetNode(conn.One).Up || !net.GetNode(conn.Other).Up {
|
||||
|
|
@ -729,6 +855,14 @@ func (net *Network) Load(snap *Snapshot) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
// Wait until all connections from the snapshot are established.
|
||||
case <-allConnected:
|
||||
// Make sure that we do not wait forever.
|
||||
case <-time.After(snapshotLoadTimeout):
|
||||
return errors.New("snapshot connections not established")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
134
p2p/simulations/test.go
Normal file
134
p2p/simulations/test.go
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
package simulations
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// NoopService is the service that does not do anything
|
||||
// but implements node.Service interface.
|
||||
type NoopService struct {
|
||||
c map[enode.ID]chan struct{}
|
||||
}
|
||||
|
||||
func NewNoopService(ackC map[enode.ID]chan struct{}) *NoopService {
|
||||
return &NoopService{
|
||||
c: ackC,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *NoopService) Protocols() []p2p.Protocol {
|
||||
return []p2p.Protocol{
|
||||
{
|
||||
Name: "noop",
|
||||
Version: 666,
|
||||
Length: 0,
|
||||
Run: func(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||
if t.c != nil {
|
||||
t.c[peer.ID()] = make(chan struct{})
|
||||
close(t.c[peer.ID()])
|
||||
}
|
||||
rw.ReadMsg()
|
||||
return nil
|
||||
},
|
||||
NodeInfo: func() interface{} {
|
||||
return struct{}{}
|
||||
},
|
||||
PeerInfo: func(id enode.ID) interface{} {
|
||||
return struct{}{}
|
||||
},
|
||||
Attributes: []enr.Entry{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *NoopService) APIs() []rpc.API {
|
||||
return []rpc.API{}
|
||||
}
|
||||
|
||||
func (t *NoopService) Start(server *p2p.Server) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *NoopService) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyRing(t *testing.T, net *Network, ids []enode.ID) {
|
||||
t.Helper()
|
||||
n := len(ids)
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i + 1; j < n; j++ {
|
||||
c := net.GetConn(ids[i], ids[j])
|
||||
if i == j-1 || (i == 0 && j == n-1) {
|
||||
if c == nil {
|
||||
t.Errorf("nodes %v and %v are not connected, but they should be", i, j)
|
||||
}
|
||||
} else {
|
||||
if c != nil {
|
||||
t.Errorf("nodes %v and %v are connected, but they should not be", i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyChain(t *testing.T, net *Network, ids []enode.ID) {
|
||||
t.Helper()
|
||||
n := len(ids)
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i + 1; j < n; j++ {
|
||||
c := net.GetConn(ids[i], ids[j])
|
||||
if i == j-1 {
|
||||
if c == nil {
|
||||
t.Errorf("nodes %v and %v are not connected, but they should be", i, j)
|
||||
}
|
||||
} else {
|
||||
if c != nil {
|
||||
t.Errorf("nodes %v and %v are connected, but they should not be", i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyFull(t *testing.T, net *Network, ids []enode.ID) {
|
||||
t.Helper()
|
||||
n := len(ids)
|
||||
var connections int
|
||||
for i, lid := range ids {
|
||||
for _, rid := range ids[i+1:] {
|
||||
if net.GetConn(lid, rid) != nil {
|
||||
connections++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
want := n * (n - 1) / 2
|
||||
if connections != want {
|
||||
t.Errorf("wrong number of connections, got: %v, want: %v", connections, want)
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyStar(t *testing.T, net *Network, ids []enode.ID, centerIndex int) {
|
||||
t.Helper()
|
||||
n := len(ids)
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i + 1; j < n; j++ {
|
||||
c := net.GetConn(ids[i], ids[j])
|
||||
if i == centerIndex || j == centerIndex {
|
||||
if c == nil {
|
||||
t.Errorf("nodes %v and %v are not connected, but they should be", i, j)
|
||||
}
|
||||
} else {
|
||||
if c != nil {
|
||||
t.Errorf("nodes %v and %v are connected, but they should not be", i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,17 +42,17 @@ var (
|
|||
EIP155Block: big.NewInt(2675000),
|
||||
EIP158Block: big.NewInt(2675000),
|
||||
ByzantiumBlock: big.NewInt(4370000),
|
||||
ConstantinopleBlock: nil,
|
||||
ConstantinopleBlock: big.NewInt(7080000),
|
||||
Ethash: new(EthashConfig),
|
||||
}
|
||||
|
||||
// MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network.
|
||||
MainnetTrustedCheckpoint = &TrustedCheckpoint{
|
||||
Name: "mainnet",
|
||||
SectionIndex: 203,
|
||||
SectionHead: common.HexToHash("0xc9e05fc67c6a9815adc8072eb18805b53da53a9a6a273e05541e1b7542cf937a"),
|
||||
CHTRoot: common.HexToHash("0xb85f42447d59f7c3e6679b9a37ed983593fd52efd6251b883592662e95769d5b"),
|
||||
BloomRoot: common.HexToHash("0xf93d50cb4c49b403c6fd33cd60896d3b36184275be0a51bae4df5e8844ac624c"),
|
||||
SectionIndex: 208,
|
||||
SectionHead: common.HexToHash("0x5e9f7696c397d9df8f3b1abda857753575c6f5cff894e1a3d9e1a2af1bd9d6ac"),
|
||||
CHTRoot: common.HexToHash("0x954a63134f6897f015f026387c59c98c4dae7b336610ff5a143455aac9153e9d"),
|
||||
BloomRoot: common.HexToHash("0x8006c5e44b14d90d7cc9cd5fa1cb48cf53697ee3bbbf4b76fdfa70b0242500a9"),
|
||||
}
|
||||
|
||||
// TestnetChainConfig contains the chain parameters to run a node on the Ropsten test network.
|
||||
|
|
@ -73,10 +73,10 @@ var (
|
|||
// TestnetTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network.
|
||||
TestnetTrustedCheckpoint = &TrustedCheckpoint{
|
||||
Name: "testnet",
|
||||
SectionIndex: 134,
|
||||
SectionHead: common.HexToHash("0x17053ecbe045bebefaa01e7716cc85a4e22647e181416cc1098ccbb73a088931"),
|
||||
CHTRoot: common.HexToHash("0x4d2b86422e46ed76f0e3f50f06632c409f809c8375e53c8bc0f782bcb93dd49a"),
|
||||
BloomRoot: common.HexToHash("0xccba62232ee56c2967afc58f136a47ba7dc545ae586e6be666430d94516306c7"),
|
||||
SectionIndex: 139,
|
||||
SectionHead: common.HexToHash("0x9fad89a5e3b993c8339b9cf2cbbeb72cd08774ea6b71b105b3dd880420c618f4"),
|
||||
CHTRoot: common.HexToHash("0xc815833881989c5d2035147e1a79a33d22cbc5313e104ff01e6ab405bd28b317"),
|
||||
BloomRoot: common.HexToHash("0xd94ee9f3c480858f53ec5d059aebdbb2e8d904702f100875ee59ec5f366e841d"),
|
||||
}
|
||||
|
||||
// RinkebyChainConfig contains the chain parameters to run a node on the Rinkeby test network.
|
||||
|
|
@ -90,7 +90,7 @@ var (
|
|||
EIP155Block: big.NewInt(3),
|
||||
EIP158Block: big.NewInt(3),
|
||||
ByzantiumBlock: big.NewInt(1035301),
|
||||
ConstantinopleBlock: nil,
|
||||
ConstantinopleBlock: big.NewInt(3660663),
|
||||
Clique: &CliqueConfig{
|
||||
Period: 15,
|
||||
Epoch: 30000,
|
||||
|
|
@ -100,10 +100,10 @@ var (
|
|||
// RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network.
|
||||
RinkebyTrustedCheckpoint = &TrustedCheckpoint{
|
||||
Name: "rinkeby",
|
||||
SectionIndex: 100,
|
||||
SectionHead: common.HexToHash("0xf18f9b43e16f37b12e68818536ffe455ff18d676274ffdd856a8520ed61bb514"),
|
||||
CHTRoot: common.HexToHash("0x473f5d603b1fedad75d97fd58692130b9ac9ade1aca01eb9363d79bd1c43c791"),
|
||||
BloomRoot: common.HexToHash("0xa39ced3ddbb87e909c7531df2afb6414bea9c9a60ab94da9c6b467535f05326e"),
|
||||
SectionIndex: 105,
|
||||
SectionHead: common.HexToHash("0xec8147d43f936258aaf1b9b9ec91b0a853abf7109f436a23649be809ea43d507"),
|
||||
CHTRoot: common.HexToHash("0xd92703b444846a3db928e87e450770e5d5cbe193131dc8f7c4cf18b4de925a75"),
|
||||
BloomRoot: common.HexToHash("0xff45a6f807138a2cde0cea0c209d9ce5ad8e43ccaae5a7c41af801bb72a1ef96"),
|
||||
}
|
||||
|
||||
// AllEthashProtocolChanges contains every protocol change (EIPs) introduced
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import (
|
|||
|
||||
const (
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 8 // Minor version component of the current release
|
||||
VersionPatch = 19 // Patch version component of the current release
|
||||
VersionMinor = 9 // Minor version component of the current release
|
||||
VersionPatch = 0 // Patch version component of the current release
|
||||
VersionMeta = "unstable" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
|
|
|||
27
rpc/http.go
27
rpc/http.go
|
|
@ -36,11 +36,15 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
contentType = "application/json"
|
||||
maxRequestContentLength = 1024 * 512
|
||||
)
|
||||
|
||||
var nullAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:0")
|
||||
var (
|
||||
// https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13
|
||||
acceptedContentTypes = []string{"application/json", "application/json-rpc", "application/jsonrequest"}
|
||||
contentType = acceptedContentTypes[0]
|
||||
nullAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:0")
|
||||
)
|
||||
|
||||
type httpConn struct {
|
||||
client *http.Client
|
||||
|
|
@ -263,12 +267,21 @@ func validateRequest(r *http.Request) (int, error) {
|
|||
err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength)
|
||||
return http.StatusRequestEntityTooLarge, err
|
||||
}
|
||||
mt, _, err := mime.ParseMediaType(r.Header.Get("content-type"))
|
||||
if r.Method != http.MethodOptions && (err != nil || mt != contentType) {
|
||||
err := fmt.Errorf("invalid content type, only %s is supported", contentType)
|
||||
return http.StatusUnsupportedMediaType, err
|
||||
// Allow OPTIONS (regardless of content-type)
|
||||
if r.Method == http.MethodOptions {
|
||||
return 0, nil
|
||||
}
|
||||
return 0, nil
|
||||
// Check content-type
|
||||
if mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")); err == nil {
|
||||
for _, accepted := range acceptedContentTypes {
|
||||
if accepted == mt {
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// Invalid content-type
|
||||
err := fmt.Errorf("invalid content type, only %s is supported", contentType)
|
||||
return http.StatusUnsupportedMediaType, err
|
||||
}
|
||||
|
||||
func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler {
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@ func (srv *Server) ServeListener(l net.Listener) error {
|
|||
for {
|
||||
conn, err := l.Accept()
|
||||
if netutil.IsTemporaryError(err) {
|
||||
log.Warn("RPC accept error", "err", err)
|
||||
log.Warn("IPC accept error", "err", err)
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Trace("Accepted connection", "addr", conn.RemoteAddr())
|
||||
log.Trace("IPC accepted connection")
|
||||
go srv.ServeCodec(NewJSONCodec(conn), OptionMethodInvocation|OptionSubscriptions)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ type SignerUI interface {
|
|||
// OnSignerStartup is invoked when the signer boots, and tells the UI info about external API location and version
|
||||
// information
|
||||
OnSignerStartup(info StartupInfo)
|
||||
// OnInputRequried is invoked when clef requires user input, for example master password or
|
||||
// OnInputRequired is invoked when clef requires user input, for example master password or
|
||||
// pin-code for unlocking hardware wallets
|
||||
OnInputRequired(info UserInputRequest) (UserInputResponse, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package client
|
|||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -26,6 +27,7 @@ import (
|
|||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"os"
|
||||
|
|
@ -33,9 +35,14 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/swarm/api"
|
||||
"github.com/ethereum/go-ethereum/swarm/spancontext"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
||||
"github.com/pborman/uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -474,6 +481,11 @@ type UploadFn func(file *File) error
|
|||
// TarUpload uses the given Uploader to upload files to swarm as a tar stream,
|
||||
// returning the resulting manifest hash
|
||||
func (c *Client) TarUpload(hash string, uploader Uploader, defaultPath string, toEncrypt bool) (string, error) {
|
||||
ctx, sp := spancontext.StartSpan(context.Background(), "api.client.tarupload")
|
||||
defer sp.Finish()
|
||||
|
||||
var tn time.Time
|
||||
|
||||
reqR, reqW := io.Pipe()
|
||||
defer reqR.Close()
|
||||
addr := hash
|
||||
|
|
@ -489,6 +501,12 @@ func (c *Client) TarUpload(hash string, uploader Uploader, defaultPath string, t
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
trace := GetClientTrace("swarm api client - upload tar", "api.client.uploadtar", uuid.New()[:8], &tn)
|
||||
|
||||
req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
|
||||
transport := http.DefaultTransport
|
||||
|
||||
req.Header.Set("Content-Type", "application/x-tar")
|
||||
if defaultPath != "" {
|
||||
q := req.URL.Query()
|
||||
|
|
@ -529,8 +547,8 @@ func (c *Client) TarUpload(hash string, uploader Uploader, defaultPath string, t
|
|||
}
|
||||
reqW.CloseWithError(err)
|
||||
}()
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
tn = time.Now()
|
||||
res, err := transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -728,3 +746,57 @@ func (c *Client) GetFeedRequest(query *feed.Query, manifestAddressOrDomain strin
|
|||
}
|
||||
return &metadata, nil
|
||||
}
|
||||
|
||||
func GetClientTrace(traceMsg, metricPrefix, ruid string, tn *time.Time) *httptrace.ClientTrace {
|
||||
trace := &httptrace.ClientTrace{
|
||||
GetConn: func(_ string) {
|
||||
log.Trace(traceMsg+" - http get", "event", "GetConn", "ruid", ruid)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".getconn", nil).Update(time.Since(*tn))
|
||||
},
|
||||
GotConn: func(_ httptrace.GotConnInfo) {
|
||||
log.Trace(traceMsg+" - http get", "event", "GotConn", "ruid", ruid)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".gotconn", nil).Update(time.Since(*tn))
|
||||
},
|
||||
PutIdleConn: func(err error) {
|
||||
log.Trace(traceMsg+" - http get", "event", "PutIdleConn", "ruid", ruid, "err", err)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".putidle", nil).Update(time.Since(*tn))
|
||||
},
|
||||
GotFirstResponseByte: func() {
|
||||
log.Trace(traceMsg+" - http get", "event", "GotFirstResponseByte", "ruid", ruid)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".firstbyte", nil).Update(time.Since(*tn))
|
||||
},
|
||||
Got100Continue: func() {
|
||||
log.Trace(traceMsg, "event", "Got100Continue", "ruid", ruid)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".got100continue", nil).Update(time.Since(*tn))
|
||||
},
|
||||
DNSStart: func(_ httptrace.DNSStartInfo) {
|
||||
log.Trace(traceMsg, "event", "DNSStart", "ruid", ruid)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".dnsstart", nil).Update(time.Since(*tn))
|
||||
},
|
||||
DNSDone: func(_ httptrace.DNSDoneInfo) {
|
||||
log.Trace(traceMsg, "event", "DNSDone", "ruid", ruid)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".dnsdone", nil).Update(time.Since(*tn))
|
||||
},
|
||||
ConnectStart: func(network, addr string) {
|
||||
log.Trace(traceMsg, "event", "ConnectStart", "ruid", ruid, "network", network, "addr", addr)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".connectstart", nil).Update(time.Since(*tn))
|
||||
},
|
||||
ConnectDone: func(network, addr string, err error) {
|
||||
log.Trace(traceMsg, "event", "ConnectDone", "ruid", ruid, "network", network, "addr", addr, "err", err)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".connectdone", nil).Update(time.Since(*tn))
|
||||
},
|
||||
WroteHeaders: func() {
|
||||
log.Trace(traceMsg, "event", "WroteHeaders(request)", "ruid", ruid)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".wroteheaders", nil).Update(time.Since(*tn))
|
||||
},
|
||||
Wait100Continue: func() {
|
||||
log.Trace(traceMsg, "event", "Wait100Continue", "ruid", ruid)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".wait100continue", nil).Update(time.Since(*tn))
|
||||
},
|
||||
WroteRequest: func(_ httptrace.WroteRequestInfo) {
|
||||
log.Trace(traceMsg, "event", "WroteRequest", "ruid", ruid)
|
||||
metrics.GetOrRegisterResettingTimer(metricPrefix+".wroterequest", nil).Update(time.Since(*tn))
|
||||
},
|
||||
}
|
||||
return trace
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"net/http"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/swarm/api"
|
||||
|
|
@ -73,9 +74,15 @@ func ParseURI(h http.Handler) http.Handler {
|
|||
|
||||
func InitLoggingResponseWriter(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tn := time.Now()
|
||||
|
||||
writer := newLoggingResponseWriter(w)
|
||||
h.ServeHTTP(writer, r)
|
||||
log.Info("request served", "ruid", GetRUID(r.Context()), "code", writer.statusCode)
|
||||
|
||||
ts := time.Since(tn)
|
||||
log.Info("request served", "ruid", GetRUID(r.Context()), "code", writer.statusCode, "time", ts*time.Millisecond)
|
||||
metrics.GetOrRegisterResettingTimer(fmt.Sprintf("http.request.%s.time", r.Method), nil).Update(ts)
|
||||
metrics.GetOrRegisterResettingTimer(fmt.Sprintf("http.request.%s.%d.time", r.Method, writer.statusCode), nil).Update(ts)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +95,7 @@ func InstrumentOpenTracing(h http.Handler) http.Handler {
|
|||
}
|
||||
spanName := fmt.Sprintf("http.%s.%s", r.Method, uri.Scheme)
|
||||
ctx, sp := spancontext.StartSpan(r.Context(), spanName)
|
||||
|
||||
defer sp.Finish()
|
||||
h.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue