mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Merge branch 'master' of https://github.com/ethereum/go-ethereum
This commit is contained in:
commit
f330119b3a
234 changed files with 52344 additions and 46664 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
|
@ -6,6 +6,7 @@ accounts/abi @gballet
|
||||||
consensus @karalabe
|
consensus @karalabe
|
||||||
core/ @karalabe @holiman
|
core/ @karalabe @holiman
|
||||||
eth/ @karalabe
|
eth/ @karalabe
|
||||||
|
graphql/ @gballet
|
||||||
les/ @zsfelfoldi
|
les/ @zsfelfoldi
|
||||||
light/ @zsfelfoldi
|
light/ @zsfelfoldi
|
||||||
mobile/ @karalabe
|
mobile/ @karalabe
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -42,6 +42,7 @@ profile.cov
|
||||||
/dashboard/assets/node_modules
|
/dashboard/assets/node_modules
|
||||||
/dashboard/assets/stats.json
|
/dashboard/assets/stats.json
|
||||||
/dashboard/assets/bundle.js
|
/dashboard/assets/bundle.js
|
||||||
|
/dashboard/assets/bundle.js.map
|
||||||
/dashboard/assets/package-lock.json
|
/dashboard/assets/package-lock.json
|
||||||
|
|
||||||
**/yarn-error.log
|
**/yarn-error.log
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ For prerequisites and detailed build instructions please read the
|
||||||
[Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum)
|
[Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum)
|
||||||
on the wiki.
|
on the wiki.
|
||||||
|
|
||||||
Building geth requires both a Go (version 1.9 or later) and a C compiler.
|
Building geth requires both a Go (version 1.10 or later) and a C compiler.
|
||||||
You can install them using your favourite package manager.
|
You can install them using your favourite package manager.
|
||||||
Once the dependencies are installed, run
|
Once the dependencies are installed, run
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ type SimulatedBackend struct {
|
||||||
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
||||||
// for testing purposes.
|
// for testing purposes.
|
||||||
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||||
database := ethdb.NewMemDatabase()
|
database := rawdb.NewMemoryDatabase()
|
||||||
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
||||||
genesis.MustCommit(database)
|
genesis.MustCommit(database)
|
||||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
|
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
|
|
|
||||||
|
|
@ -670,7 +670,7 @@ var bindTests = []struct {
|
||||||
`Eventer`,
|
`Eventer`,
|
||||||
`
|
`
|
||||||
contract Eventer {
|
contract Eventer {
|
||||||
event SimpleEvent (
|
event SimpleEvent (
|
||||||
address indexed Addr,
|
address indexed Addr,
|
||||||
bytes32 indexed Id,
|
bytes32 indexed Id,
|
||||||
bool indexed Flag,
|
bool indexed Flag,
|
||||||
|
|
@ -698,10 +698,18 @@ var bindTests = []struct {
|
||||||
function raiseDynamicEvent(string str, bytes blob) {
|
function raiseDynamicEvent(string str, bytes blob) {
|
||||||
DynamicEvent(str, blob, str, blob);
|
DynamicEvent(str, blob, str, blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event FixedBytesEvent (
|
||||||
|
bytes24 indexed IndexedBytes,
|
||||||
|
bytes24 NonIndexedBytes
|
||||||
|
);
|
||||||
|
function raiseFixedBytesEvent(bytes24 blob) {
|
||||||
|
FixedBytesEvent(blob, blob);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
`6060604052341561000f57600080fd5b61042c8061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063528300ff1461005c578063630c31e2146100fc578063c7d116dd14610156575b600080fd5b341561006757600080fd5b6100fa600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610194565b005b341561010757600080fd5b610154600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035600019169060200190919080351515906020019091908035906020019091905050610367565b005b341561016157600080fd5b610192600480803590602001909190803560010b90602001909190803563ffffffff169060200190919050506103c3565b005b806040518082805190602001908083835b6020831015156101ca57805182526020820191506020810190506020830392506101a5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020826040518082805190602001908083835b60208310151561022d5780518252602082019150602081019050602083039250610208565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f3281fd4f5e152dd3385df49104a3f633706e21c9e80672e88d3bcddf33101f008484604051808060200180602001838103835285818151815260200191508051906020019080838360005b838110156102c15780820151818401526020810190506102a6565b50505050905090810190601f1680156102ee5780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b8381101561032757808201518184015260208101905061030c565b50505050905090810190601f1680156103545780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a35050565b81151583600019168573ffffffffffffffffffffffffffffffffffffffff167f1f097de4289df643bd9c11011cc61367aa12983405c021056e706eb5ba1250c8846040518082815260200191505060405180910390a450505050565b8063ffffffff168260010b847f3ca7f3a77e5e6e15e781850bc82e32adfa378a2a609370db24b4d0fae10da2c960405160405180910390a45050505600a165627a7a72305820d1f8a8bbddbc5bb29f285891d6ae1eef8420c52afdc05e1573f6114d8e1714710029`,
|
`608060405234801561001057600080fd5b5061043f806100206000396000f3006080604052600436106100615763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663528300ff8114610066578063630c31e2146100ff5780636cc6b94014610138578063c7d116dd1461015b575b600080fd5b34801561007257600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526100fd94369492936024939284019190819084018382808284375050604080516020601f89358b018035918201839004830284018301909452808352979a9998810197919650918201945092508291508401838280828437509497506101829650505050505050565b005b34801561010b57600080fd5b506100fd73ffffffffffffffffffffffffffffffffffffffff60043516602435604435151560643561033c565b34801561014457600080fd5b506100fd67ffffffffffffffff1960043516610394565b34801561016757600080fd5b506100fd60043560243560010b63ffffffff604435166103d6565b806040518082805190602001908083835b602083106101b25780518252601f199092019160209182019101610193565b51815160209384036101000a6000190180199092169116179052604051919093018190038120875190955087945090928392508401908083835b6020831061020b5780518252601f1990920191602091820191016101ec565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f3281fd4f5e152dd3385df49104a3f633706e21c9e80672e88d3bcddf33101f008484604051808060200180602001838103835285818151815260200191508051906020019080838360005b8381101561029c578181015183820152602001610284565b50505050905090810190601f1680156102c95780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b838110156102fc5781810151838201526020016102e4565b50505050905090810190601f1680156103295780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a35050565b60408051828152905183151591859173ffffffffffffffffffffffffffffffffffffffff8816917f1f097de4289df643bd9c11011cc61367aa12983405c021056e706eb5ba1250c8919081900360200190a450505050565b6040805167ffffffffffffffff19831680825291517fcdc4c1b1aed5524ffb4198d7a5839a34712baef5fa06884fac7559f4a5854e0a9181900360200190a250565b8063ffffffff168260010b847f3ca7f3a77e5e6e15e781850bc82e32adfa378a2a609370db24b4d0fae10da2c960405160405180910390a45050505600a165627a7a72305820468b5843bf653145bd924b323c64ef035d3dd922c170644b44d61aa666ea6eee0029`,
|
||||||
`[{"constant":false,"inputs":[{"name":"str","type":"string"},{"name":"blob","type":"bytes"}],"name":"raiseDynamicEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"id","type":"bytes32"},{"name":"flag","type":"bool"},{"name":"value","type":"uint256"}],"name":"raiseSimpleEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"number","type":"uint256"},{"name":"short","type":"int16"},{"name":"long","type":"uint32"}],"name":"raiseNodataEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Addr","type":"address"},{"indexed":true,"name":"Id","type":"bytes32"},{"indexed":true,"name":"Flag","type":"bool"},{"indexed":false,"name":"Value","type":"uint256"}],"name":"SimpleEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Number","type":"uint256"},{"indexed":true,"name":"Short","type":"int16"},{"indexed":true,"name":"Long","type":"uint32"}],"name":"NodataEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"IndexedString","type":"string"},{"indexed":true,"name":"IndexedBytes","type":"bytes"},{"indexed":false,"name":"NonIndexedString","type":"string"},{"indexed":false,"name":"NonIndexedBytes","type":"bytes"}],"name":"DynamicEvent","type":"event"}]`,
|
`[{"constant":false,"inputs":[{"name":"str","type":"string"},{"name":"blob","type":"bytes"}],"name":"raiseDynamicEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"id","type":"bytes32"},{"name":"flag","type":"bool"},{"name":"value","type":"uint256"}],"name":"raiseSimpleEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"blob","type":"bytes24"}],"name":"raiseFixedBytesEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"number","type":"uint256"},{"name":"short","type":"int16"},{"name":"long","type":"uint32"}],"name":"raiseNodataEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Addr","type":"address"},{"indexed":true,"name":"Id","type":"bytes32"},{"indexed":true,"name":"Flag","type":"bool"},{"indexed":false,"name":"Value","type":"uint256"}],"name":"SimpleEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Number","type":"uint256"},{"indexed":true,"name":"Short","type":"int16"},{"indexed":true,"name":"Long","type":"uint32"}],"name":"NodataEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"IndexedString","type":"string"},{"indexed":true,"name":"IndexedBytes","type":"bytes"},{"indexed":false,"name":"NonIndexedString","type":"string"},{"indexed":false,"name":"NonIndexedBytes","type":"bytes"}],"name":"DynamicEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"IndexedBytes","type":"bytes24"},{"indexed":false,"name":"NonIndexedBytes","type":"bytes24"}],"name":"FixedBytesEvent","type":"event"}]`,
|
||||||
`
|
`
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -812,6 +820,33 @@ var bindTests = []struct {
|
||||||
if err = dit.Error(); err != nil {
|
if err = dit.Error(); err != nil {
|
||||||
t.Fatalf("dynamic event iteration failed: %v", err)
|
t.Fatalf("dynamic event iteration failed: %v", err)
|
||||||
}
|
}
|
||||||
|
// Test raising and filtering for events with fixed bytes components
|
||||||
|
var fblob [24]byte
|
||||||
|
copy(fblob[:], []byte("Fixed Bytes"))
|
||||||
|
|
||||||
|
if _, err := eventer.RaiseFixedBytesEvent(auth, fblob); err != nil {
|
||||||
|
t.Fatalf("failed to raise fixed bytes event: %v", err)
|
||||||
|
}
|
||||||
|
sim.Commit()
|
||||||
|
|
||||||
|
fit, err := eventer.FilterFixedBytesEvent(nil, [][24]byte{fblob})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to filter for fixed bytes events: %v", err)
|
||||||
|
}
|
||||||
|
defer fit.Close()
|
||||||
|
|
||||||
|
if !fit.Next() {
|
||||||
|
t.Fatalf("fixed bytes log not found: %v", fit.Error())
|
||||||
|
}
|
||||||
|
if fit.Event.NonIndexedBytes != fblob || fit.Event.IndexedBytes != fblob {
|
||||||
|
t.Errorf("fixed bytes log content mismatch: have %v, want {'%x', '%x'}", fit.Event, fblob, fblob)
|
||||||
|
}
|
||||||
|
if fit.Next() {
|
||||||
|
t.Errorf("unexpected fixed bytes event found: %+v", fit.Event)
|
||||||
|
}
|
||||||
|
if err = fit.Error(); err != nil {
|
||||||
|
t.Fatalf("fixed bytes event iteration failed: %v", err)
|
||||||
|
}
|
||||||
// Test subscribing to an event and raising it afterwards
|
// Test subscribing to an event and raising it afterwards
|
||||||
ch := make(chan *EventerSimpleEvent, 16)
|
ch := make(chan *EventerSimpleEvent, 16)
|
||||||
sub, err := eventer.WatchSimpleEvent(nil, ch, nil, nil, nil)
|
sub, err := eventer.WatchSimpleEvent(nil, ch, nil, nil, nil)
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,10 @@ func makeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||||
val := reflect.ValueOf(rule)
|
val := reflect.ValueOf(rule)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|
||||||
|
// static byte array
|
||||||
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
|
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
|
||||||
reflect.Copy(reflect.ValueOf(topic[common.HashLength-val.Len():]), val)
|
reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
|
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
|
||||||
|
|
@ -175,8 +177,10 @@ func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) er
|
||||||
default:
|
default:
|
||||||
// Ran out of custom types, try the crazies
|
// Ran out of custom types, try the crazies
|
||||||
switch {
|
switch {
|
||||||
|
|
||||||
|
// static byte array
|
||||||
case arg.Type.T == abi.FixedBytesTy:
|
case arg.Type.T == abi.FixedBytesTy:
|
||||||
reflect.Copy(field, reflect.ValueOf(topics[0][common.HashLength-arg.Type.Size:]))
|
reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size]))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported indexed type: %v", arg.Type)
|
return fmt.Errorf("unsupported indexed type: %v", arg.Type)
|
||||||
|
|
|
||||||
103
accounts/abi/bind/topics_test.go
Normal file
103
accounts/abi/bind/topics_test.go
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
// 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 bind
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMakeTopics(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
query [][]interface{}
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want [][]common.Hash
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"support fixed byte types, right padded to 32 bytes",
|
||||||
|
args{[][]interface{}{{[5]byte{1, 2, 3, 4, 5}}}},
|
||||||
|
[][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := makeTopics(tt.args.query...)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("makeTopics() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseTopics(t *testing.T) {
|
||||||
|
type bytesStruct struct {
|
||||||
|
StaticBytes [5]byte
|
||||||
|
}
|
||||||
|
bytesType, _ := abi.NewType("bytes5", nil)
|
||||||
|
type args struct {
|
||||||
|
createObj func() interface{}
|
||||||
|
resultObj func() interface{}
|
||||||
|
fields abi.Arguments
|
||||||
|
topics []common.Hash
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "support fixed byte types, right padded to 32 bytes",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return &bytesStruct{} },
|
||||||
|
resultObj: func() interface{} { return &bytesStruct{StaticBytes: [5]byte{1, 2, 3, 4, 5}} },
|
||||||
|
fields: abi.Arguments{abi.Argument{
|
||||||
|
Name: "staticBytes",
|
||||||
|
Type: bytesType,
|
||||||
|
Indexed: true,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{
|
||||||
|
{1, 2, 3, 4, 5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
createObj := tt.args.createObj()
|
||||||
|
if err := parseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
resultObj := tt.args.resultObj()
|
||||||
|
if !reflect.DeepEqual(createObj, resultObj) {
|
||||||
|
t.Errorf("parseTopics() = %v, want %v", createObj, resultObj)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -274,9 +274,7 @@ func (w *wallet) close() error {
|
||||||
w.device = nil
|
w.device = nil
|
||||||
|
|
||||||
w.accounts, w.paths = nil, nil
|
w.accounts, w.paths = nil, nil
|
||||||
w.driver.Close()
|
return w.driver.Close()
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accounts implements accounts.Wallet, returning the list of accounts pinned to
|
// Accounts implements accounts.Wallet, returning the list of accounts pinned to
|
||||||
|
|
|
||||||
|
|
@ -661,7 +661,7 @@ OBS! A slight deviation from `json` standard is in place: every request and resp
|
||||||
Whereas the `json` specification allows for linebreaks, linebreaks __should not__ be used in this communication channel, to make
|
Whereas the `json` specification allows for linebreaks, linebreaks __should not__ be used in this communication channel, to make
|
||||||
things simpler for both parties.
|
things simpler for both parties.
|
||||||
|
|
||||||
### ApproveTx
|
### ApproveTx / `ui_approveTx`
|
||||||
|
|
||||||
Invoked when there's a transaction for approval.
|
Invoked when there's a transaction for approval.
|
||||||
|
|
||||||
|
|
@ -673,13 +673,13 @@ Here's a method invocation:
|
||||||
|
|
||||||
curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
|
curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
|
||||||
```
|
```
|
||||||
|
Results in the following invocation on the UI:
|
||||||
```json
|
```json
|
||||||
|
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"method": "ApproveTx",
|
"method": "ui_approveTx",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
{
|
||||||
"transaction": {
|
"transaction": {
|
||||||
|
|
@ -724,7 +724,7 @@ curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","me
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"method": "ApproveTx",
|
"method": "ui_approveTx",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
{
|
||||||
"transaction": {
|
"transaction": {
|
||||||
|
|
@ -767,7 +767,7 @@ One which has missing `to`, but with no `data`:
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"method": "ApproveTx",
|
"method": "ui_approveTx",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
{
|
||||||
"transaction": {
|
"transaction": {
|
||||||
|
|
@ -796,33 +796,7 @@ One which has missing `to`, but with no `data`:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### ApproveExport
|
### ApproveListing / `ui_approveListing`
|
||||||
|
|
||||||
Invoked when a request to export an account has been made.
|
|
||||||
|
|
||||||
#### Sample call
|
|
||||||
|
|
||||||
```json
|
|
||||||
|
|
||||||
{
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 7,
|
|
||||||
"method": "ApproveExport",
|
|
||||||
"params": [
|
|
||||||
{
|
|
||||||
"address": "0x0000000000000000000000000000000000000000",
|
|
||||||
"meta": {
|
|
||||||
"remote": "signer binary",
|
|
||||||
"local": "main",
|
|
||||||
"scheme": "in-proc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### ApproveListing
|
|
||||||
|
|
||||||
Invoked when a request for account listing has been made.
|
Invoked when a request for account listing has been made.
|
||||||
|
|
||||||
|
|
@ -833,7 +807,7 @@ Invoked when a request for account listing has been made.
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 5,
|
"id": 5,
|
||||||
"method": "ApproveListing",
|
"method": "ui_approveListing",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
{
|
||||||
"accounts": [
|
"accounts": [
|
||||||
|
|
@ -860,7 +834,7 @@ Invoked when a request for account listing has been made.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### ApproveSignData
|
### ApproveSignData / `ui_approveSignData`
|
||||||
|
|
||||||
#### Sample call
|
#### Sample call
|
||||||
|
|
||||||
|
|
@ -868,7 +842,7 @@ Invoked when a request for account listing has been made.
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"method": "ApproveSignData",
|
"method": "ui_approveSignData",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
{
|
||||||
"address": "0x123409812340981234098123409812deadbeef42",
|
"address": "0x123409812340981234098123409812deadbeef42",
|
||||||
|
|
@ -886,7 +860,7 @@ Invoked when a request for account listing has been made.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### ShowInfo
|
### ShowInfo / `ui_showInfo`
|
||||||
|
|
||||||
The UI should show the info to the user. Does not expect response.
|
The UI should show the info to the user. Does not expect response.
|
||||||
|
|
||||||
|
|
@ -896,7 +870,7 @@ The UI should show the info to the user. Does not expect response.
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 9,
|
"id": 9,
|
||||||
"method": "ShowInfo",
|
"method": "ui_showInfo",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
{
|
||||||
"text": "Tests completed"
|
"text": "Tests completed"
|
||||||
|
|
@ -906,7 +880,7 @@ The UI should show the info to the user. Does not expect response.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### ShowError
|
### ShowError / `ui_showError`
|
||||||
|
|
||||||
The UI should show the info to the user. Does not expect response.
|
The UI should show the info to the user. Does not expect response.
|
||||||
|
|
||||||
|
|
@ -925,7 +899,7 @@ The UI should show the info to the user. Does not expect response.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### OnApproved
|
### OnApprovedTx / `ui_onApprovedTx`
|
||||||
|
|
||||||
`OnApprovedTx` is called when a transaction has been approved and signed. The call contains the return value that will be sent to the external caller. The return value from this method is ignored - the reason for having this callback is to allow the ruleset to keep track of approved transactions.
|
`OnApprovedTx` is called when a transaction has been approved and signed. The call contains the return value that will be sent to the external caller. The return value from this method is ignored - the reason for having this callback is to allow the ruleset to keep track of approved transactions.
|
||||||
|
|
||||||
|
|
@ -933,7 +907,7 @@ When implementing rate-limited rules, this callback should be used.
|
||||||
|
|
||||||
TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`.
|
TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`.
|
||||||
|
|
||||||
### OnSignerStartup
|
### OnSignerStartup / `ui_onSignerStartup`
|
||||||
|
|
||||||
This method provide the UI with information about what API version the signer uses (both internal and external) aswell as build-info and external api,
|
This method provide the UI with information about what API version the signer uses (both internal and external) aswell as build-info and external api,
|
||||||
in k/v-form.
|
in k/v-form.
|
||||||
|
|
@ -944,7 +918,7 @@ Example call:
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"method": "OnSignerStartup",
|
"method": "ui_onSignerStartup",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
{
|
||||||
"info": {
|
"info": {
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,7 @@ Response to SignDataRequest
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"approved": true,
|
"approved": true
|
||||||
"Password": "apassword"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### SignDataResponse - deny
|
### SignDataResponse - deny
|
||||||
|
|
@ -46,8 +45,7 @@ Response to SignDataRequest
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"approved": false,
|
"approved": false
|
||||||
"Password": ""
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### SignTxRequest
|
### SignTxRequest
|
||||||
|
|
@ -89,9 +87,9 @@ Example:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### SignDataResponse - approve
|
### SignTxResponse - approve
|
||||||
|
|
||||||
Response to SignDataRequest. This response needs to contain the `transaction`, because the UI is free to make modifications to the transaction.
|
Response to request to sign a transaction. This response needs to contain the `transaction`, because the UI is free to make modifications to the transaction.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
|
|
@ -105,19 +103,26 @@ Example:
|
||||||
"nonce": "0x4",
|
"nonce": "0x4",
|
||||||
"data": "0x04030201"
|
"data": "0x04030201"
|
||||||
},
|
},
|
||||||
"approved": true,
|
"approved": true
|
||||||
"password": "apassword"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### SignDataResponse - deny
|
### SignTxResponse - deny
|
||||||
|
|
||||||
Response to SignDataRequest. When denying a request, there's no need to provide the transaction in return
|
Response to SignTxRequest. When denying a request, there's no need to provide the transaction in return
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"approved": false,
|
"transaction": {
|
||||||
"Password": ""
|
"from": "0x",
|
||||||
|
"to": null,
|
||||||
|
"gas": "0x0",
|
||||||
|
"gasPrice": "0x0",
|
||||||
|
"value": "0x0",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"data": null
|
||||||
|
},
|
||||||
|
"approved": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### OnApproved - SignTransactionResult
|
### OnApproved - SignTransactionResult
|
||||||
|
|
@ -164,7 +169,7 @@ Example:
|
||||||
```
|
```
|
||||||
### UserInputResponse
|
### UserInputResponse
|
||||||
|
|
||||||
Response to SignDataRequest
|
Response to UserInputRequest
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
|
|
@ -198,7 +203,7 @@ Example:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### UserInputResponse
|
### ListResponse
|
||||||
|
|
||||||
Response to list request. The response contains a list of all addresses to show to the caller. Note: the UI is free to respond with any address the caller, regardless of whether it exists or not
|
Response to list request. The response contains a list of all addresses to show to the caller. Note: the UI is free to respond with any address the caller, regardless of whether it exists or not
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,40 @@
|
||||||
### Changelog for internal API (ui-api)
|
### Changelog for internal API (ui-api)
|
||||||
|
|
||||||
|
### 6.0.0
|
||||||
|
|
||||||
|
Removed `password` from responses to operations which require them. This is for two reasons,
|
||||||
|
|
||||||
|
- Consistency between how rulesets operate and how manual processing works. A rule can `Approve` but require the actual password to be stored in the clef storage.
|
||||||
|
With this change, the same stored password can be used even if rulesets are not enabled, but storage is.
|
||||||
|
- It also removes the usability-shortcut that a UI might otherwise want to implement; remembering passwords. Since we now will not require the
|
||||||
|
password on every `Approve`, there's no need for the UI to cache it locally.
|
||||||
|
- In a future update, we'll likely add `clef_storePassword` to the internal API, so the user can store it via his UI (currently only CLI works).
|
||||||
|
|
||||||
|
Affected datatypes:
|
||||||
|
- `SignTxResponse`
|
||||||
|
- `SignDataResponse`
|
||||||
|
- `NewAccountResponse`
|
||||||
|
|
||||||
|
If `clef` requires a password, the `OnInputRequired` will be used to collect it.
|
||||||
|
|
||||||
|
|
||||||
|
### 5.0.0
|
||||||
|
|
||||||
|
Changed the namespace format to adhere to the legacy ethereum format: `name_methodName`. Changes:
|
||||||
|
|
||||||
|
* `ApproveTx` -> `ui_approveTx`
|
||||||
|
* `ApproveSignData` -> `ui_approveSignData`
|
||||||
|
* `ApproveExport` -> `removed`
|
||||||
|
* `ApproveImport` -> `removed`
|
||||||
|
* `ApproveListing` -> `ui_approveListing`
|
||||||
|
* `ApproveNewAccount` -> `ui_approveNewAccount`
|
||||||
|
* `ShowError` -> `ui_showError`
|
||||||
|
* `ShowInfo` -> `ui_showInfo`
|
||||||
|
* `OnApprovedTx` -> `ui_onApprovedTx`
|
||||||
|
* `OnSignerStartup` -> `ui_onSignerStartup`
|
||||||
|
* `OnInputRequired` -> `ui_onInputRequired`
|
||||||
|
|
||||||
|
|
||||||
### 4.0.0
|
### 4.0.0
|
||||||
|
|
||||||
* Bidirectional communication implemented, so the UI can query `clef` via the stdin/stdout RPC channel. Methods implemented are:
|
* Bidirectional communication implemented, so the UI can query `clef` via the stdin/stdout RPC channel. Methods implemented are:
|
||||||
|
|
|
||||||
228
cmd/clef/main.go
228
cmd/clef/main.go
|
|
@ -119,7 +119,7 @@ var (
|
||||||
ruleFlag = cli.StringFlag{
|
ruleFlag = cli.StringFlag{
|
||||||
Name: "rules",
|
Name: "rules",
|
||||||
Usage: "Enable rule-engine",
|
Usage: "Enable rule-engine",
|
||||||
Value: "rules.json",
|
Value: "",
|
||||||
}
|
}
|
||||||
stdiouiFlag = cli.BoolFlag{
|
stdiouiFlag = cli.BoolFlag{
|
||||||
Name: "stdio-ui",
|
Name: "stdio-ui",
|
||||||
|
|
@ -371,17 +371,14 @@ func signer(c *cli.Context) error {
|
||||||
log.Info("Loaded 4byte db", "signatures", db.Size(), "file", fourByteDb, "local", fourByteLocal)
|
log.Info("Loaded 4byte db", "signatures", db.Size(), "file", fourByteDb, "local", fourByteLocal)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
api core.ExternalAPI
|
api core.ExternalAPI
|
||||||
|
pwStorage storage.Storage = &storage.NoStorage{}
|
||||||
)
|
)
|
||||||
|
|
||||||
configDir := c.GlobalString(configdirFlag.Name)
|
configDir := c.GlobalString(configdirFlag.Name)
|
||||||
if stretchedKey, err := readMasterKey(c, ui); err != nil {
|
if stretchedKey, err := readMasterKey(c, ui); err != nil {
|
||||||
log.Info("No master seed provided, rules disabled", "error", err)
|
log.Info("No master seed provided, rules disabled", "error", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
|
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
|
||||||
|
|
||||||
// Generate domain specific keys
|
// Generate domain specific keys
|
||||||
|
|
@ -390,30 +387,31 @@ func signer(c *cli.Context) error {
|
||||||
confkey := crypto.Keccak256([]byte("config"), stretchedKey)
|
confkey := crypto.Keccak256([]byte("config"), stretchedKey)
|
||||||
|
|
||||||
// Initialize the encrypted storages
|
// Initialize the encrypted storages
|
||||||
pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
|
pwStorage = storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
|
||||||
jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey)
|
jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey)
|
||||||
configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey)
|
configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey)
|
||||||
|
|
||||||
//Do we have a rule-file?
|
//Do we have a rule-file?
|
||||||
ruleJS, err := ioutil.ReadFile(c.GlobalString(ruleFlag.Name))
|
if ruleFile := c.GlobalString(ruleFlag.Name); ruleFile != "" {
|
||||||
if err != nil {
|
ruleJS, err := ioutil.ReadFile(c.GlobalString(ruleFile))
|
||||||
log.Info("Could not load rulefile, rules not enabled", "file", "rulefile")
|
if err != nil {
|
||||||
} else {
|
log.Info("Could not load rulefile, rules not enabled", "file", "rulefile")
|
||||||
hasher := sha256.New()
|
|
||||||
hasher.Write(ruleJS)
|
|
||||||
shasum := hasher.Sum(nil)
|
|
||||||
storedShasum := configStorage.Get("ruleset_sha256")
|
|
||||||
if storedShasum != hex.EncodeToString(shasum) {
|
|
||||||
log.Info("Could not validate ruleset hash, rules not enabled", "got", hex.EncodeToString(shasum), "expected", storedShasum)
|
|
||||||
} else {
|
} else {
|
||||||
// Initialize rules
|
shasum := sha256.Sum256(ruleJS)
|
||||||
ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage, pwStorage)
|
foundShaSum := hex.EncodeToString(shasum[:])
|
||||||
if err != nil {
|
storedShasum := configStorage.Get("ruleset_sha256")
|
||||||
utils.Fatalf(err.Error())
|
if storedShasum != foundShaSum {
|
||||||
|
log.Info("Could not validate ruleset hash, rules not enabled", "got", foundShaSum, "expected", storedShasum)
|
||||||
|
} else {
|
||||||
|
// Initialize rules
|
||||||
|
ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
ruleEngine.Init(string(ruleJS))
|
||||||
|
ui = ruleEngine
|
||||||
|
log.Info("Rule engine configured", "file", c.String(ruleFlag.Name))
|
||||||
}
|
}
|
||||||
ruleEngine.Init(string(ruleJS))
|
|
||||||
ui = ruleEngine
|
|
||||||
log.Info("Rule engine configured", "file", c.String(ruleFlag.Name))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -427,7 +425,7 @@ func signer(c *cli.Context) error {
|
||||||
log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc,
|
log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc,
|
||||||
"light-kdf", lightKdf, "advanced", advanced)
|
"light-kdf", lightKdf, "advanced", advanced)
|
||||||
am := core.StartClefAccountManager(ksLoc, nousb, lightKdf)
|
am := core.StartClefAccountManager(ksLoc, nousb, lightKdf)
|
||||||
apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced)
|
apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced, pwStorage)
|
||||||
|
|
||||||
// Establish the bidirectional communication, by creating a new UI backend and registering
|
// Establish the bidirectional communication, by creating a new UI backend and registering
|
||||||
// it with the UI.
|
// it with the UI.
|
||||||
|
|
@ -640,68 +638,124 @@ func testExternalUI(api *core.SignerAPI) {
|
||||||
ctx := context.WithValue(context.Background(), "remote", "clef binary")
|
ctx := context.WithValue(context.Background(), "remote", "clef binary")
|
||||||
ctx = context.WithValue(ctx, "scheme", "in-proc")
|
ctx = context.WithValue(ctx, "scheme", "in-proc")
|
||||||
ctx = context.WithValue(ctx, "local", "main")
|
ctx = context.WithValue(ctx, "local", "main")
|
||||||
|
|
||||||
errs := make([]string, 0)
|
errs := make([]string, 0)
|
||||||
|
|
||||||
api.UI.ShowInfo("Testing 'ShowInfo'")
|
a := common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef")
|
||||||
api.UI.ShowError("Testing 'ShowError'")
|
|
||||||
|
|
||||||
checkErr := func(method string, err error) {
|
queryUser := func(q string) string {
|
||||||
if err != nil && err != core.ErrRequestDenied {
|
resp, err := api.UI.OnInputRequired(core.UserInputRequest{
|
||||||
errs = append(errs, fmt.Sprintf("%v: %v", method, err.Error()))
|
Title: "Testing",
|
||||||
|
Prompt: q,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err.Error())
|
||||||
|
}
|
||||||
|
return resp.Text
|
||||||
|
}
|
||||||
|
expectResponse := func(testcase, question, expect string) {
|
||||||
|
if got := queryUser(question); got != expect {
|
||||||
|
errs = append(errs, fmt.Sprintf("%s: got %v, expected %v", testcase, got, expect))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var err error
|
expectApprove := func(testcase string, err error) {
|
||||||
|
if err == nil || err == accounts.ErrUnknownAccount {
|
||||||
cliqueHeader := types.Header{
|
return
|
||||||
common.HexToHash("0000H45H"),
|
|
||||||
common.HexToHash("0000H45H"),
|
|
||||||
common.HexToAddress("0000H45H"),
|
|
||||||
common.HexToHash("0000H00H"),
|
|
||||||
common.HexToHash("0000H45H"),
|
|
||||||
common.HexToHash("0000H45H"),
|
|
||||||
types.Bloom{},
|
|
||||||
big.NewInt(1337),
|
|
||||||
big.NewInt(1337),
|
|
||||||
1338,
|
|
||||||
1338,
|
|
||||||
big.NewInt(1338),
|
|
||||||
[]byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"),
|
|
||||||
common.HexToHash("0x0000H45H"),
|
|
||||||
types.BlockNonce{},
|
|
||||||
}
|
|
||||||
cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader)
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Should not error: %v", err)
|
|
||||||
}
|
|
||||||
addr, err := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Should not error: %v", err)
|
|
||||||
}
|
|
||||||
_, err = api.SignData(ctx, "application/clique", *addr, cliqueRlp)
|
|
||||||
checkErr("SignData", err)
|
|
||||||
|
|
||||||
_, err = api.SignTransaction(ctx, core.SendTxArgs{From: common.MixedcaseAddress{}}, nil)
|
|
||||||
checkErr("SignTransaction", err)
|
|
||||||
_, err = api.SignData(ctx, "text/plain", common.MixedcaseAddress{}, common.Hex2Bytes("01020304"))
|
|
||||||
checkErr("SignData", err)
|
|
||||||
//_, err = api.SignTypedData(ctx, common.MixedcaseAddress{}, core.TypedData{})
|
|
||||||
//checkErr("SignTypedData", err)
|
|
||||||
_, err = api.List(ctx)
|
|
||||||
checkErr("List", err)
|
|
||||||
_, err = api.New(ctx)
|
|
||||||
checkErr("New", err)
|
|
||||||
|
|
||||||
api.UI.ShowInfo("Tests completed")
|
|
||||||
|
|
||||||
if len(errs) > 0 {
|
|
||||||
log.Error("Got errors")
|
|
||||||
for _, e := range errs {
|
|
||||||
log.Error(e)
|
|
||||||
}
|
}
|
||||||
} else {
|
errs = append(errs, fmt.Sprintf("%v: expected no error, got %v", testcase, err.Error()))
|
||||||
log.Info("No errors")
|
|
||||||
}
|
}
|
||||||
|
expectDeny := func(testcase string, err error) {
|
||||||
|
if err == nil || err != core.ErrRequestDenied {
|
||||||
|
errs = append(errs, fmt.Sprintf("%v: expected ErrRequestDenied, got %v", testcase, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test display of info and error
|
||||||
|
{
|
||||||
|
api.UI.ShowInfo("If you see this message, enter 'yes' to next question")
|
||||||
|
expectResponse("showinfo", "Did you see the message? [yes/no]", "yes")
|
||||||
|
api.UI.ShowError("If you see this message, enter 'yes' to the next question")
|
||||||
|
expectResponse("showerror", "Did you see the message? [yes/no]", "yes")
|
||||||
|
}
|
||||||
|
{ // Sign data test - clique header
|
||||||
|
api.UI.ShowInfo("Please approve the next request for signing a clique header")
|
||||||
|
cliqueHeader := types.Header{
|
||||||
|
common.HexToHash("0000H45H"),
|
||||||
|
common.HexToHash("0000H45H"),
|
||||||
|
common.HexToAddress("0000H45H"),
|
||||||
|
common.HexToHash("0000H00H"),
|
||||||
|
common.HexToHash("0000H45H"),
|
||||||
|
common.HexToHash("0000H45H"),
|
||||||
|
types.Bloom{},
|
||||||
|
big.NewInt(1337),
|
||||||
|
big.NewInt(1337),
|
||||||
|
1338,
|
||||||
|
1338,
|
||||||
|
big.NewInt(1338),
|
||||||
|
[]byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"),
|
||||||
|
common.HexToHash("0x0000H45H"),
|
||||||
|
types.BlockNonce{},
|
||||||
|
}
|
||||||
|
cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Should not error: %v", err)
|
||||||
|
}
|
||||||
|
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
|
||||||
|
_, err = api.SignData(ctx, accounts.MimetypeClique, *addr, hexutil.Encode(cliqueRlp))
|
||||||
|
expectApprove("signdata - clique header", err)
|
||||||
|
}
|
||||||
|
{ // Sign data test - plain text
|
||||||
|
api.UI.ShowInfo("Please approve the next request for signing text")
|
||||||
|
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
|
||||||
|
_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
|
||||||
|
expectApprove("signdata - text", err)
|
||||||
|
}
|
||||||
|
{ // Sign data test - plain text reject
|
||||||
|
api.UI.ShowInfo("Please deny the next request for signing text")
|
||||||
|
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
|
||||||
|
_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
|
||||||
|
expectDeny("signdata - text", err)
|
||||||
|
}
|
||||||
|
{ // Sign transaction
|
||||||
|
|
||||||
|
api.UI.ShowInfo("Please reject next transaction")
|
||||||
|
data := hexutil.Bytes([]byte{})
|
||||||
|
to := common.NewMixedcaseAddress(a)
|
||||||
|
tx := core.SendTxArgs{
|
||||||
|
Data: &data,
|
||||||
|
Nonce: 0x1,
|
||||||
|
Value: hexutil.Big(*big.NewInt(6)),
|
||||||
|
From: common.NewMixedcaseAddress(a),
|
||||||
|
To: &to,
|
||||||
|
GasPrice: hexutil.Big(*big.NewInt(5)),
|
||||||
|
Gas: 1000,
|
||||||
|
Input: nil,
|
||||||
|
}
|
||||||
|
_, err := api.SignTransaction(ctx, tx, nil)
|
||||||
|
expectDeny("signtransaction [1]", err)
|
||||||
|
expectResponse("signtransaction [2]", "Did you see any warnings for the last transaction? (yes/no)", "no")
|
||||||
|
}
|
||||||
|
{ // Listing
|
||||||
|
api.UI.ShowInfo("Please reject listing-request")
|
||||||
|
_, err := api.List(ctx)
|
||||||
|
expectDeny("list", err)
|
||||||
|
}
|
||||||
|
{ // Import
|
||||||
|
api.UI.ShowInfo("Please reject new account-request")
|
||||||
|
_, err := api.New(ctx)
|
||||||
|
expectDeny("newaccount", err)
|
||||||
|
}
|
||||||
|
{ // Metadata
|
||||||
|
api.UI.ShowInfo("Please check if you see the Origin in next listing (approve or deny)")
|
||||||
|
api.List(context.WithValue(ctx, "Origin", "origin.com"))
|
||||||
|
expectResponse("metadata - origin", "Did you see origin (origin.com)? [yes/no] ", "yes")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
log.Error(e)
|
||||||
|
}
|
||||||
|
result := fmt.Sprintf("Tests completed. %d errors:\n%s\n", len(errs), strings.Join(errs, "\n"))
|
||||||
|
api.UI.ShowInfo(result)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPassPhrase retrieves the password associated with clef, either fetched
|
// getPassPhrase retrieves the password associated with clef, either fetched
|
||||||
|
|
@ -798,7 +852,7 @@ func GenDoc(ctx *cli.Context) {
|
||||||
}
|
}
|
||||||
{ // Sign plain text response
|
{ // Sign plain text response
|
||||||
add("SignDataResponse - approve", "Response to SignDataRequest",
|
add("SignDataResponse - approve", "Response to SignDataRequest",
|
||||||
&core.SignDataResponse{Password: "apassword", Approved: true})
|
&core.SignDataResponse{Approved: true})
|
||||||
add("SignDataResponse - deny", "Response to SignDataRequest",
|
add("SignDataResponse - deny", "Response to SignDataRequest",
|
||||||
&core.SignDataResponse{})
|
&core.SignDataResponse{})
|
||||||
}
|
}
|
||||||
|
|
@ -833,9 +887,9 @@ func GenDoc(ctx *cli.Context) {
|
||||||
}
|
}
|
||||||
{ // Sign tx response
|
{ // Sign tx response
|
||||||
data := hexutil.Bytes([]byte{0x04, 0x03, 0x02, 0x01})
|
data := hexutil.Bytes([]byte{0x04, 0x03, 0x02, 0x01})
|
||||||
add("SignDataResponse - approve", "Response to SignDataRequest. This response needs to contain the `transaction`"+
|
add("SignTxResponse - approve", "Response to request to sign a transaction. This response needs to contain the `transaction`"+
|
||||||
", because the UI is free to make modifications to the transaction.",
|
", because the UI is free to make modifications to the transaction.",
|
||||||
&core.SignTxResponse{Password: "apassword", Approved: true,
|
&core.SignTxResponse{Approved: true,
|
||||||
Transaction: core.SendTxArgs{
|
Transaction: core.SendTxArgs{
|
||||||
Data: &data,
|
Data: &data,
|
||||||
Nonce: 0x4,
|
Nonce: 0x4,
|
||||||
|
|
@ -846,9 +900,9 @@ func GenDoc(ctx *cli.Context) {
|
||||||
Gas: 1000,
|
Gas: 1000,
|
||||||
Input: nil,
|
Input: nil,
|
||||||
}})
|
}})
|
||||||
add("SignDataResponse - deny", "Response to SignDataRequest. When denying a request, there's no need to "+
|
add("SignTxResponse - deny", "Response to SignTxRequest. When denying a request, there's no need to "+
|
||||||
"provide the transaction in return",
|
"provide the transaction in return",
|
||||||
&core.SignDataResponse{})
|
&core.SignTxResponse{})
|
||||||
}
|
}
|
||||||
{ // WHen a signed tx is ready to go out
|
{ // WHen a signed tx is ready to go out
|
||||||
desc := "SignTransactionResult is used in the call `clef` -> `OnApprovedTx(result)`" +
|
desc := "SignTransactionResult is used in the call `clef` -> `OnApprovedTx(result)`" +
|
||||||
|
|
@ -874,7 +928,7 @@ func GenDoc(ctx *cli.Context) {
|
||||||
{ // User input
|
{ // User input
|
||||||
add("UserInputRequest", "Sent when clef needs the user to provide data. If 'password' is true, the input field should be treated accordingly (echo-free)",
|
add("UserInputRequest", "Sent when clef needs the user to provide data. If 'password' is true, the input field should be treated accordingly (echo-free)",
|
||||||
&core.UserInputRequest{IsPassword: true, Title: "The title here", Prompt: "The question to ask the user"})
|
&core.UserInputRequest{IsPassword: true, Title: "The title here", Prompt: "The question to ask the user"})
|
||||||
add("UserInputResponse", "Response to SignDataRequest",
|
add("UserInputResponse", "Response to UserInputRequest",
|
||||||
&core.UserInputResponse{Text: "The textual response from user"})
|
&core.UserInputResponse{Text: "The textual response from user"})
|
||||||
}
|
}
|
||||||
{ // List request
|
{ // List request
|
||||||
|
|
@ -888,7 +942,7 @@ func GenDoc(ctx *cli.Context) {
|
||||||
{b, accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}},
|
{b, accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}},
|
||||||
})
|
})
|
||||||
|
|
||||||
add("UserInputResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+
|
add("ListResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+
|
||||||
"Note: the UI is free to respond with any address the caller, regardless of whether it exists or not",
|
"Note: the UI is free to respond with any address the caller, regardless of whether it exists or not",
|
||||||
&core.ListResponse{
|
&core.ListResponse{
|
||||||
Accounts: []accounts.Account{
|
Accounts: []accounts.Account{
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import (
|
||||||
|
|
||||||
func Compile(fn string, src []byte, debug bool) (string, error) {
|
func Compile(fn string, src []byte, debug bool) (string, error) {
|
||||||
compiler := asm.NewCompiler(debug)
|
compiler := asm.NewCompiler(debug)
|
||||||
compiler.Feed(asm.Lex(fn, src, debug))
|
compiler.Feed(asm.Lex(src, debug))
|
||||||
|
|
||||||
bin, compileErrors := compiler.Compile()
|
bin, compileErrors := compiler.Compile()
|
||||||
if len(compileErrors) > 0 {
|
if len(compileErrors) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,10 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
cli "gopkg.in/urfave/cli.v1"
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
|
@ -99,12 +99,12 @@ func runCmd(ctx *cli.Context) error {
|
||||||
if ctx.GlobalString(GenesisFlag.Name) != "" {
|
if ctx.GlobalString(GenesisFlag.Name) != "" {
|
||||||
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
|
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
|
||||||
genesisConfig = gen
|
genesisConfig = gen
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
genesis := gen.ToBlock(db)
|
genesis := gen.ToBlock(db)
|
||||||
statedb, _ = state.New(genesis.Root(), state.NewDatabase(db))
|
statedb, _ = state.New(genesis.Root(), state.NewDatabase(db))
|
||||||
chainConfig = gen.Config
|
chainConfig = gen.Config
|
||||||
} else {
|
} else {
|
||||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
genesisConfig = new(core.Genesis)
|
genesisConfig = new(core.Genesis)
|
||||||
}
|
}
|
||||||
if ctx.GlobalString(SenderFlag.Name) != "" {
|
if ctx.GlobalString(SenderFlag.Name) != "" {
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,12 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/console"
|
"github.com/ethereum/go-ethereum/console"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/util"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -193,7 +191,7 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
for _, name := range []string{"chaindata", "lightchaindata"} {
|
for _, name := range []string{"chaindata", "lightchaindata"} {
|
||||||
chaindb, err := stack.OpenDatabase(name, 0, 0)
|
chaindb, err := stack.OpenDatabase(name, 0, 0, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to open database: %v", err)
|
utils.Fatalf("Failed to open database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -201,6 +199,7 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to write genesis block: %v", err)
|
utils.Fatalf("Failed to write genesis block: %v", err)
|
||||||
}
|
}
|
||||||
|
chaindb.Close()
|
||||||
log.Info("Successfully wrote genesis state", "database", name, "hash", hash)
|
log.Info("Successfully wrote genesis state", "database", name, "hash", hash)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -213,8 +212,8 @@ func importChain(ctx *cli.Context) error {
|
||||||
stack := makeFullNode(ctx)
|
stack := makeFullNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
chain, chainDb := utils.MakeChain(ctx, stack)
|
chain, db := utils.MakeChain(ctx, stack)
|
||||||
defer chainDb.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Start periodically gathering memory profiles
|
// Start periodically gathering memory profiles
|
||||||
var peakMemAlloc, peakMemSys uint64
|
var peakMemAlloc, peakMemSys uint64
|
||||||
|
|
@ -249,23 +248,18 @@ func importChain(ctx *cli.Context) error {
|
||||||
fmt.Printf("Import done in %v.\n\n", time.Since(start))
|
fmt.Printf("Import done in %v.\n\n", time.Since(start))
|
||||||
|
|
||||||
// Output pre-compaction stats mostly to see the import trashing
|
// Output pre-compaction stats mostly to see the import trashing
|
||||||
db := chainDb.(*ethdb.LDBDatabase)
|
stats, err := db.Stat("leveldb.stats")
|
||||||
|
|
||||||
stats, err := db.LDB().GetProperty("leveldb.stats")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read database stats: %v", err)
|
utils.Fatalf("Failed to read database stats: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println(stats)
|
fmt.Println(stats)
|
||||||
|
|
||||||
ioStats, err := db.LDB().GetProperty("leveldb.iostats")
|
ioStats, err := db.Stat("leveldb.iostats")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read database iostats: %v", err)
|
utils.Fatalf("Failed to read database iostats: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println(ioStats)
|
fmt.Println(ioStats)
|
||||||
|
|
||||||
fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses())
|
|
||||||
fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads())
|
|
||||||
|
|
||||||
// Print the memory statistics used by the importing
|
// Print the memory statistics used by the importing
|
||||||
mem := new(runtime.MemStats)
|
mem := new(runtime.MemStats)
|
||||||
runtime.ReadMemStats(mem)
|
runtime.ReadMemStats(mem)
|
||||||
|
|
@ -282,23 +276,22 @@ func importChain(ctx *cli.Context) error {
|
||||||
// Compact the entire database to more accurately measure disk io and print the stats
|
// Compact the entire database to more accurately measure disk io and print the stats
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
fmt.Println("Compacting entire database...")
|
fmt.Println("Compacting entire database...")
|
||||||
if err = db.LDB().CompactRange(util.Range{}); err != nil {
|
if err = db.Compact(nil, nil); err != nil {
|
||||||
utils.Fatalf("Compaction failed: %v", err)
|
utils.Fatalf("Compaction failed: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("Compaction done in %v.\n\n", time.Since(start))
|
fmt.Printf("Compaction done in %v.\n\n", time.Since(start))
|
||||||
|
|
||||||
stats, err = db.LDB().GetProperty("leveldb.stats")
|
stats, err = db.Stat("leveldb.stats")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read database stats: %v", err)
|
utils.Fatalf("Failed to read database stats: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println(stats)
|
fmt.Println(stats)
|
||||||
|
|
||||||
ioStats, err = db.LDB().GetProperty("leveldb.iostats")
|
ioStats, err = db.Stat("leveldb.iostats")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read database iostats: %v", err)
|
utils.Fatalf("Failed to read database iostats: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println(ioStats)
|
fmt.Println(ioStats)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -344,10 +337,10 @@ func importPreimages(ctx *cli.Context) error {
|
||||||
stack := makeFullNode(ctx)
|
stack := makeFullNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase)
|
db := utils.MakeChainDatabase(ctx, stack)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
if err := utils.ImportPreimages(diskdb, ctx.Args().First()); err != nil {
|
if err := utils.ImportPreimages(db, ctx.Args().First()); err != nil {
|
||||||
utils.Fatalf("Import error: %v\n", err)
|
utils.Fatalf("Import error: %v\n", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("Import done in %v\n", time.Since(start))
|
fmt.Printf("Import done in %v\n", time.Since(start))
|
||||||
|
|
@ -362,10 +355,10 @@ func exportPreimages(ctx *cli.Context) error {
|
||||||
stack := makeFullNode(ctx)
|
stack := makeFullNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase)
|
db := utils.MakeChainDatabase(ctx, stack)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
if err := utils.ExportPreimages(diskdb, ctx.Args().First()); err != nil {
|
if err := utils.ExportPreimages(db, ctx.Args().First()); err != nil {
|
||||||
utils.Fatalf("Export error: %v\n", err)
|
utils.Fatalf("Export error: %v\n", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("Export done in %v\n", time.Since(start))
|
fmt.Printf("Export done in %v\n", time.Since(start))
|
||||||
|
|
@ -386,7 +379,7 @@ func copyDb(ctx *cli.Context) error {
|
||||||
dl := downloader.New(syncmode, chainDb, new(event.TypeMux), chain, nil, nil)
|
dl := downloader.New(syncmode, chainDb, new(event.TypeMux), chain, nil, nil)
|
||||||
|
|
||||||
// Create a source peer to satisfy downloader requests from
|
// Create a source peer to satisfy downloader requests from
|
||||||
db, err := ethdb.NewLDBDatabase(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name), 256)
|
db, err := rawdb.NewLevelDBDatabase(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name), 256, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -413,11 +406,10 @@ func copyDb(ctx *cli.Context) error {
|
||||||
// Compact the entire database to remove any sync overhead
|
// Compact the entire database to remove any sync overhead
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
fmt.Println("Compacting entire database...")
|
fmt.Println("Compacting entire database...")
|
||||||
if err = chainDb.(*ethdb.LDBDatabase).LDB().CompactRange(util.Range{}); err != nil {
|
if err = db.Compact(nil, nil); err != nil {
|
||||||
utils.Fatalf("Compaction failed: %v", err)
|
utils.Fatalf("Compaction failed: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("Compaction done in %v.\n\n", time.Since(start))
|
fmt.Printf("Compaction done in %v.\n\n", time.Since(start))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -121,7 +120,7 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc
|
||||||
}
|
}
|
||||||
// Retrieve the DAO config flag from the database
|
// Retrieve the DAO config flag from the database
|
||||||
path := filepath.Join(datadir, "geth", "chaindata")
|
path := filepath.Join(datadir, "geth", "chaindata")
|
||||||
db, err := ethdb.NewLDBDatabase(path, 0, 0)
|
db, err := rawdb.NewLevelDBDatabase(path, 0, 0, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %d: failed to open test database: %v", test, err)
|
t.Fatalf("test %d: failed to open test database: %v", test, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,6 @@ var (
|
||||||
utils.CacheDatabaseFlag,
|
utils.CacheDatabaseFlag,
|
||||||
utils.CacheTrieFlag,
|
utils.CacheTrieFlag,
|
||||||
utils.CacheGCFlag,
|
utils.CacheGCFlag,
|
||||||
utils.TrieCacheGenFlag,
|
|
||||||
utils.ListenPortFlag,
|
utils.ListenPortFlag,
|
||||||
utils.MaxPeersFlag,
|
utils.MaxPeersFlag,
|
||||||
utils.MaxPendingPeersFlag,
|
utils.MaxPendingPeersFlag,
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,6 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.CacheDatabaseFlag,
|
utils.CacheDatabaseFlag,
|
||||||
utils.CacheTrieFlag,
|
utils.CacheTrieFlag,
|
||||||
utils.CacheGCFlag,
|
utils.CacheGCFlag,
|
||||||
utils.TrieCacheGenFlag,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -255,8 +255,8 @@ func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Con
|
||||||
currentConfig.LocalStoreParams.DbCapacity = storeCapacity
|
currentConfig.LocalStoreParams.DbCapacity = storeCapacity
|
||||||
}
|
}
|
||||||
|
|
||||||
if storeCacheCapacity := ctx.GlobalUint(SwarmStoreCacheCapacity.Name); storeCacheCapacity != 0 {
|
if ctx.GlobalIsSet(SwarmStoreCacheCapacity.Name) {
|
||||||
currentConfig.LocalStoreParams.CacheCapacity = storeCacheCapacity
|
currentConfig.LocalStoreParams.CacheCapacity = ctx.GlobalUint(SwarmStoreCacheCapacity.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.GlobalIsSet(SwarmBootnodeModeFlag.Name) {
|
if ctx.GlobalIsSet(SwarmBootnodeModeFlag.Name) {
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,18 @@ func TestMain(m *testing.M) {
|
||||||
func runSwarm(t *testing.T, args ...string) *cmdtest.TestCmd {
|
func runSwarm(t *testing.T, args ...string) *cmdtest.TestCmd {
|
||||||
tt := cmdtest.NewTestCmd(t, nil)
|
tt := cmdtest.NewTestCmd(t, nil)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, v := range args {
|
||||||
|
if v == "--bootnodes" {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
args = append([]string{"--bootnodes", ""}, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// Boot "swarm". This actually runs the test binary but the TestMain
|
// Boot "swarm". This actually runs the test binary but the TestMain
|
||||||
// function will prevent any tests from running.
|
// function will prevent any tests from running.
|
||||||
tt.Run("swarm-test", args...)
|
tt.Run("swarm-test", args...)
|
||||||
|
|
@ -252,6 +264,7 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
|
||||||
|
|
||||||
// start the node
|
// start the node
|
||||||
node.Cmd = runSwarm(t,
|
node.Cmd = runSwarm(t,
|
||||||
|
"--bootnodes", "",
|
||||||
"--port", p2pPort,
|
"--port", p2pPort,
|
||||||
"--nat", "extip:127.0.0.1",
|
"--nat", "extip:127.0.0.1",
|
||||||
"--datadir", dir,
|
"--datadir", dir,
|
||||||
|
|
@ -327,6 +340,7 @@ func newTestNode(t *testing.T, dir string) *testNode {
|
||||||
|
|
||||||
// start the node
|
// start the node
|
||||||
node.Cmd = runSwarm(t,
|
node.Cmd = runSwarm(t,
|
||||||
|
"--bootnodes", "",
|
||||||
"--port", p2pPort,
|
"--port", p2pPort,
|
||||||
"--nat", "extip:127.0.0.1",
|
"--nat", "extip:127.0.0.1",
|
||||||
"--datadir", dir,
|
"--datadir", dir,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -67,41 +68,50 @@ func uploadAndSyncCmd(ctx *cli.Context, tuid string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func trackChunks(testData []byte) error {
|
func trackChunks(testData []byte) error {
|
||||||
log.Warn("Test timed out; running chunk debug sequence")
|
log.Warn("Test timed out, running chunk debug sequence")
|
||||||
|
|
||||||
addrs, err := getAllRefs(testData)
|
addrs, err := getAllRefs(testData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Trace("All references retrieved")
|
|
||||||
|
|
||||||
// has-chunks
|
for i, ref := range addrs {
|
||||||
|
log.Trace(fmt.Sprintf("ref %d", i), "ref", ref)
|
||||||
|
}
|
||||||
|
|
||||||
for _, host := range hosts {
|
for _, host := range hosts {
|
||||||
httpHost := fmt.Sprintf("ws://%s:%d", host, 8546)
|
httpHost := fmt.Sprintf("ws://%s:%d", host, 8546)
|
||||||
log.Trace("Calling `Has` on host", "httpHost", httpHost)
|
|
||||||
|
hostChunks := []string{}
|
||||||
|
|
||||||
rpcClient, err := rpc.Dial(httpHost)
|
rpcClient, err := rpc.Dial(httpHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Trace("Error dialing host", "err", err)
|
log.Error("Error dialing host", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Trace("rpc dial ok")
|
|
||||||
var hasInfo []api.HasInfo
|
var hasInfo []api.HasInfo
|
||||||
err = rpcClient.Call(&hasInfo, "bzz_has", addrs)
|
err = rpcClient.Call(&hasInfo, "bzz_has", addrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Trace("Error calling host", "err", err)
|
log.Error("Error calling host", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Trace("rpc call ok")
|
|
||||||
count := 0
|
count := 0
|
||||||
for _, info := range hasInfo {
|
for _, info := range hasInfo {
|
||||||
if !info.Has {
|
if info.Has {
|
||||||
|
hostChunks = append(hostChunks, "1")
|
||||||
|
} else {
|
||||||
|
hostChunks = append(hostChunks, "0")
|
||||||
count++
|
count++
|
||||||
log.Error("Host does not have chunk", "host", httpHost, "chunk", info.Addr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
log.Info("Host reported to have all chunks", "host", httpHost)
|
log.Info("host reported to have all chunks", "host", host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Trace("chunks", "chunks", strings.Join(hostChunks, ""), "host", host)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportPreimages imports a batch of exported hash preimages into the database.
|
// ImportPreimages imports a batch of exported hash preimages into the database.
|
||||||
func ImportPreimages(db *ethdb.LDBDatabase, fn string) error {
|
func ImportPreimages(db ethdb.Database, fn string) error {
|
||||||
log.Info("Importing preimages", "file", fn)
|
log.Info("Importing preimages", "file", fn)
|
||||||
|
|
||||||
// Open the file handle and potentially unwrap the gzip stream
|
// Open the file handle and potentially unwrap the gzip stream
|
||||||
|
|
@ -285,7 +285,7 @@ func ImportPreimages(db *ethdb.LDBDatabase, fn string) error {
|
||||||
|
|
||||||
// ExportPreimages exports all known hash preimages into the specified file,
|
// ExportPreimages exports all known hash preimages into the specified file,
|
||||||
// truncating any data already present in the file.
|
// truncating any data already present in the file.
|
||||||
func ExportPreimages(db *ethdb.LDBDatabase, fn string) error {
|
func ExportPreimages(db ethdb.Database, fn string) error {
|
||||||
log.Info("Exporting preimages", "file", fn)
|
log.Info("Exporting preimages", "file", fn)
|
||||||
|
|
||||||
// Open the file handle and potentially wrap with a gzip stream
|
// Open the file handle and potentially wrap with a gzip stream
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/clique"
|
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/dashboard"
|
"github.com/ethereum/go-ethereum/dashboard"
|
||||||
|
|
@ -350,11 +349,6 @@ var (
|
||||||
Usage: "Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode)",
|
Usage: "Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode)",
|
||||||
Value: 25,
|
Value: 25,
|
||||||
}
|
}
|
||||||
TrieCacheGenFlag = cli.IntFlag{
|
|
||||||
Name: "trie-cache-gens",
|
|
||||||
Usage: "Number of trie node generations to keep in memory",
|
|
||||||
Value: int(state.MaxTrieCacheGen),
|
|
||||||
}
|
|
||||||
// Miner settings
|
// Miner settings
|
||||||
MiningEnabledFlag = cli.BoolFlag{
|
MiningEnabledFlag = cli.BoolFlag{
|
||||||
Name: "mine",
|
Name: "mine",
|
||||||
|
|
@ -779,6 +773,7 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
node, err := enode.ParseV4(url)
|
node, err := enode.ParseV4(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
|
log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
cfg.BootstrapNodes = append(cfg.BootstrapNodes, node)
|
cfg.BootstrapNodes = append(cfg.BootstrapNodes, node)
|
||||||
}
|
}
|
||||||
|
|
@ -806,12 +801,14 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
|
|
||||||
cfg.BootstrapNodesV5 = make([]*discv5.Node, 0, len(urls))
|
cfg.BootstrapNodesV5 = make([]*discv5.Node, 0, len(urls))
|
||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
node, err := discv5.ParseNode(url)
|
if url != "" {
|
||||||
if err != nil {
|
node, err := discv5.ParseNode(url)
|
||||||
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
|
if err != nil {
|
||||||
continue
|
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cfg.BootstrapNodesV5 = append(cfg.BootstrapNodesV5, node)
|
||||||
}
|
}
|
||||||
cfg.BootstrapNodesV5 = append(cfg.BootstrapNodesV5, node)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1432,10 +1429,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||||
cfg.MinerGasPrice = big.NewInt(1)
|
cfg.MinerGasPrice = big.NewInt(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO(fjl): move trie cache generations into config
|
|
||||||
if gen := ctx.GlobalInt(TrieCacheGenFlag.Name); gen > 0 {
|
|
||||||
state.MaxTrieCacheGen = uint16(gen)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDashboardConfig applies dashboard related command line flags to the config.
|
// SetDashboardConfig applies dashboard related command line flags to the config.
|
||||||
|
|
@ -1548,7 +1541,7 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||||
if ctx.GlobalString(SyncModeFlag.Name) == "light" {
|
if ctx.GlobalString(SyncModeFlag.Name) == "light" {
|
||||||
name = "lightchaindata"
|
name = "lightchaindata"
|
||||||
}
|
}
|
||||||
chainDb, err := stack.OpenDatabase(name, cache, handles)
|
chainDb, err := stack.OpenDatabase(name, cache, handles, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Could not open database: %v", err)
|
Fatalf("Could not open database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -202,9 +202,6 @@ func IsHexAddress(s string) bool {
|
||||||
// Bytes gets the string representation of the underlying address.
|
// Bytes gets the string representation of the underlying address.
|
||||||
func (a Address) Bytes() []byte { return a[:] }
|
func (a Address) Bytes() []byte { return a[:] }
|
||||||
|
|
||||||
// Big converts an address to a big integer.
|
|
||||||
func (a Address) Big() *big.Int { return new(big.Int).SetBytes(a[:]) }
|
|
||||||
|
|
||||||
// Hash converts an address to a hash by left-padding it with zeros.
|
// Hash converts an address to a hash by left-padding it with zeros.
|
||||||
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,8 +114,8 @@ func TestAddressUnmarshalJSON(t *testing.T) {
|
||||||
if test.ShouldErr {
|
if test.ShouldErr {
|
||||||
t.Errorf("test #%d: expected error, got none", i)
|
t.Errorf("test #%d: expected error, got none", i)
|
||||||
}
|
}
|
||||||
if v.Big().Cmp(test.Output) != 0 {
|
if got := new(big.Int).SetBytes(v.Bytes()); got.Cmp(test.Output) != 0 {
|
||||||
t.Errorf("test #%d: address mismatch: have %v, want %v", i, v.Big(), test.Output)
|
t.Errorf("test #%d: address mismatch: have %v, want %v", i, got, test.Output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,10 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -400,7 +400,7 @@ func TestClique(t *testing.T) {
|
||||||
copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:])
|
copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:])
|
||||||
}
|
}
|
||||||
// Create a pristine blockchain with the genesis injected
|
// Create a pristine blockchain with the genesis injected
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
genesis.Commit(db)
|
genesis.Commit(db)
|
||||||
|
|
||||||
// Assemble a chain of headers from the cast votes
|
// Assemble a chain of headers from the cast votes
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func lexAll(src string) []token {
|
func lexAll(src string) []token {
|
||||||
ch := Lex("test.asm", []byte(src), false)
|
ch := Lex([]byte(src), false)
|
||||||
|
|
||||||
var tokens []token
|
var tokens []token
|
||||||
for i := range ch {
|
for i := range ch {
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ type lexer struct {
|
||||||
|
|
||||||
// lex lexes the program by name with the given source. It returns a
|
// lex lexes the program by name with the given source. It returns a
|
||||||
// channel on which the tokens are delivered.
|
// channel on which the tokens are delivered.
|
||||||
func Lex(name string, source []byte, debug bool) <-chan token {
|
func Lex(source []byte, debug bool) <-chan token {
|
||||||
ch := make(chan token)
|
ch := make(chan token)
|
||||||
l := &lexer{
|
l := &lexer{
|
||||||
input: string(source),
|
input: string(source),
|
||||||
|
|
|
||||||
|
|
@ -150,14 +150,14 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||||
// Create the database in memory or in a temporary directory.
|
// Create the database in memory or in a temporary directory.
|
||||||
var db ethdb.Database
|
var db ethdb.Database
|
||||||
if !disk {
|
if !disk {
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
} else {
|
} else {
|
||||||
dir, err := ioutil.TempDir("", "eth-core-bench")
|
dir, err := ioutil.TempDir("", "eth-core-bench")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("cannot create temporary directory: %v", err)
|
b.Fatalf("cannot create temporary directory: %v", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
db, err = ethdb.NewLDBDatabase(dir, 128, 128)
|
db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("cannot create temporary database: %v", err)
|
b.Fatalf("cannot create temporary database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -255,7 +255,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("cannot create temporary directory: %v", err)
|
b.Fatalf("cannot create temporary directory: %v", err)
|
||||||
}
|
}
|
||||||
db, err := ethdb.NewLDBDatabase(dir, 128, 1024)
|
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +272,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
db, err := ethdb.NewLDBDatabase(dir, 128, 1024)
|
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +283,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
db, err := ethdb.NewLDBDatabase(dir, 128, 1024)
|
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||||
// transition, such as amount of used gas, the receipt roots and the state root
|
// transition, such as amount of used gas, the receipt roots and the state root
|
||||||
// itself. ValidateState returns a database batch if the validation was a success
|
// itself. ValidateState returns a database batch if the validation was a success
|
||||||
// otherwise nil and an error is returned.
|
// otherwise nil and an error is returned.
|
||||||
func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
|
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
if block.GasUsed() != usedGas {
|
if block.GasUsed() != usedGas {
|
||||||
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
|
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ import (
|
||||||
func TestHeaderVerification(t *testing.T) {
|
func TestHeaderVerification(t *testing.T) {
|
||||||
// Create a simple chain to verify
|
// Create a simple chain to verify
|
||||||
var (
|
var (
|
||||||
testdb = ethdb.NewMemDatabase()
|
testdb = rawdb.NewMemoryDatabase()
|
||||||
gspec = &Genesis{Config: params.TestChainConfig}
|
gspec = &Genesis{Config: params.TestChainConfig}
|
||||||
genesis = gspec.MustCommit(testdb)
|
genesis = gspec.MustCommit(testdb)
|
||||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
||||||
|
|
@ -84,7 +84,7 @@ func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVeri
|
||||||
func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
||||||
// Create a simple chain to verify
|
// Create a simple chain to verify
|
||||||
var (
|
var (
|
||||||
testdb = ethdb.NewMemDatabase()
|
testdb = rawdb.NewMemoryDatabase()
|
||||||
gspec = &Genesis{Config: params.TestChainConfig}
|
gspec = &Genesis{Config: params.TestChainConfig}
|
||||||
genesis = gspec.MustCommit(testdb)
|
genesis = gspec.MustCommit(testdb)
|
||||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
||||||
|
|
@ -156,7 +156,7 @@ func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion
|
||||||
func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||||
// Create a simple chain to verify
|
// Create a simple chain to verify
|
||||||
var (
|
var (
|
||||||
testdb = ethdb.NewMemDatabase()
|
testdb = rawdb.NewMemoryDatabase()
|
||||||
gspec = &Genesis{Config: params.TestChainConfig}
|
gspec = &Genesis{Config: params.TestChainConfig}
|
||||||
genesis = gspec.MustCommit(testdb)
|
genesis = gspec.MustCommit(testdb)
|
||||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil)
|
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil)
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -291,7 +291,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
// Rewind the header chain, deleting all block bodies until then
|
// Rewind the header chain, deleting all block bodies until then
|
||||||
delFn := func(db rawdb.DatabaseDeleter, hash common.Hash, num uint64) {
|
delFn := func(db ethdb.Deleter, hash common.Hash, num uint64) {
|
||||||
rawdb.DeleteBody(db, hash, num)
|
rawdb.DeleteBody(db, hash, num)
|
||||||
}
|
}
|
||||||
bc.hc.SetHead(head, delFn)
|
bc.hc.SetHead(head, delFn)
|
||||||
|
|
@ -342,7 +342,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return fmt.Errorf("non existent block [%x…]", hash[:4])
|
return fmt.Errorf("non existent block [%x…]", hash[:4])
|
||||||
}
|
}
|
||||||
if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB(), 0); err != nil {
|
if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// If all checks out, manually set the head block
|
// If all checks out, manually set the head block
|
||||||
|
|
@ -1221,9 +1221,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
|
|
||||||
parent := it.previous()
|
parent := it.previous()
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||||
}
|
}
|
||||||
state, err := state.New(parent.Root(), bc.stateCache)
|
state, err := state.New(parent.Root, bc.stateCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, events, coalescedLogs, err
|
||||||
}
|
}
|
||||||
|
|
@ -1236,7 +1236,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, events, coalescedLogs, err
|
||||||
}
|
}
|
||||||
// Validate the state using the default validator
|
// Validate the state using the default validator
|
||||||
if err := bc.Validator().ValidateState(block, parent, state, receipts, usedGas); err != nil {
|
if err := bc.Validator().ValidateState(block, state, receipts, usedGas); err != nil {
|
||||||
bc.reportBlock(block, receipts, err)
|
bc.reportBlock(block, receipts, err)
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, events, coalescedLogs, err
|
||||||
}
|
}
|
||||||
|
|
@ -1368,7 +1368,7 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i
|
||||||
// blocks to regenerate the required state
|
// blocks to regenerate the required state
|
||||||
localTd := bc.GetTd(current.Hash(), current.NumberU64())
|
localTd := bc.GetTd(current.Hash(), current.NumberU64())
|
||||||
if localTd.Cmp(externTd) > 0 {
|
if localTd.Cmp(externTd) > 0 {
|
||||||
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().NumberU64(), "sidetd", externTd, "localtd", localTd)
|
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
|
||||||
return it.index, nil, nil, err
|
return it.index, nil, nil, err
|
||||||
}
|
}
|
||||||
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
||||||
|
|
@ -1376,7 +1376,7 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i
|
||||||
hashes []common.Hash
|
hashes []common.Hash
|
||||||
numbers []uint64
|
numbers []uint64
|
||||||
)
|
)
|
||||||
parent := bc.GetHeader(it.previous().Hash(), it.previous().NumberU64())
|
parent := it.previous()
|
||||||
for parent != nil && !bc.HasState(parent.Root) {
|
for parent != nil && !bc.HasState(parent.Root) {
|
||||||
hashes = append(hashes, parent.Hash())
|
hashes = append(hashes, parent.Hash())
|
||||||
numbers = append(numbers, parent.Number.Uint64())
|
numbers = append(numbers, parent.Number.Uint64())
|
||||||
|
|
|
||||||
|
|
@ -111,12 +111,12 @@ func (it *insertIterator) next() (*types.Block, error) {
|
||||||
return it.chain[it.index], it.validator.ValidateBody(it.chain[it.index])
|
return it.chain[it.index], it.validator.ValidateBody(it.chain[it.index])
|
||||||
}
|
}
|
||||||
|
|
||||||
// previous returns the previous block was being processed, or nil
|
// previous returns the previous header that was being processed, or nil.
|
||||||
func (it *insertIterator) previous() *types.Block {
|
func (it *insertIterator) previous() *types.Header {
|
||||||
if it.index < 1 {
|
if it.index < 1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return it.chain[it.index-1]
|
return it.chain[it.index-1].Header()
|
||||||
}
|
}
|
||||||
|
|
||||||
// first returns the first block in the it.
|
// first returns the first block in the it.
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ var (
|
||||||
// header only chain.
|
// header only chain.
|
||||||
func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *BlockChain, error) {
|
func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *BlockChain, error) {
|
||||||
var (
|
var (
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
genesis = new(Genesis).MustCommit(db)
|
genesis = new(Genesis).MustCommit(db)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -149,7 +149,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||||
blockchain.reportBlock(block, receipts, err)
|
blockchain.reportBlock(block, receipts, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = blockchain.validator.ValidateState(block, blockchain.GetBlockByHash(block.ParentHash()), statedb, receipts, usedGas)
|
err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
blockchain.reportBlock(block, receipts, err)
|
blockchain.reportBlock(block, receipts, err)
|
||||||
return err
|
return err
|
||||||
|
|
@ -586,7 +586,7 @@ func testInsertNonceError(t *testing.T, full bool) {
|
||||||
func TestFastVsFullChains(t *testing.T) {
|
func TestFastVsFullChains(t *testing.T) {
|
||||||
// Configure and generate a sample block chain
|
// Configure and generate a sample block chain
|
||||||
var (
|
var (
|
||||||
gendb = ethdb.NewMemDatabase()
|
gendb = rawdb.NewMemoryDatabase()
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000)
|
||||||
|
|
@ -616,7 +616,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Import the chain as an archive node for the comparison baseline
|
// Import the chain as an archive node for the comparison baseline
|
||||||
archiveDb := ethdb.NewMemDatabase()
|
archiveDb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(archiveDb)
|
gspec.MustCommit(archiveDb)
|
||||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer archive.Stop()
|
defer archive.Stop()
|
||||||
|
|
@ -625,7 +625,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||||
t.Fatalf("failed to process block %d: %v", n, err)
|
t.Fatalf("failed to process block %d: %v", n, err)
|
||||||
}
|
}
|
||||||
// Fast import the chain as a non-archive node to test
|
// Fast import the chain as a non-archive node to test
|
||||||
fastDb := ethdb.NewMemDatabase()
|
fastDb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(fastDb)
|
gspec.MustCommit(fastDb)
|
||||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer fast.Stop()
|
defer fast.Stop()
|
||||||
|
|
@ -674,7 +674,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||||
func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
// Configure and generate a sample block chain
|
// Configure and generate a sample block chain
|
||||||
var (
|
var (
|
||||||
gendb = ethdb.NewMemDatabase()
|
gendb = rawdb.NewMemoryDatabase()
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000)
|
||||||
|
|
@ -702,7 +702,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Import the chain as an archive node and ensure all pointers are updated
|
// Import the chain as an archive node and ensure all pointers are updated
|
||||||
archiveDb := ethdb.NewMemDatabase()
|
archiveDb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(archiveDb)
|
gspec.MustCommit(archiveDb)
|
||||||
|
|
||||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
|
|
@ -716,7 +716,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
assert(t, "archive", archive, height/2, height/2, height/2)
|
assert(t, "archive", archive, height/2, height/2, height/2)
|
||||||
|
|
||||||
// Import the chain as a non-archive node and ensure all pointers are updated
|
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||||
fastDb := ethdb.NewMemDatabase()
|
fastDb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(fastDb)
|
gspec.MustCommit(fastDb)
|
||||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer fast.Stop()
|
defer fast.Stop()
|
||||||
|
|
@ -736,7 +736,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
assert(t, "fast", fast, height/2, height/2, 0)
|
assert(t, "fast", fast, height/2, height/2, 0)
|
||||||
|
|
||||||
// Import the chain as a light node and ensure all pointers are updated
|
// Import the chain as a light node and ensure all pointers are updated
|
||||||
lightDb := ethdb.NewMemDatabase()
|
lightDb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(lightDb)
|
gspec.MustCommit(lightDb)
|
||||||
|
|
||||||
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
|
|
@ -759,7 +759,7 @@ func TestChainTxReorgs(t *testing.T) {
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
GasLimit: 3141592,
|
GasLimit: 3141592,
|
||||||
|
|
@ -871,7 +871,7 @@ func TestLogReorgs(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
// this code generates a log
|
// this code generates a log
|
||||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||||
|
|
@ -915,7 +915,7 @@ func TestLogReorgs(t *testing.T) {
|
||||||
|
|
||||||
func TestReorgSideEvent(t *testing.T) {
|
func TestReorgSideEvent(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
|
|
@ -1043,7 +1043,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||||
func TestEIP155Transition(t *testing.T) {
|
func TestEIP155Transition(t *testing.T) {
|
||||||
// Configure and generate a sample block chain
|
// Configure and generate a sample block chain
|
||||||
var (
|
var (
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000)
|
||||||
|
|
@ -1146,7 +1146,7 @@ func TestEIP155Transition(t *testing.T) {
|
||||||
func TestEIP161AccountRemoval(t *testing.T) {
|
func TestEIP161AccountRemoval(t *testing.T) {
|
||||||
// Configure and generate a sample block chain
|
// Configure and generate a sample block chain
|
||||||
var (
|
var (
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000)
|
||||||
|
|
@ -1218,7 +1218,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||||
// Generate a canonical chain to act as the main dataset
|
// Generate a canonical chain to act as the main dataset
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
|
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
genesis := new(Genesis).MustCommit(db)
|
genesis := new(Genesis).MustCommit(db)
|
||||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||||
|
|
||||||
|
|
@ -1234,7 +1234,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Import the canonical and fork chain side by side, verifying the current block
|
// Import the canonical and fork chain side by side, verifying the current block
|
||||||
// and current header consistency
|
// and current header consistency
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
new(Genesis).MustCommit(diskdb)
|
new(Genesis).MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||||
|
|
@ -1263,7 +1263,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||||
// Generate a canonical chain to act as the main dataset
|
// Generate a canonical chain to act as the main dataset
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
|
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
genesis := new(Genesis).MustCommit(db)
|
genesis := new(Genesis).MustCommit(db)
|
||||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||||
|
|
||||||
|
|
@ -1278,7 +1278,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||||
forks[i] = fork[0]
|
forks[i] = fork[0]
|
||||||
}
|
}
|
||||||
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
|
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
new(Genesis).MustCommit(diskdb)
|
new(Genesis).MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||||
|
|
@ -1309,7 +1309,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
||||||
// Generate the original common chain segment and the two competing forks
|
// Generate the original common chain segment and the two competing forks
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
|
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
genesis := new(Genesis).MustCommit(db)
|
genesis := new(Genesis).MustCommit(db)
|
||||||
|
|
||||||
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||||
|
|
@ -1317,7 +1317,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
||||||
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
||||||
|
|
||||||
// Import the shared chain and the original canonical one
|
// Import the shared chain and the original canonical one
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
new(Genesis).MustCommit(diskdb)
|
new(Genesis).MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||||
|
|
@ -1377,7 +1377,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||||
)
|
)
|
||||||
// Generate the original common chain segment and the two competing forks
|
// Generate the original common chain segment and the two competing forks
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
genesis := gspec.MustCommit(db)
|
genesis := gspec.MustCommit(db)
|
||||||
|
|
||||||
blockGenerator := func(i int, block *BlockGen) {
|
blockGenerator := func(i int, block *BlockGen) {
|
||||||
|
|
@ -1399,7 +1399,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
// Import the shared chain and the original canonical one
|
// Import the shared chain and the original canonical one
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(diskdb)
|
gspec.MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||||
|
|
@ -1477,7 +1477,7 @@ func BenchmarkBlockChain_1x1000Executions(b *testing.B) {
|
||||||
func TestLowDiffLongChain(t *testing.T) {
|
func TestLowDiffLongChain(t *testing.T) {
|
||||||
// Generate a canonical chain to act as the main dataset
|
// Generate a canonical chain to act as the main dataset
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
genesis := new(Genesis).MustCommit(db)
|
genesis := new(Genesis).MustCommit(db)
|
||||||
|
|
||||||
// We must use a pretty long chain to ensure that the fork doesn't overtake us
|
// We must use a pretty long chain to ensure that the fork doesn't overtake us
|
||||||
|
|
@ -1488,7 +1488,7 @@ func TestLowDiffLongChain(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Import the canonical chain
|
// Import the canonical chain
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
new(Genesis).MustCommit(diskdb)
|
new(Genesis).MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||||
|
|
@ -1531,12 +1531,12 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
|
|
||||||
// Generate a canonical chain to act as the main dataset
|
// Generate a canonical chain to act as the main dataset
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
genesis := new(Genesis).MustCommit(db)
|
genesis := new(Genesis).MustCommit(db)
|
||||||
|
|
||||||
// Generate and import the canonical chain
|
// Generate and import the canonical chain
|
||||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, nil)
|
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, nil)
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
new(Genesis).MustCommit(diskdb)
|
new(Genesis).MustCommit(diskdb)
|
||||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ type ChainIndexer struct {
|
||||||
// NewChainIndexer creates a new chain indexer to do background processing on
|
// NewChainIndexer creates a new chain indexer to do background processing on
|
||||||
// chain segments of a given size after certain number of confirmations passed.
|
// chain segments of a given size after certain number of confirmations passed.
|
||||||
// The throttling parameter might be used to prevent database thrashing.
|
// The throttling parameter might be used to prevent database thrashing.
|
||||||
func NewChainIndexer(chainDb, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer {
|
func NewChainIndexer(chainDb ethdb.Database, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer {
|
||||||
c := &ChainIndexer{
|
c := &ChainIndexer{
|
||||||
chainDb: chainDb,
|
chainDb: chainDb,
|
||||||
indexDb: indexDb,
|
indexDb: indexDb,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Runs multiple tests with randomized parameters.
|
// Runs multiple tests with randomized parameters.
|
||||||
|
|
@ -49,7 +48,7 @@ func TestChainIndexerWithChildren(t *testing.T) {
|
||||||
// multiple backends. The section size and required confirmation count parameters
|
// multiple backends. The section size and required confirmation count parameters
|
||||||
// are randomized.
|
// are randomized.
|
||||||
func testChainIndexer(t *testing.T, count int) {
|
func testChainIndexer(t *testing.T, count int) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Create a chain of indexers and ensure they all report empty
|
// Create a chain of indexers and ensure they all report empty
|
||||||
|
|
@ -60,7 +59,7 @@ func testChainIndexer(t *testing.T, count int) {
|
||||||
confirmsReq = uint64(rand.Intn(10))
|
confirmsReq = uint64(rand.Intn(10))
|
||||||
)
|
)
|
||||||
backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)}
|
backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)}
|
||||||
backends[i].indexer = NewChainIndexer(db, ethdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i))
|
backends[i].indexer = NewChainIndexer(db, rawdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i))
|
||||||
|
|
||||||
if sections, _, _ := backends[i].indexer.Sections(); sections != 0 {
|
if sections, _, _ := backends[i].indexer.Sections(); sections != 0 {
|
||||||
t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0)
|
t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0)
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ func ExampleGenerateChain() {
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure that key1 has some funds in the genesis block.
|
// Ensure that key1 has some funds in the genesis block.
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -32,13 +32,13 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
forkBlock := big.NewInt(32)
|
forkBlock := big.NewInt(32)
|
||||||
|
|
||||||
// Generate a common prefix for both pro-forkers and non-forkers
|
// Generate a common prefix for both pro-forkers and non-forkers
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
gspec := new(Genesis)
|
gspec := new(Genesis)
|
||||||
genesis := gspec.MustCommit(db)
|
genesis := gspec.MustCommit(db)
|
||||||
prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
||||||
|
|
||||||
// Create the concurrent, conflicting two nodes
|
// Create the concurrent, conflicting two nodes
|
||||||
proDb := ethdb.NewMemDatabase()
|
proDb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(proDb)
|
gspec.MustCommit(proDb)
|
||||||
|
|
||||||
proConf := *params.TestChainConfig
|
proConf := *params.TestChainConfig
|
||||||
|
|
@ -48,7 +48,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
|
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer proBc.Stop()
|
defer proBc.Stop()
|
||||||
|
|
||||||
conDb := ethdb.NewMemDatabase()
|
conDb := rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(conDb)
|
gspec.MustCommit(conDb)
|
||||||
|
|
||||||
conConf := *params.TestChainConfig
|
conConf := *params.TestChainConfig
|
||||||
|
|
@ -67,7 +67,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
// Try to expand both pro-fork and non-fork chains iteratively with other camp's blocks
|
// Try to expand both pro-fork and non-fork chains iteratively with other camp's blocks
|
||||||
for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ {
|
for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ {
|
||||||
// Create a pro-fork block, and try to feed into the no-fork chain
|
// Create a pro-fork block, and try to feed into the no-fork chain
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(db)
|
gspec.MustCommit(db)
|
||||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
|
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer bc.Stop()
|
defer bc.Stop()
|
||||||
|
|
@ -92,7 +92,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err)
|
t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err)
|
||||||
}
|
}
|
||||||
// Create a no-fork block, and try to feed into the pro-fork chain
|
// Create a no-fork block, and try to feed into the pro-fork chain
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(db)
|
gspec.MustCommit(db)
|
||||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
|
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer bc.Stop()
|
defer bc.Stop()
|
||||||
|
|
@ -118,7 +118,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(db)
|
gspec.MustCommit(db)
|
||||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
|
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer bc.Stop()
|
defer bc.Stop()
|
||||||
|
|
@ -138,7 +138,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err)
|
t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err)
|
||||||
}
|
}
|
||||||
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec.MustCommit(db)
|
gspec.MustCommit(db)
|
||||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
|
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer bc.Stop()
|
defer bc.Stop()
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ type GenesisMismatchError struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *GenesisMismatchError) Error() string {
|
func (e *GenesisMismatchError) Error() string {
|
||||||
return fmt.Sprintf("database already contains an incompatible genesis block (have %x, new %x)", e.Stored[:8], e.New[:8])
|
return fmt.Sprintf("database contains incompatible genesis (have %x, new %x)", e.Stored, e.New)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupGenesisBlock writes or updates the genesis block in db.
|
// SetupGenesisBlock writes or updates the genesis block in db.
|
||||||
|
|
@ -228,7 +228,7 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||||
// to the given database (or discards it if nil).
|
// to the given database (or discards it if nil).
|
||||||
func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||||
if db == nil {
|
if db == nil {
|
||||||
db = ethdb.NewMemDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
}
|
}
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||||
for addr, account := range g.Alloc {
|
for addr, account := range g.Alloc {
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ func TestSetupGenesis(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
config, hash, err := test.fn(db)
|
config, hash, err := test.fn(db)
|
||||||
// Check the return values.
|
// Check the return values.
|
||||||
if !reflect.DeepEqual(err, test.wantErr) {
|
if !reflect.DeepEqual(err, test.wantErr) {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -455,7 +455,7 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
|
||||||
|
|
||||||
// DeleteCallback is a callback function that is called by SetHead before
|
// DeleteCallback is a callback function that is called by SetHead before
|
||||||
// each header is deleted.
|
// each header is deleted.
|
||||||
type DeleteCallback func(rawdb.DatabaseDeleter, common.Hash, uint64)
|
type DeleteCallback func(ethdb.Deleter, common.Hash, uint64)
|
||||||
|
|
||||||
// SetHead rewinds the local chain to a new head. Everything above the new head
|
// SetHead rewinds the local chain to a new head. Everything above the new head
|
||||||
// will be deleted and the new one set.
|
// will be deleted and the new one set.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package core
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
|
@ -78,7 +79,7 @@ func (tm *TestManager) Db() ethdb.Database {
|
||||||
func NewTestManager() *TestManager {
|
func NewTestManager() *TestManager {
|
||||||
testManager := &TestManager{}
|
testManager := &TestManager{}
|
||||||
testManager.eventMux = new(event.TypeMux)
|
testManager.eventMux = new(event.TypeMux)
|
||||||
testManager.db = ethdb.NewMemDatabase()
|
testManager.db = rawdb.NewMemoryDatabase()
|
||||||
// testManager.txPool = NewTxPool(testManager)
|
// testManager.txPool = NewTxPool(testManager)
|
||||||
// testManager.blockChain = NewBlockChain(testManager)
|
// testManager.blockChain = NewBlockChain(testManager)
|
||||||
// testManager.stateManager = NewStateManager(testManager)
|
// testManager.stateManager = NewStateManager(testManager)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ func makelist(g *core.Genesis) allocList {
|
||||||
if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 {
|
if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 {
|
||||||
panic(fmt.Sprintf("can't encode account %x", addr))
|
panic(fmt.Sprintf("can't encode account %x", addr))
|
||||||
}
|
}
|
||||||
a = append(a, allocItem{addr.Big(), account.Balance})
|
bigAddr := new(big.Int).SetBytes(addr.Bytes())
|
||||||
|
a = append(a, allocItem{bigAddr, account.Balance})
|
||||||
}
|
}
|
||||||
sort.Sort(a)
|
sort.Sort(a)
|
||||||
return a
|
return a
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,13 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
|
// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
|
||||||
func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash {
|
func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash {
|
||||||
data, _ := db.Get(headerHashKey(number))
|
data, _ := db.Get(headerHashKey(number))
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
|
|
@ -37,21 +38,21 @@ func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteCanonicalHash stores the hash assigned to a canonical block number.
|
// WriteCanonicalHash stores the hash assigned to a canonical block number.
|
||||||
func WriteCanonicalHash(db DatabaseWriter, hash common.Hash, number uint64) {
|
func WriteCanonicalHash(db ethdb.Writer, hash common.Hash, number uint64) {
|
||||||
if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil {
|
if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil {
|
||||||
log.Crit("Failed to store number to hash mapping", "err", err)
|
log.Crit("Failed to store number to hash mapping", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCanonicalHash removes the number to hash canonical mapping.
|
// DeleteCanonicalHash removes the number to hash canonical mapping.
|
||||||
func DeleteCanonicalHash(db DatabaseDeleter, number uint64) {
|
func DeleteCanonicalHash(db ethdb.Deleter, number uint64) {
|
||||||
if err := db.Delete(headerHashKey(number)); err != nil {
|
if err := db.Delete(headerHashKey(number)); err != nil {
|
||||||
log.Crit("Failed to delete number to hash mapping", "err", err)
|
log.Crit("Failed to delete number to hash mapping", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadHeaderNumber returns the header number assigned to a hash.
|
// ReadHeaderNumber returns the header number assigned to a hash.
|
||||||
func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 {
|
func ReadHeaderNumber(db ethdb.Reader, hash common.Hash) *uint64 {
|
||||||
data, _ := db.Get(headerNumberKey(hash))
|
data, _ := db.Get(headerNumberKey(hash))
|
||||||
if len(data) != 8 {
|
if len(data) != 8 {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -61,7 +62,7 @@ func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadHeadHeaderHash retrieves the hash of the current canonical head header.
|
// ReadHeadHeaderHash retrieves the hash of the current canonical head header.
|
||||||
func ReadHeadHeaderHash(db DatabaseReader) common.Hash {
|
func ReadHeadHeaderHash(db ethdb.Reader) common.Hash {
|
||||||
data, _ := db.Get(headHeaderKey)
|
data, _ := db.Get(headHeaderKey)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
|
|
@ -70,14 +71,14 @@ func ReadHeadHeaderHash(db DatabaseReader) common.Hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteHeadHeaderHash stores the hash of the current canonical head header.
|
// WriteHeadHeaderHash stores the hash of the current canonical head header.
|
||||||
func WriteHeadHeaderHash(db DatabaseWriter, hash common.Hash) {
|
func WriteHeadHeaderHash(db ethdb.Writer, hash common.Hash) {
|
||||||
if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
|
if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
|
||||||
log.Crit("Failed to store last header's hash", "err", err)
|
log.Crit("Failed to store last header's hash", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadHeadBlockHash retrieves the hash of the current canonical head block.
|
// ReadHeadBlockHash retrieves the hash of the current canonical head block.
|
||||||
func ReadHeadBlockHash(db DatabaseReader) common.Hash {
|
func ReadHeadBlockHash(db ethdb.Reader) common.Hash {
|
||||||
data, _ := db.Get(headBlockKey)
|
data, _ := db.Get(headBlockKey)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
|
|
@ -86,14 +87,14 @@ func ReadHeadBlockHash(db DatabaseReader) common.Hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteHeadBlockHash stores the head block's hash.
|
// WriteHeadBlockHash stores the head block's hash.
|
||||||
func WriteHeadBlockHash(db DatabaseWriter, hash common.Hash) {
|
func WriteHeadBlockHash(db ethdb.Writer, hash common.Hash) {
|
||||||
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
|
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
|
||||||
log.Crit("Failed to store last block's hash", "err", err)
|
log.Crit("Failed to store last block's hash", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block.
|
// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block.
|
||||||
func ReadHeadFastBlockHash(db DatabaseReader) common.Hash {
|
func ReadHeadFastBlockHash(db ethdb.Reader) common.Hash {
|
||||||
data, _ := db.Get(headFastBlockKey)
|
data, _ := db.Get(headFastBlockKey)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
|
|
@ -102,7 +103,7 @@ func ReadHeadFastBlockHash(db DatabaseReader) common.Hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteHeadFastBlockHash stores the hash of the current fast-sync head block.
|
// WriteHeadFastBlockHash stores the hash of the current fast-sync head block.
|
||||||
func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) {
|
func WriteHeadFastBlockHash(db ethdb.Writer, hash common.Hash) {
|
||||||
if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil {
|
if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil {
|
||||||
log.Crit("Failed to store last fast block's hash", "err", err)
|
log.Crit("Failed to store last fast block's hash", "err", err)
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +111,7 @@ func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) {
|
||||||
|
|
||||||
// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow
|
// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow
|
||||||
// reporting correct numbers across restarts.
|
// reporting correct numbers across restarts.
|
||||||
func ReadFastTrieProgress(db DatabaseReader) uint64 {
|
func ReadFastTrieProgress(db ethdb.Reader) uint64 {
|
||||||
data, _ := db.Get(fastTrieProgressKey)
|
data, _ := db.Get(fastTrieProgressKey)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -120,20 +121,20 @@ func ReadFastTrieProgress(db DatabaseReader) uint64 {
|
||||||
|
|
||||||
// WriteFastTrieProgress stores the fast sync trie process counter to support
|
// WriteFastTrieProgress stores the fast sync trie process counter to support
|
||||||
// retrieving it across restarts.
|
// retrieving it across restarts.
|
||||||
func WriteFastTrieProgress(db DatabaseWriter, count uint64) {
|
func WriteFastTrieProgress(db ethdb.Writer, count uint64) {
|
||||||
if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
|
if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
|
||||||
log.Crit("Failed to store fast sync trie progress", "err", err)
|
log.Crit("Failed to store fast sync trie progress", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
||||||
func ReadHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
|
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||||
data, _ := db.Get(headerKey(number, hash))
|
data, _ := db.Get(headerKey(number, hash))
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasHeader verifies the existence of a block header corresponding to the hash.
|
// HasHeader verifies the existence of a block header corresponding to the hash.
|
||||||
func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool {
|
func HasHeader(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||||
if has, err := db.Has(headerKey(number, hash)); !has || err != nil {
|
if has, err := db.Has(headerKey(number, hash)); !has || err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +142,7 @@ func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadHeader retrieves the block header corresponding to the hash.
|
// ReadHeader retrieves the block header corresponding to the hash.
|
||||||
func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Header {
|
func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header {
|
||||||
data := ReadHeaderRLP(db, hash, number)
|
data := ReadHeaderRLP(db, hash, number)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -156,7 +157,7 @@ func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Heade
|
||||||
|
|
||||||
// WriteHeader stores a block header into the database and also stores the hash-
|
// WriteHeader stores a block header into the database and also stores the hash-
|
||||||
// to-number mapping.
|
// to-number mapping.
|
||||||
func WriteHeader(db DatabaseWriter, header *types.Header) {
|
func WriteHeader(db ethdb.Writer, header *types.Header) {
|
||||||
// Write the hash -> number mapping
|
// Write the hash -> number mapping
|
||||||
var (
|
var (
|
||||||
hash = header.Hash()
|
hash = header.Hash()
|
||||||
|
|
@ -179,30 +180,36 @@ func WriteHeader(db DatabaseWriter, header *types.Header) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHeader removes all block header data associated with a hash.
|
// DeleteHeader removes all block header data associated with a hash.
|
||||||
func DeleteHeader(db DatabaseDeleter, hash common.Hash, number uint64) {
|
func DeleteHeader(db ethdb.Deleter, hash common.Hash, number uint64) {
|
||||||
if err := db.Delete(headerKey(number, hash)); err != nil {
|
deleteHeaderWithoutNumber(db, hash, number)
|
||||||
log.Crit("Failed to delete header", "err", err)
|
|
||||||
}
|
|
||||||
if err := db.Delete(headerNumberKey(hash)); err != nil {
|
if err := db.Delete(headerNumberKey(hash)); err != nil {
|
||||||
log.Crit("Failed to delete hash to number mapping", "err", err)
|
log.Crit("Failed to delete hash to number mapping", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deleteHeaderWithoutNumber removes only the block header but does not remove
|
||||||
|
// the hash to number mapping.
|
||||||
|
func deleteHeaderWithoutNumber(db ethdb.Deleter, hash common.Hash, number uint64) {
|
||||||
|
if err := db.Delete(headerKey(number, hash)); err != nil {
|
||||||
|
log.Crit("Failed to delete header", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
|
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
|
||||||
func ReadBodyRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
|
func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||||
data, _ := db.Get(blockBodyKey(number, hash))
|
data, _ := db.Get(blockBodyKey(number, hash))
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteBodyRLP stores an RLP encoded block body into the database.
|
// WriteBodyRLP stores an RLP encoded block body into the database.
|
||||||
func WriteBodyRLP(db DatabaseWriter, hash common.Hash, number uint64, rlp rlp.RawValue) {
|
func WriteBodyRLP(db ethdb.Writer, hash common.Hash, number uint64, rlp rlp.RawValue) {
|
||||||
if err := db.Put(blockBodyKey(number, hash), rlp); err != nil {
|
if err := db.Put(blockBodyKey(number, hash), rlp); err != nil {
|
||||||
log.Crit("Failed to store block body", "err", err)
|
log.Crit("Failed to store block body", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasBody verifies the existence of a block body corresponding to the hash.
|
// HasBody verifies the existence of a block body corresponding to the hash.
|
||||||
func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool {
|
func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||||
if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
|
if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -210,7 +217,7 @@ func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadBody retrieves the block body corresponding to the hash.
|
// ReadBody retrieves the block body corresponding to the hash.
|
||||||
func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body {
|
func ReadBody(db ethdb.Reader, hash common.Hash, number uint64) *types.Body {
|
||||||
data := ReadBodyRLP(db, hash, number)
|
data := ReadBodyRLP(db, hash, number)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -224,7 +231,7 @@ func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteBody storea a block body into the database.
|
// WriteBody storea a block body into the database.
|
||||||
func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.Body) {
|
func WriteBody(db ethdb.Writer, hash common.Hash, number uint64, body *types.Body) {
|
||||||
data, err := rlp.EncodeToBytes(body)
|
data, err := rlp.EncodeToBytes(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("Failed to RLP encode body", "err", err)
|
log.Crit("Failed to RLP encode body", "err", err)
|
||||||
|
|
@ -233,15 +240,21 @@ func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.B
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteBody removes all block body data associated with a hash.
|
// DeleteBody removes all block body data associated with a hash.
|
||||||
func DeleteBody(db DatabaseDeleter, hash common.Hash, number uint64) {
|
func DeleteBody(db ethdb.Deleter, hash common.Hash, number uint64) {
|
||||||
if err := db.Delete(blockBodyKey(number, hash)); err != nil {
|
if err := db.Delete(blockBodyKey(number, hash)); err != nil {
|
||||||
log.Crit("Failed to delete block body", "err", err)
|
log.Crit("Failed to delete block body", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadTd retrieves a block's total difficulty corresponding to the hash.
|
// ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding.
|
||||||
func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int {
|
func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||||
data, _ := db.Get(headerTDKey(number, hash))
|
data, _ := db.Get(headerTDKey(number, hash))
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadTd retrieves a block's total difficulty corresponding to the hash.
|
||||||
|
func ReadTd(db ethdb.Reader, hash common.Hash, number uint64) *big.Int {
|
||||||
|
data := ReadTdRLP(db, hash, number)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -254,7 +267,7 @@ func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteTd stores the total difficulty of a block into the database.
|
// WriteTd stores the total difficulty of a block into the database.
|
||||||
func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) {
|
func WriteTd(db ethdb.Writer, hash common.Hash, number uint64, td *big.Int) {
|
||||||
data, err := rlp.EncodeToBytes(td)
|
data, err := rlp.EncodeToBytes(td)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("Failed to RLP encode block total difficulty", "err", err)
|
log.Crit("Failed to RLP encode block total difficulty", "err", err)
|
||||||
|
|
@ -265,7 +278,7 @@ func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTd removes all block total difficulty data associated with a hash.
|
// DeleteTd removes all block total difficulty data associated with a hash.
|
||||||
func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) {
|
func DeleteTd(db ethdb.Deleter, hash common.Hash, number uint64) {
|
||||||
if err := db.Delete(headerTDKey(number, hash)); err != nil {
|
if err := db.Delete(headerTDKey(number, hash)); err != nil {
|
||||||
log.Crit("Failed to delete block total difficulty", "err", err)
|
log.Crit("Failed to delete block total difficulty", "err", err)
|
||||||
}
|
}
|
||||||
|
|
@ -273,17 +286,23 @@ func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||||
|
|
||||||
// HasReceipts verifies the existence of all the transaction receipts belonging
|
// HasReceipts verifies the existence of all the transaction receipts belonging
|
||||||
// to a block.
|
// to a block.
|
||||||
func HasReceipts(db DatabaseReader, hash common.Hash, number uint64) bool {
|
func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||||
if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil {
|
if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadReceipts retrieves all the transaction receipts belonging to a block.
|
// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
|
||||||
func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Receipts {
|
func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||||
// Retrieve the flattened receipt slice
|
|
||||||
data, _ := db.Get(blockReceiptsKey(number, hash))
|
data, _ := db.Get(blockReceiptsKey(number, hash))
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadReceipts retrieves all the transaction receipts belonging to a block.
|
||||||
|
func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts {
|
||||||
|
// Retrieve the flattened receipt slice
|
||||||
|
data := ReadReceiptsRLP(db, hash, number)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -311,7 +330,7 @@ func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Rece
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteReceipts stores all the transaction receipts belonging to a block.
|
// WriteReceipts stores all the transaction receipts belonging to a block.
|
||||||
func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts types.Receipts) {
|
func WriteReceipts(db ethdb.Writer, hash common.Hash, number uint64, receipts types.Receipts) {
|
||||||
// Convert the receipts into their storage form and serialize them
|
// Convert the receipts into their storage form and serialize them
|
||||||
storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
|
storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
|
||||||
for i, receipt := range receipts {
|
for i, receipt := range receipts {
|
||||||
|
|
@ -328,7 +347,7 @@ func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteReceipts removes all receipt data associated with a block hash.
|
// DeleteReceipts removes all receipt data associated with a block hash.
|
||||||
func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) {
|
func DeleteReceipts(db ethdb.Deleter, hash common.Hash, number uint64) {
|
||||||
if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
|
if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
|
||||||
log.Crit("Failed to delete block receipts", "err", err)
|
log.Crit("Failed to delete block receipts", "err", err)
|
||||||
}
|
}
|
||||||
|
|
@ -340,7 +359,7 @@ func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||||
//
|
//
|
||||||
// Note, due to concurrent download of header and block body the header and thus
|
// Note, due to concurrent download of header and block body the header and thus
|
||||||
// canonical hash can be stored in the database but the body data not (yet).
|
// canonical hash can be stored in the database but the body data not (yet).
|
||||||
func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block {
|
func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
|
||||||
header := ReadHeader(db, hash, number)
|
header := ReadHeader(db, hash, number)
|
||||||
if header == nil {
|
if header == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -353,21 +372,30 @@ func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteBlock serializes a block into the database, header and body separately.
|
// WriteBlock serializes a block into the database, header and body separately.
|
||||||
func WriteBlock(db DatabaseWriter, block *types.Block) {
|
func WriteBlock(db ethdb.Writer, block *types.Block) {
|
||||||
WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
|
WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
|
||||||
WriteHeader(db, block.Header())
|
WriteHeader(db, block.Header())
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteBlock removes all block data associated with a hash.
|
// DeleteBlock removes all block data associated with a hash.
|
||||||
func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) {
|
func DeleteBlock(db ethdb.Deleter, hash common.Hash, number uint64) {
|
||||||
DeleteReceipts(db, hash, number)
|
DeleteReceipts(db, hash, number)
|
||||||
DeleteHeader(db, hash, number)
|
DeleteHeader(db, hash, number)
|
||||||
DeleteBody(db, hash, number)
|
DeleteBody(db, hash, number)
|
||||||
DeleteTd(db, hash, number)
|
DeleteTd(db, hash, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deleteBlockWithoutNumber removes all block data associated with a hash, except
|
||||||
|
// the hash to number mapping.
|
||||||
|
func deleteBlockWithoutNumber(db ethdb.Deleter, hash common.Hash, number uint64) {
|
||||||
|
DeleteReceipts(db, hash, number)
|
||||||
|
deleteHeaderWithoutNumber(db, hash, number)
|
||||||
|
DeleteBody(db, hash, number)
|
||||||
|
DeleteTd(db, hash, number)
|
||||||
|
}
|
||||||
|
|
||||||
// FindCommonAncestor returns the last common ancestor of two block headers
|
// FindCommonAncestor returns the last common ancestor of two block headers
|
||||||
func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header {
|
func FindCommonAncestor(db ethdb.Reader, a, b *types.Header) *types.Header {
|
||||||
for bn := b.Number.Uint64(); a.Number.Uint64() > bn; {
|
for bn := b.Number.Uint64(); a.Number.Uint64() > bn; {
|
||||||
a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
|
a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
|
||||||
if a == nil {
|
if a == nil {
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,13 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests block header storage and retrieval operations.
|
// Tests block header storage and retrieval operations.
|
||||||
func TestHeaderStorage(t *testing.T) {
|
func TestHeaderStorage(t *testing.T) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := NewMemoryDatabase()
|
||||||
|
|
||||||
// Create a test header to move around the database and make sure it's really new
|
// Create a test header to move around the database and make sure it's really new
|
||||||
header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
|
header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
|
||||||
|
|
@ -63,7 +62,7 @@ func TestHeaderStorage(t *testing.T) {
|
||||||
|
|
||||||
// Tests block body storage and retrieval operations.
|
// Tests block body storage and retrieval operations.
|
||||||
func TestBodyStorage(t *testing.T) {
|
func TestBodyStorage(t *testing.T) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := NewMemoryDatabase()
|
||||||
|
|
||||||
// Create a test body to move around the database and make sure it's really new
|
// Create a test body to move around the database and make sure it's really new
|
||||||
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
|
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
|
||||||
|
|
@ -101,7 +100,7 @@ func TestBodyStorage(t *testing.T) {
|
||||||
|
|
||||||
// Tests block storage and retrieval operations.
|
// Tests block storage and retrieval operations.
|
||||||
func TestBlockStorage(t *testing.T) {
|
func TestBlockStorage(t *testing.T) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := NewMemoryDatabase()
|
||||||
|
|
||||||
// Create a test block to move around the database and make sure it's really new
|
// Create a test block to move around the database and make sure it's really new
|
||||||
block := types.NewBlockWithHeader(&types.Header{
|
block := types.NewBlockWithHeader(&types.Header{
|
||||||
|
|
@ -151,7 +150,7 @@ func TestBlockStorage(t *testing.T) {
|
||||||
|
|
||||||
// Tests that partial block contents don't get reassembled into full blocks.
|
// Tests that partial block contents don't get reassembled into full blocks.
|
||||||
func TestPartialBlockStorage(t *testing.T) {
|
func TestPartialBlockStorage(t *testing.T) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := NewMemoryDatabase()
|
||||||
block := types.NewBlockWithHeader(&types.Header{
|
block := types.NewBlockWithHeader(&types.Header{
|
||||||
Extra: []byte("test block"),
|
Extra: []byte("test block"),
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
|
@ -185,7 +184,7 @@ func TestPartialBlockStorage(t *testing.T) {
|
||||||
|
|
||||||
// Tests block total difficulty storage and retrieval operations.
|
// Tests block total difficulty storage and retrieval operations.
|
||||||
func TestTdStorage(t *testing.T) {
|
func TestTdStorage(t *testing.T) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := NewMemoryDatabase()
|
||||||
|
|
||||||
// Create a test TD to move around the database and make sure it's really new
|
// Create a test TD to move around the database and make sure it's really new
|
||||||
hash, td := common.Hash{}, big.NewInt(314)
|
hash, td := common.Hash{}, big.NewInt(314)
|
||||||
|
|
@ -208,7 +207,7 @@ func TestTdStorage(t *testing.T) {
|
||||||
|
|
||||||
// Tests that canonical numbers can be mapped to hashes and retrieved.
|
// Tests that canonical numbers can be mapped to hashes and retrieved.
|
||||||
func TestCanonicalMappingStorage(t *testing.T) {
|
func TestCanonicalMappingStorage(t *testing.T) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := NewMemoryDatabase()
|
||||||
|
|
||||||
// Create a test canonical number and assinged hash to move around
|
// Create a test canonical number and assinged hash to move around
|
||||||
hash, number := common.Hash{0: 0xff}, uint64(314)
|
hash, number := common.Hash{0: 0xff}, uint64(314)
|
||||||
|
|
@ -231,7 +230,7 @@ func TestCanonicalMappingStorage(t *testing.T) {
|
||||||
|
|
||||||
// Tests that head headers and head blocks can be assigned, individually.
|
// Tests that head headers and head blocks can be assigned, individually.
|
||||||
func TestHeadStorage(t *testing.T) {
|
func TestHeadStorage(t *testing.T) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := NewMemoryDatabase()
|
||||||
|
|
||||||
blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
|
blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
|
||||||
blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
|
blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
|
||||||
|
|
@ -266,7 +265,7 @@ func TestHeadStorage(t *testing.T) {
|
||||||
|
|
||||||
// Tests that receipts associated with a single block can be stored and retrieved.
|
// Tests that receipts associated with a single block can be stored and retrieved.
|
||||||
func TestBlockReceiptStorage(t *testing.T) {
|
func TestBlockReceiptStorage(t *testing.T) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := NewMemoryDatabase()
|
||||||
|
|
||||||
receipt1 := &types.Receipt{
|
receipt1 := &types.Receipt{
|
||||||
Status: types.ReceiptStatusFailed,
|
Status: types.ReceiptStatusFailed,
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,14 @@ package rawdb
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadTxLookupEntry retrieves the positional metadata associated with a transaction
|
// ReadTxLookupEntry retrieves the positional metadata associated with a transaction
|
||||||
// hash to allow retrieving the transaction or receipt by hash.
|
// hash to allow retrieving the transaction or receipt by hash.
|
||||||
func ReadTxLookupEntry(db DatabaseReader, hash common.Hash) common.Hash {
|
func ReadTxLookupEntry(db ethdb.Reader, hash common.Hash) common.Hash {
|
||||||
data, _ := db.Get(txLookupKey(hash))
|
data, _ := db.Get(txLookupKey(hash))
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
|
|
@ -44,7 +45,7 @@ func ReadTxLookupEntry(db DatabaseReader, hash common.Hash) common.Hash {
|
||||||
|
|
||||||
// WriteTxLookupEntries stores a positional metadata for every transaction from
|
// WriteTxLookupEntries stores a positional metadata for every transaction from
|
||||||
// a block, enabling hash based transaction and receipt lookups.
|
// a block, enabling hash based transaction and receipt lookups.
|
||||||
func WriteTxLookupEntries(db DatabaseWriter, block *types.Block) {
|
func WriteTxLookupEntries(db ethdb.Writer, block *types.Block) {
|
||||||
for _, tx := range block.Transactions() {
|
for _, tx := range block.Transactions() {
|
||||||
if err := db.Put(txLookupKey(tx.Hash()), block.Hash().Bytes()); err != nil {
|
if err := db.Put(txLookupKey(tx.Hash()), block.Hash().Bytes()); err != nil {
|
||||||
log.Crit("Failed to store transaction lookup entry", "err", err)
|
log.Crit("Failed to store transaction lookup entry", "err", err)
|
||||||
|
|
@ -53,13 +54,13 @@ func WriteTxLookupEntries(db DatabaseWriter, block *types.Block) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTxLookupEntry removes all transaction data associated with a hash.
|
// DeleteTxLookupEntry removes all transaction data associated with a hash.
|
||||||
func DeleteTxLookupEntry(db DatabaseDeleter, hash common.Hash) {
|
func DeleteTxLookupEntry(db ethdb.Deleter, hash common.Hash) {
|
||||||
db.Delete(txLookupKey(hash))
|
db.Delete(txLookupKey(hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadTransaction retrieves a specific transaction from the database, along with
|
// ReadTransaction retrieves a specific transaction from the database, along with
|
||||||
// its added positional metadata.
|
// its added positional metadata.
|
||||||
func ReadTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
||||||
blockHash := ReadTxLookupEntry(db, hash)
|
blockHash := ReadTxLookupEntry(db, hash)
|
||||||
if blockHash == (common.Hash{}) {
|
if blockHash == (common.Hash{}) {
|
||||||
return nil, common.Hash{}, 0, 0
|
return nil, common.Hash{}, 0, 0
|
||||||
|
|
@ -84,7 +85,7 @@ func ReadTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, c
|
||||||
|
|
||||||
// ReadReceipt retrieves a specific transaction receipt from the database, along with
|
// ReadReceipt retrieves a specific transaction receipt from the database, along with
|
||||||
// its added positional metadata.
|
// its added positional metadata.
|
||||||
func ReadReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
|
func ReadReceipt(db ethdb.Reader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
|
||||||
blockHash := ReadTxLookupEntry(db, hash)
|
blockHash := ReadTxLookupEntry(db, hash)
|
||||||
if blockHash == (common.Hash{}) {
|
if blockHash == (common.Hash{}) {
|
||||||
return nil, common.Hash{}, 0, 0
|
return nil, common.Hash{}, 0, 0
|
||||||
|
|
@ -105,13 +106,13 @@ func ReadReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Ha
|
||||||
|
|
||||||
// ReadBloomBits retrieves the compressed bloom bit vector belonging to the given
|
// ReadBloomBits retrieves the compressed bloom bit vector belonging to the given
|
||||||
// section and bit index from the.
|
// section and bit index from the.
|
||||||
func ReadBloomBits(db DatabaseReader, bit uint, section uint64, head common.Hash) ([]byte, error) {
|
func ReadBloomBits(db ethdb.Reader, bit uint, section uint64, head common.Hash) ([]byte, error) {
|
||||||
return db.Get(bloomBitsKey(bit, section, head))
|
return db.Get(bloomBitsKey(bit, section, head))
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteBloomBits stores the compressed bloom bits vector belonging to the given
|
// WriteBloomBits stores the compressed bloom bits vector belonging to the given
|
||||||
// section and bit index.
|
// section and bit index.
|
||||||
func WriteBloomBits(db DatabaseWriter, bit uint, section uint64, head common.Hash, bits []byte) {
|
func WriteBloomBits(db ethdb.Writer, bit uint, section uint64, head common.Hash, bits []byte) {
|
||||||
if err := db.Put(bloomBitsKey(bit, section, head), bits); err != nil {
|
if err := db.Put(bloomBitsKey(bit, section, head), bits); err != nil {
|
||||||
log.Crit("Failed to store bloom bits", "err", err)
|
log.Crit("Failed to store bloom bits", "err", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,12 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests that positional lookup metadata can be stored and retrieved.
|
// Tests that positional lookup metadata can be stored and retrieved.
|
||||||
func TestLookupStorage(t *testing.T) {
|
func TestLookupStorage(t *testing.T) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := NewMemoryDatabase()
|
||||||
|
|
||||||
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
|
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
|
||||||
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
|
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,14 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadDatabaseVersion retrieves the version number of the database.
|
// ReadDatabaseVersion retrieves the version number of the database.
|
||||||
func ReadDatabaseVersion(db DatabaseReader) *uint64 {
|
func ReadDatabaseVersion(db ethdb.Reader) *uint64 {
|
||||||
var version uint64
|
var version uint64
|
||||||
|
|
||||||
enc, _ := db.Get(databaseVerisionKey)
|
enc, _ := db.Get(databaseVerisionKey)
|
||||||
|
|
@ -41,7 +42,7 @@ func ReadDatabaseVersion(db DatabaseReader) *uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteDatabaseVersion stores the version number of the database
|
// WriteDatabaseVersion stores the version number of the database
|
||||||
func WriteDatabaseVersion(db DatabaseWriter, version uint64) {
|
func WriteDatabaseVersion(db ethdb.Writer, version uint64) {
|
||||||
enc, err := rlp.EncodeToBytes(version)
|
enc, err := rlp.EncodeToBytes(version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("Failed to encode database version", "err", err)
|
log.Crit("Failed to encode database version", "err", err)
|
||||||
|
|
@ -52,7 +53,7 @@ func WriteDatabaseVersion(db DatabaseWriter, version uint64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadChainConfig retrieves the consensus settings based on the given genesis hash.
|
// ReadChainConfig retrieves the consensus settings based on the given genesis hash.
|
||||||
func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig {
|
func ReadChainConfig(db ethdb.Reader, hash common.Hash) *params.ChainConfig {
|
||||||
data, _ := db.Get(configKey(hash))
|
data, _ := db.Get(configKey(hash))
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -66,7 +67,7 @@ func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteChainConfig writes the chain config settings to the database.
|
// WriteChainConfig writes the chain config settings to the database.
|
||||||
func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConfig) {
|
func WriteChainConfig(db ethdb.Writer, hash common.Hash, cfg *params.ChainConfig) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -80,13 +81,13 @@ func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConf
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadPreimage retrieves a single preimage of the provided hash.
|
// ReadPreimage retrieves a single preimage of the provided hash.
|
||||||
func ReadPreimage(db DatabaseReader, hash common.Hash) []byte {
|
func ReadPreimage(db ethdb.Reader, hash common.Hash) []byte {
|
||||||
data, _ := db.Get(preimageKey(hash))
|
data, _ := db.Get(preimageKey(hash))
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// WritePreimages writes the provided set of preimages to the database.
|
// WritePreimages writes the provided set of preimages to the database.
|
||||||
func WritePreimages(db DatabaseWriter, preimages map[common.Hash][]byte) {
|
func WritePreimages(db ethdb.Writer, preimages map[common.Hash][]byte) {
|
||||||
for hash, preimage := range preimages {
|
for hash, preimage := range preimages {
|
||||||
if err := db.Put(preimageKey(hash), preimage); err != nil {
|
if err := db.Put(preimageKey(hash), preimage); err != nil {
|
||||||
log.Crit("Failed to store trie preimage", "err", err)
|
log.Crit("Failed to store trie preimage", "err", err)
|
||||||
|
|
|
||||||
52
core/rawdb/database.go
Normal file
52
core/rawdb/database.go
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// 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 rawdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb/leveldb"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDatabase creates a high level database on top of a given key-value data
|
||||||
|
// store without a freezer moving immutable chain segments into cold storage.
|
||||||
|
func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMemoryDatabase creates an ephemeral in-memory key-value database without a
|
||||||
|
// freezer moving immutable chain segments into cold storage.
|
||||||
|
func NewMemoryDatabase() ethdb.Database {
|
||||||
|
return NewDatabase(memorydb.New())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database with
|
||||||
|
// an initial starting capacity, but without a freezer moving immutable chain
|
||||||
|
// segments into cold storage.
|
||||||
|
func NewMemoryDatabaseWithCap(size int) ethdb.Database {
|
||||||
|
return NewDatabase(memorydb.NewWithCap(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLevelDBDatabase creates a persistent key-value database without a freezer
|
||||||
|
// moving immutable chain segments into cold storage.
|
||||||
|
func NewLevelDBDatabase(file string, cache int, handles int, namespace string) (ethdb.Database, error) {
|
||||||
|
db, err := leveldb.New(file, cache, handles, namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewDatabase(db), nil
|
||||||
|
}
|
||||||
|
|
@ -78,6 +78,11 @@ func encodeBlockNumber(number uint64) []byte {
|
||||||
return enc
|
return enc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// headerKeyPrefix = headerPrefix + num (uint64 big endian)
|
||||||
|
func headerKeyPrefix(number uint64) []byte {
|
||||||
|
return append(headerPrefix, encodeBlockNumber(number)...)
|
||||||
|
}
|
||||||
|
|
||||||
// headerKey = headerPrefix + num (uint64 big endian) + hash
|
// headerKey = headerPrefix + num (uint64 big endian) + hash
|
||||||
func headerKey(number uint64, hash common.Hash) []byte {
|
func headerKey(number uint64, hash common.Hash) []byte {
|
||||||
return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||||
|
|
|
||||||
150
core/rawdb/table.go
Normal file
150
core/rawdb/table.go
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
// 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 rawdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// table is a wrapper around a database that prefixes each key access with a pre-
|
||||||
|
// configured string.
|
||||||
|
type table struct {
|
||||||
|
db ethdb.Database
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTable returns a database object that prefixes all keys with a given string.
|
||||||
|
func NewTable(db ethdb.Database, prefix string) ethdb.Database {
|
||||||
|
return &table{
|
||||||
|
db: db,
|
||||||
|
prefix: prefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is a noop to implement the Database interface.
|
||||||
|
func (t *table) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has retrieves if a prefixed version of a key is present in the database.
|
||||||
|
func (t *table) Has(key []byte) (bool, error) {
|
||||||
|
return t.db.Has(append([]byte(t.prefix), key...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the given prefixed key if it's present in the database.
|
||||||
|
func (t *table) Get(key []byte) ([]byte, error) {
|
||||||
|
return t.db.Get(append([]byte(t.prefix), key...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put inserts the given value into the database at a prefixed version of the
|
||||||
|
// provided key.
|
||||||
|
func (t *table) Put(key []byte, value []byte) error {
|
||||||
|
return t.db.Put(append([]byte(t.prefix), key...), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes the given prefixed key from the database.
|
||||||
|
func (t *table) Delete(key []byte) error {
|
||||||
|
return t.db.Delete(append([]byte(t.prefix), key...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIterator creates a binary-alphabetical iterator over the entire keyspace
|
||||||
|
// contained within the database.
|
||||||
|
func (t *table) NewIterator() ethdb.Iterator {
|
||||||
|
return t.NewIteratorWithPrefix(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset
|
||||||
|
// of database content with a particular key prefix.
|
||||||
|
func (t *table) NewIteratorWithPrefix(prefix []byte) ethdb.Iterator {
|
||||||
|
return t.db.NewIteratorWithPrefix(append([]byte(t.prefix), prefix...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat returns a particular internal stat of the database.
|
||||||
|
func (t *table) Stat(property string) (string, error) {
|
||||||
|
return t.db.Stat(property)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact flattens the underlying data store for the given key range. In essence,
|
||||||
|
// deleted and overwritten versions are discarded, and the data is rearranged to
|
||||||
|
// reduce the cost of operations needed to access them.
|
||||||
|
//
|
||||||
|
// A nil start is treated as a key before all keys in the data store; a nil limit
|
||||||
|
// is treated as a key after all keys in the data store. If both is nil then it
|
||||||
|
// will compact entire data store.
|
||||||
|
func (t *table) Compact(start []byte, limit []byte) error {
|
||||||
|
// If no start was specified, use the table prefix as the first value
|
||||||
|
if start == nil {
|
||||||
|
start = []byte(t.prefix)
|
||||||
|
}
|
||||||
|
// If no limit was specified, use the first element not matching the prefix
|
||||||
|
// as the limit
|
||||||
|
if limit == nil {
|
||||||
|
limit = []byte(t.prefix)
|
||||||
|
for i := len(limit) - 1; i >= 0; i-- {
|
||||||
|
// Bump the current character, stopping if it doesn't overflow
|
||||||
|
limit[i]++
|
||||||
|
if limit[i] > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Character overflown, proceed to the next or nil if the last
|
||||||
|
if i == 0 {
|
||||||
|
limit = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Range correctly calculated based on table prefix, delegate down
|
||||||
|
return t.db.Compact(start, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatch creates a write-only database that buffers changes to its host db
|
||||||
|
// until a final write is called, each operation prefixing all keys with the
|
||||||
|
// pre-configured string.
|
||||||
|
func (t *table) NewBatch() ethdb.Batch {
|
||||||
|
return &tableBatch{t.db.NewBatch(), t.prefix}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tableBatch is a wrapper around a database batch that prefixes each key access
|
||||||
|
// with a pre-configured string.
|
||||||
|
type tableBatch struct {
|
||||||
|
batch ethdb.Batch
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put inserts the given value into the batch for later committing.
|
||||||
|
func (b *tableBatch) Put(key, value []byte) error {
|
||||||
|
return b.batch.Put(append([]byte(b.prefix), key...), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete inserts the a key removal into the batch for later committing.
|
||||||
|
func (b *tableBatch) Delete(key []byte) error {
|
||||||
|
return b.batch.Delete(append([]byte(b.prefix), key...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueSize retrieves the amount of data queued up for writing.
|
||||||
|
func (b *tableBatch) ValueSize() int {
|
||||||
|
return b.batch.ValueSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write flushes any accumulated data to disk.
|
||||||
|
func (b *tableBatch) Write() error {
|
||||||
|
return b.batch.Write()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets the batch for reuse.
|
||||||
|
func (b *tableBatch) Reset() {
|
||||||
|
b.batch.Reset()
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,6 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
|
@ -26,14 +25,7 @@ import (
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Trie cache generation limit after which to evict trie nodes from memory.
|
|
||||||
var MaxTrieCacheGen = uint16(120)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Number of past tries to keep. This value is chosen such that
|
|
||||||
// reasonable chain reorg depths will hit an existing trie.
|
|
||||||
maxPastTries = 12
|
|
||||||
|
|
||||||
// Number of codehash->size associations to keep.
|
// Number of codehash->size associations to keep.
|
||||||
codeSizeCacheSize = 100000
|
codeSizeCacheSize = 100000
|
||||||
)
|
)
|
||||||
|
|
@ -59,28 +51,61 @@ type Database interface {
|
||||||
TrieDB() *trie.Database
|
TrieDB() *trie.Database
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trie is a Ethereum Merkle Trie.
|
// Trie is a Ethereum Merkle Patricia trie.
|
||||||
type Trie interface {
|
type Trie interface {
|
||||||
|
// GetKey returns the sha3 preimage of a hashed key that was previously used
|
||||||
|
// to store a value.
|
||||||
|
//
|
||||||
|
// TODO(fjl): remove this when SecureTrie is removed
|
||||||
|
GetKey([]byte) []byte
|
||||||
|
|
||||||
|
// TryGet returns the value for key stored in the trie. The value bytes must
|
||||||
|
// not be modified by the caller. If a node was not found in the database, a
|
||||||
|
// trie.MissingNodeError is returned.
|
||||||
TryGet(key []byte) ([]byte, error)
|
TryGet(key []byte) ([]byte, error)
|
||||||
|
|
||||||
|
// TryUpdate associates key with value in the trie. If value has length zero, any
|
||||||
|
// existing value is deleted from the trie. The value bytes must not be modified
|
||||||
|
// by the caller while they are stored in the trie. If a node was not found in the
|
||||||
|
// database, a trie.MissingNodeError is returned.
|
||||||
TryUpdate(key, value []byte) error
|
TryUpdate(key, value []byte) error
|
||||||
|
|
||||||
|
// TryDelete removes any existing value for key from the trie. If a node was not
|
||||||
|
// found in the database, a trie.MissingNodeError is returned.
|
||||||
TryDelete(key []byte) error
|
TryDelete(key []byte) error
|
||||||
Commit(onleaf trie.LeafCallback) (common.Hash, error)
|
|
||||||
|
// Hash returns the root hash of the trie. It does not write to the database and
|
||||||
|
// can be used even if the trie doesn't have one.
|
||||||
Hash() common.Hash
|
Hash() common.Hash
|
||||||
|
|
||||||
|
// Commit writes all nodes to the trie's memory database, tracking the internal
|
||||||
|
// and external (for account tries) references.
|
||||||
|
Commit(onleaf trie.LeafCallback) (common.Hash, error)
|
||||||
|
|
||||||
|
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
|
||||||
|
// starts at the key after the given start key.
|
||||||
NodeIterator(startKey []byte) trie.NodeIterator
|
NodeIterator(startKey []byte) trie.NodeIterator
|
||||||
GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed
|
|
||||||
Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error
|
// Prove constructs a Merkle proof for key. The result contains all encoded nodes
|
||||||
|
// on the path to the value at key. The value itself is also included in the last
|
||||||
|
// node and can be retrieved by verifying the proof.
|
||||||
|
//
|
||||||
|
// If the trie does not contain a value for key, the returned proof contains all
|
||||||
|
// nodes of the longest existing prefix of the key (at least the root), ending
|
||||||
|
// with the node that proves the absence of the key.
|
||||||
|
Prove(key []byte, fromLevel uint, proofDb ethdb.Writer) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabase creates a backing store for state. The returned database is safe for
|
// NewDatabase creates a backing store for state. The returned database is safe for
|
||||||
// concurrent use and retains a few recent expanded trie nodes in memory. To keep
|
// concurrent use, but does not retain any recent trie nodes in memory. To keep some
|
||||||
// more historical state in memory, use the NewDatabaseWithCache constructor.
|
// historical state in memory, use the NewDatabaseWithCache constructor.
|
||||||
func NewDatabase(db ethdb.Database) Database {
|
func NewDatabase(db ethdb.Database) Database {
|
||||||
return NewDatabaseWithCache(db, 0)
|
return NewDatabaseWithCache(db, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabase creates a backing store for state. The returned database is safe for
|
// NewDatabaseWithCache creates a backing store for state. The returned database
|
||||||
// concurrent use and retains both a few recent expanded trie nodes in memory, as
|
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
|
||||||
// well as a lot of collapsed RLP trie nodes in a large memory cache.
|
// large memory cache.
|
||||||
func NewDatabaseWithCache(db ethdb.Database, cache int) Database {
|
func NewDatabaseWithCache(db ethdb.Database, cache int) Database {
|
||||||
csc, _ := lru.New(codeSizeCacheSize)
|
csc, _ := lru.New(codeSizeCacheSize)
|
||||||
return &cachingDB{
|
return &cachingDB{
|
||||||
|
|
@ -91,50 +116,22 @@ func NewDatabaseWithCache(db ethdb.Database, cache int) Database {
|
||||||
|
|
||||||
type cachingDB struct {
|
type cachingDB struct {
|
||||||
db *trie.Database
|
db *trie.Database
|
||||||
mu sync.Mutex
|
|
||||||
pastTries []*trie.SecureTrie
|
|
||||||
codeSizeCache *lru.Cache
|
codeSizeCache *lru.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenTrie opens the main account trie.
|
// OpenTrie opens the main account trie at a specific root hash.
|
||||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||||
db.mu.Lock()
|
return trie.NewSecure(root, db.db)
|
||||||
defer db.mu.Unlock()
|
|
||||||
|
|
||||||
for i := len(db.pastTries) - 1; i >= 0; i-- {
|
|
||||||
if db.pastTries[i].Hash() == root {
|
|
||||||
return cachedTrie{db.pastTries[i].Copy(), db}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return cachedTrie{tr, db}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *cachingDB) pushTrie(t *trie.SecureTrie) {
|
|
||||||
db.mu.Lock()
|
|
||||||
defer db.mu.Unlock()
|
|
||||||
|
|
||||||
if len(db.pastTries) >= maxPastTries {
|
|
||||||
copy(db.pastTries, db.pastTries[1:])
|
|
||||||
db.pastTries[len(db.pastTries)-1] = t
|
|
||||||
} else {
|
|
||||||
db.pastTries = append(db.pastTries, t)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenStorageTrie opens the storage trie of an account.
|
// OpenStorageTrie opens the storage trie of an account.
|
||||||
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
|
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
|
||||||
return trie.NewSecure(root, db.db, 0)
|
return trie.NewSecure(root, db.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyTrie returns an independent copy of the given trie.
|
// CopyTrie returns an independent copy of the given trie.
|
||||||
func (db *cachingDB) CopyTrie(t Trie) Trie {
|
func (db *cachingDB) CopyTrie(t Trie) Trie {
|
||||||
switch t := t.(type) {
|
switch t := t.(type) {
|
||||||
case cachedTrie:
|
|
||||||
return cachedTrie{t.SecureTrie.Copy(), db}
|
|
||||||
case *trie.SecureTrie:
|
case *trie.SecureTrie:
|
||||||
return t.Copy()
|
return t.Copy()
|
||||||
default:
|
default:
|
||||||
|
|
@ -164,21 +161,3 @@ func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, erro
|
||||||
func (db *cachingDB) TrieDB() *trie.Database {
|
func (db *cachingDB) TrieDB() *trie.Database {
|
||||||
return db.db
|
return db.db
|
||||||
}
|
}
|
||||||
|
|
||||||
// cachedTrie inserts its trie into a cachingDB on commit.
|
|
||||||
type cachedTrie struct {
|
|
||||||
*trie.SecureTrie
|
|
||||||
db *cachingDB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m cachedTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) {
|
|
||||||
root, err := m.SecureTrie.Commit(onleaf)
|
|
||||||
if err == nil {
|
|
||||||
m.db.pushTrie(m.SecureTrie)
|
|
||||||
}
|
|
||||||
return root, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m cachedTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
|
|
||||||
return m.SecureTrie.Prove(key, fromLevel, proofDb)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,9 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
||||||
t.Errorf("state entry not reported %x", hash)
|
t.Errorf("state entry not reported %x", hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, key := range db.TrieDB().DiskDB().(*ethdb.MemDatabase).Keys() {
|
it := db.TrieDB().DiskDB().(ethdb.Database).NewIterator()
|
||||||
|
for it.Next() {
|
||||||
|
key := it.Key()
|
||||||
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -59,4 +61,5 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
||||||
t.Errorf("state entry not reported %x", key)
|
t.Errorf("state entry not reported %x", key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
it.Release()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,13 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
var addr = common.BytesToAddress([]byte("test"))
|
var addr = common.BytesToAddress([]byte("test"))
|
||||||
|
|
||||||
func create() (*ManagedState, *account) {
|
func create() (*ManagedState, *account) {
|
||||||
statedb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
ms := ManageState(statedb)
|
ms := ManageState(statedb)
|
||||||
ms.StateDB.SetNonce(addr, 100)
|
ms.StateDB.SetNonce(addr, 100)
|
||||||
ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr))
|
ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr))
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,14 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
checker "gopkg.in/check.v1"
|
checker "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StateSuite struct {
|
type StateSuite struct {
|
||||||
db *ethdb.MemDatabase
|
db ethdb.Database
|
||||||
state *StateDB
|
state *StateDB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +88,7 @@ func (s *StateSuite) TestDump(c *checker.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateSuite) SetUpTest(c *checker.C) {
|
func (s *StateSuite) SetUpTest(c *checker.C) {
|
||||||
s.db = ethdb.NewMemDatabase()
|
s.db = rawdb.NewMemoryDatabase()
|
||||||
s.state, _ = New(common.Hash{}, NewDatabase(s.db))
|
s.state, _ = New(common.Hash{}, NewDatabase(s.db))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +142,7 @@ func (s *StateSuite) TestSnapshotEmpty(c *checker.C) {
|
||||||
// use testing instead of checker because checker does not support
|
// use testing instead of checker because checker does not support
|
||||||
// printing/logging in tests (-check.vv does not work)
|
// printing/logging in tests (-check.vv does not work)
|
||||||
func TestSnapshot2(t *testing.T) {
|
func TestSnapshot2(t *testing.T) {
|
||||||
state, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
|
||||||
stateobjaddr0 := toAddr([]byte("so0"))
|
stateobjaddr0 := toAddr([]byte("so0"))
|
||||||
stateobjaddr1 := toAddr([]byte("so1"))
|
stateobjaddr1 := toAddr([]byte("so1"))
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ type revision struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// emptyState is the known hash of an empty state trie entry.
|
// emptyRoot is the known root hash of an empty trie.
|
||||||
emptyState = crypto.Keccak256Hash(nil)
|
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||||
|
|
||||||
// emptyCode is the known hash of the empty EVM bytecode.
|
// emptyCode is the known hash of the empty EVM bytecode.
|
||||||
emptyCode = crypto.Keccak256Hash(nil)
|
emptyCode = crypto.Keccak256Hash(nil)
|
||||||
|
|
@ -653,7 +653,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
|
||||||
if err := rlp.DecodeBytes(leaf, &account); err != nil {
|
if err := rlp.DecodeBytes(leaf, &account); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if account.Root != emptyState {
|
if account.Root != emptyRoot {
|
||||||
s.db.TrieDB().Reference(account.Root, parent)
|
s.db.TrieDB().Reference(account.Root, parent)
|
||||||
}
|
}
|
||||||
code := common.BytesToHash(account.CodeHash)
|
code := common.BytesToHash(account.CodeHash)
|
||||||
|
|
@ -662,6 +662,5 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
|
|
||||||
return root, err
|
return root, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,15 +31,15 @@ import (
|
||||||
check "gopkg.in/check.v1"
|
check "gopkg.in/check.v1"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests that updating a state trie does not leak any database writes prior to
|
// Tests that updating a state trie does not leak any database writes prior to
|
||||||
// actually committing the state.
|
// actually committing the state.
|
||||||
func TestUpdateLeaks(t *testing.T) {
|
func TestUpdateLeaks(t *testing.T) {
|
||||||
// Create an empty state database
|
// Create an empty state database
|
||||||
db := ethdb.NewMemDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
state, _ := New(common.Hash{}, NewDatabase(db))
|
state, _ := New(common.Hash{}, NewDatabase(db))
|
||||||
|
|
||||||
// Update it with some accounts
|
// Update it with some accounts
|
||||||
|
|
@ -56,18 +56,19 @@ func TestUpdateLeaks(t *testing.T) {
|
||||||
state.IntermediateRoot(false)
|
state.IntermediateRoot(false)
|
||||||
}
|
}
|
||||||
// Ensure that no data was leaked into the database
|
// Ensure that no data was leaked into the database
|
||||||
for _, key := range db.Keys() {
|
it := db.NewIterator()
|
||||||
value, _ := db.Get(key)
|
for it.Next() {
|
||||||
t.Errorf("State leaked into database: %x -> %x", key, value)
|
t.Errorf("State leaked into database: %x -> %x", it.Key(), it.Value())
|
||||||
}
|
}
|
||||||
|
it.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that no intermediate state of an object is stored into the database,
|
// Tests that no intermediate state of an object is stored into the database,
|
||||||
// only the one right before the commit.
|
// only the one right before the commit.
|
||||||
func TestIntermediateLeaks(t *testing.T) {
|
func TestIntermediateLeaks(t *testing.T) {
|
||||||
// Create two state databases, one transitioning to the final state, the other final from the beginning
|
// Create two state databases, one transitioning to the final state, the other final from the beginning
|
||||||
transDb := ethdb.NewMemDatabase()
|
transDb := rawdb.NewMemoryDatabase()
|
||||||
finalDb := ethdb.NewMemDatabase()
|
finalDb := rawdb.NewMemoryDatabase()
|
||||||
transState, _ := New(common.Hash{}, NewDatabase(transDb))
|
transState, _ := New(common.Hash{}, NewDatabase(transDb))
|
||||||
finalState, _ := New(common.Hash{}, NewDatabase(finalDb))
|
finalState, _ := New(common.Hash{}, NewDatabase(finalDb))
|
||||||
|
|
||||||
|
|
@ -103,16 +104,20 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||||
if _, err := finalState.Commit(false); err != nil {
|
if _, err := finalState.Commit(false); err != nil {
|
||||||
t.Fatalf("failed to commit final state: %v", err)
|
t.Fatalf("failed to commit final state: %v", err)
|
||||||
}
|
}
|
||||||
for _, key := range finalDb.Keys() {
|
it := finalDb.NewIterator()
|
||||||
|
for it.Next() {
|
||||||
|
key := it.Key()
|
||||||
if _, err := transDb.Get(key); err != nil {
|
if _, err := transDb.Get(key); err != nil {
|
||||||
val, _ := finalDb.Get(key)
|
t.Errorf("entry missing from the transition database: %x -> %x", key, it.Value())
|
||||||
t.Errorf("entry missing from the transition database: %x -> %x", key, val)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, key := range transDb.Keys() {
|
it.Release()
|
||||||
|
|
||||||
|
it = transDb.NewIterator()
|
||||||
|
for it.Next() {
|
||||||
|
key := it.Key()
|
||||||
if _, err := finalDb.Get(key); err != nil {
|
if _, err := finalDb.Get(key); err != nil {
|
||||||
val, _ := transDb.Get(key)
|
t.Errorf("extra entry in the transition database: %x -> %x", key, it.Value())
|
||||||
t.Errorf("extra entry in the transition database: %x -> %x", key, val)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +127,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||||
// https://github.com/ethereum/go-ethereum/pull/15549.
|
// https://github.com/ethereum/go-ethereum/pull/15549.
|
||||||
func TestCopy(t *testing.T) {
|
func TestCopy(t *testing.T) {
|
||||||
// Create a random state test to copy and modify "independently"
|
// Create a random state test to copy and modify "independently"
|
||||||
orig, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
orig, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
|
||||||
for i := byte(0); i < 255; i++ {
|
for i := byte(0); i < 255; i++ {
|
||||||
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||||
|
|
@ -342,7 +347,7 @@ func (test *snapshotTest) String() string {
|
||||||
func (test *snapshotTest) run() bool {
|
func (test *snapshotTest) run() bool {
|
||||||
// Run all actions and create snapshots.
|
// Run all actions and create snapshots.
|
||||||
var (
|
var (
|
||||||
state, _ = New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
state, _ = New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
snapshotRevs = make([]int, len(test.snapshots))
|
snapshotRevs = make([]int, len(test.snapshots))
|
||||||
sindex = 0
|
sindex = 0
|
||||||
)
|
)
|
||||||
|
|
@ -433,7 +438,7 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
|
||||||
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
|
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
|
||||||
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
|
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
|
||||||
func TestCopyOfCopy(t *testing.T) {
|
func TestCopyOfCopy(t *testing.T) {
|
||||||
sdb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
sdb, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
addr := common.HexToAddress("aaaa")
|
addr := common.HexToAddress("aaaa")
|
||||||
sdb.SetBalance(addr, big.NewInt(42))
|
sdb.SetBalance(addr, big.NewInt(42))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,13 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewStateSync create a new state trie download scheduler.
|
// NewStateSync create a new state trie download scheduler.
|
||||||
func NewStateSync(root common.Hash, database trie.DatabaseReader) *trie.Sync {
|
func NewStateSync(root common.Hash, database ethdb.Reader) *trie.Sync {
|
||||||
var syncer *trie.Sync
|
var syncer *trie.Sync
|
||||||
callback := func(leaf []byte, parent common.Hash) error {
|
callback := func(leaf []byte, parent common.Hash) error {
|
||||||
var obj Account
|
var obj Account
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
|
@ -38,7 +39,7 @@ type testAccount struct {
|
||||||
// makeTestState create a sample test state to test node-wise reconstruction.
|
// makeTestState create a sample test state to test node-wise reconstruction.
|
||||||
func makeTestState() (Database, common.Hash, []*testAccount) {
|
func makeTestState() (Database, common.Hash, []*testAccount) {
|
||||||
// Create an empty state
|
// Create an empty state
|
||||||
db := NewDatabase(ethdb.NewMemDatabase())
|
db := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
state, _ := New(common.Hash{}, db)
|
state, _ := New(common.Hash{}, db)
|
||||||
|
|
||||||
// Fill it with some arbitrary data
|
// Fill it with some arbitrary data
|
||||||
|
|
@ -124,7 +125,7 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
|
||||||
// Tests that an empty state is not scheduled for syncing.
|
// Tests that an empty state is not scheduled for syncing.
|
||||||
func TestEmptyStateSync(t *testing.T) {
|
func TestEmptyStateSync(t *testing.T) {
|
||||||
empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||||
if req := NewStateSync(empty, ethdb.NewMemDatabase()).Missing(1); len(req) != 0 {
|
if req := NewStateSync(empty, rawdb.NewMemoryDatabase()).Missing(1); len(req) != 0 {
|
||||||
t.Errorf("content requested for empty state: %v", req)
|
t.Errorf("content requested for empty state: %v", req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +140,7 @@ func testIterativeStateSync(t *testing.T, batch int) {
|
||||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||||
|
|
||||||
// Create a destination state and sync with the scheduler
|
// Create a destination state and sync with the scheduler
|
||||||
dstDb := ethdb.NewMemDatabase()
|
dstDb := rawdb.NewMemoryDatabase()
|
||||||
sched := NewStateSync(srcRoot, dstDb)
|
sched := NewStateSync(srcRoot, dstDb)
|
||||||
|
|
||||||
queue := append([]common.Hash{}, sched.Missing(batch)...)
|
queue := append([]common.Hash{}, sched.Missing(batch)...)
|
||||||
|
|
@ -171,7 +172,7 @@ func TestIterativeDelayedStateSync(t *testing.T) {
|
||||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||||
|
|
||||||
// Create a destination state and sync with the scheduler
|
// Create a destination state and sync with the scheduler
|
||||||
dstDb := ethdb.NewMemDatabase()
|
dstDb := rawdb.NewMemoryDatabase()
|
||||||
sched := NewStateSync(srcRoot, dstDb)
|
sched := NewStateSync(srcRoot, dstDb)
|
||||||
|
|
||||||
queue := append([]common.Hash{}, sched.Missing(0)...)
|
queue := append([]common.Hash{}, sched.Missing(0)...)
|
||||||
|
|
@ -208,7 +209,7 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
|
||||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||||
|
|
||||||
// Create a destination state and sync with the scheduler
|
// Create a destination state and sync with the scheduler
|
||||||
dstDb := ethdb.NewMemDatabase()
|
dstDb := rawdb.NewMemoryDatabase()
|
||||||
sched := NewStateSync(srcRoot, dstDb)
|
sched := NewStateSync(srcRoot, dstDb)
|
||||||
|
|
||||||
queue := make(map[common.Hash]struct{})
|
queue := make(map[common.Hash]struct{})
|
||||||
|
|
@ -248,7 +249,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||||
|
|
||||||
// Create a destination state and sync with the scheduler
|
// Create a destination state and sync with the scheduler
|
||||||
dstDb := ethdb.NewMemDatabase()
|
dstDb := rawdb.NewMemoryDatabase()
|
||||||
sched := NewStateSync(srcRoot, dstDb)
|
sched := NewStateSync(srcRoot, dstDb)
|
||||||
|
|
||||||
queue := make(map[common.Hash]struct{})
|
queue := make(map[common.Hash]struct{})
|
||||||
|
|
@ -295,7 +296,7 @@ func TestIncompleteStateSync(t *testing.T) {
|
||||||
checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
|
checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
|
||||||
|
|
||||||
// Create a destination state and sync with the scheduler
|
// Create a destination state and sync with the scheduler
|
||||||
dstDb := ethdb.NewMemDatabase()
|
dstDb := rawdb.NewMemoryDatabase()
|
||||||
sched := NewStateSync(srcRoot, dstDb)
|
sched := NewStateSync(srcRoot, dstDb)
|
||||||
|
|
||||||
added := []common.Hash{}
|
added := []common.Hash{}
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
@ -78,7 +78,7 @@ func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ec
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
|
|
@ -163,7 +163,7 @@ func (c *testChain) State() (*state.StateDB, error) {
|
||||||
// a state change between those fetches.
|
// a state change between those fetches.
|
||||||
stdb := c.statedb
|
stdb := c.statedb
|
||||||
if *c.trigger {
|
if *c.trigger {
|
||||||
c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
// simulate that the new head block included tx0 and tx1
|
// simulate that the new head block included tx0 and tx1
|
||||||
c.statedb.SetNonce(c.address, 2)
|
c.statedb.SetNonce(c.address, 2)
|
||||||
c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether))
|
c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether))
|
||||||
|
|
@ -181,7 +181,7 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
key, _ = crypto.GenerateKey()
|
key, _ = crypto.GenerateKey()
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
trigger = false
|
trigger = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -335,7 +335,7 @@ func TestTransactionChainFork(t *testing.T) {
|
||||||
|
|
||||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
resetState := func() {
|
resetState := func() {
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
||||||
|
|
||||||
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
@ -364,7 +364,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
|
||||||
|
|
||||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
resetState := func() {
|
resetState := func() {
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
||||||
|
|
||||||
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
@ -554,7 +554,7 @@ func TestTransactionPostponing(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the postponing with
|
// Create the pool to test the postponing with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||||
|
|
@ -769,7 +769,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the limit enforcement with
|
// Create the pool to test the limit enforcement with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
config := testTxPoolConfig
|
config := testTxPoolConfig
|
||||||
|
|
@ -857,7 +857,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
|
||||||
evictionInterval = time.Second
|
evictionInterval = time.Second
|
||||||
|
|
||||||
// Create the pool to test the non-expiration enforcement
|
// Create the pool to test the non-expiration enforcement
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
config := testTxPoolConfig
|
config := testTxPoolConfig
|
||||||
|
|
@ -1011,7 +1011,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the limit enforcement with
|
// Create the pool to test the limit enforcement with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
config := testTxPoolConfig
|
config := testTxPoolConfig
|
||||||
|
|
@ -1057,7 +1057,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the limit enforcement with
|
// Create the pool to test the limit enforcement with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
config := testTxPoolConfig
|
config := testTxPoolConfig
|
||||||
|
|
@ -1091,7 +1091,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the limit enforcement with
|
// Create the pool to test the limit enforcement with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
config := testTxPoolConfig
|
config := testTxPoolConfig
|
||||||
|
|
@ -1139,7 +1139,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the pricing enforcement with
|
// Create the pool to test the pricing enforcement with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||||
|
|
@ -1260,7 +1260,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the pricing enforcement with
|
// Create the pool to test the pricing enforcement with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||||
|
|
@ -1322,7 +1322,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the pricing enforcement with
|
// Create the pool to test the pricing enforcement with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
config := testTxPoolConfig
|
config := testTxPoolConfig
|
||||||
|
|
@ -1428,7 +1428,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the pricing enforcement with
|
// Create the pool to test the pricing enforcement with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
config := testTxPoolConfig
|
config := testTxPoolConfig
|
||||||
|
|
@ -1494,7 +1494,7 @@ func TestTransactionReplacement(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the pricing enforcement with
|
// Create the pool to test the pricing enforcement with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||||
|
|
@ -1588,7 +1588,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
||||||
os.Remove(journal)
|
os.Remove(journal)
|
||||||
|
|
||||||
// Create the original pool to inject transaction into the journal
|
// Create the original pool to inject transaction into the journal
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
config := testTxPoolConfig
|
config := testTxPoolConfig
|
||||||
|
|
@ -1686,7 +1686,7 @@ func TestTransactionStatusCheck(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create the pool to test the status retrievals with
|
// Create the pool to test the status retrievals with
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
|
|
||||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ type Validator interface {
|
||||||
|
|
||||||
// ValidateState validates the given statedb and optionally the receipts and
|
// ValidateState validates the given statedb and optionally the receipts and
|
||||||
// gas used.
|
// gas used.
|
||||||
ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error
|
ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processor is an interface for processing blocks using a given initial state.
|
// Processor is an interface for processing blocks using a given initial state.
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,31 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// calculates the memory size required for a step
|
// calcMemSize64 calculates the required memory size, and returns
|
||||||
func calcMemSize(off, l *big.Int) *big.Int {
|
// the size and whether the result overflowed uint64
|
||||||
if l.Sign() == 0 {
|
func calcMemSize64(off, l *big.Int) (uint64, bool) {
|
||||||
return common.Big0
|
if !l.IsUint64() {
|
||||||
|
return 0, true
|
||||||
}
|
}
|
||||||
|
return calcMemSize64WithUint(off, l.Uint64())
|
||||||
|
}
|
||||||
|
|
||||||
return new(big.Int).Add(off, l)
|
// calcMemSize64WithUint calculates the required memory size, and returns
|
||||||
|
// the size and whether the result overflowed uint64
|
||||||
|
// Identical to calcMemSize64, but length is a uint64
|
||||||
|
func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
|
||||||
|
// if length is zero, memsize is always zero, regardless of offset
|
||||||
|
if length64 == 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
// Check that offset doesn't overflow
|
||||||
|
if !off.IsUint64() {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
offset64 := off.Uint64()
|
||||||
|
val := offset64 + length64
|
||||||
|
// if value < either of it's parts, then it overflowed
|
||||||
|
return val, val < offset64
|
||||||
}
|
}
|
||||||
|
|
||||||
// getData returns a slice from the data based on the start and size and pads
|
// getData returns a slice from the data based on the start and size and pads
|
||||||
|
|
@ -59,7 +77,7 @@ func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
|
||||||
// bigUint64 returns the integer casted to a uint64 and returns whether it
|
// bigUint64 returns the integer casted to a uint64 and returns whether it
|
||||||
// overflowed in the process.
|
// overflowed in the process.
|
||||||
func bigUint64(v *big.Int) (uint64, bool) {
|
func bigUint64(v *big.Int) (uint64, bool) {
|
||||||
return v.Uint64(), v.BitLen() > 64
|
return v.Uint64(), !v.IsUint64()
|
||||||
}
|
}
|
||||||
|
|
||||||
// toWordSize returns the ceiled word size required for memory expansion.
|
// toWordSize returns the ceiled word size required for memory expansion.
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,11 @@ func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big.
|
||||||
// If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150
|
// If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150
|
||||||
// is smaller than the requested amount. Therefor we return the new gas instead
|
// is smaller than the requested amount. Therefor we return the new gas instead
|
||||||
// of returning an error.
|
// of returning an error.
|
||||||
if callCost.BitLen() > 64 || gas < callCost.Uint64() {
|
if !callCost.IsUint64() || gas < callCost.Uint64() {
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if callCost.BitLen() > 64 {
|
if !callCost.IsUint64() {
|
||||||
return 0, errGasUintOverflow
|
return 0, errGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,12 +57,6 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func constGasFunc(gas uint64) gasFunc {
|
|
||||||
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas, err := memoryGasCost(mem, memorySize)
|
gas, err := memoryGasCost(mem, memorySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -521,15 +515,3 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
return GasFastestStep, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
return GasFastestStep, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
return GasFastestStep, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -35,6 +34,7 @@ var (
|
||||||
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
|
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
|
||||||
errExecutionReverted = errors.New("evm: execution reverted")
|
errExecutionReverted = errors.New("evm: execution reverted")
|
||||||
errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
|
errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
|
||||||
|
errInvalidJump = errors.New("evm: invalid jump destination")
|
||||||
)
|
)
|
||||||
|
|
||||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opAdd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
|
|
@ -405,7 +405,7 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
|
||||||
}
|
}
|
||||||
|
|
||||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
stack.push(contract.Address().Big())
|
stack.push(interpreter.intPool.get().SetBytes(contract.Address().Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -416,12 +416,12 @@ func opBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
|
||||||
}
|
}
|
||||||
|
|
||||||
func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
stack.push(interpreter.evm.Origin.Big())
|
stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
stack.push(contract.Caller().Big())
|
stack.push(interpreter.intPool.get().SetBytes(contract.Caller().Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -467,7 +467,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contrac
|
||||||
)
|
)
|
||||||
defer interpreter.intPool.put(memOffset, dataOffset, length, end)
|
defer interpreter.intPool.put(memOffset, dataOffset, length, end)
|
||||||
|
|
||||||
if end.BitLen() > 64 || uint64(len(interpreter.returnData)) < end.Uint64() {
|
if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
|
||||||
return nil, errReturnDataOutOfBounds
|
return nil, errReturnDataOutOfBounds
|
||||||
}
|
}
|
||||||
memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
|
memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
|
||||||
|
|
@ -572,7 +572,7 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, me
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
stack.push(interpreter.evm.Coinbase.Big())
|
stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -645,8 +645,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
|
||||||
func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
pos := stack.pop()
|
pos := stack.pop()
|
||||||
if !contract.validJumpdest(pos) {
|
if !contract.validJumpdest(pos) {
|
||||||
nop := contract.GetOp(pos.Uint64())
|
return nil, errInvalidJump
|
||||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
|
||||||
}
|
}
|
||||||
*pc = pos.Uint64()
|
*pc = pos.Uint64()
|
||||||
|
|
||||||
|
|
@ -658,8 +657,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
|
||||||
pos, cond := stack.pop(), stack.pop()
|
pos, cond := stack.pop(), stack.pop()
|
||||||
if cond.Sign() != 0 {
|
if cond.Sign() != 0 {
|
||||||
if !contract.validJumpdest(pos) {
|
if !contract.validJumpdest(pos) {
|
||||||
nop := contract.GetOp(pos.Uint64())
|
return nil, errInvalidJump
|
||||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
|
||||||
}
|
}
|
||||||
*pc = pos.Uint64()
|
*pc = pos.Uint64()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -711,7 +709,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
|
||||||
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
|
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
|
||||||
stack.push(interpreter.intPool.getZero())
|
stack.push(interpreter.intPool.getZero())
|
||||||
} else {
|
} else {
|
||||||
stack.push(addr.Big())
|
stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
|
||||||
}
|
}
|
||||||
contract.Gas += returnGas
|
contract.Gas += returnGas
|
||||||
interpreter.intPool.put(value, offset, size)
|
interpreter.intPool.put(value, offset, size)
|
||||||
|
|
@ -739,7 +737,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
|
||||||
if suberr != nil {
|
if suberr != nil {
|
||||||
stack.push(interpreter.intPool.getZero())
|
stack.push(interpreter.intPool.getZero())
|
||||||
} else {
|
} else {
|
||||||
stack.push(addr.Big())
|
stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
|
||||||
}
|
}
|
||||||
contract.Gas += returnGas
|
contract.Gas += returnGas
|
||||||
interpreter.intPool.put(endowment, offset, size, salt)
|
interpreter.intPool.put(endowment, offset, size, salt)
|
||||||
|
|
@ -912,6 +910,21 @@ func makeLog(size int) executionFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// opPush1 is a specialized version of pushN
|
||||||
|
func opPush1(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
codeLen = uint64(len(contract.Code))
|
||||||
|
integer = interpreter.intPool.get()
|
||||||
|
)
|
||||||
|
*pc += 1
|
||||||
|
if *pc < codeLen {
|
||||||
|
stack.push(integer.SetUint64(uint64(contract.Code[*pc])))
|
||||||
|
} else {
|
||||||
|
stack.push(integer.SetUint64(0))
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// make push instruction function
|
// make push instruction function
|
||||||
func makePush(size uint64, pushByteSize int) executionFunc {
|
func makePush(size uint64, pushByteSize int) executionFunc {
|
||||||
return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
|
|
|
||||||
|
|
@ -118,22 +118,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
|
|
||||||
if in.evm.chainRules.IsByzantium {
|
|
||||||
if in.readOnly {
|
|
||||||
// If the interpreter is operating in readonly mode, make sure no
|
|
||||||
// state-modifying operation is performed. The 3rd stack item
|
|
||||||
// for a call operation is the value. Transferring value from one
|
|
||||||
// account to the others means the state is modified and should also
|
|
||||||
// return with an error.
|
|
||||||
if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) {
|
|
||||||
return errWriteProtection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run loops and evaluates the contract's code with the given input data and returns
|
// Run loops and evaluates the contract's code with the given input data and returns
|
||||||
// the return byte-slice and an error if one occurred.
|
// the return byte-slice and an error if one occurred.
|
||||||
//
|
//
|
||||||
|
|
@ -217,19 +201,35 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
if !operation.valid {
|
if !operation.valid {
|
||||||
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
|
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
|
||||||
}
|
}
|
||||||
if err = operation.validateStack(stack); err != nil {
|
// Validate stack
|
||||||
return nil, err
|
if sLen := stack.len(); sLen < operation.minStack {
|
||||||
|
return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack)
|
||||||
|
} else if sLen > operation.maxStack {
|
||||||
|
return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack)
|
||||||
}
|
}
|
||||||
// If the operation is valid, enforce and write restrictions
|
// If the operation is valid, enforce and write restrictions
|
||||||
if err = in.enforceRestrictions(op, operation, stack); err != nil {
|
if in.readOnly && in.evm.chainRules.IsByzantium {
|
||||||
return nil, err
|
// If the interpreter is operating in readonly mode, make sure no
|
||||||
|
// state-modifying operation is performed. The 3rd stack item
|
||||||
|
// for a call operation is the value. Transferring value from one
|
||||||
|
// account to the others means the state is modified and should also
|
||||||
|
// return with an error.
|
||||||
|
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
|
||||||
|
return nil, errWriteProtection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Static portion of gas
|
||||||
|
if !contract.UseGas(operation.constantGas) {
|
||||||
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
var memorySize uint64
|
var memorySize uint64
|
||||||
// calculate the new memory size and expand the memory to fit
|
// calculate the new memory size and expand the memory to fit
|
||||||
// the operation
|
// the operation
|
||||||
|
// Memory check needs to be done prior to evaluating the dynamic gas portion,
|
||||||
|
// to detect calculation overflows
|
||||||
if operation.memorySize != nil {
|
if operation.memorySize != nil {
|
||||||
memSize, overflow := bigUint64(operation.memorySize(stack))
|
memSize, overflow := operation.memorySize(stack)
|
||||||
if overflow {
|
if overflow {
|
||||||
return nil, errGasUintOverflow
|
return nil, errGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
@ -239,11 +239,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
return nil, errGasUintOverflow
|
return nil, errGasUintOverflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Dynamic portion of gas
|
||||||
// consume the gas and return an error if not enough gas is available.
|
// consume the gas and return an error if not enough gas is available.
|
||||||
// cost is explicitly set so that the capture state defer method can get the proper cost
|
// cost is explicitly set so that the capture state defer method can get the proper cost
|
||||||
cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)
|
if operation.dynamicGas != nil {
|
||||||
if err != nil || !contract.UseGas(cost) {
|
cost, err = operation.dynamicGas(in.gasTable, in.evm, contract, stack, mem, memorySize)
|
||||||
return nil, ErrOutOfGas
|
if err != nil || !contract.UseGas(cost) {
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if memorySize > 0 {
|
if memorySize > 0 {
|
||||||
mem.Resize(memorySize)
|
mem.Resize(memorySize)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -16,82 +16,98 @@
|
||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
func memorySha3(stack *Stack) (uint64, bool) {
|
||||||
"math/big"
|
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
)
|
|
||||||
|
|
||||||
func memorySha3(stack *Stack) *big.Int {
|
|
||||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCallDataCopy(stack *Stack) *big.Int {
|
func memoryCallDataCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryReturnDataCopy(stack *Stack) *big.Int {
|
func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCodeCopy(stack *Stack) *big.Int {
|
func memoryCodeCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryExtCodeCopy(stack *Stack) *big.Int {
|
func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(1), stack.Back(3))
|
return calcMemSize64(stack.Back(1), stack.Back(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryMLoad(stack *Stack) *big.Int {
|
func memoryMLoad(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), big.NewInt(32))
|
return calcMemSize64WithUint(stack.Back(0), 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryMStore8(stack *Stack) *big.Int {
|
func memoryMStore8(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), big.NewInt(1))
|
return calcMemSize64WithUint(stack.Back(0), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryMStore(stack *Stack) *big.Int {
|
func memoryMStore(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), big.NewInt(32))
|
return calcMemSize64WithUint(stack.Back(0), 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCreate(stack *Stack) *big.Int {
|
func memoryCreate(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(1), stack.Back(2))
|
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCreate2(stack *Stack) *big.Int {
|
func memoryCreate2(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(1), stack.Back(2))
|
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCall(stack *Stack) *big.Int {
|
func memoryCall(stack *Stack) (uint64, bool) {
|
||||||
x := calcMemSize(stack.Back(5), stack.Back(6))
|
x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
|
||||||
y := calcMemSize(stack.Back(3), stack.Back(4))
|
if overflow {
|
||||||
|
return 0, true
|
||||||
return math.BigMax(x, y)
|
}
|
||||||
|
y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
|
||||||
|
if overflow {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if x > y {
|
||||||
|
return x, false
|
||||||
|
}
|
||||||
|
return y, false
|
||||||
|
}
|
||||||
|
func memoryDelegateCall(stack *Stack) (uint64, bool) {
|
||||||
|
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
|
||||||
|
if overflow {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
||||||
|
if overflow {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if x > y {
|
||||||
|
return x, false
|
||||||
|
}
|
||||||
|
return y, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryDelegateCall(stack *Stack) *big.Int {
|
func memoryStaticCall(stack *Stack) (uint64, bool) {
|
||||||
x := calcMemSize(stack.Back(4), stack.Back(5))
|
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
|
||||||
y := calcMemSize(stack.Back(2), stack.Back(3))
|
if overflow {
|
||||||
|
return 0, true
|
||||||
return math.BigMax(x, y)
|
}
|
||||||
|
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
||||||
|
if overflow {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if x > y {
|
||||||
|
return x, false
|
||||||
|
}
|
||||||
|
return y, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryStaticCall(stack *Stack) *big.Int {
|
func memoryReturn(stack *Stack) (uint64, bool) {
|
||||||
x := calcMemSize(stack.Back(4), stack.Back(5))
|
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||||
y := calcMemSize(stack.Back(2), stack.Back(3))
|
|
||||||
|
|
||||||
return math.BigMax(x, y)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryReturn(stack *Stack) *big.Int {
|
func memoryRevert(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryRevert(stack *Stack) *big.Int {
|
func memoryLog(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||||
}
|
|
||||||
|
|
||||||
func memoryLog(stack *Stack) *big.Int {
|
|
||||||
mSize, mStart := stack.Back(1), stack.Back(0)
|
|
||||||
return calcMemSize(mStart, mSize)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||||
setDefaults(cfg)
|
setDefaults(cfg)
|
||||||
|
|
||||||
if cfg.State == nil {
|
if cfg.State == nil {
|
||||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
address = common.BytesToAddress([]byte("contract"))
|
address = common.BytesToAddress([]byte("contract"))
|
||||||
|
|
@ -129,7 +129,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
||||||
setDefaults(cfg)
|
setDefaults(cfg)
|
||||||
|
|
||||||
if cfg.State == nil {
|
if cfg.State == nil {
|
||||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
vmenv = NewEnv(cfg)
|
vmenv = NewEnv(cfg)
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -95,7 +95,7 @@ func TestExecute(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCall(t *testing.T) {
|
func TestCall(t *testing.T) {
|
||||||
state, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
address := common.HexToAddress("0x0a")
|
address := common.HexToAddress("0x0a")
|
||||||
state.SetCode(address, []byte{
|
state.SetCode(address, []byte{
|
||||||
byte(vm.PUSH1), 10,
|
byte(vm.PUSH1), 10,
|
||||||
|
|
@ -151,7 +151,7 @@ func BenchmarkCall(b *testing.B) {
|
||||||
}
|
}
|
||||||
func benchmarkEVM_Create(bench *testing.B, code string) {
|
func benchmarkEVM_Create(bench *testing.B, code string) {
|
||||||
var (
|
var (
|
||||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
sender = common.BytesToAddress([]byte("sender"))
|
sender = common.BytesToAddress([]byte("sender"))
|
||||||
receiver = common.BytesToAddress([]byte("receiver"))
|
receiver = common.BytesToAddress([]byte("receiver"))
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,28 +17,26 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeStackFunc(pop, push int) stackValidationFunc {
|
func minSwapStack(n int) int {
|
||||||
return func(stack *Stack) error {
|
return minStack(n, n)
|
||||||
if err := stack.require(pop); err != nil {
|
}
|
||||||
return err
|
func maxSwapStack(n int) int {
|
||||||
}
|
return maxStack(n, n)
|
||||||
|
|
||||||
if stack.len()+push-pop > int(params.StackLimit) {
|
|
||||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeDupStackFunc(n int) stackValidationFunc {
|
func minDupStack(n int) int {
|
||||||
return makeStackFunc(n, n+1)
|
return minStack(n, n+1)
|
||||||
|
}
|
||||||
|
func maxDupStack(n int) int {
|
||||||
|
return maxStack(n, n+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeSwapStackFunc(n int) stackValidationFunc {
|
func maxStack(pop, push int) int {
|
||||||
return makeStackFunc(n, n)
|
return int(params.StackLimit) + pop - push
|
||||||
|
}
|
||||||
|
func minStack(pops, push int) int {
|
||||||
|
return pops
|
||||||
}
|
}
|
||||||
|
|
|
||||||
79308
dashboard/assets.go
79308
dashboard/assets.go
File diff suppressed because one or more lines are too long
3
dashboard/assets/.eslintignore
Normal file
3
dashboard/assets/.eslintignore
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules/* #ignored by default
|
||||||
|
flow-typed/*
|
||||||
|
bundle.js
|
||||||
|
|
@ -16,71 +16,66 @@
|
||||||
|
|
||||||
// React syntax style mostly according to https://github.com/airbnb/javascript/tree/master/react
|
// React syntax style mostly according to https://github.com/airbnb/javascript/tree/master/react
|
||||||
{
|
{
|
||||||
'env': {
|
"env": {
|
||||||
'browser': true,
|
"browser": true,
|
||||||
'node': true,
|
"node": true,
|
||||||
'es6': true,
|
"es6": true
|
||||||
},
|
},
|
||||||
'parser': 'babel-eslint',
|
"parser": "babel-eslint",
|
||||||
'parserOptions': {
|
"parserOptions": {
|
||||||
'sourceType': 'module',
|
"sourceType": "module",
|
||||||
'ecmaVersion': 6,
|
"ecmaVersion": 6,
|
||||||
'ecmaFeatures': {
|
"ecmaFeatures": {
|
||||||
'jsx': true,
|
"jsx": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'extends': 'airbnb',
|
"extends": [
|
||||||
'plugins': [
|
"eslint:recommended",
|
||||||
'flowtype',
|
"airbnb",
|
||||||
'react',
|
"plugin:flowtype/recommended",
|
||||||
|
"plugin:react/recommended"
|
||||||
],
|
],
|
||||||
'rules': {
|
"plugins": [
|
||||||
'no-tabs': 'off',
|
"flowtype",
|
||||||
'indent': ['error', 'tab'],
|
"react"
|
||||||
'react/jsx-indent': ['error', 'tab'],
|
],
|
||||||
'react/jsx-indent-props': ['error', 'tab'],
|
"rules": {
|
||||||
'react/prefer-stateless-function': 'off',
|
"no-tabs": "off",
|
||||||
'jsx-quotes': ['error', 'prefer-single'],
|
"indent": ["error", "tab"],
|
||||||
'no-plusplus': 'off',
|
"react/jsx-indent": ["error", "tab"],
|
||||||
'no-console': ['error', { allow: ['error'] }],
|
"react/jsx-indent-props": ["error", "tab"],
|
||||||
|
"react/prefer-stateless-function": "off",
|
||||||
|
"react/destructuring-assignment": ["error", "always", {"ignoreClassFields": true}],
|
||||||
|
"jsx-quotes": ["error", "prefer-single"],
|
||||||
|
"no-plusplus": "off",
|
||||||
|
"no-console": ["error", { "allow": ["error"] }],
|
||||||
// Specifies the maximum length of a line.
|
// Specifies the maximum length of a line.
|
||||||
'max-len': ['warn', 120, 2, {
|
"max-len": ["warn", 120, 2, {
|
||||||
'ignoreUrls': true,
|
"ignoreUrls": true,
|
||||||
'ignoreComments': false,
|
"ignoreComments": false,
|
||||||
'ignoreRegExpLiterals': true,
|
"ignoreRegExpLiterals": true,
|
||||||
'ignoreStrings': true,
|
"ignoreStrings": true,
|
||||||
'ignoreTemplateLiterals': true,
|
"ignoreTemplateLiterals": true
|
||||||
}],
|
}],
|
||||||
// Enforces consistent spacing between keys and values in object literal properties.
|
// Enforces consistent spacing between keys and values in object literal properties.
|
||||||
'key-spacing': ['error', {'align': {
|
"key-spacing": ["error", {"align": {
|
||||||
'beforeColon': false,
|
"beforeColon": false,
|
||||||
'afterColon': true,
|
"afterColon": true,
|
||||||
'on': 'value'
|
"on": "value"
|
||||||
}}],
|
}}],
|
||||||
// Prohibits padding inside curly braces.
|
// Prohibits padding inside curly braces.
|
||||||
'object-curly-spacing': ['error', 'never'],
|
"object-curly-spacing": ["error", "never"],
|
||||||
'no-use-before-define': 'off', // messageAPI
|
"no-use-before-define": "off", // message types
|
||||||
'default-case': 'off',
|
"default-case": "off"
|
||||||
|
|
||||||
'flowtype/boolean-style': ['error', 'boolean'],
|
|
||||||
'flowtype/define-flow-type': 'warn',
|
|
||||||
'flowtype/generic-spacing': ['error', 'never'],
|
|
||||||
'flowtype/no-primitive-constructor-types': 'error',
|
|
||||||
'flowtype/no-weak-types': 'error',
|
|
||||||
'flowtype/object-type-delimiter': ['error', 'comma'],
|
|
||||||
'flowtype/require-valid-file-annotation': 'error',
|
|
||||||
'flowtype/semi': ['error', 'always'],
|
|
||||||
'flowtype/space-after-type-colon': ['error', 'always'],
|
|
||||||
'flowtype/space-before-generic-bracket': ['error', 'never'],
|
|
||||||
'flowtype/space-before-type-colon': ['error', 'never'],
|
|
||||||
'flowtype/union-intersection-spacing': ['error', 'always'],
|
|
||||||
'flowtype/use-flow-type': 'warn',
|
|
||||||
'flowtype/valid-syntax': 'warn',
|
|
||||||
},
|
},
|
||||||
'settings': {
|
"settings": {
|
||||||
'flowtype': {
|
"import/resolver": {
|
||||||
'onlyFilesWithFlowAnnotation': true,
|
"node": {
|
||||||
|
"paths": ["components"] // import './components/Component' -> import 'Component'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flowtype": {
|
||||||
|
"onlyFilesWithFlowAnnotation": true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,5 @@ node_modules/jss/flow-typed
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
include_warnings=true
|
include_warnings=true
|
||||||
|
module.system.node.resolve_dirname=node_modules
|
||||||
|
module.system.node.resolve_dirname=components
|
||||||
|
|
|
||||||
|
|
@ -16,43 +16,46 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import {faHome, faLink, faGlobeEurope, faTachometerAlt, faList} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import {faCreditCard} from '@fortawesome/free-regular-svg-icons';
|
||||||
|
|
||||||
type ProvidedMenuProp = {|title: string, icon: string|};
|
type ProvidedMenuProp = {|title: string, icon: string|};
|
||||||
const menuSkeletons: Array<{|id: string, menu: ProvidedMenuProp|}> = [
|
const menuSkeletons: Array<{|id: string, menu: ProvidedMenuProp|}> = [
|
||||||
{
|
{
|
||||||
id: 'home',
|
id: 'home',
|
||||||
menu: {
|
menu: {
|
||||||
title: 'Home',
|
title: 'Home',
|
||||||
icon: 'home',
|
icon: faHome,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
id: 'chain',
|
id: 'chain',
|
||||||
menu: {
|
menu: {
|
||||||
title: 'Chain',
|
title: 'Chain',
|
||||||
icon: 'link',
|
icon: faLink,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
id: 'txpool',
|
id: 'txpool',
|
||||||
menu: {
|
menu: {
|
||||||
title: 'TxPool',
|
title: 'TxPool',
|
||||||
icon: 'credit-card',
|
icon: faCreditCard,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
id: 'network',
|
id: 'network',
|
||||||
menu: {
|
menu: {
|
||||||
title: 'Network',
|
title: 'Network',
|
||||||
icon: 'globe',
|
icon: faGlobeEurope,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
id: 'system',
|
id: 'system',
|
||||||
menu: {
|
menu: {
|
||||||
title: 'System',
|
title: 'System',
|
||||||
icon: 'tachometer',
|
icon: faTachometerAlt,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
id: 'logs',
|
id: 'logs',
|
||||||
menu: {
|
menu: {
|
||||||
title: 'Logs',
|
title: 'Logs',
|
||||||
icon: 'list',
|
icon: faList,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -64,8 +67,26 @@ export const MENU: Map<string, {...MenuProp}> = new Map(menuSkeletons.map(({id,
|
||||||
|
|
||||||
export const DURATION = 200;
|
export const DURATION = 200;
|
||||||
|
|
||||||
|
export const chartStrokeWidth = 0.2;
|
||||||
|
|
||||||
export const styles = {
|
export const styles = {
|
||||||
light: {
|
light: {
|
||||||
color: 'rgba(255, 255, 255, 0.54)',
|
color: 'rgba(255, 255, 255, 0.54)',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// unit contains the units for the bytePlotter.
|
||||||
|
export const unit = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
|
||||||
|
|
||||||
|
// simplifyBytes returns the simplified version of the given value followed by the unit.
|
||||||
|
export const simplifyBytes = (x: number) => {
|
||||||
|
let i = 0;
|
||||||
|
for (; x > 1024 && i < 8; i++) {
|
||||||
|
x /= 1024;
|
||||||
|
}
|
||||||
|
return x.toFixed(2).toString().concat(' ', unit[i], 'B');
|
||||||
|
};
|
||||||
|
|
||||||
|
// hues contains predefined colors for gradient stop colors.
|
||||||
|
export const hues = ['#00FF00', '#FFFF00', '#FF7F00', '#FF0000'];
|
||||||
|
export const hueScale = [0, 2048, 102400, 2097152];
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import type {ChildrenArray} from 'react';
|
import type {ChildrenArray} from 'react';
|
||||||
|
|
||||||
import Grid from 'material-ui/Grid';
|
import Grid from '@material-ui/core/Grid';
|
||||||
|
|
||||||
// styles contains the constant styles of the component.
|
// styles contains the constant styles of the component.
|
||||||
const styles = {
|
const styles = {
|
||||||
|
|
@ -33,7 +33,7 @@ const styles = {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
children: ChildrenArray<React$Element<any>>,
|
children: ChildrenArray<React$Element<any>>,
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
import Typography from 'material-ui/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import {styles} from '../common';
|
import {styles, simplifyBytes} from '../common';
|
||||||
|
|
||||||
// multiplier multiplies a number by another.
|
// multiplier multiplies a number by another.
|
||||||
export const multiplier = <T>(by: number = 1) => (x: number) => x * by;
|
export const multiplier = <T>(by: number = 1) => (x: number) => x * by;
|
||||||
|
|
@ -37,18 +37,6 @@ export const percentPlotter = <T>(text: string, mapper: (T => T) = multiplier(1)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// unit contains the units for the bytePlotter.
|
|
||||||
const unit = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
|
|
||||||
|
|
||||||
// simplifyBytes returns the simplified version of the given value followed by the unit.
|
|
||||||
const simplifyBytes = (x: number) => {
|
|
||||||
let i = 0;
|
|
||||||
for (; x > 1024 && i < 8; i++) {
|
|
||||||
x /= 1024;
|
|
||||||
}
|
|
||||||
return x.toFixed(2).toString().concat(' ', unit[i], 'B');
|
|
||||||
};
|
|
||||||
|
|
||||||
// bytePlotter renders a tooltip, which displays the payload as a byte value.
|
// bytePlotter renders a tooltip, which displays the payload as a byte value.
|
||||||
export const bytePlotter = <T>(text: string, mapper: (T => T) = multiplier(1)) => (payload: T) => {
|
export const bytePlotter = <T>(text: string, mapper: (T => T) = multiplier(1)) => (payload: T) => {
|
||||||
const p = mapper(payload);
|
const p = mapper(payload);
|
||||||
|
|
@ -70,7 +58,8 @@ export const bytePerSecPlotter = <T>(text: string, mapper: (T => T) = multiplier
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Typography type='caption' color='inherit'>
|
<Typography type='caption' color='inherit'>
|
||||||
<span style={styles.light}>{text}</span> {simplifyBytes(p)}/s
|
<span style={styles.light}>{text}</span>
|
||||||
|
{simplifyBytes(p)}/s
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,16 @@
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
import {hot} from 'react-hot-loader';
|
||||||
|
|
||||||
import withStyles from 'material-ui/styles/withStyles';
|
import withStyles from '@material-ui/core/styles/withStyles';
|
||||||
|
|
||||||
import Header from './Header';
|
import Header from 'Header';
|
||||||
import Body from './Body';
|
import Body from 'Body';
|
||||||
|
import {inserter as logInserter, SAME} from 'Logs';
|
||||||
|
import {inserter as peerInserter} from 'Network';
|
||||||
import {MENU} from '../common';
|
import {MENU} from '../common';
|
||||||
import type {Content} from '../types/content';
|
import type {Content} from '../types/content';
|
||||||
import {inserter as logInserter} from './Logs';
|
|
||||||
|
|
||||||
// deepUpdate updates an object corresponding to the given update data, which has
|
// deepUpdate updates an object corresponding to the given update data, which has
|
||||||
// the shape of the same structure as the original object. updater also has the same
|
// the shape of the same structure as the original object. updater also has the same
|
||||||
|
|
@ -37,7 +39,6 @@ import {inserter as logInserter} from './Logs';
|
||||||
// of the update.
|
// of the update.
|
||||||
const deepUpdate = (updater: Object, update: Object, prev: Object): $Shape<Content> => {
|
const deepUpdate = (updater: Object, update: Object, prev: Object): $Shape<Content> => {
|
||||||
if (typeof update === 'undefined') {
|
if (typeof update === 'undefined') {
|
||||||
// TODO (kurkomisi): originally this was deep copy, investigate it.
|
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
if (typeof updater === 'function') {
|
if (typeof updater === 'function') {
|
||||||
|
|
@ -88,8 +89,13 @@ const defaultContent: () => Content = () => ({
|
||||||
home: {},
|
home: {},
|
||||||
chain: {},
|
chain: {},
|
||||||
txpool: {},
|
txpool: {},
|
||||||
network: {},
|
network: {
|
||||||
system: {
|
peers: {
|
||||||
|
bundles: {},
|
||||||
|
},
|
||||||
|
diff: [],
|
||||||
|
},
|
||||||
|
system: {
|
||||||
activeMemory: [],
|
activeMemory: [],
|
||||||
virtualMemory: [],
|
virtualMemory: [],
|
||||||
networkIngress: [],
|
networkIngress: [],
|
||||||
|
|
@ -103,8 +109,8 @@ const defaultContent: () => Content = () => ({
|
||||||
chunks: [],
|
chunks: [],
|
||||||
endTop: false,
|
endTop: false,
|
||||||
endBottom: true,
|
endBottom: true,
|
||||||
topChanged: 0,
|
topChanged: SAME,
|
||||||
bottomChanged: 0,
|
bottomChanged: SAME,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -119,7 +125,7 @@ const updaters = {
|
||||||
home: null,
|
home: null,
|
||||||
chain: null,
|
chain: null,
|
||||||
txpool: null,
|
txpool: null,
|
||||||
network: null,
|
network: peerInserter(200),
|
||||||
system: {
|
system: {
|
||||||
activeMemory: appender(200),
|
activeMemory: appender(200),
|
||||||
virtualMemory: appender(200),
|
virtualMemory: appender(200),
|
||||||
|
|
@ -186,8 +192,8 @@ class Dashboard extends Component<Props, State> {
|
||||||
// reconnect establishes a websocket connection with the server, listens for incoming messages
|
// reconnect establishes a websocket connection with the server, listens for incoming messages
|
||||||
// and tries to reconnect on connection loss.
|
// and tries to reconnect on connection loss.
|
||||||
reconnect = () => {
|
reconnect = () => {
|
||||||
// PROD is defined by webpack.
|
const host = process.env.NODE_ENV === 'production' ? window.location.host : 'localhost:8080';
|
||||||
const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://')}${PROD ? window.location.host : 'localhost:8080'}/api`);
|
const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://')}${host}/api`);
|
||||||
server.onopen = () => {
|
server.onopen = () => {
|
||||||
this.setState({content: defaultContent(), shouldUpdate: {}, server});
|
this.setState({content: defaultContent(), shouldUpdate: {}, server});
|
||||||
};
|
};
|
||||||
|
|
@ -249,4 +255,4 @@ class Dashboard extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(themeStyles)(Dashboard);
|
export default hot(module)(withStyles(themeStyles)(Dashboard));
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,19 @@
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
import withStyles from 'material-ui/styles/withStyles';
|
import withStyles from '@material-ui/core/styles/withStyles';
|
||||||
import Typography from 'material-ui/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import Grid from 'material-ui/Grid';
|
import Grid from '@material-ui/core/Grid';
|
||||||
import {ResponsiveContainer, AreaChart, Area, Tooltip} from 'recharts';
|
import ResponsiveContainer from 'recharts/es6/component/ResponsiveContainer';
|
||||||
|
import AreaChart from 'recharts/es6/chart/AreaChart';
|
||||||
|
import Area from 'recharts/es6/cartesian/Area';
|
||||||
|
import ReferenceLine from 'recharts/es6/cartesian/ReferenceLine';
|
||||||
|
import Label from 'recharts/es6/component/Label';
|
||||||
|
import Tooltip from 'recharts/es6/component/Tooltip';
|
||||||
|
|
||||||
import ChartRow from './ChartRow';
|
import ChartRow from 'ChartRow';
|
||||||
import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from './CustomTooltip';
|
import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from 'CustomTooltip';
|
||||||
import {styles as commonStyles} from '../common';
|
import {chartStrokeWidth, styles as commonStyles} from '../common';
|
||||||
import type {General, System} from '../types/content';
|
import type {General, System} from '../types/content';
|
||||||
|
|
||||||
const FOOTER_SYNC_ID = 'footerSyncId';
|
const FOOTER_SYNC_ID = 'footerSyncId';
|
||||||
|
|
@ -38,6 +43,15 @@ const TRAFFIC = 'traffic';
|
||||||
const TOP = 'Top';
|
const TOP = 'Top';
|
||||||
const BOTTOM = 'Bottom';
|
const BOTTOM = 'Bottom';
|
||||||
|
|
||||||
|
const cpuLabelTop = 'Process load';
|
||||||
|
const cpuLabelBottom = 'System load';
|
||||||
|
const memoryLabelTop = 'Active memory';
|
||||||
|
const memoryLabelBottom = 'Virtual memory';
|
||||||
|
const diskLabelTop = 'Disk read';
|
||||||
|
const diskLabelBottom = 'Disk write';
|
||||||
|
const trafficLabelTop = 'Download';
|
||||||
|
const trafficLabelBottom = 'Upload';
|
||||||
|
|
||||||
// styles contains the constant styles of the component.
|
// styles contains the constant styles of the component.
|
||||||
const styles = {
|
const styles = {
|
||||||
footer: {
|
footer: {
|
||||||
|
|
@ -53,6 +67,10 @@ const styles = {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
width: '99%',
|
width: '99%',
|
||||||
},
|
},
|
||||||
|
link: {
|
||||||
|
color: 'inherit',
|
||||||
|
textDecoration: 'none',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// themeStyles returns the styles generated from the theme for the component.
|
// themeStyles returns the styles generated from the theme for the component.
|
||||||
|
|
@ -73,18 +91,23 @@ export type Props = {
|
||||||
shouldUpdate: Object,
|
shouldUpdate: Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type State = {};
|
||||||
|
|
||||||
// Footer renders the footer of the dashboard.
|
// Footer renders the footer of the dashboard.
|
||||||
class Footer extends Component<Props> {
|
class Footer extends Component<Props, State> {
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps: Readonly<Props>, nextState: Readonly<State>, nextContext: any) {
|
||||||
return typeof nextProps.shouldUpdate.general !== 'undefined' || typeof nextProps.shouldUpdate.system !== 'undefined';
|
return typeof nextProps.shouldUpdate.general !== 'undefined' || typeof nextProps.shouldUpdate.system !== 'undefined';
|
||||||
}
|
}
|
||||||
|
|
||||||
// halfHeightChart renders an area chart with half of the height of its parent.
|
// halfHeightChart renders an area chart with half of the height of its parent.
|
||||||
halfHeightChart = (chartProps, tooltip, areaProps) => (
|
halfHeightChart = (chartProps, tooltip, areaProps, label, position) => (
|
||||||
<ResponsiveContainer width='100%' height='50%'>
|
<ResponsiveContainer width='100%' height='50%'>
|
||||||
<AreaChart {...chartProps} >
|
<AreaChart {...chartProps}>
|
||||||
{!tooltip || (<Tooltip cursor={false} content={<CustomTooltip tooltip={tooltip} />} />)}
|
{!tooltip || (<Tooltip cursor={false} content={<CustomTooltip tooltip={tooltip} />} />)}
|
||||||
<Area isAnimationActive={false} type='monotone' {...areaProps} />
|
<Area isAnimationActive={false} strokeWidth={chartStrokeWidth} type='monotone' {...areaProps} />
|
||||||
|
<ReferenceLine x={0} strokeWidth={0}>
|
||||||
|
<Label fill={areaProps.fill} value={label} position={position} />
|
||||||
|
</ReferenceLine>
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
);
|
);
|
||||||
|
|
@ -111,6 +134,8 @@ class Footer extends Component<Props> {
|
||||||
},
|
},
|
||||||
topChart.tooltip,
|
topChart.tooltip,
|
||||||
{dataKey: topKey, stroke: topColor, fill: topColor},
|
{dataKey: topKey, stroke: topColor, fill: topColor},
|
||||||
|
topChart.label,
|
||||||
|
'insideBottomLeft',
|
||||||
)}
|
)}
|
||||||
{this.halfHeightChart(
|
{this.halfHeightChart(
|
||||||
{
|
{
|
||||||
|
|
@ -120,6 +145,8 @@ class Footer extends Component<Props> {
|
||||||
},
|
},
|
||||||
bottomChart.tooltip,
|
bottomChart.tooltip,
|
||||||
{dataKey: bottomKey, stroke: bottomColor, fill: bottomColor},
|
{dataKey: bottomKey, stroke: bottomColor, fill: bottomColor},
|
||||||
|
bottomChart.label,
|
||||||
|
'insideTopLeft',
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -135,37 +162,42 @@ class Footer extends Component<Props> {
|
||||||
{this.doubleChart(
|
{this.doubleChart(
|
||||||
FOOTER_SYNC_ID,
|
FOOTER_SYNC_ID,
|
||||||
CPU,
|
CPU,
|
||||||
{data: system.processCPU, tooltip: percentPlotter('Process load')},
|
{data: system.processCPU, tooltip: percentPlotter(cpuLabelTop), label: cpuLabelTop},
|
||||||
{data: system.systemCPU, tooltip: percentPlotter('System load', multiplier(-1))},
|
{data: system.systemCPU, tooltip: percentPlotter(cpuLabelBottom, multiplier(-1)), label: cpuLabelBottom},
|
||||||
)}
|
)}
|
||||||
{this.doubleChart(
|
{this.doubleChart(
|
||||||
FOOTER_SYNC_ID,
|
FOOTER_SYNC_ID,
|
||||||
MEMORY,
|
MEMORY,
|
||||||
{data: system.activeMemory, tooltip: bytePlotter('Active memory')},
|
{data: system.activeMemory, tooltip: bytePlotter(memoryLabelTop), label: memoryLabelTop},
|
||||||
{data: system.virtualMemory, tooltip: bytePlotter('Virtual memory', multiplier(-1))},
|
{data: system.virtualMemory, tooltip: bytePlotter(memoryLabelBottom, multiplier(-1)), label: memoryLabelBottom},
|
||||||
)}
|
)}
|
||||||
{this.doubleChart(
|
{this.doubleChart(
|
||||||
FOOTER_SYNC_ID,
|
FOOTER_SYNC_ID,
|
||||||
DISK,
|
DISK,
|
||||||
{data: system.diskRead, tooltip: bytePerSecPlotter('Disk read')},
|
{data: system.diskRead, tooltip: bytePerSecPlotter(diskLabelTop), label: diskLabelTop},
|
||||||
{data: system.diskWrite, tooltip: bytePerSecPlotter('Disk write', multiplier(-1))},
|
{data: system.diskWrite, tooltip: bytePerSecPlotter(diskLabelBottom, multiplier(-1)), label: diskLabelBottom},
|
||||||
)}
|
)}
|
||||||
{this.doubleChart(
|
{this.doubleChart(
|
||||||
FOOTER_SYNC_ID,
|
FOOTER_SYNC_ID,
|
||||||
TRAFFIC,
|
TRAFFIC,
|
||||||
{data: system.networkIngress, tooltip: bytePerSecPlotter('Download')},
|
{data: system.networkIngress, tooltip: bytePerSecPlotter(trafficLabelTop), label: trafficLabelTop},
|
||||||
{data: system.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))},
|
{data: system.networkEgress, tooltip: bytePerSecPlotter(trafficLabelBottom, multiplier(-1)), label: trafficLabelBottom},
|
||||||
)}
|
)}
|
||||||
</ChartRow>
|
</ChartRow>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item >
|
<Grid item>
|
||||||
<Typography type='caption' color='inherit'>
|
<Typography type='caption' color='inherit'>
|
||||||
<span style={commonStyles.light}>Geth</span> {general.version}
|
<span style={commonStyles.light}>Geth</span> {general.version}
|
||||||
</Typography>
|
</Typography>
|
||||||
{general.commit && (
|
{general.commit && (
|
||||||
<Typography type='caption' color='inherit'>
|
<Typography type='caption' color='inherit'>
|
||||||
<span style={commonStyles.light}>{'Commit '}</span>
|
<span style={commonStyles.light}>{'Commit '}</span>
|
||||||
<a href={`https://github.com/ethereum/go-ethereum/commit/${general.commit}`} target='_blank' style={{color: 'inherit', textDecoration: 'none'}} >
|
<a
|
||||||
|
href={`https://github.com/ethereum/go-ethereum/commit/${general.commit}`}
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
style={styles.link}
|
||||||
|
>
|
||||||
{general.commit.substring(0, 8)}
|
{general.commit.substring(0, 8)}
|
||||||
</a>
|
</a>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,13 @@
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
import withStyles from 'material-ui/styles/withStyles';
|
import withStyles from '@material-ui/core/styles/withStyles';
|
||||||
import AppBar from 'material-ui/AppBar';
|
import AppBar from '@material-ui/core/AppBar';
|
||||||
import Toolbar from 'material-ui/Toolbar';
|
import Toolbar from '@material-ui/core/Toolbar';
|
||||||
import IconButton from 'material-ui/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import Icon from 'material-ui/Icon';
|
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||||
import MenuIcon from 'material-ui-icons/Menu';
|
import {faBars} from '@fortawesome/free-solid-svg-icons';
|
||||||
import Typography from 'material-ui/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
|
||||||
// styles contains the constant styles of the component.
|
// styles contains the constant styles of the component.
|
||||||
const styles = {
|
const styles = {
|
||||||
|
|
@ -67,9 +67,7 @@ class Header extends Component<Props> {
|
||||||
<AppBar position='static' className={classes.header} style={styles.header}>
|
<AppBar position='static' className={classes.header} style={styles.header}>
|
||||||
<Toolbar className={classes.toolbar} style={styles.toolbar}>
|
<Toolbar className={classes.toolbar} style={styles.toolbar}>
|
||||||
<IconButton onClick={this.props.switchSideBar}>
|
<IconButton onClick={this.props.switchSideBar}>
|
||||||
<Icon>
|
<FontAwesomeIcon icon={faBars} />
|
||||||
<MenuIcon />
|
|
||||||
</Icon>
|
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography type='title' color='inherit' noWrap className={classes.title}>
|
<Typography type='title' color='inherit' noWrap className={classes.title}>
|
||||||
Go Ethereum Dashboard
|
Go Ethereum Dashboard
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
import List, {ListItem} from 'material-ui/List';
|
import List from '@material-ui/core/List';
|
||||||
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
import escapeHtml from 'escape-html';
|
import escapeHtml from 'escape-html';
|
||||||
import type {Record, Content, LogsMessage, Logs as LogsType} from '../types/content';
|
import type {Record, Content, LogsMessage, Logs as LogsType} from '../types/content';
|
||||||
|
|
||||||
|
|
@ -104,9 +105,9 @@ const createChunk = (records: Array<Record>) => {
|
||||||
|
|
||||||
// ADDED, SAME and REMOVED are used to track the change of the log chunk array.
|
// ADDED, SAME and REMOVED are used to track the change of the log chunk array.
|
||||||
// The scroll position is set using these values.
|
// The scroll position is set using these values.
|
||||||
const ADDED = 1;
|
export const ADDED = 1;
|
||||||
const SAME = 0;
|
export const SAME = 0;
|
||||||
const REMOVED = -1;
|
export const REMOVED = -1;
|
||||||
|
|
||||||
// inserter is a state updater function for the main component, which inserts the new log chunk into the chunk array.
|
// inserter is a state updater function for the main component, which inserts the new log chunk into the chunk array.
|
||||||
// limit is the maximum length of the chunk array, used in order to prevent the browser from OOM.
|
// limit is the maximum length of the chunk array, used in order to prevent the browser from OOM.
|
||||||
|
|
@ -166,7 +167,7 @@ export const inserter = (limit: number) => (update: LogsMessage, prev: LogsType)
|
||||||
// styles contains the constant styles of the component.
|
// styles contains the constant styles of the component.
|
||||||
const styles = {
|
const styles = {
|
||||||
logListItem: {
|
logListItem: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
lineHeight: 1.231,
|
lineHeight: 1.231,
|
||||||
},
|
},
|
||||||
logChunk: {
|
logChunk: {
|
||||||
|
|
@ -251,15 +252,15 @@ class Logs extends Component<Props, State> {
|
||||||
// atBottom checks if the scroll position it at the bottom of the container.
|
// atBottom checks if the scroll position it at the bottom of the container.
|
||||||
atBottom = () => {
|
atBottom = () => {
|
||||||
const {container} = this.props;
|
const {container} = this.props;
|
||||||
return container.scrollHeight - container.scrollTop <=
|
return container.scrollHeight - container.scrollTop
|
||||||
container.clientHeight + container.scrollHeight * requestBand;
|
<= container.clientHeight + container.scrollHeight * requestBand;
|
||||||
};
|
};
|
||||||
|
|
||||||
// beforeUpdate is called by the parent component, saves the previous scroll position
|
// beforeUpdate is called by the parent component, saves the previous scroll position
|
||||||
// and the height of the first log chunk, which can be deleted during the insertion.
|
// and the height of the first log chunk, which can be deleted during the insertion.
|
||||||
beforeUpdate = () => {
|
beforeUpdate = () => {
|
||||||
let firstHeight = 0;
|
let firstHeight = 0;
|
||||||
let chunkList = this.content.children[1];
|
const chunkList = this.content.children[1];
|
||||||
if (chunkList && chunkList.children[0]) {
|
if (chunkList && chunkList.children[0]) {
|
||||||
firstHeight = chunkList.children[0].clientHeight;
|
firstHeight = chunkList.children[0].clientHeight;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,12 @@
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
import withStyles from 'material-ui/styles/withStyles';
|
import withStyles from '@material-ui/core/styles/withStyles';
|
||||||
|
|
||||||
|
import Network from 'Network';
|
||||||
|
import Logs from 'Logs';
|
||||||
|
import Footer from 'Footer';
|
||||||
import {MENU} from '../common';
|
import {MENU} from '../common';
|
||||||
import Logs from './Logs';
|
|
||||||
import Footer from './Footer';
|
|
||||||
import type {Content} from '../types/content';
|
import type {Content} from '../types/content';
|
||||||
|
|
||||||
// styles contains the constant styles of the component.
|
// styles contains the constant styles of the component.
|
||||||
|
|
@ -33,7 +34,7 @@ const styles = {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -54,21 +55,16 @@ export type Props = {
|
||||||
send: string => void,
|
send: string => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type State = {};
|
||||||
|
|
||||||
// Main renders the chosen content.
|
// Main renders the chosen content.
|
||||||
class Main extends Component<Props> {
|
class Main extends Component<Props, State> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.container = React.createRef();
|
this.container = React.createRef();
|
||||||
this.content = React.createRef();
|
this.content = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
getSnapshotBeforeUpdate() {
|
|
||||||
if (this.content && typeof this.content.beforeUpdate === 'function') {
|
|
||||||
return this.content.beforeUpdate();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||||
if (this.content && typeof this.content.didUpdate === 'function') {
|
if (this.content && typeof this.content.didUpdate === 'function') {
|
||||||
this.content.didUpdate(prevProps, prevState, snapshot);
|
this.content.didUpdate(prevProps, prevState, snapshot);
|
||||||
|
|
@ -81,6 +77,13 @@ class Main extends Component<Props> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getSnapshotBeforeUpdate(prevProps: Readonly<P>, prevState: Readonly<S>) {
|
||||||
|
if (this.content && typeof this.content.beforeUpdate === 'function') {
|
||||||
|
return this.content.beforeUpdate();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
classes, active, content, shouldUpdate,
|
classes, active, content, shouldUpdate,
|
||||||
|
|
@ -89,9 +92,20 @@ class Main extends Component<Props> {
|
||||||
let children = null;
|
let children = null;
|
||||||
switch (active) {
|
switch (active) {
|
||||||
case MENU.get('home').id:
|
case MENU.get('home').id:
|
||||||
|
children = <div>Work in progress.</div>;
|
||||||
|
break;
|
||||||
case MENU.get('chain').id:
|
case MENU.get('chain').id:
|
||||||
|
children = <div>Work in progress.</div>;
|
||||||
|
break;
|
||||||
case MENU.get('txpool').id:
|
case MENU.get('txpool').id:
|
||||||
|
children = <div>Work in progress.</div>;
|
||||||
|
break;
|
||||||
case MENU.get('network').id:
|
case MENU.get('network').id:
|
||||||
|
children = <Network
|
||||||
|
content={this.props.content.network}
|
||||||
|
container={this.container}
|
||||||
|
/>;
|
||||||
|
break;
|
||||||
case MENU.get('system').id:
|
case MENU.get('system').id:
|
||||||
children = <div>Work in progress.</div>;
|
children = <div>Work in progress.</div>;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
529
dashboard/assets/components/Network.jsx
Normal file
529
dashboard/assets/components/Network.jsx
Normal file
|
|
@ -0,0 +1,529 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
|
import Table from '@material-ui/core/Table';
|
||||||
|
import TableHead from '@material-ui/core/TableHead';
|
||||||
|
import TableBody from '@material-ui/core/TableBody';
|
||||||
|
import TableRow from '@material-ui/core/TableRow';
|
||||||
|
import TableCell from '@material-ui/core/TableCell';
|
||||||
|
import Grid from '@material-ui/core/Grid/Grid';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
import {AreaChart, Area, Tooltip, YAxis} from 'recharts';
|
||||||
|
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||||
|
import {faCircle as fasCircle} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import {faCircle as farCircle} from '@fortawesome/free-regular-svg-icons';
|
||||||
|
import convert from 'color-convert';
|
||||||
|
|
||||||
|
import CustomTooltip, {bytePlotter, multiplier} from 'CustomTooltip';
|
||||||
|
import type {Network as NetworkType, PeerEvent} from '../types/content';
|
||||||
|
import {styles as commonStyles, chartStrokeWidth, hues, hueScale} from '../common';
|
||||||
|
|
||||||
|
// Peer chart dimensions.
|
||||||
|
const trafficChartHeight = 18;
|
||||||
|
const trafficChartWidth = 400;
|
||||||
|
|
||||||
|
// setMaxIngress adjusts the peer chart's gradient values based on the given value.
|
||||||
|
const setMaxIngress = (peer, value) => {
|
||||||
|
peer.maxIngress = value;
|
||||||
|
peer.ingressGradient = [];
|
||||||
|
peer.ingressGradient.push({offset: hueScale[0], color: hues[0]});
|
||||||
|
let i = 1;
|
||||||
|
for (; i < hues.length && value > hueScale[i]; i++) {
|
||||||
|
peer.ingressGradient.push({offset: Math.floor(hueScale[i] * 100 / value), color: hues[i]});
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
if (i < hues.length - 1) {
|
||||||
|
// Usually the maximum value gets between two points on the predefined
|
||||||
|
// color scale (e.g. 123KB is somewhere between 100KB (#FFFF00) and
|
||||||
|
// 1MB (#FF0000)), and the charts need to be comparable by the colors,
|
||||||
|
// so we have to calculate the last hue using the maximum value and the
|
||||||
|
// surrounding hues in order to avoid the uniformity of the top colors
|
||||||
|
// on the charts. For this reason the two hues are translated into the
|
||||||
|
// CIELAB color space, and the top color will be their weighted average
|
||||||
|
// (CIELAB is perceptually uniform, meaning that any point on the line
|
||||||
|
// between two pure color points is also a pure color, so the weighted
|
||||||
|
// average will not lose from the saturation).
|
||||||
|
//
|
||||||
|
// In case the maximum value is greater than the biggest predefined
|
||||||
|
// scale value, the top of the chart will have uniform color.
|
||||||
|
const lastHue = convert.hex.lab(hues[i]);
|
||||||
|
const proportion = (value - hueScale[i]) * 100 / (hueScale[i + 1] - hueScale[i]);
|
||||||
|
convert.hex.lab(hues[i + 1]).forEach((val, j) => {
|
||||||
|
lastHue[j] = (lastHue[j] * proportion + val * (100 - proportion)) / 100;
|
||||||
|
});
|
||||||
|
peer.ingressGradient.push({offset: 100, color: `#${convert.lab.hex(lastHue)}`});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// setMaxEgress adjusts the peer chart's gradient values based on the given value.
|
||||||
|
// In case of the egress the chart is upside down, so the gradients need to be
|
||||||
|
// calculated inversely compared to the ingress.
|
||||||
|
const setMaxEgress = (peer, value) => {
|
||||||
|
peer.maxEgress = value;
|
||||||
|
peer.egressGradient = [];
|
||||||
|
peer.egressGradient.push({offset: 100 - hueScale[0], color: hues[0]});
|
||||||
|
let i = 1;
|
||||||
|
for (; i < hues.length && value > hueScale[i]; i++) {
|
||||||
|
peer.egressGradient.unshift({offset: 100 - Math.floor(hueScale[i] * 100 / value), color: hues[i]});
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
if (i < hues.length - 1) {
|
||||||
|
// Calculate the last hue.
|
||||||
|
const lastHue = convert.hex.lab(hues[i]);
|
||||||
|
const proportion = (value - hueScale[i]) * 100 / (hueScale[i + 1] - hueScale[i]);
|
||||||
|
convert.hex.lab(hues[i + 1]).forEach((val, j) => {
|
||||||
|
lastHue[j] = (lastHue[j] * proportion + val * (100 - proportion)) / 100;
|
||||||
|
});
|
||||||
|
peer.egressGradient.unshift({offset: 0, color: `#${convert.lab.hex(lastHue)}`});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// setIngressChartAttributes searches for the maximum value of the ingress
|
||||||
|
// samples, and adjusts the peer chart's gradient values accordingly.
|
||||||
|
const setIngressChartAttributes = (peer) => {
|
||||||
|
let max = 0;
|
||||||
|
peer.ingress.forEach(({value}) => {
|
||||||
|
if (value > max) {
|
||||||
|
max = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setMaxIngress(peer, max);
|
||||||
|
};
|
||||||
|
|
||||||
|
// setEgressChartAttributes searches for the maximum value of the egress
|
||||||
|
// samples, and adjusts the peer chart's gradient values accordingly.
|
||||||
|
const setEgressChartAttributes = (peer) => {
|
||||||
|
let max = 0;
|
||||||
|
peer.egress.forEach(({value}) => {
|
||||||
|
if (value > max) {
|
||||||
|
max = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setMaxEgress(peer, max);
|
||||||
|
};
|
||||||
|
|
||||||
|
// inserter is a state updater function for the main component, which handles the peers.
|
||||||
|
export const inserter = (sampleLimit: number) => (update: NetworkType, prev: NetworkType) => {
|
||||||
|
// The first message contains the metered peer history.
|
||||||
|
if (update.peers && update.peers.bundles) {
|
||||||
|
prev.peers = update.peers;
|
||||||
|
Object.values(prev.peers.bundles).forEach((bundle) => {
|
||||||
|
if (bundle.knownPeers) {
|
||||||
|
Object.values(bundle.knownPeers).forEach((peer) => {
|
||||||
|
if (!peer.maxIngress) {
|
||||||
|
setIngressChartAttributes(peer);
|
||||||
|
}
|
||||||
|
if (!peer.maxEgress) {
|
||||||
|
setEgressChartAttributes(peer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (Array.isArray(update.diff)) {
|
||||||
|
update.diff.forEach((event: PeerEvent) => {
|
||||||
|
if (!event.ip) {
|
||||||
|
console.error('Peer event without IP', event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (event.remove) {
|
||||||
|
case 'bundle': {
|
||||||
|
delete prev.peers.bundles[event.ip];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'known': {
|
||||||
|
if (!event.id) {
|
||||||
|
console.error('Remove known peer event without ID', event.ip);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const bundle = prev.peers.bundles[event.ip];
|
||||||
|
if (!bundle || !bundle.knownPeers || !bundle.knownPeers[event.id]) {
|
||||||
|
console.error('No known peer to remove', event.ip, event.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete bundle.knownPeers[event.id];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'attempt': {
|
||||||
|
const bundle = prev.peers.bundles[event.ip];
|
||||||
|
if (!bundle || !Array.isArray(bundle.attempts) || bundle.attempts.length < 1) {
|
||||||
|
console.error('No unknown peer to remove', event.ip);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bundle.attempts.splice(0, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!prev.peers.bundles[event.ip]) {
|
||||||
|
prev.peers.bundles[event.ip] = {
|
||||||
|
location: {
|
||||||
|
country: '',
|
||||||
|
city: '',
|
||||||
|
latitude: 0,
|
||||||
|
longitude: 0,
|
||||||
|
},
|
||||||
|
knownPeers: {},
|
||||||
|
attempts: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const bundle = prev.peers.bundles[event.ip];
|
||||||
|
if (event.location) {
|
||||||
|
bundle.location = event.location;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!event.id) {
|
||||||
|
if (!bundle.attempts) {
|
||||||
|
bundle.attempts = [];
|
||||||
|
}
|
||||||
|
bundle.attempts.push({
|
||||||
|
connected: event.connected,
|
||||||
|
disconnected: event.disconnected,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!bundle.knownPeers) {
|
||||||
|
bundle.knownPeers = {};
|
||||||
|
}
|
||||||
|
if (!bundle.knownPeers[event.id]) {
|
||||||
|
bundle.knownPeers[event.id] = {
|
||||||
|
connected: [],
|
||||||
|
disconnected: [],
|
||||||
|
ingress: [],
|
||||||
|
egress: [],
|
||||||
|
active: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const peer = bundle.knownPeers[event.id];
|
||||||
|
if (!peer.maxIngress) {
|
||||||
|
setIngressChartAttributes(peer);
|
||||||
|
}
|
||||||
|
if (!peer.maxEgress) {
|
||||||
|
setEgressChartAttributes(peer);
|
||||||
|
}
|
||||||
|
if (event.connected) {
|
||||||
|
if (!peer.connected) {
|
||||||
|
console.warn('peer.connected should exist');
|
||||||
|
peer.connected = [];
|
||||||
|
}
|
||||||
|
peer.connected.push(event.connected);
|
||||||
|
}
|
||||||
|
if (event.disconnected) {
|
||||||
|
if (!peer.disconnected) {
|
||||||
|
console.warn('peer.disconnected should exist');
|
||||||
|
peer.disconnected = [];
|
||||||
|
}
|
||||||
|
peer.disconnected.push(event.disconnected);
|
||||||
|
}
|
||||||
|
switch (event.activity) {
|
||||||
|
case 'active':
|
||||||
|
peer.active = true;
|
||||||
|
break;
|
||||||
|
case 'inactive':
|
||||||
|
peer.active = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (Array.isArray(event.ingress) && Array.isArray(event.egress)) {
|
||||||
|
if (event.ingress.length !== event.egress.length) {
|
||||||
|
console.error('Different traffic sample length', event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check if there is a new maximum value, and reset the colors in case.
|
||||||
|
let maxIngress = peer.maxIngress;
|
||||||
|
event.ingress.forEach(({value}) => {
|
||||||
|
if (value > maxIngress) {
|
||||||
|
maxIngress = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (maxIngress > peer.maxIngress) {
|
||||||
|
setMaxIngress(peer, maxIngress);
|
||||||
|
}
|
||||||
|
// Push the new values.
|
||||||
|
peer.ingress.splice(peer.ingress.length, 0, ...event.ingress);
|
||||||
|
const ingressDiff = peer.ingress.length - sampleLimit;
|
||||||
|
if (ingressDiff > 0) {
|
||||||
|
// Check if the maximum value is in the beginning.
|
||||||
|
let i = 0;
|
||||||
|
while (i < ingressDiff && peer.ingress[i].value < peer.maxIngress) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// Remove the old values from the beginning.
|
||||||
|
peer.ingress.splice(0, ingressDiff);
|
||||||
|
if (i < ingressDiff) {
|
||||||
|
// Reset the colors if the maximum value leaves the chart.
|
||||||
|
setIngressChartAttributes(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if there is a new maximum value, and reset the colors in case.
|
||||||
|
let maxEgress = peer.maxEgress;
|
||||||
|
event.egress.forEach(({value}) => {
|
||||||
|
if (value > maxEgress) {
|
||||||
|
maxEgress = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (maxEgress > peer.maxEgress) {
|
||||||
|
setMaxEgress(peer, maxEgress);
|
||||||
|
}
|
||||||
|
// Push the new values.
|
||||||
|
peer.egress.splice(peer.egress.length, 0, ...event.egress);
|
||||||
|
const egressDiff = peer.egress.length - sampleLimit;
|
||||||
|
if (egressDiff > 0) {
|
||||||
|
// Check if the maximum value is in the beginning.
|
||||||
|
let i = 0;
|
||||||
|
while (i < egressDiff && peer.egress[i].value < peer.maxEgress) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// Remove the old values from the beginning.
|
||||||
|
peer.egress.splice(0, egressDiff);
|
||||||
|
if (i < egressDiff) {
|
||||||
|
// Reset the colors if the maximum value leaves the chart.
|
||||||
|
setEgressChartAttributes(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
// styles contains the constant styles of the component.
|
||||||
|
const styles = {
|
||||||
|
tableHead: {
|
||||||
|
height: 'auto',
|
||||||
|
},
|
||||||
|
tableRow: {
|
||||||
|
height: 'auto',
|
||||||
|
},
|
||||||
|
tableCell: {
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingRight: 5,
|
||||||
|
paddingBottom: 0,
|
||||||
|
paddingLeft: 5,
|
||||||
|
border: 'none',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
container: Object,
|
||||||
|
content: NetworkType,
|
||||||
|
shouldUpdate: Object,
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {};
|
||||||
|
|
||||||
|
// Network renders the network page.
|
||||||
|
class Network extends Component<Props, State> {
|
||||||
|
componentDidMount() {
|
||||||
|
const {container} = this.props;
|
||||||
|
if (typeof container === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
container.scrollTop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatTime = (t: string) => {
|
||||||
|
const time = new Date(t);
|
||||||
|
if (isNaN(time)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const month = `0${time.getMonth() + 1}`.slice(-2);
|
||||||
|
const date = `0${time.getDate()}`.slice(-2);
|
||||||
|
const hours = `0${time.getHours()}`.slice(-2);
|
||||||
|
const minutes = `0${time.getMinutes()}`.slice(-2);
|
||||||
|
const seconds = `0${time.getSeconds()}`.slice(-2);
|
||||||
|
return `${month}/${date}/${hours}:${minutes}:${seconds}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
copyToClipboard = (id) => (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
navigator.clipboard.writeText(id).then(() => {}, () => {
|
||||||
|
console.error("Failed to copy node id", id);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
peerTableRow = (ip, id, bundle, peer) => {
|
||||||
|
const ingressValues = peer.ingress.map(({value}) => ({ingress: value || 0.001}));
|
||||||
|
const egressValues = peer.egress.map(({value}) => ({egress: -value || -0.001}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow key={`known_${ip}_${id}`} style={styles.tableRow}>
|
||||||
|
<TableCell style={styles.tableCell}>
|
||||||
|
{peer.active
|
||||||
|
? <FontAwesomeIcon icon={fasCircle} color='green' />
|
||||||
|
: <FontAwesomeIcon icon={farCircle} style={commonStyles.light} />
|
||||||
|
}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell style={{fontFamily: 'monospace', cursor: 'copy', ...styles.tableCell, ...commonStyles.light}} onClick={this.copyToClipboard(id)}>
|
||||||
|
{id.substring(0, 10)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell style={styles.tableCell}>
|
||||||
|
{bundle.location ? (() => {
|
||||||
|
const l = bundle.location;
|
||||||
|
return `${l.country ? l.country : ''}${l.city ? `/${l.city}` : ''}`;
|
||||||
|
})() : ''}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell style={styles.tableCell}>
|
||||||
|
<AreaChart
|
||||||
|
width={trafficChartWidth}
|
||||||
|
height={trafficChartHeight}
|
||||||
|
data={ingressValues}
|
||||||
|
margin={{top: 5, right: 5, bottom: 0, left: 5}}
|
||||||
|
syncId={`peerIngress_${ip}_${id}`}
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id={`ingressGradient_${ip}_${id}`} x1='0' y1='1' x2='0' y2='0'>
|
||||||
|
{peer.ingressGradient
|
||||||
|
&& peer.ingressGradient.map(({offset, color}, i) => (
|
||||||
|
<stop
|
||||||
|
key={`ingressStop_${ip}_${id}_${i}`}
|
||||||
|
offset={`${offset}%`}
|
||||||
|
stopColor={color}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<Tooltip cursor={false} content={<CustomTooltip tooltip={bytePlotter('Download')} />} />
|
||||||
|
<YAxis hide scale='sqrt' domain={[0.001, dataMax => Math.max(dataMax, 0)]} />
|
||||||
|
<Area
|
||||||
|
dataKey='ingress'
|
||||||
|
isAnimationActive={false}
|
||||||
|
type='monotone'
|
||||||
|
fill={`url(#ingressGradient_${ip}_${id})`}
|
||||||
|
stroke={peer.ingressGradient[peer.ingressGradient.length - 1].color}
|
||||||
|
strokeWidth={chartStrokeWidth}
|
||||||
|
/>
|
||||||
|
</AreaChart>
|
||||||
|
<AreaChart
|
||||||
|
width={trafficChartWidth}
|
||||||
|
height={trafficChartHeight}
|
||||||
|
data={egressValues}
|
||||||
|
margin={{top: 0, right: 5, bottom: 5, left: 5}}
|
||||||
|
syncId={`peerIngress_${ip}_${id}`}
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id={`egressGradient_${ip}_${id}`} x1='0' y1='1' x2='0' y2='0'>
|
||||||
|
{peer.egressGradient
|
||||||
|
&& peer.egressGradient.map(({offset, color}, i) => (
|
||||||
|
<stop
|
||||||
|
key={`egressStop_${ip}_${id}_${i}`}
|
||||||
|
offset={`${offset}%`}
|
||||||
|
stopColor={color}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<Tooltip cursor={false} content={<CustomTooltip tooltip={bytePlotter('Upload', multiplier(-1))} />} />
|
||||||
|
<YAxis hide scale='sqrt' domain={[dataMin => Math.min(dataMin, 0), -0.001]} />
|
||||||
|
<Area
|
||||||
|
dataKey='egress'
|
||||||
|
isAnimationActive={false}
|
||||||
|
type='monotone'
|
||||||
|
fill={`url(#egressGradient_${ip}_${id})`}
|
||||||
|
stroke={peer.egressGradient[0].color}
|
||||||
|
strokeWidth={chartStrokeWidth}
|
||||||
|
/>
|
||||||
|
</AreaChart>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Grid container direction='row' justify='space-between'>
|
||||||
|
<Grid item>
|
||||||
|
<Table>
|
||||||
|
<TableHead style={styles.tableHead}>
|
||||||
|
<TableRow style={styles.tableRow}>
|
||||||
|
<TableCell style={styles.tableCell} />
|
||||||
|
<TableCell style={styles.tableCell}>Node ID</TableCell>
|
||||||
|
<TableCell style={styles.tableCell}>Location</TableCell>
|
||||||
|
<TableCell style={styles.tableCell}>Traffic</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{Object.entries(this.props.content.peers.bundles).map(([ip, bundle]) => {
|
||||||
|
if (!bundle.knownPeers || Object.keys(bundle.knownPeers).length < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Object.entries(bundle.knownPeers).map(([id, peer]) => {
|
||||||
|
if (peer.active === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.peerTableRow(ip, id, bundle, peer);
|
||||||
|
});
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
<TableBody>
|
||||||
|
{Object.entries(this.props.content.peers.bundles).map(([ip, bundle]) => {
|
||||||
|
if (!bundle.knownPeers || Object.keys(bundle.knownPeers).length < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Object.entries(bundle.knownPeers).map(([id, peer]) => {
|
||||||
|
if (peer.active === true) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.peerTableRow(ip, id, bundle, peer);
|
||||||
|
});
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant='subtitle1' gutterBottom>
|
||||||
|
Connection attempts
|
||||||
|
</Typography>
|
||||||
|
<Table>
|
||||||
|
<TableHead style={styles.tableHead}>
|
||||||
|
<TableRow style={styles.tableRow}>
|
||||||
|
<TableCell style={styles.tableCell}>IP</TableCell>
|
||||||
|
<TableCell style={styles.tableCell}>Location</TableCell>
|
||||||
|
<TableCell style={styles.tableCell}>Nr</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{Object.entries(this.props.content.peers.bundles).map(([ip, bundle]) => {
|
||||||
|
if (!bundle.attempts || bundle.attempts.length < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<TableRow key={`attempt_${ip}`} style={styles.tableRow}>
|
||||||
|
<TableCell style={styles.tableCell}>{ip}</TableCell>
|
||||||
|
<TableCell style={styles.tableCell}>
|
||||||
|
{bundle.location ? (() => {
|
||||||
|
const l = bundle.location;
|
||||||
|
return `${l.country ? l.country : ''}${l.city ? `/${l.city}` : ''}`;
|
||||||
|
})() : ''}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell style={styles.tableCell}>
|
||||||
|
{Object.values(bundle.attempts).length}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Network;
|
||||||
|
|
@ -18,11 +18,14 @@
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
import withStyles from 'material-ui/styles/withStyles';
|
import withStyles from '@material-ui/core/styles/withStyles';
|
||||||
import List, {ListItem, ListItemIcon, ListItemText} from 'material-ui/List';
|
import List from '@material-ui/core/List';
|
||||||
import Icon from 'material-ui/Icon';
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
|
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||||
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
|
import Icon from '@material-ui/core/Icon';
|
||||||
import Transition from 'react-transition-group/Transition';
|
import Transition from 'react-transition-group/Transition';
|
||||||
import {Icon as FontAwesome} from 'react-fa';
|
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
import {MENU, DURATION} from '../common';
|
import {MENU, DURATION} from '../common';
|
||||||
|
|
||||||
|
|
@ -48,6 +51,7 @@ const themeStyles = theme => ({
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
fontSize: theme.spacing.unit * 3,
|
fontSize: theme.spacing.unit * 3,
|
||||||
|
overflow: 'unset',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -57,9 +61,11 @@ export type Props = {
|
||||||
changeContent: string => void,
|
changeContent: string => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type State = {}
|
||||||
|
|
||||||
// SideBar renders the sidebar of the dashboard.
|
// SideBar renders the sidebar of the dashboard.
|
||||||
class SideBar extends Component<Props> {
|
class SideBar extends Component<Props, State> {
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps: Readonly<Props>, nextState: Readonly<State>, nextContext: any) {
|
||||||
return nextProps.opened !== this.props.opened;
|
return nextProps.opened !== this.props.opened;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +84,7 @@ class SideBar extends Component<Props> {
|
||||||
<ListItem button key={menu.id} onClick={this.clickOn(menu.id)} className={classes.listItem}>
|
<ListItem button key={menu.id} onClick={this.clickOn(menu.id)} className={classes.listItem}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<Icon className={classes.icon}>
|
<Icon className={classes.icon}>
|
||||||
<FontAwesome name={menu.icon} />
|
<FontAwesomeIcon icon={menu.icon} />
|
||||||
</Icon>
|
</Icon>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,8 @@
|
||||||
|
|
||||||
// fa-only-woff-loader removes the .eot, .ttf, .svg dependencies of the FontAwesome library,
|
// fa-only-woff-loader removes the .eot, .ttf, .svg dependencies of the FontAwesome library,
|
||||||
// because they produce unused extra blobs.
|
// because they produce unused extra blobs.
|
||||||
module.exports = function(content) {
|
module.exports = content => content
|
||||||
return content
|
.replace(/src.*url(?!.*url.*(\.eot)).*(\.eot)[^;]*;/, '')
|
||||||
.replace(/src.*url(?!.*url.*(\.eot)).*(\.eot)[^;]*;/,'')
|
.replace(/url(?!.*url.*(\.eot)).*(\.eot)[^,]*,/, '')
|
||||||
.replace(/url(?!.*url.*(\.eot)).*(\.eot)[^,]*,/,'')
|
.replace(/url(?!.*url.*(\.ttf)).*(\.ttf)[^,]*,/, '')
|
||||||
.replace(/url(?!.*url.*(\.ttf)).*(\.ttf)[^,]*,/,'')
|
.replace(/,[^,]*url(?!.*url.*(\.svg)).*(\.svg)[^;]*;/, ';');
|
||||||
.replace(/,[^,]*url(?!.*url.*(\.svg)).*(\.svg)[^;]*;/,';');
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body style="height: 100%; margin: 0">
|
<body style="height: 100%; margin: 0">
|
||||||
<div id="dashboard" style="height: 100%"></div>
|
<div id="dashboard" style="height: 100%"></div>
|
||||||
<script src="bundle.js"></script>
|
<script type="text/javascript" src="bundle.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {render} from 'react-dom';
|
import {render} from 'react-dom';
|
||||||
|
|
||||||
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
|
import MuiThemeProvider from '@material-ui/core/styles/MuiThemeProvider';
|
||||||
import createMuiTheme from 'material-ui/styles/createMuiTheme';
|
import createMuiTheme from '@material-ui/core/styles/createMuiTheme';
|
||||||
|
|
||||||
import Dashboard from './components/Dashboard';
|
import Dashboard from './components/Dashboard';
|
||||||
|
|
||||||
const theme: Object = createMuiTheme({
|
const theme: Object = createMuiTheme({
|
||||||
|
// typography: {
|
||||||
|
// useNextVariants: true,
|
||||||
|
// },
|
||||||
palette: {
|
palette: {
|
||||||
type: 'dark',
|
type: 'dark',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,65 @@
|
||||||
{
|
{
|
||||||
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-core": "^6.26.0",
|
"@babel/core": "7.3.4",
|
||||||
"babel-eslint": "^8.2.1",
|
"@babel/plugin-proposal-class-properties": "7.3.4",
|
||||||
"babel-loader": "^7.1.2",
|
"@babel/plugin-proposal-function-bind": "7.2.0",
|
||||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
"@babel/plugin-transform-flow-strip-types": "7.3.4",
|
||||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
"@babel/preset-env": "7.3.4",
|
||||||
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"babel-plugin-transform-runtime": "^6.23.0",
|
"@babel/preset-stage-0": "^7.0.0",
|
||||||
"babel-preset-env": "^1.6.1",
|
"@fortawesome/fontawesome-free-regular": "^5.0.13",
|
||||||
"babel-preset-react": "^6.24.1",
|
"@fortawesome/fontawesome-svg-core": "^1.2.15",
|
||||||
"babel-preset-stage-0": "^6.24.1",
|
"@fortawesome/free-regular-svg-icons": "^5.7.2",
|
||||||
"babel-runtime": "^6.26.0",
|
"@fortawesome/free-solid-svg-icons": "^5.7.2",
|
||||||
"classnames": "^2.2.5",
|
"@fortawesome/react-fontawesome": "^0.1.4",
|
||||||
"css-loader": "^0.28.9",
|
"@material-ui/core": "3.9.2",
|
||||||
|
"@material-ui/icons": "3.0.2",
|
||||||
|
"babel-eslint": "10.0.1",
|
||||||
|
"babel-loader": "8.0.5",
|
||||||
|
"classnames": "^2.2.6",
|
||||||
|
"color-convert": "^2.0.0",
|
||||||
|
"css-loader": "2.1.1",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
"eslint": "^4.16.0",
|
"eslint": "5.15.1",
|
||||||
"eslint-config-airbnb": "^16.1.0",
|
"eslint-config-airbnb": "^17.0.0",
|
||||||
"eslint-loader": "^2.0.0",
|
"eslint-loader": "2.1.2",
|
||||||
"eslint-plugin-flowtype": "^2.41.0",
|
"eslint-plugin-flowtype": "3.4.2",
|
||||||
"eslint-plugin-import": "^2.8.0",
|
"eslint-plugin-import": "2.16.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.0.3",
|
"eslint-plugin-jsx-a11y": "6.2.1",
|
||||||
"eslint-plugin-react": "^7.5.1",
|
"eslint-plugin-node": "8.0.1",
|
||||||
"file-loader": "^1.1.6",
|
"eslint-plugin-promise": "4.0.1",
|
||||||
"flow-bin": "^0.63.1",
|
"eslint-plugin-react": "7.12.4",
|
||||||
"flow-bin-loader": "^1.0.2",
|
"file-loader": "3.0.1",
|
||||||
"flow-typed": "^2.2.3",
|
"flow-bin": "0.94.0",
|
||||||
"material-ui": "^1.0.0-beta.30",
|
"flow-bin-loader": "^1.0.3",
|
||||||
"material-ui-icons": "^1.0.0-beta.17",
|
"flow-typed": "^2.5.1",
|
||||||
|
"js-beautify": "1.9.0",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"react": "^16.2.0",
|
"react": "16.8.4",
|
||||||
"react-dom": "^16.2.0",
|
"react-dom": "16.8.4",
|
||||||
"react-fa": "^5.0.0",
|
"react-hot-loader": "4.8.0",
|
||||||
"react-transition-group": "^2.2.1",
|
"react-transition-group": "2.6.0",
|
||||||
"recharts": "^1.0.0-beta.9",
|
"recharts": "1.5.0",
|
||||||
"style-loader": "^0.19.1",
|
"style-loader": "0.23.1",
|
||||||
|
"terser-webpack-plugin": "^1.2.3",
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
"url-loader": "^0.6.2",
|
"url-loader": "1.1.2",
|
||||||
"webpack": "^3.10.0",
|
"webpack": "4.29.6",
|
||||||
"webpack-dev-server": "^2.11.1"
|
"webpack-cli": "3.2.3",
|
||||||
|
"webpack-dashboard": "3.0.0",
|
||||||
|
"webpack-dev-server": "3.2.1",
|
||||||
|
"webpack-merge": "4.2.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "NODE_ENV=production webpack",
|
"build": "webpack --config webpack.config.prod.js",
|
||||||
"stats": "webpack --profile --json > stats.json",
|
"stats": "webpack --config webpack.config.prod.js --profile --json > stats.json",
|
||||||
"dev": "webpack-dev-server --port 8081",
|
"dev": "webpack-dev-server --open --config webpack.config.dev.js",
|
||||||
"flow": "flow-typed install"
|
"dash": "webpack-dashboard -- yarn dev",
|
||||||
}
|
"install-flow": "flow-typed install",
|
||||||
|
"flow": "flow status --show-all-errors",
|
||||||
|
"eslint": "eslint **/*"
|
||||||
|
},
|
||||||
|
"sideEffects": false,
|
||||||
|
"license": "LGPL-3.0-or-later"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ export type Content = {
|
||||||
export type ChartEntries = Array<ChartEntry>;
|
export type ChartEntries = Array<ChartEntry>;
|
||||||
|
|
||||||
export type ChartEntry = {
|
export type ChartEntry = {
|
||||||
time: Date,
|
|
||||||
value: number,
|
value: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -51,7 +50,50 @@ export type TxPool = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Network = {
|
export type Network = {
|
||||||
/* TODO (kurkomisi) */
|
peers: Peers,
|
||||||
|
diff: Array<PeerEvent>
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PeerEvent = {
|
||||||
|
ip: string,
|
||||||
|
id: string,
|
||||||
|
remove: string,
|
||||||
|
location: GeoLocation,
|
||||||
|
connected: Date,
|
||||||
|
disconnected: Date,
|
||||||
|
ingress: ChartEntries,
|
||||||
|
egress: ChartEntries,
|
||||||
|
activity: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Peers = {
|
||||||
|
bundles: {[string]: PeerBundle},
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PeerBundle = {
|
||||||
|
location: GeoLocation,
|
||||||
|
knownPeers: {[string]: KnownPeer},
|
||||||
|
attempts: Array<UnknownPeer>,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type KnownPeer = {
|
||||||
|
connected: Array<Date>,
|
||||||
|
disconnected: Array<Date>,
|
||||||
|
ingress: Array<ChartEntries>,
|
||||||
|
egress: Array<ChartEntries>,
|
||||||
|
active: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UnknownPeer = {
|
||||||
|
connected: Date,
|
||||||
|
disconnected: Date,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GeoLocation = {
|
||||||
|
country: string,
|
||||||
|
city: string,
|
||||||
|
latitude: number,
|
||||||
|
longitude: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type System = {
|
export type System = {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
// Copyright 2018 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -14,28 +14,25 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const webpack = require('webpack');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
target: 'web',
|
||||||
|
entry: {
|
||||||
|
bundle: './index',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: '[name].js',
|
||||||
|
path: path.resolve(__dirname, ''),
|
||||||
|
sourceMapFilename: '[file].map',
|
||||||
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
modules: [
|
||||||
|
'node_modules',
|
||||||
|
path.resolve(__dirname, 'components'), // import './components/Component' -> import 'Component'
|
||||||
|
],
|
||||||
extensions: ['.js', '.jsx'],
|
extensions: ['.js', '.jsx'],
|
||||||
},
|
},
|
||||||
entry: './index',
|
|
||||||
output: {
|
|
||||||
path: path.resolve(__dirname, ''),
|
|
||||||
filename: 'bundle.js',
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new webpack.optimize.UglifyJsPlugin({
|
|
||||||
comments: false,
|
|
||||||
mangle: false,
|
|
||||||
beautify: true,
|
|
||||||
}),
|
|
||||||
new webpack.DefinePlugin({
|
|
||||||
PROD: process.env.NODE_ENV === 'production',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
|
@ -45,27 +42,38 @@ module.exports = {
|
||||||
{
|
{
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
plugins: [ // order: from top to bottom
|
|
||||||
// 'transform-decorators-legacy', // @withStyles, @withTheme
|
|
||||||
'transform-class-properties', // static defaultProps
|
|
||||||
'transform-flow-strip-types',
|
|
||||||
],
|
|
||||||
presets: [ // order: from bottom to top
|
presets: [ // order: from bottom to top
|
||||||
'env',
|
'@babel/env',
|
||||||
'react',
|
'@babel/react',
|
||||||
'stage-0',
|
],
|
||||||
|
plugins: [ // order: from top to bottom
|
||||||
|
'@babel/proposal-function-bind', // instead of stage 0
|
||||||
|
'@babel/proposal-class-properties', // static defaultProps
|
||||||
|
'@babel/transform-flow-strip-types',
|
||||||
|
'react-hot-loader/babel',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 'eslint-loader', // show errors not only in the editor, but also in the console
|
// 'eslint-loader', // show errors in the console
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /font-awesome\.css$/,
|
test: /\.css$/,
|
||||||
use: [
|
oneOf: [
|
||||||
'style-loader',
|
{
|
||||||
'css-loader',
|
test: /font-awesome/,
|
||||||
path.resolve(__dirname, './fa-only-woff-loader.js'),
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
'css-loader',
|
||||||
|
path.resolve(__dirname, './fa-only-woff-loader.js'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
'css-loader',
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -14,20 +14,22 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package rawdb
|
const webpack = require('webpack');
|
||||||
|
const merge = require('webpack-merge');
|
||||||
|
const WebpackDashboard = require('webpack-dashboard/plugin');
|
||||||
|
const common = require('./webpack.config.common.js');
|
||||||
|
|
||||||
// DatabaseReader wraps the Has and Get method of a backing data store.
|
module.exports = merge(common, {
|
||||||
type DatabaseReader interface {
|
mode: 'development',
|
||||||
Has(key []byte) (bool, error)
|
plugins: [
|
||||||
Get(key []byte) ([]byte, error)
|
new WebpackDashboard(),
|
||||||
}
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
],
|
||||||
// DatabaseWriter wraps the Put method of a backing data store.
|
// devtool: 'eval',
|
||||||
type DatabaseWriter interface {
|
devtool: 'source-map',
|
||||||
Put(key []byte, value []byte) error
|
devServer: {
|
||||||
}
|
port: 8081,
|
||||||
|
hot: true,
|
||||||
// DatabaseDeleter wraps the Delete method of a backing data store.
|
compress: true,
|
||||||
type DatabaseDeleter interface {
|
},
|
||||||
Delete(key []byte) error
|
});
|
||||||
}
|
|
||||||
41
dashboard/assets/webpack.config.prod.js
Normal file
41
dashboard/assets/webpack.config.prod.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
|
const merge = require('webpack-merge');
|
||||||
|
const common = require('./webpack.config.common.js');
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: 'production',
|
||||||
|
devtool: 'source-map',
|
||||||
|
optimization: {
|
||||||
|
minimize: true,
|
||||||
|
namedModules: true, // Module names instead of numbers - resolves the large diff problem.
|
||||||
|
minimizer: [
|
||||||
|
new TerserPlugin({
|
||||||
|
cache: true,
|
||||||
|
parallel: true,
|
||||||
|
sourceMap: true,
|
||||||
|
terserOptions: {
|
||||||
|
output: {
|
||||||
|
comments: false,
|
||||||
|
beautify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -18,8 +18,10 @@ package dashboard
|
||||||
|
|
||||||
//go:generate yarn --cwd ./assets install
|
//go:generate yarn --cwd ./assets install
|
||||||
//go:generate yarn --cwd ./assets build
|
//go:generate yarn --cwd ./assets build
|
||||||
//go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg dashboard assets/index.html assets/bundle.js
|
//go:generate yarn --cwd ./assets js-beautify -f bundle.js.map -r -w 1
|
||||||
|
//go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg dashboard assets/index.html assets/bundle.js assets/bundle.js.map
|
||||||
//go:generate sh -c "sed 's#var _bundleJs#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
|
//go:generate sh -c "sed 's#var _bundleJs#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
|
||||||
|
//go:generate sh -c "sed 's#var _bundleJsMap#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
|
||||||
//go:generate sh -c "sed 's#var _indexHtml#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
|
//go:generate sh -c "sed 's#var _indexHtml#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
|
||||||
//go:generate gofmt -w -s assets.go
|
//go:generate gofmt -w -s assets.go
|
||||||
|
|
||||||
|
|
@ -27,16 +29,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/elastic/gosigar"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
@ -45,31 +44,29 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
activeMemorySampleLimit = 200 // Maximum number of active memory data samples
|
sampleLimit = 200 // Maximum number of data samples
|
||||||
virtualMemorySampleLimit = 200 // Maximum number of virtual memory data samples
|
|
||||||
networkIngressSampleLimit = 200 // Maximum number of network ingress data samples
|
|
||||||
networkEgressSampleLimit = 200 // Maximum number of network egress data samples
|
|
||||||
processCPUSampleLimit = 200 // Maximum number of process cpu data samples
|
|
||||||
systemCPUSampleLimit = 200 // Maximum number of system cpu data samples
|
|
||||||
diskReadSampleLimit = 200 // Maximum number of disk read data samples
|
|
||||||
diskWriteSampleLimit = 200 // Maximum number of disk write data samples
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var nextID uint32 // Next connection id
|
|
||||||
|
|
||||||
// Dashboard contains the dashboard internals.
|
// Dashboard contains the dashboard internals.
|
||||||
type Dashboard struct {
|
type Dashboard struct {
|
||||||
config *Config
|
config *Config // Configuration values for the dashboard
|
||||||
|
|
||||||
listener net.Listener
|
listener net.Listener // Network listener listening for dashboard clients
|
||||||
conns map[uint32]*client // Currently live websocket connections
|
conns map[uint32]*client // Currently live websocket connections
|
||||||
history *Message
|
nextConnID uint32 // Next connection id
|
||||||
lock sync.RWMutex // Lock protecting the dashboard's internals
|
|
||||||
|
|
||||||
logdir string
|
history *Message // Stored historical data
|
||||||
|
|
||||||
|
lock sync.Mutex // Lock protecting the dashboard's internals
|
||||||
|
sysLock sync.RWMutex // Lock protecting the stored system data
|
||||||
|
peerLock sync.RWMutex // Lock protecting the stored peer data
|
||||||
|
logLock sync.RWMutex // Lock protecting the stored log data
|
||||||
|
|
||||||
|
geodb *geoDB // geoip database instance for IP to geographical information conversions
|
||||||
|
logdir string // Directory containing the log files
|
||||||
|
|
||||||
quit chan chan error // Channel used for graceful exit
|
quit chan chan error // Channel used for graceful exit
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup // Wait group used to close the data collector threads
|
||||||
}
|
}
|
||||||
|
|
||||||
// client represents active websocket connection with a remote browser.
|
// client represents active websocket connection with a remote browser.
|
||||||
|
|
@ -96,14 +93,14 @@ func New(config *Config, commit string, logdir string) *Dashboard {
|
||||||
Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta),
|
Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta),
|
||||||
},
|
},
|
||||||
System: &SystemMessage{
|
System: &SystemMessage{
|
||||||
ActiveMemory: emptyChartEntries(now, activeMemorySampleLimit, config.Refresh),
|
ActiveMemory: emptyChartEntries(now, sampleLimit),
|
||||||
VirtualMemory: emptyChartEntries(now, virtualMemorySampleLimit, config.Refresh),
|
VirtualMemory: emptyChartEntries(now, sampleLimit),
|
||||||
NetworkIngress: emptyChartEntries(now, networkIngressSampleLimit, config.Refresh),
|
NetworkIngress: emptyChartEntries(now, sampleLimit),
|
||||||
NetworkEgress: emptyChartEntries(now, networkEgressSampleLimit, config.Refresh),
|
NetworkEgress: emptyChartEntries(now, sampleLimit),
|
||||||
ProcessCPU: emptyChartEntries(now, processCPUSampleLimit, config.Refresh),
|
ProcessCPU: emptyChartEntries(now, sampleLimit),
|
||||||
SystemCPU: emptyChartEntries(now, systemCPUSampleLimit, config.Refresh),
|
SystemCPU: emptyChartEntries(now, sampleLimit),
|
||||||
DiskRead: emptyChartEntries(now, diskReadSampleLimit, config.Refresh),
|
DiskRead: emptyChartEntries(now, sampleLimit),
|
||||||
DiskWrite: emptyChartEntries(now, diskWriteSampleLimit, config.Refresh),
|
DiskWrite: emptyChartEntries(now, sampleLimit),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logdir: logdir,
|
logdir: logdir,
|
||||||
|
|
@ -111,12 +108,10 @@ func New(config *Config, commit string, logdir string) *Dashboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
// emptyChartEntries returns a ChartEntry array containing limit number of empty samples.
|
// emptyChartEntries returns a ChartEntry array containing limit number of empty samples.
|
||||||
func emptyChartEntries(t time.Time, limit int, refresh time.Duration) ChartEntries {
|
func emptyChartEntries(t time.Time, limit int) ChartEntries {
|
||||||
ce := make(ChartEntries, limit)
|
ce := make(ChartEntries, limit)
|
||||||
for i := 0; i < limit; i++ {
|
for i := 0; i < limit; i++ {
|
||||||
ce[i] = &ChartEntry{
|
ce[i] = new(ChartEntry)
|
||||||
Time: t.Add(-time.Duration(i) * refresh),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ce
|
return ce
|
||||||
}
|
}
|
||||||
|
|
@ -132,9 +127,10 @@ func (db *Dashboard) APIs() []rpc.API { return nil }
|
||||||
func (db *Dashboard) Start(server *p2p.Server) error {
|
func (db *Dashboard) Start(server *p2p.Server) error {
|
||||||
log.Info("Starting dashboard")
|
log.Info("Starting dashboard")
|
||||||
|
|
||||||
db.wg.Add(2)
|
db.wg.Add(3)
|
||||||
go db.collectData()
|
go db.collectSystemData()
|
||||||
go db.streamLogs()
|
go db.streamLogs()
|
||||||
|
go db.collectPeerData()
|
||||||
|
|
||||||
http.HandleFunc("/", db.webHandler)
|
http.HandleFunc("/", db.webHandler)
|
||||||
http.Handle("/api", websocket.Handler(db.apiHandler))
|
http.Handle("/api", websocket.Handler(db.apiHandler))
|
||||||
|
|
@ -160,7 +156,7 @@ func (db *Dashboard) Stop() error {
|
||||||
}
|
}
|
||||||
// Close the collectors.
|
// Close the collectors.
|
||||||
errc := make(chan error, 1)
|
errc := make(chan error, 1)
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
db.quit <- errc
|
db.quit <- errc
|
||||||
if err := <-errc; err != nil {
|
if err := <-errc; err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
|
@ -206,7 +202,7 @@ func (db *Dashboard) webHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// apiHandler handles requests for the dashboard.
|
// apiHandler handles requests for the dashboard.
|
||||||
func (db *Dashboard) apiHandler(conn *websocket.Conn) {
|
func (db *Dashboard) apiHandler(conn *websocket.Conn) {
|
||||||
id := atomic.AddUint32(&nextID, 1)
|
id := atomic.AddUint32(&db.nextConnID, 1)
|
||||||
client := &client{
|
client := &client{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
msg: make(chan *Message, 128),
|
msg: make(chan *Message, 128),
|
||||||
|
|
@ -233,10 +229,21 @@ func (db *Dashboard) apiHandler(conn *websocket.Conn) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
db.lock.Lock()
|
|
||||||
// Send the past data.
|
// Send the past data.
|
||||||
client.msg <- deepcopy.Copy(db.history).(*Message)
|
db.sysLock.RLock()
|
||||||
|
db.peerLock.RLock()
|
||||||
|
db.logLock.RLock()
|
||||||
|
|
||||||
|
h := deepcopy.Copy(db.history).(*Message)
|
||||||
|
|
||||||
|
db.sysLock.RUnlock()
|
||||||
|
db.peerLock.RUnlock()
|
||||||
|
db.logLock.RUnlock()
|
||||||
|
|
||||||
|
client.msg <- h
|
||||||
|
|
||||||
// Start tracking the connection and drop at connection loss.
|
// Start tracking the connection and drop at connection loss.
|
||||||
|
db.lock.Lock()
|
||||||
db.conns[id] = client
|
db.conns[id] = client
|
||||||
db.lock.Unlock()
|
db.lock.Unlock()
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -259,136 +266,6 @@ func (db *Dashboard) apiHandler(conn *websocket.Conn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// meterCollector returns a function, which retrieves a specific meter.
|
|
||||||
func meterCollector(name string) func() int64 {
|
|
||||||
if metric := metrics.DefaultRegistry.Get(name); metric != nil {
|
|
||||||
m := metric.(metrics.Meter)
|
|
||||||
return func() int64 {
|
|
||||||
return m.Count()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return func() int64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// collectData collects the required data to plot on the dashboard.
|
|
||||||
func (db *Dashboard) collectData() {
|
|
||||||
defer db.wg.Done()
|
|
||||||
|
|
||||||
systemCPUUsage := gosigar.Cpu{}
|
|
||||||
systemCPUUsage.Get()
|
|
||||||
var (
|
|
||||||
mem runtime.MemStats
|
|
||||||
|
|
||||||
collectNetworkIngress = meterCollector("p2p/InboundTraffic")
|
|
||||||
collectNetworkEgress = meterCollector("p2p/OutboundTraffic")
|
|
||||||
collectDiskRead = meterCollector("eth/db/chaindata/disk/read")
|
|
||||||
collectDiskWrite = meterCollector("eth/db/chaindata/disk/write")
|
|
||||||
|
|
||||||
prevNetworkIngress = collectNetworkIngress()
|
|
||||||
prevNetworkEgress = collectNetworkEgress()
|
|
||||||
prevProcessCPUTime = getProcessCPUTime()
|
|
||||||
prevSystemCPUUsage = systemCPUUsage
|
|
||||||
prevDiskRead = collectDiskRead()
|
|
||||||
prevDiskWrite = collectDiskWrite()
|
|
||||||
|
|
||||||
frequency = float64(db.config.Refresh / time.Second)
|
|
||||||
numCPU = float64(runtime.NumCPU())
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case errc := <-db.quit:
|
|
||||||
errc <- nil
|
|
||||||
return
|
|
||||||
case <-time.After(db.config.Refresh):
|
|
||||||
systemCPUUsage.Get()
|
|
||||||
var (
|
|
||||||
curNetworkIngress = collectNetworkIngress()
|
|
||||||
curNetworkEgress = collectNetworkEgress()
|
|
||||||
curProcessCPUTime = getProcessCPUTime()
|
|
||||||
curSystemCPUUsage = systemCPUUsage
|
|
||||||
curDiskRead = collectDiskRead()
|
|
||||||
curDiskWrite = collectDiskWrite()
|
|
||||||
|
|
||||||
deltaNetworkIngress = float64(curNetworkIngress - prevNetworkIngress)
|
|
||||||
deltaNetworkEgress = float64(curNetworkEgress - prevNetworkEgress)
|
|
||||||
deltaProcessCPUTime = curProcessCPUTime - prevProcessCPUTime
|
|
||||||
deltaSystemCPUUsage = curSystemCPUUsage.Delta(prevSystemCPUUsage)
|
|
||||||
deltaDiskRead = curDiskRead - prevDiskRead
|
|
||||||
deltaDiskWrite = curDiskWrite - prevDiskWrite
|
|
||||||
)
|
|
||||||
prevNetworkIngress = curNetworkIngress
|
|
||||||
prevNetworkEgress = curNetworkEgress
|
|
||||||
prevProcessCPUTime = curProcessCPUTime
|
|
||||||
prevSystemCPUUsage = curSystemCPUUsage
|
|
||||||
prevDiskRead = curDiskRead
|
|
||||||
prevDiskWrite = curDiskWrite
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
runtime.ReadMemStats(&mem)
|
|
||||||
activeMemory := &ChartEntry{
|
|
||||||
Time: now,
|
|
||||||
Value: float64(mem.Alloc) / frequency,
|
|
||||||
}
|
|
||||||
virtualMemory := &ChartEntry{
|
|
||||||
Time: now,
|
|
||||||
Value: float64(mem.Sys) / frequency,
|
|
||||||
}
|
|
||||||
networkIngress := &ChartEntry{
|
|
||||||
Time: now,
|
|
||||||
Value: deltaNetworkIngress / frequency,
|
|
||||||
}
|
|
||||||
networkEgress := &ChartEntry{
|
|
||||||
Time: now,
|
|
||||||
Value: deltaNetworkEgress / frequency,
|
|
||||||
}
|
|
||||||
processCPU := &ChartEntry{
|
|
||||||
Time: now,
|
|
||||||
Value: deltaProcessCPUTime / frequency / numCPU * 100,
|
|
||||||
}
|
|
||||||
systemCPU := &ChartEntry{
|
|
||||||
Time: now,
|
|
||||||
Value: float64(deltaSystemCPUUsage.Sys+deltaSystemCPUUsage.User) / frequency / numCPU,
|
|
||||||
}
|
|
||||||
diskRead := &ChartEntry{
|
|
||||||
Time: now,
|
|
||||||
Value: float64(deltaDiskRead) / frequency,
|
|
||||||
}
|
|
||||||
diskWrite := &ChartEntry{
|
|
||||||
Time: now,
|
|
||||||
Value: float64(deltaDiskWrite) / frequency,
|
|
||||||
}
|
|
||||||
sys := db.history.System
|
|
||||||
db.lock.Lock()
|
|
||||||
sys.ActiveMemory = append(sys.ActiveMemory[1:], activeMemory)
|
|
||||||
sys.VirtualMemory = append(sys.VirtualMemory[1:], virtualMemory)
|
|
||||||
sys.NetworkIngress = append(sys.NetworkIngress[1:], networkIngress)
|
|
||||||
sys.NetworkEgress = append(sys.NetworkEgress[1:], networkEgress)
|
|
||||||
sys.ProcessCPU = append(sys.ProcessCPU[1:], processCPU)
|
|
||||||
sys.SystemCPU = append(sys.SystemCPU[1:], systemCPU)
|
|
||||||
sys.DiskRead = append(sys.DiskRead[1:], diskRead)
|
|
||||||
sys.DiskWrite = append(sys.DiskWrite[1:], diskWrite)
|
|
||||||
db.lock.Unlock()
|
|
||||||
|
|
||||||
db.sendToAll(&Message{
|
|
||||||
System: &SystemMessage{
|
|
||||||
ActiveMemory: ChartEntries{activeMemory},
|
|
||||||
VirtualMemory: ChartEntries{virtualMemory},
|
|
||||||
NetworkIngress: ChartEntries{networkIngress},
|
|
||||||
NetworkEgress: ChartEntries{networkEgress},
|
|
||||||
ProcessCPU: ChartEntries{processCPU},
|
|
||||||
SystemCPU: ChartEntries{systemCPU},
|
|
||||||
DiskRead: ChartEntries{diskRead},
|
|
||||||
DiskWrite: ChartEntries{diskWrite},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendToAll sends the given message to the active dashboards.
|
// sendToAll sends the given message to the active dashboards.
|
||||||
func (db *Dashboard) sendToAll(msg *Message) {
|
func (db *Dashboard) sendToAll(msg *Message) {
|
||||||
db.lock.Lock()
|
db.lock.Lock()
|
||||||
|
|
|
||||||
98
dashboard/geoip.go
Normal file
98
dashboard/geoip.go
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
// 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 dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/apilayer/freegeoip"
|
||||||
|
)
|
||||||
|
|
||||||
|
// geoDBInfo contains all the geographical information we could extract based on an IP
|
||||||
|
// address.
|
||||||
|
type geoDBInfo struct {
|
||||||
|
Country struct {
|
||||||
|
Names struct {
|
||||||
|
English string `maxminddb:"en" json:"en,omitempty"`
|
||||||
|
} `maxminddb:"names" json:"names,omitempty"`
|
||||||
|
} `maxminddb:"country" json:"country,omitempty"`
|
||||||
|
City struct {
|
||||||
|
Names struct {
|
||||||
|
English string `maxminddb:"en" json:"en,omitempty"`
|
||||||
|
} `maxminddb:"names" json:"names,omitempty"`
|
||||||
|
} `maxminddb:"city" json:"city,omitempty"`
|
||||||
|
Location struct {
|
||||||
|
Latitude float64 `maxminddb:"latitude" json:"latitude,omitempty"`
|
||||||
|
Longitude float64 `maxminddb:"longitude" json:"longitude,omitempty"`
|
||||||
|
} `maxminddb:"location" json:"location,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// geoLocation contains geographical information.
|
||||||
|
type geoLocation struct {
|
||||||
|
Country string `json:"country,omitempty"`
|
||||||
|
City string `json:"city,omitempty"`
|
||||||
|
Latitude float64 `json:"latitude,omitempty"`
|
||||||
|
Longitude float64 `json:"longitude,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// geoDB represents a geoip database that can be queried for IP to geographical
|
||||||
|
// information conversions.
|
||||||
|
type geoDB struct {
|
||||||
|
geodb *freegeoip.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open creates a new geoip database with an up-to-date database from the internet.
|
||||||
|
func openGeoDB() (*geoDB, error) {
|
||||||
|
// Initiate a geoip database to cross reference locations
|
||||||
|
db, err := freegeoip.OpenURL(freegeoip.MaxMindDB, 24*time.Hour, time.Hour)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Wait until the database is updated to the latest data
|
||||||
|
select {
|
||||||
|
case <-db.NotifyOpen():
|
||||||
|
case err := <-db.NotifyError():
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Assemble and return our custom wrapper
|
||||||
|
return &geoDB{geodb: db}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close terminates the database background updater.
|
||||||
|
func (db *geoDB) close() error {
|
||||||
|
db.geodb.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup converts an IP address to a geographical location.
|
||||||
|
func (db *geoDB) lookup(ip net.IP) *geoDBInfo {
|
||||||
|
result := new(geoDBInfo)
|
||||||
|
db.geodb.Lookup(ip, result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Location retrieves the geographical location of the given IP address.
|
||||||
|
func (db *geoDB) location(ip string) *geoLocation {
|
||||||
|
location := db.lookup(net.ParseIP(ip))
|
||||||
|
return &geoLocation{
|
||||||
|
Country: location.Country.Names.English,
|
||||||
|
City: location.City.Names.English,
|
||||||
|
Latitude: location.Location.Latitude,
|
||||||
|
Longitude: location.Location.Longitude,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -94,13 +94,13 @@ func (db *Dashboard) handleLogRequest(r *LogsRequest, c *client) {
|
||||||
// The last file is continuously updated, and its chunks are streamed,
|
// The last file is continuously updated, and its chunks are streamed,
|
||||||
// so in order to avoid log record duplication on the client side, it is
|
// so in order to avoid log record duplication on the client side, it is
|
||||||
// handled differently. Its actual content is always saved in the history.
|
// handled differently. Its actual content is always saved in the history.
|
||||||
db.lock.Lock()
|
db.logLock.RLock()
|
||||||
if db.history.Logs != nil {
|
if db.history.Logs != nil {
|
||||||
c.msg <- &Message{
|
c.msg <- &Message{
|
||||||
Logs: db.history.Logs,
|
Logs: deepcopy.Copy(db.history.Logs).(*LogsMessage),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
db.lock.Unlock()
|
db.logLock.RUnlock()
|
||||||
return
|
return
|
||||||
case fileNames[idx] == r.Name:
|
case fileNames[idx] == r.Name:
|
||||||
idx++
|
idx++
|
||||||
|
|
@ -174,7 +174,7 @@ func (db *Dashboard) streamLogs() {
|
||||||
log.Warn("Problem with file", "name", opened.Name(), "err", err)
|
log.Warn("Problem with file", "name", opened.Name(), "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
db.lock.Lock()
|
db.logLock.Lock()
|
||||||
db.history.Logs = &LogsMessage{
|
db.history.Logs = &LogsMessage{
|
||||||
Source: &LogFile{
|
Source: &LogFile{
|
||||||
Name: fi.Name(),
|
Name: fi.Name(),
|
||||||
|
|
@ -182,7 +182,7 @@ func (db *Dashboard) streamLogs() {
|
||||||
},
|
},
|
||||||
Chunk: emptyChunk,
|
Chunk: emptyChunk,
|
||||||
}
|
}
|
||||||
db.lock.Unlock()
|
db.logLock.Unlock()
|
||||||
|
|
||||||
watcher := make(chan notify.EventInfo, 10)
|
watcher := make(chan notify.EventInfo, 10)
|
||||||
if err := notify.Watch(db.logdir, watcher, notify.Create); err != nil {
|
if err := notify.Watch(db.logdir, watcher, notify.Create); err != nil {
|
||||||
|
|
@ -240,10 +240,10 @@ loop:
|
||||||
log.Warn("Problem with file", "name", opened.Name(), "err", err)
|
log.Warn("Problem with file", "name", opened.Name(), "err", err)
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
db.lock.Lock()
|
db.logLock.Lock()
|
||||||
db.history.Logs.Source.Name = fi.Name()
|
db.history.Logs.Source.Name = fi.Name()
|
||||||
db.history.Logs.Chunk = emptyChunk
|
db.history.Logs.Chunk = emptyChunk
|
||||||
db.lock.Unlock()
|
db.logLock.Unlock()
|
||||||
case <-ticker.C: // Send log updates to the client.
|
case <-ticker.C: // Send log updates to the client.
|
||||||
if opened == nil {
|
if opened == nil {
|
||||||
log.Warn("The last log file is not opened")
|
log.Warn("The last log file is not opened")
|
||||||
|
|
@ -266,7 +266,7 @@ loop:
|
||||||
|
|
||||||
var l *LogsMessage
|
var l *LogsMessage
|
||||||
// Update the history.
|
// Update the history.
|
||||||
db.lock.Lock()
|
db.logLock.Lock()
|
||||||
if bytes.Equal(db.history.Logs.Chunk, emptyChunk) {
|
if bytes.Equal(db.history.Logs.Chunk, emptyChunk) {
|
||||||
db.history.Logs.Chunk = chunk
|
db.history.Logs.Chunk = chunk
|
||||||
l = deepcopy.Copy(db.history.Logs).(*LogsMessage)
|
l = deepcopy.Copy(db.history.Logs).(*LogsMessage)
|
||||||
|
|
@ -278,7 +278,7 @@ loop:
|
||||||
db.history.Logs.Chunk = b
|
db.history.Logs.Chunk = b
|
||||||
l = &LogsMessage{Chunk: chunk}
|
l = &LogsMessage{Chunk: chunk}
|
||||||
}
|
}
|
||||||
db.lock.Unlock()
|
db.logLock.Unlock()
|
||||||
|
|
||||||
db.sendToAll(&Message{Logs: l})
|
db.sendToAll(&Message{Logs: l})
|
||||||
case errc = <-db.quit:
|
case errc = <-db.quit:
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package dashboard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
|
|
@ -34,8 +33,7 @@ type Message struct {
|
||||||
type ChartEntries []*ChartEntry
|
type ChartEntries []*ChartEntry
|
||||||
|
|
||||||
type ChartEntry struct {
|
type ChartEntry struct {
|
||||||
Time time.Time `json:"time,omitempty"`
|
Value float64 `json:"value"`
|
||||||
Value float64 `json:"value,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GeneralMessage struct {
|
type GeneralMessage struct {
|
||||||
|
|
@ -55,10 +53,14 @@ type TxPoolMessage struct {
|
||||||
/* TODO (kurkomisi) */
|
/* TODO (kurkomisi) */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NetworkMessage contains information about the peers
|
||||||
|
// organized based on their IP address and node ID.
|
||||||
type NetworkMessage struct {
|
type NetworkMessage struct {
|
||||||
/* TODO (kurkomisi) */
|
Peers *peerContainer `json:"peers,omitempty"` // Peer tree.
|
||||||
|
Diff []*peerEvent `json:"diff,omitempty"` // Events that change the peer tree.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SystemMessage contains the metered system data samples.
|
||||||
type SystemMessage struct {
|
type SystemMessage struct {
|
||||||
ActiveMemory ChartEntries `json:"activeMemory,omitempty"`
|
ActiveMemory ChartEntries `json:"activeMemory,omitempty"`
|
||||||
VirtualMemory ChartEntries `json:"virtualMemory,omitempty"`
|
VirtualMemory ChartEntries `json:"virtualMemory,omitempty"`
|
||||||
|
|
@ -70,7 +72,7 @@ type SystemMessage struct {
|
||||||
DiskWrite ChartEntries `json:"diskWrite,omitempty"`
|
DiskWrite ChartEntries `json:"diskWrite,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogsMessage wraps up a log chunk. If Source isn't present, the chunk is a stream chunk.
|
// LogsMessage wraps up a log chunk. If 'Source' isn't present, the chunk is a stream chunk.
|
||||||
type LogsMessage struct {
|
type LogsMessage struct {
|
||||||
Source *LogFile `json:"source,omitempty"` // Attributes of the log file.
|
Source *LogFile `json:"source,omitempty"` // Attributes of the log file.
|
||||||
Chunk json.RawMessage `json:"chunk"` // Contains log records.
|
Chunk json.RawMessage `json:"chunk"` // Contains log records.
|
||||||
|
|
@ -87,6 +89,7 @@ type Request struct {
|
||||||
Logs *LogsRequest `json:"logs,omitempty"`
|
Logs *LogsRequest `json:"logs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LogsRequest contains the attributes of the log file the client wants to receive.
|
||||||
type LogsRequest struct {
|
type LogsRequest struct {
|
||||||
Name string `json:"name"` // The request handler searches for log file based on this file name.
|
Name string `json:"name"` // The request handler searches for log file based on this file name.
|
||||||
Past bool `json:"past"` // Denotes whether the client wants the previous or the next file.
|
Past bool `json:"past"` // Denotes whether the client wants the previous or the next file.
|
||||||
|
|
|
||||||
552
dashboard/peers.go
Normal file
552
dashboard/peers.go
Normal file
|
|
@ -0,0 +1,552 @@
|
||||||
|
// 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 dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
eventBufferLimit = 128 // Maximum number of buffered peer events.
|
||||||
|
knownPeerLimit = 100 // Maximum number of stored peers, which successfully made the handshake.
|
||||||
|
attemptLimit = 200 // Maximum number of stored peers, which failed to make the handshake.
|
||||||
|
|
||||||
|
// eventLimit is the maximum number of the dashboard's custom peer events,
|
||||||
|
// that are collected between two metering period and sent to the clients
|
||||||
|
// as one message.
|
||||||
|
// TODO (kurkomisi): Limit the number of events.
|
||||||
|
eventLimit = knownPeerLimit << 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// peerContainer contains information about the node's peers. This data structure
|
||||||
|
// maintains the metered peer data based on the different behaviours of the peers.
|
||||||
|
//
|
||||||
|
// Every peer has an IP address, and the peers that manage to make the handshake
|
||||||
|
// (known peers) have node IDs too. There can appear more peers with the same IP,
|
||||||
|
// therefore the peer container data structure is a tree consisting of a map of
|
||||||
|
// maps, where the first key groups the peers by IP, while the second one groups
|
||||||
|
// them by the node ID. The known peers can be active if their connection is still
|
||||||
|
// open, or inactive otherwise. The peers failing before the handshake (unknown
|
||||||
|
// peers) only have IP addresses, so their connection attempts are stored as part
|
||||||
|
// of the value of the outer map.
|
||||||
|
//
|
||||||
|
// Another criteria is to limit the number of metered peers so that
|
||||||
|
// they don't fill the memory. The selection order is based on the
|
||||||
|
// peers activity: the peers that are inactive for the longest time
|
||||||
|
// are thrown first. For the selection a fifo list is used which is
|
||||||
|
// linked to the bottom of the peer tree in a way that every activity
|
||||||
|
// of the peer pushes the peer to the end of the list, so the inactive
|
||||||
|
// ones come to the front. When a peer has some activity, it is removed
|
||||||
|
// from and reinserted into the list. When the length of the list reaches
|
||||||
|
// the limit, the first element is removed from the list, as well as from
|
||||||
|
// the tree.
|
||||||
|
//
|
||||||
|
// The active peers have priority over the inactive ones, therefore
|
||||||
|
// they have their own list. The separation makes it sure that the
|
||||||
|
// inactive peers are always removed before the active ones.
|
||||||
|
//
|
||||||
|
// The peers that don't manage to make handshake are not inserted into the list,
|
||||||
|
// only their connection attempts are appended to the array belonging to their IP.
|
||||||
|
// In order to keep the fifo principle, a super array contains the order of the
|
||||||
|
// attempts, and when the overall count reaches the limit, the earliest attempt is
|
||||||
|
// removed from the beginning of its array.
|
||||||
|
//
|
||||||
|
// This data structure makes it possible to marshal the peer
|
||||||
|
// history simply by passing it to the JSON marshaler.
|
||||||
|
type peerContainer struct {
|
||||||
|
// Bundles is the outer map using the peer's IP address as key.
|
||||||
|
Bundles map[string]*peerBundle `json:"bundles,omitempty"`
|
||||||
|
|
||||||
|
activeCount int // Number of the still connected peers
|
||||||
|
|
||||||
|
// inactivePeers contains the peers with closed connection in chronological order.
|
||||||
|
inactivePeers *list.List
|
||||||
|
|
||||||
|
// attemptOrder is the super array containing the IP addresses, from which
|
||||||
|
// the peers attempted to connect then failed before/during the handshake.
|
||||||
|
// Its values are appended in chronological order, which means that the
|
||||||
|
// oldest attempt is at the beginning of the array. When the first element
|
||||||
|
// is removed, the first element of the related bundle's attempt array is
|
||||||
|
// removed too, ensuring that always the latest attempts are stored.
|
||||||
|
attemptOrder []string
|
||||||
|
|
||||||
|
// geodb is the geoip database used to retrieve the peers' geographical location.
|
||||||
|
geodb *geoDB
|
||||||
|
}
|
||||||
|
|
||||||
|
// newPeerContainer returns a new instance of the peer container.
|
||||||
|
func newPeerContainer(geodb *geoDB) *peerContainer {
|
||||||
|
return &peerContainer{
|
||||||
|
Bundles: make(map[string]*peerBundle),
|
||||||
|
inactivePeers: list.New(),
|
||||||
|
attemptOrder: make([]string, 0, attemptLimit),
|
||||||
|
geodb: geodb,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bundle inserts a new peer bundle into the map, if the peer belonging
|
||||||
|
// to the given IP wasn't metered so far. In this case retrieves the location of
|
||||||
|
// the IP address from the database and creates a corresponding peer event.
|
||||||
|
// Returns the bundle belonging to the given IP and the events occurring during
|
||||||
|
// the initialization.
|
||||||
|
func (pc *peerContainer) bundle(ip string) (*peerBundle, []*peerEvent) {
|
||||||
|
var events []*peerEvent
|
||||||
|
if _, ok := pc.Bundles[ip]; !ok {
|
||||||
|
location := pc.geodb.location(ip)
|
||||||
|
events = append(events, &peerEvent{
|
||||||
|
IP: ip,
|
||||||
|
Location: location,
|
||||||
|
})
|
||||||
|
pc.Bundles[ip] = &peerBundle{
|
||||||
|
Location: location,
|
||||||
|
KnownPeers: make(map[string]*knownPeer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pc.Bundles[ip], events
|
||||||
|
}
|
||||||
|
|
||||||
|
// extendKnown handles the events of the successfully connected peers.
|
||||||
|
// Returns the events occurring during the extension.
|
||||||
|
func (pc *peerContainer) extendKnown(event *peerEvent) []*peerEvent {
|
||||||
|
bundle, events := pc.bundle(event.IP)
|
||||||
|
peer, peerEvents := bundle.knownPeer(event.IP, event.ID)
|
||||||
|
events = append(events, peerEvents...)
|
||||||
|
// Append the connect and the disconnect events to
|
||||||
|
// the corresponding arrays keeping the limit.
|
||||||
|
switch {
|
||||||
|
case event.Connected != nil:
|
||||||
|
peer.Connected = append(peer.Connected, event.Connected)
|
||||||
|
if first := len(peer.Connected) - sampleLimit; first > 0 {
|
||||||
|
peer.Connected = peer.Connected[first:]
|
||||||
|
}
|
||||||
|
peer.Active = true
|
||||||
|
events = append(events, &peerEvent{
|
||||||
|
Activity: Active,
|
||||||
|
IP: peer.ip,
|
||||||
|
ID: peer.id,
|
||||||
|
})
|
||||||
|
pc.activeCount++
|
||||||
|
if peer.listElement != nil {
|
||||||
|
_ = pc.inactivePeers.Remove(peer.listElement)
|
||||||
|
peer.listElement = nil
|
||||||
|
}
|
||||||
|
case event.Disconnected != nil:
|
||||||
|
peer.Disconnected = append(peer.Disconnected, event.Disconnected)
|
||||||
|
if first := len(peer.Disconnected) - sampleLimit; first > 0 {
|
||||||
|
peer.Disconnected = peer.Disconnected[first:]
|
||||||
|
}
|
||||||
|
peer.Active = false
|
||||||
|
events = append(events, &peerEvent{
|
||||||
|
Activity: Inactive,
|
||||||
|
IP: peer.ip,
|
||||||
|
ID: peer.id,
|
||||||
|
})
|
||||||
|
pc.activeCount--
|
||||||
|
if peer.listElement != nil {
|
||||||
|
// If the peer is already in the list, remove and reinsert it.
|
||||||
|
_ = pc.inactivePeers.Remove(peer.listElement)
|
||||||
|
}
|
||||||
|
// Insert the peer into the list.
|
||||||
|
peer.listElement = pc.inactivePeers.PushBack(peer)
|
||||||
|
}
|
||||||
|
for pc.inactivePeers.Len() > 0 && pc.activeCount+pc.inactivePeers.Len() > knownPeerLimit {
|
||||||
|
// While the count of the known peers is greater than the limit,
|
||||||
|
// remove the first element from the inactive peer list and from the map.
|
||||||
|
if removedPeer, ok := pc.inactivePeers.Remove(pc.inactivePeers.Front()).(*knownPeer); ok {
|
||||||
|
events = append(events, pc.removeKnown(removedPeer.ip, removedPeer.id)...)
|
||||||
|
} else {
|
||||||
|
log.Warn("Failed to parse the removed peer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pc.activeCount > knownPeerLimit {
|
||||||
|
log.Warn("Number of active peers is greater than the limit")
|
||||||
|
}
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleAttempt handles the events of the peers failing before/during the handshake.
|
||||||
|
// Returns the events occurring during the extension.
|
||||||
|
func (pc *peerContainer) handleAttempt(event *peerEvent) []*peerEvent {
|
||||||
|
bundle, events := pc.bundle(event.IP)
|
||||||
|
bundle.Attempts = append(bundle.Attempts, &peerAttempt{
|
||||||
|
Connected: *event.Connected,
|
||||||
|
Disconnected: *event.Disconnected,
|
||||||
|
})
|
||||||
|
pc.attemptOrder = append(pc.attemptOrder, event.IP)
|
||||||
|
for len(pc.attemptOrder) > attemptLimit {
|
||||||
|
// While the length of the connection attempt order array is greater
|
||||||
|
// than the limit, remove the first element from the involved peer's
|
||||||
|
// array and also from the super array.
|
||||||
|
events = append(events, pc.removeAttempt(pc.attemptOrder[0])...)
|
||||||
|
pc.attemptOrder = pc.attemptOrder[1:]
|
||||||
|
}
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
|
||||||
|
// peerBundle contains the peers belonging to a given IP address.
|
||||||
|
type peerBundle struct {
|
||||||
|
// Location contains the geographical location based on the bundle's IP address.
|
||||||
|
Location *geoLocation `json:"location,omitempty"`
|
||||||
|
|
||||||
|
// KnownPeers is the inner map of the metered peer
|
||||||
|
// maintainer data structure using the node ID as key.
|
||||||
|
KnownPeers map[string]*knownPeer `json:"knownPeers,omitempty"`
|
||||||
|
|
||||||
|
// Attempts contains the failed connection attempts of the
|
||||||
|
// peers belonging to a given IP address in chronological order.
|
||||||
|
Attempts []*peerAttempt `json:"attempts,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeKnown removes the known peer belonging to the
|
||||||
|
// given IP address and node ID from the peer tree.
|
||||||
|
func (pc *peerContainer) removeKnown(ip, id string) (events []*peerEvent) {
|
||||||
|
// TODO (kurkomisi): Remove peers that don't have traffic samples anymore.
|
||||||
|
if bundle, ok := pc.Bundles[ip]; ok {
|
||||||
|
if _, ok := bundle.KnownPeers[id]; ok {
|
||||||
|
events = append(events, &peerEvent{
|
||||||
|
Remove: RemoveKnown,
|
||||||
|
IP: ip,
|
||||||
|
ID: id,
|
||||||
|
})
|
||||||
|
delete(bundle.KnownPeers, id)
|
||||||
|
} else {
|
||||||
|
log.Warn("No peer to remove", "ip", ip, "id", id)
|
||||||
|
}
|
||||||
|
if len(bundle.KnownPeers) < 1 && len(bundle.Attempts) < 1 {
|
||||||
|
events = append(events, &peerEvent{
|
||||||
|
Remove: RemoveBundle,
|
||||||
|
IP: ip,
|
||||||
|
})
|
||||||
|
delete(pc.Bundles, ip)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Warn("No bundle to remove", "ip", ip)
|
||||||
|
}
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeAttempt removes the peer attempt belonging to the
|
||||||
|
// given IP address and node ID from the peer tree.
|
||||||
|
func (pc *peerContainer) removeAttempt(ip string) (events []*peerEvent) {
|
||||||
|
if bundle, ok := pc.Bundles[ip]; ok {
|
||||||
|
if len(bundle.Attempts) > 0 {
|
||||||
|
events = append(events, &peerEvent{
|
||||||
|
Remove: RemoveAttempt,
|
||||||
|
IP: ip,
|
||||||
|
})
|
||||||
|
bundle.Attempts = bundle.Attempts[1:]
|
||||||
|
}
|
||||||
|
if len(bundle.Attempts) < 1 && len(bundle.KnownPeers) < 1 {
|
||||||
|
events = append(events, &peerEvent{
|
||||||
|
Remove: RemoveBundle,
|
||||||
|
IP: ip,
|
||||||
|
})
|
||||||
|
delete(pc.Bundles, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
|
||||||
|
// knownPeer inserts a new peer into the map, if the peer belonging
|
||||||
|
// to the given IP address and node ID wasn't metered so far. Returns the peer
|
||||||
|
// belonging to the given IP and ID as well as the events occurring during the
|
||||||
|
// initialization.
|
||||||
|
func (bundle *peerBundle) knownPeer(ip, id string) (*knownPeer, []*peerEvent) {
|
||||||
|
var events []*peerEvent
|
||||||
|
if _, ok := bundle.KnownPeers[id]; !ok {
|
||||||
|
now := time.Now()
|
||||||
|
ingress := emptyChartEntries(now, sampleLimit)
|
||||||
|
egress := emptyChartEntries(now, sampleLimit)
|
||||||
|
events = append(events, &peerEvent{
|
||||||
|
IP: ip,
|
||||||
|
ID: id,
|
||||||
|
Ingress: append([]*ChartEntry{}, ingress...),
|
||||||
|
Egress: append([]*ChartEntry{}, egress...),
|
||||||
|
})
|
||||||
|
bundle.KnownPeers[id] = &knownPeer{
|
||||||
|
ip: ip,
|
||||||
|
id: id,
|
||||||
|
Ingress: ingress,
|
||||||
|
Egress: egress,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bundle.KnownPeers[id], events
|
||||||
|
}
|
||||||
|
|
||||||
|
// knownPeer contains the metered data of a particular peer.
|
||||||
|
type knownPeer struct {
|
||||||
|
// Connected contains the timestamps of the peer's connection events.
|
||||||
|
Connected []*time.Time `json:"connected,omitempty"`
|
||||||
|
|
||||||
|
// Disconnected contains the timestamps of the peer's disconnection events.
|
||||||
|
Disconnected []*time.Time `json:"disconnected,omitempty"`
|
||||||
|
|
||||||
|
// Ingress and Egress contain the peer's traffic samples, which are collected
|
||||||
|
// periodically from the metrics registry.
|
||||||
|
//
|
||||||
|
// A peer can connect multiple times, and we want to visualize the time
|
||||||
|
// passed between two connections, so after the first connection a 0 value
|
||||||
|
// is appended to the traffic arrays even if the peer is inactive until the
|
||||||
|
// peer is removed.
|
||||||
|
Ingress ChartEntries `json:"ingress,omitempty"`
|
||||||
|
Egress ChartEntries `json:"egress,omitempty"`
|
||||||
|
|
||||||
|
Active bool `json:"active"` // Denotes if the peer is still connected.
|
||||||
|
|
||||||
|
listElement *list.Element // Pointer to the peer element in the list.
|
||||||
|
ip, id string // The IP and the ID by which the peer can be accessed in the tree.
|
||||||
|
prevIngress float64
|
||||||
|
prevEgress float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// peerAttempt contains a failed peer connection attempt's attributes.
|
||||||
|
type peerAttempt struct {
|
||||||
|
// Connected contains the timestamp of the connection attempt's moment.
|
||||||
|
Connected time.Time `json:"connected"`
|
||||||
|
|
||||||
|
// Disconnected contains the timestamp of the
|
||||||
|
// moment when the connection attempt failed.
|
||||||
|
Disconnected time.Time `json:"disconnected"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemovedPeerType string
|
||||||
|
type ActivityType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
RemoveKnown RemovedPeerType = "known"
|
||||||
|
RemoveAttempt RemovedPeerType = "attempt"
|
||||||
|
RemoveBundle RemovedPeerType = "bundle"
|
||||||
|
|
||||||
|
Active ActivityType = "active"
|
||||||
|
Inactive ActivityType = "inactive"
|
||||||
|
)
|
||||||
|
|
||||||
|
// peerEvent contains the attributes of a peer event.
|
||||||
|
type peerEvent struct {
|
||||||
|
IP string `json:"ip,omitempty"` // IP address of the peer.
|
||||||
|
ID string `json:"id,omitempty"` // Node ID of the peer.
|
||||||
|
Remove RemovedPeerType `json:"remove,omitempty"` // Type of the peer that is to be removed.
|
||||||
|
Location *geoLocation `json:"location,omitempty"` // Geographical location of the peer.
|
||||||
|
Connected *time.Time `json:"connected,omitempty"` // Timestamp of the connection moment.
|
||||||
|
Disconnected *time.Time `json:"disconnected,omitempty"` // Timestamp of the disonnection moment.
|
||||||
|
Ingress ChartEntries `json:"ingress,omitempty"` // Ingress samples.
|
||||||
|
Egress ChartEntries `json:"egress,omitempty"` // Egress samples.
|
||||||
|
Activity ActivityType `json:"activity,omitempty"` // Connection status change.
|
||||||
|
}
|
||||||
|
|
||||||
|
// trafficMap is a container for the periodically collected peer traffic.
|
||||||
|
type trafficMap map[string]map[string]float64
|
||||||
|
|
||||||
|
// insert inserts a new value to the traffic map. Overwrites
|
||||||
|
// the value at the given ip and id if that already exists.
|
||||||
|
func (m *trafficMap) insert(ip, id string, val float64) {
|
||||||
|
if _, ok := (*m)[ip]; !ok {
|
||||||
|
(*m)[ip] = make(map[string]float64)
|
||||||
|
}
|
||||||
|
(*m)[ip][id] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectPeerData gathers data about the peers and sends it to the clients.
|
||||||
|
func (db *Dashboard) collectPeerData() {
|
||||||
|
defer db.wg.Done()
|
||||||
|
|
||||||
|
// Open the geodb database for IP to geographical information conversions.
|
||||||
|
var err error
|
||||||
|
db.geodb, err = openGeoDB()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to open geodb", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer db.geodb.close()
|
||||||
|
|
||||||
|
peerCh := make(chan p2p.MeteredPeerEvent, eventBufferLimit) // Peer event channel.
|
||||||
|
subPeer := p2p.SubscribeMeteredPeerEvent(peerCh) // Subscribe to peer events.
|
||||||
|
defer subPeer.Unsubscribe() // Unsubscribe at the end.
|
||||||
|
|
||||||
|
ticker := time.NewTicker(db.config.Refresh)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
type registryFunc func(name string, i interface{})
|
||||||
|
type collectorFunc func(traffic *trafficMap) registryFunc
|
||||||
|
|
||||||
|
// trafficCollector generates a function that can be passed to
|
||||||
|
// the prefixed peer registry in order to collect the metered
|
||||||
|
// traffic data from each peer meter.
|
||||||
|
trafficCollector := func(prefix string) collectorFunc {
|
||||||
|
// This part makes is possible to collect the
|
||||||
|
// traffic data into a map from outside.
|
||||||
|
return func(traffic *trafficMap) registryFunc {
|
||||||
|
// The function which can be passed to the registry.
|
||||||
|
return func(name string, i interface{}) {
|
||||||
|
if m, ok := i.(metrics.Meter); ok {
|
||||||
|
// The name of the meter has the format: <common traffic prefix><IP>/<ID>
|
||||||
|
if k := strings.Split(strings.TrimPrefix(name, prefix), "/"); len(k) == 2 {
|
||||||
|
traffic.insert(k[0], k[1], float64(m.Count()))
|
||||||
|
} else {
|
||||||
|
log.Warn("Invalid meter name", "name", name, "prefix", prefix)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Warn("Invalid meter type", "name", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collectIngress := trafficCollector(p2p.MetricsInboundTraffic + "/")
|
||||||
|
collectEgress := trafficCollector(p2p.MetricsOutboundTraffic + "/")
|
||||||
|
|
||||||
|
peers := newPeerContainer(db.geodb)
|
||||||
|
db.peerLock.Lock()
|
||||||
|
db.history.Network = &NetworkMessage{
|
||||||
|
Peers: peers,
|
||||||
|
}
|
||||||
|
db.peerLock.Unlock()
|
||||||
|
|
||||||
|
// newPeerEvents contains peer events, which trigger operations that
|
||||||
|
// will be executed on the peer tree after a metering period.
|
||||||
|
newPeerEvents := make([]*peerEvent, 0, eventLimit)
|
||||||
|
ingress, egress := new(trafficMap), new(trafficMap)
|
||||||
|
*ingress, *egress = make(trafficMap), make(trafficMap)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-peerCh:
|
||||||
|
now := time.Now()
|
||||||
|
switch event.Type {
|
||||||
|
case p2p.PeerConnected:
|
||||||
|
connected := now.Add(-event.Elapsed)
|
||||||
|
newPeerEvents = append(newPeerEvents, &peerEvent{
|
||||||
|
IP: event.IP.String(),
|
||||||
|
ID: event.ID.String(),
|
||||||
|
Connected: &connected,
|
||||||
|
})
|
||||||
|
case p2p.PeerDisconnected:
|
||||||
|
ip, id := event.IP.String(), event.ID.String()
|
||||||
|
newPeerEvents = append(newPeerEvents, &peerEvent{
|
||||||
|
IP: ip,
|
||||||
|
ID: id,
|
||||||
|
Disconnected: &now,
|
||||||
|
})
|
||||||
|
// The disconnect event comes with the last metered traffic count,
|
||||||
|
// because after the disconnection the peer's meter is removed
|
||||||
|
// from the registry. It can happen, that between two metering
|
||||||
|
// period the same peer disconnects multiple times, and appending
|
||||||
|
// all the samples to the traffic arrays would shift the metering,
|
||||||
|
// so only the last metering is stored, overwriting the previous one.
|
||||||
|
ingress.insert(ip, id, float64(event.Ingress))
|
||||||
|
egress.insert(ip, id, float64(event.Egress))
|
||||||
|
case p2p.PeerHandshakeFailed:
|
||||||
|
connected := now.Add(-event.Elapsed)
|
||||||
|
newPeerEvents = append(newPeerEvents, &peerEvent{
|
||||||
|
IP: event.IP.String(),
|
||||||
|
Connected: &connected,
|
||||||
|
Disconnected: &now,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
log.Error("Unknown metered peer event type", "type", event.Type)
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
// Collect the traffic samples from the registry.
|
||||||
|
p2p.PeerIngressRegistry.Each(collectIngress(ingress))
|
||||||
|
p2p.PeerEgressRegistry.Each(collectEgress(egress))
|
||||||
|
|
||||||
|
// Protect 'peers', because it is part of the history.
|
||||||
|
db.peerLock.Lock()
|
||||||
|
|
||||||
|
var diff []*peerEvent
|
||||||
|
for i := 0; i < len(newPeerEvents); i++ {
|
||||||
|
if newPeerEvents[i].IP == "" {
|
||||||
|
log.Warn("Peer event without IP", "event", *newPeerEvents[i])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
diff = append(diff, newPeerEvents[i])
|
||||||
|
// There are two main branches of peer events coming from the event
|
||||||
|
// feed, one belongs to the known peers, one to the unknown peers.
|
||||||
|
// If the event has node ID, it belongs to a known peer, otherwise
|
||||||
|
// to an unknown one, which is considered as connection attempt.
|
||||||
|
//
|
||||||
|
// The extension can produce additional peer events, such
|
||||||
|
// as remove, location and initial samples events.
|
||||||
|
if newPeerEvents[i].ID == "" {
|
||||||
|
diff = append(diff, peers.handleAttempt(newPeerEvents[i])...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
diff = append(diff, peers.extendKnown(newPeerEvents[i])...)
|
||||||
|
}
|
||||||
|
// Update the peer tree using the traffic maps.
|
||||||
|
for ip, bundle := range peers.Bundles {
|
||||||
|
for id, peer := range bundle.KnownPeers {
|
||||||
|
// Value is 0 if the traffic map doesn't have the
|
||||||
|
// entry corresponding to the given IP and ID.
|
||||||
|
curIngress, curEgress := (*ingress)[ip][id], (*egress)[ip][id]
|
||||||
|
deltaIngress, deltaEgress := curIngress, curEgress
|
||||||
|
if deltaIngress >= peer.prevIngress {
|
||||||
|
deltaIngress -= peer.prevIngress
|
||||||
|
}
|
||||||
|
if deltaEgress >= peer.prevEgress {
|
||||||
|
deltaEgress -= peer.prevEgress
|
||||||
|
}
|
||||||
|
peer.prevIngress, peer.prevEgress = curIngress, curEgress
|
||||||
|
i := &ChartEntry{
|
||||||
|
Value: deltaIngress,
|
||||||
|
}
|
||||||
|
e := &ChartEntry{
|
||||||
|
Value: deltaEgress,
|
||||||
|
}
|
||||||
|
peer.Ingress = append(peer.Ingress, i)
|
||||||
|
peer.Egress = append(peer.Egress, e)
|
||||||
|
if first := len(peer.Ingress) - sampleLimit; first > 0 {
|
||||||
|
peer.Ingress = peer.Ingress[first:]
|
||||||
|
}
|
||||||
|
if first := len(peer.Egress) - sampleLimit; first > 0 {
|
||||||
|
peer.Egress = peer.Egress[first:]
|
||||||
|
}
|
||||||
|
// Creating the traffic sample events.
|
||||||
|
diff = append(diff, &peerEvent{
|
||||||
|
IP: ip,
|
||||||
|
ID: id,
|
||||||
|
Ingress: ChartEntries{i},
|
||||||
|
Egress: ChartEntries{e},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.peerLock.Unlock()
|
||||||
|
|
||||||
|
if len(diff) > 0 {
|
||||||
|
db.sendToAll(&Message{Network: &NetworkMessage{
|
||||||
|
Diff: diff,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
// Clear the traffic maps, and the event array,
|
||||||
|
// prepare them for the next metering.
|
||||||
|
*ingress, *egress = make(trafficMap), make(trafficMap)
|
||||||
|
newPeerEvents = newPeerEvents[:0]
|
||||||
|
case err := <-subPeer.Err():
|
||||||
|
log.Warn("Peer subscription error", "err", err)
|
||||||
|
return
|
||||||
|
case errc := <-db.quit:
|
||||||
|
errc <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
146
dashboard/system.go
Normal file
146
dashboard/system.go
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
// 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 dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/elastic/gosigar"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
)
|
||||||
|
|
||||||
|
// meterCollector returns a function, which retrieves the count of a specific meter.
|
||||||
|
func meterCollector(name string) func() int64 {
|
||||||
|
if meter := metrics.Get(name); meter != nil {
|
||||||
|
m := meter.(metrics.Meter)
|
||||||
|
return func() int64 {
|
||||||
|
return m.Count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func() int64 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectSystemData gathers data about the system and sends it to the clients.
|
||||||
|
func (db *Dashboard) collectSystemData() {
|
||||||
|
defer db.wg.Done()
|
||||||
|
|
||||||
|
systemCPUUsage := gosigar.Cpu{}
|
||||||
|
systemCPUUsage.Get()
|
||||||
|
var (
|
||||||
|
mem runtime.MemStats
|
||||||
|
|
||||||
|
collectNetworkIngress = meterCollector(p2p.MetricsInboundTraffic)
|
||||||
|
collectNetworkEgress = meterCollector(p2p.MetricsOutboundTraffic)
|
||||||
|
collectDiskRead = meterCollector("eth/db/chaindata/disk/read")
|
||||||
|
collectDiskWrite = meterCollector("eth/db/chaindata/disk/write")
|
||||||
|
|
||||||
|
prevNetworkIngress = collectNetworkIngress()
|
||||||
|
prevNetworkEgress = collectNetworkEgress()
|
||||||
|
prevProcessCPUTime = getProcessCPUTime()
|
||||||
|
prevSystemCPUUsage = systemCPUUsage
|
||||||
|
prevDiskRead = collectDiskRead()
|
||||||
|
prevDiskWrite = collectDiskWrite()
|
||||||
|
|
||||||
|
frequency = float64(db.config.Refresh / time.Second)
|
||||||
|
numCPU = float64(runtime.NumCPU())
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case errc := <-db.quit:
|
||||||
|
errc <- nil
|
||||||
|
return
|
||||||
|
case <-time.After(db.config.Refresh):
|
||||||
|
systemCPUUsage.Get()
|
||||||
|
var (
|
||||||
|
curNetworkIngress = collectNetworkIngress()
|
||||||
|
curNetworkEgress = collectNetworkEgress()
|
||||||
|
curProcessCPUTime = getProcessCPUTime()
|
||||||
|
curSystemCPUUsage = systemCPUUsage
|
||||||
|
curDiskRead = collectDiskRead()
|
||||||
|
curDiskWrite = collectDiskWrite()
|
||||||
|
|
||||||
|
deltaNetworkIngress = float64(curNetworkIngress - prevNetworkIngress)
|
||||||
|
deltaNetworkEgress = float64(curNetworkEgress - prevNetworkEgress)
|
||||||
|
deltaProcessCPUTime = curProcessCPUTime - prevProcessCPUTime
|
||||||
|
deltaSystemCPUUsage = curSystemCPUUsage.Delta(prevSystemCPUUsage)
|
||||||
|
deltaDiskRead = curDiskRead - prevDiskRead
|
||||||
|
deltaDiskWrite = curDiskWrite - prevDiskWrite
|
||||||
|
)
|
||||||
|
prevNetworkIngress = curNetworkIngress
|
||||||
|
prevNetworkEgress = curNetworkEgress
|
||||||
|
prevProcessCPUTime = curProcessCPUTime
|
||||||
|
prevSystemCPUUsage = curSystemCPUUsage
|
||||||
|
prevDiskRead = curDiskRead
|
||||||
|
prevDiskWrite = curDiskWrite
|
||||||
|
|
||||||
|
runtime.ReadMemStats(&mem)
|
||||||
|
activeMemory := &ChartEntry{
|
||||||
|
Value: float64(mem.Alloc) / frequency,
|
||||||
|
}
|
||||||
|
virtualMemory := &ChartEntry{
|
||||||
|
Value: float64(mem.Sys) / frequency,
|
||||||
|
}
|
||||||
|
networkIngress := &ChartEntry{
|
||||||
|
Value: deltaNetworkIngress / frequency,
|
||||||
|
}
|
||||||
|
networkEgress := &ChartEntry{
|
||||||
|
Value: deltaNetworkEgress / frequency,
|
||||||
|
}
|
||||||
|
processCPU := &ChartEntry{
|
||||||
|
Value: deltaProcessCPUTime / frequency / numCPU * 100,
|
||||||
|
}
|
||||||
|
systemCPU := &ChartEntry{
|
||||||
|
Value: float64(deltaSystemCPUUsage.Sys+deltaSystemCPUUsage.User) / frequency / numCPU,
|
||||||
|
}
|
||||||
|
diskRead := &ChartEntry{
|
||||||
|
Value: float64(deltaDiskRead) / frequency,
|
||||||
|
}
|
||||||
|
diskWrite := &ChartEntry{
|
||||||
|
Value: float64(deltaDiskWrite) / frequency,
|
||||||
|
}
|
||||||
|
db.sysLock.Lock()
|
||||||
|
sys := db.history.System
|
||||||
|
sys.ActiveMemory = append(sys.ActiveMemory[1:], activeMemory)
|
||||||
|
sys.VirtualMemory = append(sys.VirtualMemory[1:], virtualMemory)
|
||||||
|
sys.NetworkIngress = append(sys.NetworkIngress[1:], networkIngress)
|
||||||
|
sys.NetworkEgress = append(sys.NetworkEgress[1:], networkEgress)
|
||||||
|
sys.ProcessCPU = append(sys.ProcessCPU[1:], processCPU)
|
||||||
|
sys.SystemCPU = append(sys.SystemCPU[1:], systemCPU)
|
||||||
|
sys.DiskRead = append(sys.DiskRead[1:], diskRead)
|
||||||
|
sys.DiskWrite = append(sys.DiskWrite[1:], diskWrite)
|
||||||
|
db.sysLock.Unlock()
|
||||||
|
|
||||||
|
db.sendToAll(&Message{
|
||||||
|
System: &SystemMessage{
|
||||||
|
ActiveMemory: ChartEntries{activeMemory},
|
||||||
|
VirtualMemory: ChartEntries{virtualMemory},
|
||||||
|
NetworkIngress: ChartEntries{networkIngress},
|
||||||
|
NetworkEgress: ChartEntries{networkEgress},
|
||||||
|
ProcessCPU: ChartEntries{processCPU},
|
||||||
|
SystemCPU: ChartEntries{systemCPU},
|
||||||
|
DiskRead: ChartEntries{diskRead},
|
||||||
|
DiskWrite: ChartEntries{diskWrite},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue