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
My Merge Commit
This commit is contained in:
commit
e95388dda6
101 changed files with 1277 additions and 2121 deletions
|
|
@ -927,7 +927,7 @@ func TestABI_MethodById(t *testing.T) {
|
||||||
}
|
}
|
||||||
b := fmt.Sprintf("%v", m2)
|
b := fmt.Sprintf("%v", m2)
|
||||||
if a != b {
|
if a != b {
|
||||||
t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.ID()))
|
t.Errorf("Method %v (id %x) not 'findable' by id in ABI", name, m.ID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Also test empty
|
// Also test empty
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,16 @@ const (
|
||||||
// enforces compile time type safety and naming convention opposed to having to
|
// enforces compile time type safety and naming convention opposed to having to
|
||||||
// manually maintain hard coded strings that break on runtime.
|
// manually maintain hard coded strings that break on runtime.
|
||||||
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
|
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
|
||||||
// Process each individual contract requested binding
|
var (
|
||||||
contracts := make(map[string]*tmplContract)
|
// contracts is the map of each individual contract requested binding
|
||||||
|
contracts = make(map[string]*tmplContract)
|
||||||
|
|
||||||
// Map used to flag each encountered library as such
|
// structs is the map of all reclared structs shared by passed contracts.
|
||||||
isLib := make(map[string]struct{})
|
structs = make(map[string]*tmplStruct)
|
||||||
|
|
||||||
|
// isLib is the map used to flag each encountered library as such
|
||||||
|
isLib = make(map[string]struct{})
|
||||||
|
)
|
||||||
for i := 0; i < len(types); i++ {
|
for i := 0; i < len(types); i++ {
|
||||||
// Parse the actual ABI to generate the binding for
|
// Parse the actual ABI to generate the binding for
|
||||||
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
||||||
|
|
@ -73,7 +77,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
calls = make(map[string]*tmplMethod)
|
calls = make(map[string]*tmplMethod)
|
||||||
transacts = make(map[string]*tmplMethod)
|
transacts = make(map[string]*tmplMethod)
|
||||||
events = make(map[string]*tmplEvent)
|
events = make(map[string]*tmplEvent)
|
||||||
structs = make(map[string]*tmplStruct)
|
|
||||||
|
|
||||||
// identifiers are used to detect duplicated identifier of function
|
// identifiers are used to detect duplicated identifier of function
|
||||||
// and event. For all calls, transacts and events, abigen will generate
|
// and event. For all calls, transacts and events, abigen will generate
|
||||||
|
|
@ -168,7 +171,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
Transacts: transacts,
|
Transacts: transacts,
|
||||||
Events: events,
|
Events: events,
|
||||||
Libraries: make(map[string]string),
|
Libraries: make(map[string]string),
|
||||||
Structs: structs,
|
|
||||||
}
|
}
|
||||||
// Function 4-byte signatures are stored in the same sequence
|
// Function 4-byte signatures are stored in the same sequence
|
||||||
// as types, if available.
|
// as types, if available.
|
||||||
|
|
@ -200,6 +202,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
Package: pkg,
|
Package: pkg,
|
||||||
Contracts: contracts,
|
Contracts: contracts,
|
||||||
Libraries: libs,
|
Libraries: libs,
|
||||||
|
Structs: structs,
|
||||||
}
|
}
|
||||||
buffer := new(bytes.Buffer)
|
buffer := new(bytes.Buffer)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1448,6 +1448,88 @@ var bindTests = []struct {
|
||||||
map[string]string{"_myVar": "pubVar"}, // alias MyVar to PubVar
|
map[string]string{"_myVar": "pubVar"}, // alias MyVar to PubVar
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"MultiContracts",
|
||||||
|
`
|
||||||
|
pragma solidity ^0.5.11;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
library ExternalLib {
|
||||||
|
struct SharedStruct{
|
||||||
|
uint256 f1;
|
||||||
|
bytes32 f2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ContractOne {
|
||||||
|
function foo(ExternalLib.SharedStruct memory s) pure public {
|
||||||
|
// Do stuff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ContractTwo {
|
||||||
|
function bar(ExternalLib.SharedStruct memory s) pure public {
|
||||||
|
// Do stuff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
[]string{
|
||||||
|
`60806040523480156100115760006000fd5b50610017565b6101b5806100266000396000f3fe60806040523480156100115760006000fd5b50600436106100305760003560e01c80639d8a8ba81461003657610030565b60006000fd5b610050600480360361004b91908101906100d1565b610052565b005b5b5056610171565b6000813590506100698161013d565b92915050565b6000604082840312156100825760006000fd5b61008c60406100fb565b9050600061009c848285016100bc565b60008301525060206100b08482850161005a565b60208301525092915050565b6000813590506100cb81610157565b92915050565b6000604082840312156100e45760006000fd5b60006100f28482850161006f565b91505092915050565b6000604051905081810181811067ffffffffffffffff8211171561011f5760006000fd5b8060405250919050565b6000819050919050565b6000819050919050565b61014681610129565b811415156101545760006000fd5b50565b61016081610133565b8114151561016e5760006000fd5b50565bfea365627a7a72315820749274eb7f6c01010d5322af4e1668b0a154409eb7968bd6cae5524c7ed669bb6c6578706572696d656e74616cf564736f6c634300050c0040`,
|
||||||
|
`60806040523480156100115760006000fd5b50610017565b6101b5806100266000396000f3fe60806040523480156100115760006000fd5b50600436106100305760003560e01c8063db8ba08c1461003657610030565b60006000fd5b610050600480360361004b91908101906100d1565b610052565b005b5b5056610171565b6000813590506100698161013d565b92915050565b6000604082840312156100825760006000fd5b61008c60406100fb565b9050600061009c848285016100bc565b60008301525060206100b08482850161005a565b60208301525092915050565b6000813590506100cb81610157565b92915050565b6000604082840312156100e45760006000fd5b60006100f28482850161006f565b91505092915050565b6000604051905081810181811067ffffffffffffffff8211171561011f5760006000fd5b8060405250919050565b6000819050919050565b6000819050919050565b61014681610129565b811415156101545760006000fd5b50565b61016081610133565b8114151561016e5760006000fd5b50565bfea365627a7a723158209bc28ee7ea97c131a13330d77ec73b4493b5c59c648352da81dd288b021192596c6578706572696d656e74616cf564736f6c634300050c0040`,
|
||||||
|
`606c6026600b82828239805160001a6073141515601857fe5b30600052607381538281f350fe73000000000000000000000000000000000000000030146080604052600436106023575b60006000fdfea365627a7a72315820518f0110144f5b3de95697d05e456a064656890d08e6f9cff47f3be710cc46a36c6578706572696d656e74616cf564736f6c634300050c0040`,
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
`[{"constant":true,"inputs":[{"components":[{"internalType":"uint256","name":"f1","type":"uint256"},{"internalType":"bytes32","name":"f2","type":"bytes32"}],"internalType":"struct ExternalLib.SharedStruct","name":"s","type":"tuple"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"}]`,
|
||||||
|
`[{"constant":true,"inputs":[{"components":[{"internalType":"uint256","name":"f1","type":"uint256"},{"internalType":"bytes32","name":"f2","type":"bytes32"}],"internalType":"struct ExternalLib.SharedStruct","name":"s","type":"tuple"}],"name":"bar","outputs":[],"payable":false,"stateMutability":"pure","type":"function"}]`,
|
||||||
|
`[]`,
|
||||||
|
},
|
||||||
|
`
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
|
||||||
|
// Deploy registrar contract
|
||||||
|
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000)
|
||||||
|
defer sim.Close()
|
||||||
|
|
||||||
|
transactOpts := bind.NewKeyedTransactor(key)
|
||||||
|
_, _, c1, err := DeployContractOne(transactOpts, sim)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to deploy contract")
|
||||||
|
}
|
||||||
|
sim.Commit()
|
||||||
|
err = c1.Foo(nil, ExternalLibSharedStruct{
|
||||||
|
F1: big.NewInt(100),
|
||||||
|
F2: [32]byte{0x01, 0x02, 0x03},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to invoke function")
|
||||||
|
}
|
||||||
|
_, _, c2, err := DeployContractTwo(transactOpts, sim)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to deploy contract")
|
||||||
|
}
|
||||||
|
sim.Commit()
|
||||||
|
err = c2.Bar(nil, ExternalLibSharedStruct{
|
||||||
|
F1: big.NewInt(100),
|
||||||
|
F2: [32]byte{0x01, 0x02, 0x03},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to invoke function")
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
[]string{"ContractOne", "ContractTwo", "ExternalLib"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that packages generated by the binder can be successfully compiled and
|
// Tests that packages generated by the binder can be successfully compiled and
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ type tmplData struct {
|
||||||
Package string // Name of the package to place the generated file in
|
Package string // Name of the package to place the generated file in
|
||||||
Contracts map[string]*tmplContract // List of contracts to generate into this file
|
Contracts map[string]*tmplContract // List of contracts to generate into this file
|
||||||
Libraries map[string]string // Map the bytecode's link pattern to the library name
|
Libraries map[string]string // Map the bytecode's link pattern to the library name
|
||||||
|
Structs map[string]*tmplStruct // Contract struct type definitions
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplContract contains the data needed to generate an individual contract binding.
|
// tmplContract contains the data needed to generate an individual contract binding.
|
||||||
|
|
@ -36,8 +37,7 @@ type tmplContract struct {
|
||||||
Transacts map[string]*tmplMethod // Contract calls that write state data
|
Transacts map[string]*tmplMethod // Contract calls that write state data
|
||||||
Events map[string]*tmplEvent // Contract events accessors
|
Events map[string]*tmplEvent // Contract events accessors
|
||||||
Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
|
Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
|
||||||
Structs map[string]*tmplStruct // Contract struct type definitions
|
Library bool // Indicator whether the contract is a library
|
||||||
Library bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
|
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
|
||||||
|
|
@ -108,8 +108,16 @@ var (
|
||||||
_ = event.NewSubscription
|
_ = event.NewSubscription
|
||||||
)
|
)
|
||||||
|
|
||||||
|
{{$structs := .Structs}}
|
||||||
|
{{range $structs}}
|
||||||
|
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
|
||||||
|
type {{.Name}} struct {
|
||||||
|
{{range $field := .Fields}}
|
||||||
|
{{$field.Name}} {{$field.Type}}{{end}}
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{range $contract := .Contracts}}
|
{{range $contract := .Contracts}}
|
||||||
{{$structs := $contract.Structs}}
|
|
||||||
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
||||||
const {{.Type}}ABI = "{{.InputABI}}"
|
const {{.Type}}ABI = "{{.InputABI}}"
|
||||||
|
|
||||||
|
|
@ -285,14 +293,6 @@ var (
|
||||||
return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
|
return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
{{range .Structs}}
|
|
||||||
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
|
|
||||||
type {{.Name}} struct {
|
|
||||||
{{range $field := .Fields}}
|
|
||||||
{{$field.Name}} {{$field.Type}}{{end}}
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range .Calls}}
|
{{range .Calls}}
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
|
|
@ -507,8 +507,8 @@ package {{.Package}};
|
||||||
import org.ethereum.geth.*;
|
import org.ethereum.geth.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
{{$structs := .Structs}}
|
||||||
{{range $contract := .Contracts}}
|
{{range $contract := .Contracts}}
|
||||||
{{$structs := $contract.Structs}}
|
|
||||||
{{if not .Library}}public {{end}}class {{.Type}} {
|
{{if not .Library}}public {{end}}class {{.Type}} {
|
||||||
// ABI is the input ABI used to generate the binding from.
|
// ABI is the input ABI used to generate the binding from.
|
||||||
public final static String ABI = "{{.InputABI}}";
|
public final static String ABI = "{{.InputABI}}";
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ func TestEventTupleUnpack(t *testing.T) {
|
||||||
type EventTransferWithTag struct {
|
type EventTransferWithTag struct {
|
||||||
// this is valid because `value` is not exportable,
|
// this is valid because `value` is not exportable,
|
||||||
// so value is only unmarshalled into `Value1`.
|
// so value is only unmarshalled into `Value1`.
|
||||||
value *big.Int
|
value *big.Int //lint:ignore U1000 unused field is part of test
|
||||||
Value1 *big.Int `abi:"value"`
|
Value1 *big.Int `abi:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,40 +354,6 @@ func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, ass
|
||||||
return a.Unpack(dest, "e", data)
|
return a.Unpack(dest, "e", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Taken from
|
|
||||||
https://github.com/ethereum/go-ethereum/pull/15568
|
|
||||||
*/
|
|
||||||
|
|
||||||
type testResult struct {
|
|
||||||
Values [2]*big.Int
|
|
||||||
Value1 *big.Int
|
|
||||||
Value2 *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type testCase struct {
|
|
||||||
definition string
|
|
||||||
want testResult
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc testCase) encoded(intType, arrayType Type) []byte {
|
|
||||||
var b bytes.Buffer
|
|
||||||
if tc.want.Value1 != nil {
|
|
||||||
val, _ := intType.pack(reflect.ValueOf(tc.want.Value1))
|
|
||||||
b.Write(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(tc.want.Values, [2]*big.Int{nil, nil}) {
|
|
||||||
val, _ := arrayType.pack(reflect.ValueOf(tc.want.Values))
|
|
||||||
b.Write(val)
|
|
||||||
}
|
|
||||||
if tc.want.Value2 != nil {
|
|
||||||
val, _ := intType.pack(reflect.ValueOf(tc.want.Value2))
|
|
||||||
b.Write(val)
|
|
||||||
}
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
|
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
|
||||||
func TestEventUnpackIndexed(t *testing.T) {
|
func TestEventUnpackIndexed(t *testing.T) {
|
||||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ func packNum(value reflect.Value) []byte {
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
return U256(big.NewInt(value.Int()))
|
return U256(big.NewInt(value.Int()))
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
return U256(value.Interface().(*big.Int))
|
return U256(new(big.Int).Set(value.Interface().(*big.Int)))
|
||||||
default:
|
default:
|
||||||
panic("abi: fatal error")
|
panic("abi: fatal error")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
|
||||||
"Please file a ticket at:\n\n" +
|
"Please file a ticket at:\n\n" +
|
||||||
"https://github.com/ethereum/go-ethereum/issues." +
|
"https://github.com/ethereum/go-ethereum/issues." +
|
||||||
"The error was : %s"
|
"The error was : %s"
|
||||||
|
//lint:ignore ST1005 This is a message for the user
|
||||||
return fmt.Errorf(msg, tmpName, err)
|
return fmt.Errorf(msg, tmpName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +238,7 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
||||||
|
|
||||||
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||||
if cryptoJson.Cipher != "aes-128-ctr" {
|
if cryptoJson.Cipher != "aes-128-ctr" {
|
||||||
return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
|
return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
|
||||||
}
|
}
|
||||||
mac, err := hex.DecodeString(cryptoJson.MAC)
|
mac, err := hex.DecodeString(cryptoJson.MAC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -273,7 +274,7 @@ func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||||
|
|
||||||
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
|
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
|
||||||
if keyProtected.Version != version {
|
if keyProtected.Version != version {
|
||||||
return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
|
return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
|
||||||
}
|
}
|
||||||
keyId = uuid.Parse(keyProtected.Id)
|
keyId = uuid.Parse(keyProtected.Id)
|
||||||
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
||||||
|
|
@ -335,13 +336,13 @@ func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
|
||||||
c := ensureInt(cryptoJSON.KDFParams["c"])
|
c := ensureInt(cryptoJSON.KDFParams["c"])
|
||||||
prf := cryptoJSON.KDFParams["prf"].(string)
|
prf := cryptoJSON.KDFParams["prf"].(string)
|
||||||
if prf != "hmac-sha256" {
|
if prf != "hmac-sha256" {
|
||||||
return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
|
return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
|
||||||
}
|
}
|
||||||
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
|
return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: can we do without this when unmarshalling dynamic JSON?
|
// TODO: can we do without this when unmarshalling dynamic JSON?
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSes
|
||||||
|
|
||||||
cardPublic, ok := gen.Unmarshal(keyData)
|
cardPublic, ok := gen.Unmarshal(keyData)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("Could not unmarshal public key from card")
|
return nil, fmt.Errorf("could not unmarshal public key from card")
|
||||||
}
|
}
|
||||||
|
|
||||||
secret, err := gen.GenerateSharedSecret(private, cardPublic)
|
secret, err := gen.GenerateSharedSecret(private, cardPublic)
|
||||||
|
|
@ -109,7 +109,7 @@ func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
|
||||||
cardChallenge := response.Data[32:64]
|
cardChallenge := response.Data[32:64]
|
||||||
|
|
||||||
if !bytes.Equal(expectedCryptogram, cardCryptogram) {
|
if !bytes.Equal(expectedCryptogram, cardCryptogram) {
|
||||||
return fmt.Errorf("Invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram)
|
return fmt.Errorf("invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram)
|
||||||
}
|
}
|
||||||
|
|
||||||
md.Reset()
|
md.Reset()
|
||||||
|
|
@ -132,7 +132,7 @@ func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
|
||||||
// Unpair disestablishes an existing pairing.
|
// Unpair disestablishes an existing pairing.
|
||||||
func (s *SecureChannelSession) Unpair() error {
|
func (s *SecureChannelSession) Unpair() error {
|
||||||
if s.PairingKey == nil {
|
if s.PairingKey == nil {
|
||||||
return fmt.Errorf("Cannot unpair: not paired")
|
return fmt.Errorf("cannot unpair: not paired")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
|
_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
|
||||||
|
|
@ -148,7 +148,7 @@ func (s *SecureChannelSession) Unpair() error {
|
||||||
// Open initializes the secure channel.
|
// Open initializes the secure channel.
|
||||||
func (s *SecureChannelSession) Open() error {
|
func (s *SecureChannelSession) Open() error {
|
||||||
if s.iv != nil {
|
if s.iv != nil {
|
||||||
return fmt.Errorf("Session already opened")
|
return fmt.Errorf("session already opened")
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := s.open()
|
response, err := s.open()
|
||||||
|
|
@ -185,11 +185,11 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
|
if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
|
||||||
return fmt.Errorf("Got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
|
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(response.Data) != scSecretLength {
|
if len(response.Data) != scSecretLength {
|
||||||
return fmt.Errorf("Response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
|
return fmt.Errorf("response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -222,7 +222,7 @@ func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error
|
||||||
// transmitEncrypted sends an encrypted message, and decrypts and returns the response.
|
// transmitEncrypted sends an encrypted message, and decrypts and returns the response.
|
||||||
func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
|
func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
|
||||||
if s.iv == nil {
|
if s.iv == nil {
|
||||||
return nil, fmt.Errorf("Channel not open")
|
return nil, fmt.Errorf("channel not open")
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := s.encryptAPDU(data)
|
data, err := s.encryptAPDU(data)
|
||||||
|
|
@ -261,14 +261,14 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !bytes.Equal(s.iv, rmac) {
|
if !bytes.Equal(s.iv, rmac) {
|
||||||
return nil, fmt.Errorf("Invalid MAC in response")
|
return nil, fmt.Errorf("invalid MAC in response")
|
||||||
}
|
}
|
||||||
|
|
||||||
rapdu := &responseAPDU{}
|
rapdu := &responseAPDU{}
|
||||||
rapdu.deserialize(plainData)
|
rapdu.deserialize(plainData)
|
||||||
|
|
||||||
if rapdu.Sw1 != sw1Ok {
|
if rapdu.Sw1 != sw1Ok {
|
||||||
return nil, fmt.Errorf("Unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
return nil, fmt.Errorf("unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return rapdu, nil
|
return rapdu, nil
|
||||||
|
|
@ -277,7 +277,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
||||||
// encryptAPDU is an internal method that serializes and encrypts an APDU.
|
// encryptAPDU is an internal method that serializes and encrypts an APDU.
|
||||||
func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
||||||
if len(data) > maxPayloadSize {
|
if len(data) > maxPayloadSize {
|
||||||
return nil, fmt.Errorf("Payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
|
return nil, fmt.Errorf("payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
|
||||||
}
|
}
|
||||||
data = pad(data, 0x80)
|
data = pad(data, 0x80)
|
||||||
|
|
||||||
|
|
@ -323,10 +323,10 @@ func unpad(data []byte, terminator byte) ([]byte, error) {
|
||||||
case terminator:
|
case terminator:
|
||||||
return data[:len(data)-i], nil
|
return data[:len(data)-i], nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Expected end of padding, got %d", data[len(data)-i])
|
return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Expected end of padding, got 0")
|
return nil, fmt.Errorf("expected end of padding, got 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateIV is an internal method that updates the initialization vector after
|
// updateIV is an internal method that updates the initialization vector after
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.Sw1 != sw1Ok {
|
if response.Sw1 != sw1Ok {
|
||||||
return nil, fmt.Errorf("Unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
return nil, fmt.Errorf("unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
|
|
@ -252,7 +252,7 @@ func (w *Wallet) release() error {
|
||||||
// with the wallet.
|
// with the wallet.
|
||||||
func (w *Wallet) pair(puk []byte) error {
|
func (w *Wallet) pair(puk []byte) error {
|
||||||
if w.session.paired() {
|
if w.session.paired() {
|
||||||
return fmt.Errorf("Wallet already paired")
|
return fmt.Errorf("wallet already paired")
|
||||||
}
|
}
|
||||||
pairing, err := w.session.pair(puk)
|
pairing, err := w.session.pair(puk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -773,12 +773,12 @@ func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationP
|
||||||
|
|
||||||
// Look for the path in the URL
|
// Look for the path in the URL
|
||||||
if account.URL.Scheme != w.Hub.scheme {
|
if account.URL.Scheme != w.Hub.scheme {
|
||||||
return nil, fmt.Errorf("Scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme)
|
return nil, fmt.Errorf("scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.SplitN(account.URL.Path, "/", 2)
|
parts := strings.SplitN(account.URL.Path, "/", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return nil, fmt.Errorf("Invalid URL format: %s", account.URL)
|
return nil, fmt.Errorf("invalid URL format: %s", account.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
if parts[0] != fmt.Sprintf("%x", w.PublicKey[1:3]) {
|
if parts[0] != fmt.Sprintf("%x", w.PublicKey[1:3]) {
|
||||||
|
|
@ -813,7 +813,7 @@ func (s *Session) pair(secret []byte) (smartcardPairing, error) {
|
||||||
// unpair deletes an existing pairing.
|
// unpair deletes an existing pairing.
|
||||||
func (s *Session) unpair() error {
|
func (s *Session) unpair() error {
|
||||||
if !s.verified {
|
if !s.verified {
|
||||||
return fmt.Errorf("Unpair requires that the PIN be verified")
|
return fmt.Errorf("unpair requires that the PIN be verified")
|
||||||
}
|
}
|
||||||
return s.Channel.Unpair()
|
return s.Channel.Unpair()
|
||||||
}
|
}
|
||||||
|
|
@ -850,7 +850,7 @@ func (s *Session) paired() bool {
|
||||||
// authenticate uses an existing pairing to establish a secure channel.
|
// authenticate uses an existing pairing to establish a secure channel.
|
||||||
func (s *Session) authenticate(pairing smartcardPairing) error {
|
func (s *Session) authenticate(pairing smartcardPairing) error {
|
||||||
if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) {
|
if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) {
|
||||||
return fmt.Errorf("Cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
|
return fmt.Errorf("cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
|
||||||
}
|
}
|
||||||
s.Channel.PairingKey = pairing.PairingKey
|
s.Channel.PairingKey = pairing.PairingKey
|
||||||
s.Channel.PairingIndex = pairing.PairingIndex
|
s.Channel.PairingIndex = pairing.PairingIndex
|
||||||
|
|
@ -879,6 +879,7 @@ func (s *Session) walletStatus() (*walletStatus, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// derivationPath fetches the wallet's current derivation path from the card.
|
// derivationPath fetches the wallet's current derivation path from the card.
|
||||||
|
//lint:ignore U1000 needs to be added to the console interface
|
||||||
func (s *Session) derivationPath() (accounts.DerivationPath, error) {
|
func (s *Session) derivationPath() (accounts.DerivationPath, error) {
|
||||||
response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
|
response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -993,12 +994,14 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyExport contains information on an exported keypair.
|
// keyExport contains information on an exported keypair.
|
||||||
|
//lint:ignore U1000 needs to be added to the console interface
|
||||||
type keyExport struct {
|
type keyExport struct {
|
||||||
PublicKey []byte `asn1:"tag:0"`
|
PublicKey []byte `asn1:"tag:0"`
|
||||||
PrivateKey []byte `asn1:"tag:1,optional"`
|
PrivateKey []byte `asn1:"tag:1,optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// publicKey returns the public key for the current derivation path.
|
// publicKey returns the public key for the current derivation path.
|
||||||
|
//lint:ignore U1000 needs to be added to the console interface
|
||||||
func (s *Session) publicKey() ([]byte, error) {
|
func (s *Session) publicKey() ([]byte, error) {
|
||||||
response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)
|
response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,8 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
|
||||||
return common.Address{}, nil, accounts.ErrWalletClosed
|
return common.Address{}, nil, accounts.ErrWalletClosed
|
||||||
}
|
}
|
||||||
// Ensure the wallet is capable of signing the given transaction
|
// Ensure the wallet is capable of signing the given transaction
|
||||||
if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
|
if chainID != nil && w.version[0] <= 1 && w.version[2] <= 2 {
|
||||||
|
//lint:ignore ST1005 brand name displayed on the console
|
||||||
return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
|
return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
|
||||||
}
|
}
|
||||||
// All infos gathered and metadata checks out, request signing
|
// All infos gathered and metadata checks out, request signing
|
||||||
|
|
|
||||||
|
|
@ -32,19 +32,6 @@ import (
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
commandHelperTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
|
|
||||||
{{if .Description}}{{.Description}}
|
|
||||||
{{end}}{{if .Subcommands}}
|
|
||||||
SUBCOMMANDS:
|
|
||||||
{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
|
|
||||||
{{end}}{{end}}{{if .Flags}}
|
|
||||||
OPTIONS:
|
|
||||||
{{range $.Flags}}{{"\t"}}{{.}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Git SHA1 commit hash of the release (set via linker flags)
|
// Git SHA1 commit hash of the release (set via linker flags)
|
||||||
gitCommit = ""
|
gitCommit = ""
|
||||||
|
|
@ -128,7 +115,7 @@ func init() {
|
||||||
aliasFlag,
|
aliasFlag,
|
||||||
}
|
}
|
||||||
app.Action = utils.MigrateFlags(abigen)
|
app.Action = utils.MigrateFlags(abigen)
|
||||||
cli.CommandHelpTemplate = commandHelperTemplate
|
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
func abigen(c *cli.Context) error {
|
func abigen(c *cli.Context) error {
|
||||||
|
|
|
||||||
|
|
@ -28,19 +28,6 @@ import (
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
commandHelperTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
|
|
||||||
{{if .Description}}{{.Description}}
|
|
||||||
{{end}}{{if .Subcommands}}
|
|
||||||
SUBCOMMANDS:
|
|
||||||
{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
|
|
||||||
{{end}}{{end}}{{if .Flags}}
|
|
||||||
OPTIONS:
|
|
||||||
{{range $.Flags}}{{"\t"}}{{.}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Git SHA1 commit hash of the release (set via linker flags)
|
// Git SHA1 commit hash of the release (set via linker flags)
|
||||||
gitCommit = ""
|
gitCommit = ""
|
||||||
|
|
@ -61,7 +48,7 @@ func init() {
|
||||||
oracleFlag,
|
oracleFlag,
|
||||||
nodeURLFlag,
|
nodeURLFlag,
|
||||||
}
|
}
|
||||||
cli.CommandHelpTemplate = commandHelperTemplate
|
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commonly used command line flags.
|
// Commonly used command line flags.
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,7 @@ func init() {
|
||||||
}
|
}
|
||||||
app.Action = signer
|
app.Action = signer
|
||||||
app.Commands = []cli.Command{initCommand, attestCommand, setCredentialCommand, delCredentialCommand, gendocCommand}
|
app.Commands = []cli.Command{initCommand, attestCommand, setCredentialCommand, delCredentialCommand, gendocCommand}
|
||||||
|
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ func init() {
|
||||||
commandSignMessage,
|
commandSignMessage,
|
||||||
commandVerifyMessage,
|
commandVerifyMessage,
|
||||||
}
|
}
|
||||||
|
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commonly used command line flags.
|
// Commonly used command line flags.
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ func init() {
|
||||||
runCommand,
|
runCommand,
|
||||||
stateTestCommand,
|
stateTestCommand,
|
||||||
}
|
}
|
||||||
|
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -351,6 +351,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if head == nil || balance == nil {
|
if head == nil || balance == nil {
|
||||||
// Report the faucet offline until initial stats are ready
|
// Report the faucet offline until initial stats are ready
|
||||||
|
//lint:ignore ST1005 This error is to be displayed in the browser
|
||||||
if err = sendError(conn, errors.New("Faucet offline")); err != nil {
|
if err = sendError(conn, errors.New("Faucet offline")); err != nil {
|
||||||
log.Warn("Failed to send faucet error to client", "err", err)
|
log.Warn("Failed to send faucet error to client", "err", err)
|
||||||
return
|
return
|
||||||
|
|
@ -392,6 +393,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if msg.Tier >= uint(*tiersFlag) {
|
if msg.Tier >= uint(*tiersFlag) {
|
||||||
|
//lint:ignore ST1005 This error is to be displayed in the browser
|
||||||
if err = sendError(conn, errors.New("Invalid funding tier requested")); err != nil {
|
if err = sendError(conn, errors.New("Invalid funding tier requested")); err != nil {
|
||||||
log.Warn("Failed to send tier error to client", "err", err)
|
log.Warn("Failed to send tier error to client", "err", err)
|
||||||
return
|
return
|
||||||
|
|
@ -429,6 +431,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
if !result.Success {
|
if !result.Success {
|
||||||
log.Warn("Captcha verification failed", "err", string(result.Errors))
|
log.Warn("Captcha verification failed", "err", string(result.Errors))
|
||||||
|
//lint:ignore ST1005 it's funny and the robot won't mind
|
||||||
if err = sendError(conn, errors.New("Beep-bop, you're a robot!")); err != nil {
|
if err = sendError(conn, errors.New("Beep-bop, you're a robot!")); err != nil {
|
||||||
log.Warn("Failed to send captcha failure to client", "err", err)
|
log.Warn("Failed to send captcha failure to client", "err", err)
|
||||||
return
|
return
|
||||||
|
|
@ -450,6 +453,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
case strings.HasPrefix(msg.URL, "https://plus.google.com/"):
|
case strings.HasPrefix(msg.URL, "https://plus.google.com/"):
|
||||||
|
//lint:ignore ST1005 Google is a company name and should be capitalized.
|
||||||
if err = sendError(conn, errors.New("Google+ authentication discontinued as the service was sunset")); err != nil {
|
if err = sendError(conn, errors.New("Google+ authentication discontinued as the service was sunset")); err != nil {
|
||||||
log.Warn("Failed to send Google+ deprecation to client", "err", err)
|
log.Warn("Failed to send Google+ deprecation to client", "err", err)
|
||||||
return
|
return
|
||||||
|
|
@ -462,6 +466,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
case *noauthFlag:
|
case *noauthFlag:
|
||||||
username, avatar, address, err = authNoAuth(msg.URL)
|
username, avatar, address, err = authNoAuth(msg.URL)
|
||||||
default:
|
default:
|
||||||
|
//lint:ignore ST1005 This error is to be displayed in the browser
|
||||||
err = errors.New("Something funky happened, please open an issue at https://github.com/ethereum/go-ethereum/issues")
|
err = errors.New("Something funky happened, please open an issue at https://github.com/ethereum/go-ethereum/issues")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -520,7 +525,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Send an error if too frequent funding, othewise a success
|
// Send an error if too frequent funding, othewise a success
|
||||||
if !fund {
|
if !fund {
|
||||||
if err = sendError(conn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(timeout.Sub(time.Now())))); err != nil { // nolint: gosimple
|
if err = sendError(conn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil { // nolint: gosimple
|
||||||
log.Warn("Failed to send funding error to client", "err", err)
|
log.Warn("Failed to send funding error to client", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -682,6 +687,7 @@ func authTwitter(url string) (string, string, common.Address, error) {
|
||||||
// Ensure the user specified a meaningful URL, no fancy nonsense
|
// Ensure the user specified a meaningful URL, no fancy nonsense
|
||||||
parts := strings.Split(url, "/")
|
parts := strings.Split(url, "/")
|
||||||
if len(parts) < 4 || parts[len(parts)-2] != "status" {
|
if len(parts) < 4 || parts[len(parts)-2] != "status" {
|
||||||
|
//lint:ignore ST1005 This error is to be displayed in the browser
|
||||||
return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
|
return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
|
||||||
}
|
}
|
||||||
// Twitter's API isn't really friendly with direct links. Still, we don't
|
// Twitter's API isn't really friendly with direct links. Still, we don't
|
||||||
|
|
@ -696,6 +702,7 @@ func authTwitter(url string) (string, string, common.Address, error) {
|
||||||
// Resolve the username from the final redirect, no intermediate junk
|
// Resolve the username from the final redirect, no intermediate junk
|
||||||
parts = strings.Split(res.Request.URL.String(), "/")
|
parts = strings.Split(res.Request.URL.String(), "/")
|
||||||
if len(parts) < 4 || parts[len(parts)-2] != "status" {
|
if len(parts) < 4 || parts[len(parts)-2] != "status" {
|
||||||
|
//lint:ignore ST1005 This error is to be displayed in the browser
|
||||||
return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
|
return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
|
||||||
}
|
}
|
||||||
username := parts[len(parts)-3]
|
username := parts[len(parts)-3]
|
||||||
|
|
@ -706,6 +713,7 @@ func authTwitter(url string) (string, string, common.Address, error) {
|
||||||
}
|
}
|
||||||
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
|
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
|
||||||
if address == (common.Address{}) {
|
if address == (common.Address{}) {
|
||||||
|
//lint:ignore ST1005 This error is to be displayed in the browser
|
||||||
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
|
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
|
||||||
}
|
}
|
||||||
var avatar string
|
var avatar string
|
||||||
|
|
@ -721,6 +729,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
|
||||||
// Ensure the user specified a meaningful URL, no fancy nonsense
|
// Ensure the user specified a meaningful URL, no fancy nonsense
|
||||||
parts := strings.Split(url, "/")
|
parts := strings.Split(url, "/")
|
||||||
if len(parts) < 4 || parts[len(parts)-2] != "posts" {
|
if len(parts) < 4 || parts[len(parts)-2] != "posts" {
|
||||||
|
//lint:ignore ST1005 This error is to be displayed in the browser
|
||||||
return "", "", common.Address{}, errors.New("Invalid Facebook post URL")
|
return "", "", common.Address{}, errors.New("Invalid Facebook post URL")
|
||||||
}
|
}
|
||||||
username := parts[len(parts)-3]
|
username := parts[len(parts)-3]
|
||||||
|
|
@ -740,6 +749,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
|
||||||
}
|
}
|
||||||
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
|
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
|
||||||
if address == (common.Address{}) {
|
if address == (common.Address{}) {
|
||||||
|
//lint:ignore ST1005 This error is to be displayed in the browser
|
||||||
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
|
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
|
||||||
}
|
}
|
||||||
var avatar string
|
var avatar string
|
||||||
|
|
@ -755,6 +765,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
|
||||||
func authNoAuth(url string) (string, string, common.Address, error) {
|
func authNoAuth(url string) (string, string, common.Address, error) {
|
||||||
address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(url))
|
address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(url))
|
||||||
if address == (common.Address{}) {
|
if address == (common.Address{}) {
|
||||||
|
//lint:ignore ST1005 This error is to be displayed in the browser
|
||||||
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
|
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
|
||||||
}
|
}
|
||||||
return address.Hex() + "@noauth", "", address, nil
|
return address.Hex() + "@noauth", "", address, nil
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,6 @@ func version(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
fmt.Println("Architecture:", runtime.GOARCH)
|
fmt.Println("Architecture:", runtime.GOARCH)
|
||||||
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
|
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
|
||||||
fmt.Println("Network Id:", eth.DefaultConfig.NetworkId)
|
|
||||||
fmt.Println("Go Version:", runtime.Version())
|
fmt.Println("Go Version:", runtime.Version())
|
||||||
fmt.Println("Operating System:", runtime.GOOS)
|
fmt.Println("Operating System:", runtime.GOOS)
|
||||||
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
||||||
|
|
|
||||||
|
|
@ -495,7 +495,6 @@ func (api *RetestethAPI) mineBlock() error {
|
||||||
txCount := 0
|
txCount := 0
|
||||||
var txs []*types.Transaction
|
var txs []*types.Transaction
|
||||||
var receipts []*types.Receipt
|
var receipts []*types.Receipt
|
||||||
var coalescedLogs []*types.Log
|
|
||||||
var blockFull = gasPool.Gas() < params.TxGas
|
var blockFull = gasPool.Gas() < params.TxGas
|
||||||
for address := range api.txSenders {
|
for address := range api.txSenders {
|
||||||
if blockFull {
|
if blockFull {
|
||||||
|
|
@ -522,7 +521,6 @@ func (api *RetestethAPI) mineBlock() error {
|
||||||
}
|
}
|
||||||
txs = append(txs, tx)
|
txs = append(txs, tx)
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
coalescedLogs = append(coalescedLogs, receipt.Logs...)
|
|
||||||
delete(m, nonce)
|
delete(m, nonce)
|
||||||
if len(m) == 0 {
|
if len(m) == 0 {
|
||||||
// Last tx for the sender
|
// Last tx for the sender
|
||||||
|
|
@ -682,9 +680,6 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
|
||||||
for i := 0; i < int(maxResults) && it.Next(); i++ {
|
for i := 0; i < int(maxResults) && it.Next(); i++ {
|
||||||
if preimage := accountTrie.GetKey(it.Key); preimage != nil {
|
if preimage := accountTrie.GetKey(it.Key); preimage != nil {
|
||||||
result.AddressMap[common.BytesToHash(it.Key)] = common.BytesToAddress(preimage)
|
result.AddressMap[common.BytesToHash(it.Key)] = common.BytesToAddress(preimage)
|
||||||
//fmt.Printf("%x: %x\n", it.Key, preimage)
|
|
||||||
} else {
|
|
||||||
//fmt.Printf("could not find preimage for %x\n", it.Key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//fmt.Printf("Number of entries returned: %d\n", len(result.AddressMap))
|
//fmt.Printf("Number of entries returned: %d\n", len(result.AddressMap))
|
||||||
|
|
@ -808,9 +803,6 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
|
||||||
Key: string(ks),
|
Key: string(ks),
|
||||||
Value: string(vs),
|
Value: string(vs),
|
||||||
}
|
}
|
||||||
//fmt.Printf("Key: %s, Value: %s\n", ks, vs)
|
|
||||||
} else {
|
|
||||||
//fmt.Printf("Did not find preimage for %x\n", it.Key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if it.Next() {
|
if it.Next() {
|
||||||
|
|
@ -889,7 +881,7 @@ func retesteth(ctx *cli.Context) error {
|
||||||
log.Info("HTTP endpoint closed", "url", httpEndpoint)
|
log.Info("HTTP endpoint closed", "url", httpEndpoint)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
abortChan := make(chan os.Signal)
|
abortChan := make(chan os.Signal, 11)
|
||||||
signal.Notify(abortChan, os.Interrupt)
|
signal.Notify(abortChan, os.Interrupt)
|
||||||
|
|
||||||
sig := <-abortChan
|
sig := <-abortChan
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,17 @@ SUBCOMMANDS:
|
||||||
{{range $categorized.Flags}}{{"\t"}}{{.}}
|
{{range $categorized.Flags}}{{"\t"}}{{.}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}{{end}}`
|
{{end}}{{end}}`
|
||||||
|
|
||||||
|
OriginCommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
|
||||||
|
{{if .Description}}{{.Description}}
|
||||||
|
{{end}}{{if .Subcommands}}
|
||||||
|
SUBCOMMANDS:
|
||||||
|
{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
|
||||||
|
{{end}}{{end}}{{if .Flags}}
|
||||||
|
OPTIONS:
|
||||||
|
{{range $.Flags}}{{"\t"}}{{.}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}`
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
||||||
|
|
@ -729,7 +729,7 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) {
|
||||||
|
|
||||||
go func(idx int) {
|
go func(idx int) {
|
||||||
defer pend.Done()
|
defer pend.Done()
|
||||||
ethash := New(Config{cachedir, 0, 1, "", 0, 0, ModeNormal}, nil, false)
|
ethash := New(Config{cachedir, 0, 1, "", 0, 0, ModeNormal, nil}, nil, false)
|
||||||
defer ethash.Close()
|
defer ethash.Close()
|
||||||
if err := ethash.VerifySeal(nil, block.Header()); err != nil {
|
if err := ethash.VerifySeal(nil, block.Header()); err != nil {
|
||||||
t.Errorf("proc %d: block verification failed: %v", idx, err)
|
t.Errorf("proc %d: block verification failed: %v", idx, err)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ var errEthashStopped = errors.New("ethash stopped")
|
||||||
|
|
||||||
// API exposes ethash related methods for the RPC interface.
|
// API exposes ethash related methods for the RPC interface.
|
||||||
type API struct {
|
type API struct {
|
||||||
ethash *Ethash // Make sure the mode of ethash is normal.
|
ethash *Ethash
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWork returns a work package for external miner.
|
// GetWork returns a work package for external miner.
|
||||||
|
|
@ -39,7 +39,7 @@ type API struct {
|
||||||
// result[2] - 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
// result[2] - 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
||||||
// result[3] - hex encoded block number
|
// result[3] - hex encoded block number
|
||||||
func (api *API) GetWork() ([4]string, error) {
|
func (api *API) GetWork() ([4]string, error) {
|
||||||
if api.ethash.config.PowMode != ModeNormal && api.ethash.config.PowMode != ModeTest {
|
if api.ethash.remote == nil {
|
||||||
return [4]string{}, errors.New("not supported")
|
return [4]string{}, errors.New("not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,13 +47,11 @@ func (api *API) GetWork() ([4]string, error) {
|
||||||
workCh = make(chan [4]string, 1)
|
workCh = make(chan [4]string, 1)
|
||||||
errc = make(chan error, 1)
|
errc = make(chan error, 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case api.ethash.fetchWorkCh <- &sealWork{errc: errc, res: workCh}:
|
case api.ethash.remote.fetchWorkCh <- &sealWork{errc: errc, res: workCh}:
|
||||||
case <-api.ethash.exitCh:
|
case <-api.ethash.remote.exitCh:
|
||||||
return [4]string{}, errEthashStopped
|
return [4]string{}, errEthashStopped
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case work := <-workCh:
|
case work := <-workCh:
|
||||||
return work, nil
|
return work, nil
|
||||||
|
|
@ -66,23 +64,21 @@ func (api *API) GetWork() ([4]string, error) {
|
||||||
// It returns an indication if the work was accepted.
|
// It returns an indication if the work was accepted.
|
||||||
// Note either an invalid solution, a stale work a non-existent work will return false.
|
// Note either an invalid solution, a stale work a non-existent work will return false.
|
||||||
func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) bool {
|
func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) bool {
|
||||||
if api.ethash.config.PowMode != ModeNormal && api.ethash.config.PowMode != ModeTest {
|
if api.ethash.remote == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var errc = make(chan error, 1)
|
var errc = make(chan error, 1)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case api.ethash.submitWorkCh <- &mineResult{
|
case api.ethash.remote.submitWorkCh <- &mineResult{
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
mixDigest: digest,
|
mixDigest: digest,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
errc: errc,
|
errc: errc,
|
||||||
}:
|
}:
|
||||||
case <-api.ethash.exitCh:
|
case <-api.ethash.remote.exitCh:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
err := <-errc
|
err := <-errc
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
@ -94,21 +90,19 @@ func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) boo
|
||||||
// It accepts the miner hash rate and an identifier which must be unique
|
// It accepts the miner hash rate and an identifier which must be unique
|
||||||
// between nodes.
|
// between nodes.
|
||||||
func (api *API) SubmitHashRate(rate hexutil.Uint64, id common.Hash) bool {
|
func (api *API) SubmitHashRate(rate hexutil.Uint64, id common.Hash) bool {
|
||||||
if api.ethash.config.PowMode != ModeNormal && api.ethash.config.PowMode != ModeTest {
|
if api.ethash.remote == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var done = make(chan struct{}, 1)
|
var done = make(chan struct{}, 1)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case api.ethash.submitRateCh <- &hashrate{done: done, rate: uint64(rate), id: id}:
|
case api.ethash.remote.submitRateCh <- &hashrate{done: done, rate: uint64(rate), id: id}:
|
||||||
case <-api.ethash.exitCh:
|
case <-api.ethash.remote.exitCh:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block until hash rate submitted successfully.
|
// Block until hash rate submitted successfully.
|
||||||
<-done
|
<-done
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,7 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
mmap "github.com/edsrzf/mmap-go"
|
mmap "github.com/edsrzf/mmap-go"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
@ -50,7 +48,7 @@ var (
|
||||||
two256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
|
two256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
|
||||||
|
|
||||||
// sharedEthash is a full instance that can be shared between multiple users.
|
// sharedEthash is a full instance that can be shared between multiple users.
|
||||||
sharedEthash = New(Config{"", 3, 0, "", 1, 0, ModeNormal}, nil, false)
|
sharedEthash = New(Config{"", 3, 0, "", 1, 0, ModeNormal, nil}, nil, false)
|
||||||
|
|
||||||
// algorithmRevision is the data structure version used for file naming.
|
// algorithmRevision is the data structure version used for file naming.
|
||||||
algorithmRevision = 23
|
algorithmRevision = 23
|
||||||
|
|
@ -403,36 +401,8 @@ type Config struct {
|
||||||
DatasetsInMem int
|
DatasetsInMem int
|
||||||
DatasetsOnDisk int
|
DatasetsOnDisk int
|
||||||
PowMode Mode
|
PowMode Mode
|
||||||
}
|
|
||||||
|
|
||||||
// sealTask wraps a seal block with relative result channel for remote sealer thread.
|
Log log.Logger `toml:"-"`
|
||||||
type sealTask struct {
|
|
||||||
block *types.Block
|
|
||||||
results chan<- *types.Block
|
|
||||||
}
|
|
||||||
|
|
||||||
// mineResult wraps the pow solution parameters for the specified block.
|
|
||||||
type mineResult struct {
|
|
||||||
nonce types.BlockNonce
|
|
||||||
mixDigest common.Hash
|
|
||||||
hash common.Hash
|
|
||||||
|
|
||||||
errc chan error
|
|
||||||
}
|
|
||||||
|
|
||||||
// hashrate wraps the hash rate submitted by the remote sealer.
|
|
||||||
type hashrate struct {
|
|
||||||
id common.Hash
|
|
||||||
ping time.Time
|
|
||||||
rate uint64
|
|
||||||
|
|
||||||
done chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sealWork wraps a seal work package for remote sealer.
|
|
||||||
type sealWork struct {
|
|
||||||
errc chan error
|
|
||||||
res chan [4]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ethash is a consensus engine based on proof-of-work implementing the ethash
|
// Ethash is a consensus engine based on proof-of-work implementing the ethash
|
||||||
|
|
@ -448,52 +418,42 @@ type Ethash struct {
|
||||||
threads int // Number of threads to mine on if mining
|
threads int // Number of threads to mine on if mining
|
||||||
update chan struct{} // Notification channel to update mining parameters
|
update chan struct{} // Notification channel to update mining parameters
|
||||||
hashrate metrics.Meter // Meter tracking the average hashrate
|
hashrate metrics.Meter // Meter tracking the average hashrate
|
||||||
|
remote *remoteSealer
|
||||||
// Remote sealer related fields
|
|
||||||
workCh chan *sealTask // Notification channel to push new work and relative result channel to remote sealer
|
|
||||||
fetchWorkCh chan *sealWork // Channel used for remote sealer to fetch mining work
|
|
||||||
submitWorkCh chan *mineResult // Channel used for remote sealer to submit their mining result
|
|
||||||
fetchRateCh chan chan uint64 // Channel used to gather submitted hash rate for local or remote sealer.
|
|
||||||
submitRateCh chan *hashrate // Channel used for remote sealer to submit their mining hashrate
|
|
||||||
|
|
||||||
// The fields below are hooks for testing
|
// The fields below are hooks for testing
|
||||||
shared *Ethash // Shared PoW verifier to avoid cache regeneration
|
shared *Ethash // Shared PoW verifier to avoid cache regeneration
|
||||||
fakeFail uint64 // Block number which fails PoW check even in fake mode
|
fakeFail uint64 // Block number which fails PoW check even in fake mode
|
||||||
fakeDelay time.Duration // Time delay to sleep for before returning from verify
|
fakeDelay time.Duration // Time delay to sleep for before returning from verify
|
||||||
|
|
||||||
lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
|
lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
|
||||||
closeOnce sync.Once // Ensures exit channel will not be closed twice.
|
closeOnce sync.Once // Ensures exit channel will not be closed twice.
|
||||||
exitCh chan chan error // Notification channel to exiting backend threads
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a full sized ethash PoW scheme and starts a background thread for
|
// New creates a full sized ethash PoW scheme and starts a background thread for
|
||||||
// remote mining, also optionally notifying a batch of remote services of new work
|
// remote mining, also optionally notifying a batch of remote services of new work
|
||||||
// packages.
|
// packages.
|
||||||
func New(config Config, notify []string, noverify bool) *Ethash {
|
func New(config Config, notify []string, noverify bool) *Ethash {
|
||||||
|
if config.Log == nil {
|
||||||
|
config.Log = log.Root()
|
||||||
|
}
|
||||||
if config.CachesInMem <= 0 {
|
if config.CachesInMem <= 0 {
|
||||||
log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem)
|
config.Log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem)
|
||||||
config.CachesInMem = 1
|
config.CachesInMem = 1
|
||||||
}
|
}
|
||||||
if config.CacheDir != "" && config.CachesOnDisk > 0 {
|
if config.CacheDir != "" && config.CachesOnDisk > 0 {
|
||||||
log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk)
|
config.Log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk)
|
||||||
}
|
}
|
||||||
if config.DatasetDir != "" && config.DatasetsOnDisk > 0 {
|
if config.DatasetDir != "" && config.DatasetsOnDisk > 0 {
|
||||||
log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk)
|
config.Log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk)
|
||||||
}
|
}
|
||||||
ethash := &Ethash{
|
ethash := &Ethash{
|
||||||
config: config,
|
config: config,
|
||||||
caches: newlru("cache", config.CachesInMem, newCache),
|
caches: newlru("cache", config.CachesInMem, newCache),
|
||||||
datasets: newlru("dataset", config.DatasetsInMem, newDataset),
|
datasets: newlru("dataset", config.DatasetsInMem, newDataset),
|
||||||
update: make(chan struct{}),
|
update: make(chan struct{}),
|
||||||
hashrate: metrics.NewMeterForced(),
|
hashrate: metrics.NewMeterForced(),
|
||||||
workCh: make(chan *sealTask),
|
|
||||||
fetchWorkCh: make(chan *sealWork),
|
|
||||||
submitWorkCh: make(chan *mineResult),
|
|
||||||
fetchRateCh: make(chan chan uint64),
|
|
||||||
submitRateCh: make(chan *hashrate),
|
|
||||||
exitCh: make(chan chan error),
|
|
||||||
}
|
}
|
||||||
go ethash.remote(notify, noverify)
|
ethash.remote = startRemoteSealer(ethash, notify, noverify)
|
||||||
return ethash
|
return ethash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -501,19 +461,13 @@ func New(config Config, notify []string, noverify bool) *Ethash {
|
||||||
// purposes.
|
// purposes.
|
||||||
func NewTester(notify []string, noverify bool) *Ethash {
|
func NewTester(notify []string, noverify bool) *Ethash {
|
||||||
ethash := &Ethash{
|
ethash := &Ethash{
|
||||||
config: Config{PowMode: ModeTest},
|
config: Config{PowMode: ModeTest, Log: log.Root()},
|
||||||
caches: newlru("cache", 1, newCache),
|
caches: newlru("cache", 1, newCache),
|
||||||
datasets: newlru("dataset", 1, newDataset),
|
datasets: newlru("dataset", 1, newDataset),
|
||||||
update: make(chan struct{}),
|
update: make(chan struct{}),
|
||||||
hashrate: metrics.NewMeterForced(),
|
hashrate: metrics.NewMeterForced(),
|
||||||
workCh: make(chan *sealTask),
|
|
||||||
fetchWorkCh: make(chan *sealWork),
|
|
||||||
submitWorkCh: make(chan *mineResult),
|
|
||||||
fetchRateCh: make(chan chan uint64),
|
|
||||||
submitRateCh: make(chan *hashrate),
|
|
||||||
exitCh: make(chan chan error),
|
|
||||||
}
|
}
|
||||||
go ethash.remote(notify, noverify)
|
ethash.remote = startRemoteSealer(ethash, notify, noverify)
|
||||||
return ethash
|
return ethash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -524,6 +478,7 @@ func NewFaker() *Ethash {
|
||||||
return &Ethash{
|
return &Ethash{
|
||||||
config: Config{
|
config: Config{
|
||||||
PowMode: ModeFake,
|
PowMode: ModeFake,
|
||||||
|
Log: log.Root(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -535,6 +490,7 @@ func NewFakeFailer(fail uint64) *Ethash {
|
||||||
return &Ethash{
|
return &Ethash{
|
||||||
config: Config{
|
config: Config{
|
||||||
PowMode: ModeFake,
|
PowMode: ModeFake,
|
||||||
|
Log: log.Root(),
|
||||||
},
|
},
|
||||||
fakeFail: fail,
|
fakeFail: fail,
|
||||||
}
|
}
|
||||||
|
|
@ -547,6 +503,7 @@ func NewFakeDelayer(delay time.Duration) *Ethash {
|
||||||
return &Ethash{
|
return &Ethash{
|
||||||
config: Config{
|
config: Config{
|
||||||
PowMode: ModeFake,
|
PowMode: ModeFake,
|
||||||
|
Log: log.Root(),
|
||||||
},
|
},
|
||||||
fakeDelay: delay,
|
fakeDelay: delay,
|
||||||
}
|
}
|
||||||
|
|
@ -558,6 +515,7 @@ func NewFullFaker() *Ethash {
|
||||||
return &Ethash{
|
return &Ethash{
|
||||||
config: Config{
|
config: Config{
|
||||||
PowMode: ModeFullFake,
|
PowMode: ModeFullFake,
|
||||||
|
Log: log.Root(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -573,13 +531,11 @@ func (ethash *Ethash) Close() error {
|
||||||
var err error
|
var err error
|
||||||
ethash.closeOnce.Do(func() {
|
ethash.closeOnce.Do(func() {
|
||||||
// Short circuit if the exit channel is not allocated.
|
// Short circuit if the exit channel is not allocated.
|
||||||
if ethash.exitCh == nil {
|
if ethash.remote == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
errc := make(chan error)
|
close(ethash.remote.requestExit)
|
||||||
ethash.exitCh <- errc
|
<-ethash.remote.exitCh
|
||||||
err = <-errc
|
|
||||||
close(ethash.exitCh)
|
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -680,8 +636,8 @@ func (ethash *Ethash) Hashrate() float64 {
|
||||||
var res = make(chan uint64, 1)
|
var res = make(chan uint64, 1)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case ethash.fetchRateCh <- res:
|
case ethash.remote.fetchRateCh <- res:
|
||||||
case <-ethash.exitCh:
|
case <-ethash.remote.exitCh:
|
||||||
// Return local hashrate only if ethash is stopped.
|
// Return local hashrate only if ethash is stopped.
|
||||||
return ethash.hashrate.Rate1()
|
return ethash.hashrate.Rate1()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package ethash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -33,7 +34,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -56,7 +56,7 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, resu
|
||||||
select {
|
select {
|
||||||
case results <- block.WithSeal(header):
|
case results <- block.WithSeal(header):
|
||||||
default:
|
default:
|
||||||
log.Warn("Sealing result is not read by miner", "mode", "fake", "sealhash", ethash.SealHash(block.Header()))
|
ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "fake", "sealhash", ethash.SealHash(block.Header()))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -85,8 +85,8 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, resu
|
||||||
threads = 0 // Allows disabling local mining without extra logic around local/remote
|
threads = 0 // Allows disabling local mining without extra logic around local/remote
|
||||||
}
|
}
|
||||||
// Push new work to remote sealer
|
// Push new work to remote sealer
|
||||||
if ethash.workCh != nil {
|
if ethash.remote != nil {
|
||||||
ethash.workCh <- &sealTask{block: block, results: results}
|
ethash.remote.workCh <- &sealTask{block: block, results: results}
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
pend sync.WaitGroup
|
pend sync.WaitGroup
|
||||||
|
|
@ -111,14 +111,14 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, resu
|
||||||
select {
|
select {
|
||||||
case results <- result:
|
case results <- result:
|
||||||
default:
|
default:
|
||||||
log.Warn("Sealing result is not read by miner", "mode", "local", "sealhash", ethash.SealHash(block.Header()))
|
ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "local", "sealhash", ethash.SealHash(block.Header()))
|
||||||
}
|
}
|
||||||
close(abort)
|
close(abort)
|
||||||
case <-ethash.update:
|
case <-ethash.update:
|
||||||
// Thread count was changed on user request, restart
|
// Thread count was changed on user request, restart
|
||||||
close(abort)
|
close(abort)
|
||||||
if err := ethash.Seal(chain, block, results, stop); err != nil {
|
if err := ethash.Seal(chain, block, results, stop); err != nil {
|
||||||
log.Error("Failed to restart sealing after update", "err", err)
|
ethash.config.Log.Error("Failed to restart sealing after update", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Wait for all miners to terminate and return the block
|
// Wait for all miners to terminate and return the block
|
||||||
|
|
@ -143,7 +143,7 @@ func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan s
|
||||||
attempts = int64(0)
|
attempts = int64(0)
|
||||||
nonce = seed
|
nonce = seed
|
||||||
)
|
)
|
||||||
logger := log.New("miner", id)
|
logger := ethash.config.Log.New("miner", id)
|
||||||
logger.Trace("Started ethash search for new nonces", "seed", seed)
|
logger.Trace("Started ethash search for new nonces", "seed", seed)
|
||||||
search:
|
search:
|
||||||
for {
|
for {
|
||||||
|
|
@ -186,160 +186,128 @@ search:
|
||||||
runtime.KeepAlive(dataset)
|
runtime.KeepAlive(dataset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// remote is a standalone goroutine to handle remote mining related stuff.
|
// This is the timeout for HTTP requests to notify external miners.
|
||||||
func (ethash *Ethash) remote(notify []string, noverify bool) {
|
const remoteSealerTimeout = 1 * time.Second
|
||||||
var (
|
|
||||||
works = make(map[common.Hash]*types.Block)
|
|
||||||
rates = make(map[common.Hash]hashrate)
|
|
||||||
|
|
||||||
results chan<- *types.Block
|
type remoteSealer struct {
|
||||||
currentBlock *types.Block
|
works map[common.Hash]*types.Block
|
||||||
currentWork [4]string
|
rates map[common.Hash]hashrate
|
||||||
|
currentBlock *types.Block
|
||||||
|
currentWork [4]string
|
||||||
|
notifyCtx context.Context
|
||||||
|
cancelNotify context.CancelFunc // cancels all notification requests
|
||||||
|
reqWG sync.WaitGroup // tracks notification request goroutines
|
||||||
|
|
||||||
notifyTransport = &http.Transport{}
|
ethash *Ethash
|
||||||
notifyClient = &http.Client{
|
noverify bool
|
||||||
Transport: notifyTransport,
|
notifyURLs []string
|
||||||
Timeout: time.Second,
|
results chan<- *types.Block
|
||||||
}
|
workCh chan *sealTask // Notification channel to push new work and relative result channel to remote sealer
|
||||||
notifyReqs = make([]*http.Request, len(notify))
|
fetchWorkCh chan *sealWork // Channel used for remote sealer to fetch mining work
|
||||||
)
|
submitWorkCh chan *mineResult // Channel used for remote sealer to submit their mining result
|
||||||
// notifyWork notifies all the specified mining endpoints of the availability of
|
fetchRateCh chan chan uint64 // Channel used to gather submitted hash rate for local or remote sealer.
|
||||||
// new work to be processed.
|
submitRateCh chan *hashrate // Channel used for remote sealer to submit their mining hashrate
|
||||||
notifyWork := func() {
|
requestExit chan struct{}
|
||||||
work := currentWork
|
exitCh chan struct{}
|
||||||
blob, _ := json.Marshal(work)
|
}
|
||||||
|
|
||||||
for i, url := range notify {
|
// sealTask wraps a seal block with relative result channel for remote sealer thread.
|
||||||
// Terminate any previously pending request and create the new work
|
type sealTask struct {
|
||||||
if notifyReqs[i] != nil {
|
block *types.Block
|
||||||
notifyTransport.CancelRequest(notifyReqs[i])
|
results chan<- *types.Block
|
||||||
}
|
}
|
||||||
notifyReqs[i], _ = http.NewRequest("POST", url, bytes.NewReader(blob))
|
|
||||||
notifyReqs[i].Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
// Push the new work concurrently to all the remote nodes
|
// mineResult wraps the pow solution parameters for the specified block.
|
||||||
go func(req *http.Request, url string) {
|
type mineResult struct {
|
||||||
res, err := notifyClient.Do(req)
|
nonce types.BlockNonce
|
||||||
if err != nil {
|
mixDigest common.Hash
|
||||||
log.Warn("Failed to notify remote miner", "err", err)
|
hash common.Hash
|
||||||
} else {
|
|
||||||
log.Trace("Notified remote miner", "miner", url, "hash", log.Lazy{Fn: func() common.Hash { return common.HexToHash(work[0]) }}, "target", work[2])
|
errc chan error
|
||||||
res.Body.Close()
|
}
|
||||||
}
|
|
||||||
}(notifyReqs[i], url)
|
// hashrate wraps the hash rate submitted by the remote sealer.
|
||||||
}
|
type hashrate struct {
|
||||||
|
id common.Hash
|
||||||
|
ping time.Time
|
||||||
|
rate uint64
|
||||||
|
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sealWork wraps a seal work package for remote sealer.
|
||||||
|
type sealWork struct {
|
||||||
|
errc chan error
|
||||||
|
res chan [4]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func startRemoteSealer(ethash *Ethash, urls []string, noverify bool) *remoteSealer {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
s := &remoteSealer{
|
||||||
|
ethash: ethash,
|
||||||
|
noverify: noverify,
|
||||||
|
notifyURLs: urls,
|
||||||
|
notifyCtx: ctx,
|
||||||
|
cancelNotify: cancel,
|
||||||
|
works: make(map[common.Hash]*types.Block),
|
||||||
|
rates: make(map[common.Hash]hashrate),
|
||||||
|
workCh: make(chan *sealTask),
|
||||||
|
fetchWorkCh: make(chan *sealWork),
|
||||||
|
submitWorkCh: make(chan *mineResult),
|
||||||
|
fetchRateCh: make(chan chan uint64),
|
||||||
|
submitRateCh: make(chan *hashrate),
|
||||||
|
requestExit: make(chan struct{}),
|
||||||
|
exitCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
// makeWork creates a work package for external miner.
|
go s.loop()
|
||||||
//
|
return s
|
||||||
// The work package consists of 3 strings:
|
}
|
||||||
// result[0], 32 bytes hex encoded current block header pow-hash
|
|
||||||
// result[1], 32 bytes hex encoded seed hash used for DAG
|
|
||||||
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
|
||||||
// result[3], hex encoded block number
|
|
||||||
makeWork := func(block *types.Block) {
|
|
||||||
hash := ethash.SealHash(block.Header())
|
|
||||||
|
|
||||||
currentWork[0] = hash.Hex()
|
func (s *remoteSealer) loop() {
|
||||||
currentWork[1] = common.BytesToHash(SeedHash(block.NumberU64())).Hex()
|
defer func() {
|
||||||
currentWork[2] = common.BytesToHash(new(big.Int).Div(two256, block.Difficulty()).Bytes()).Hex()
|
s.ethash.config.Log.Trace("Ethash remote sealer is exiting")
|
||||||
currentWork[3] = hexutil.EncodeBig(block.Number())
|
s.cancelNotify()
|
||||||
|
s.reqWG.Wait()
|
||||||
// Trace the seal work fetched by remote sealer.
|
close(s.exitCh)
|
||||||
currentBlock = block
|
}()
|
||||||
works[hash] = block
|
|
||||||
}
|
|
||||||
// submitWork verifies the submitted pow solution, returning
|
|
||||||
// whether the solution was accepted or not (not can be both a bad pow as well as
|
|
||||||
// any other error, like no pending work or stale mining result).
|
|
||||||
submitWork := func(nonce types.BlockNonce, mixDigest common.Hash, sealhash common.Hash) bool {
|
|
||||||
if currentBlock == nil {
|
|
||||||
log.Error("Pending work without block", "sealhash", sealhash)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Make sure the work submitted is present
|
|
||||||
block := works[sealhash]
|
|
||||||
if block == nil {
|
|
||||||
log.Warn("Work submitted but none pending", "sealhash", sealhash, "curnumber", currentBlock.NumberU64())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Verify the correctness of submitted result.
|
|
||||||
header := block.Header()
|
|
||||||
header.Nonce = nonce
|
|
||||||
header.MixDigest = mixDigest
|
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
if !noverify {
|
|
||||||
if err := ethash.verifySeal(nil, header, true); err != nil {
|
|
||||||
log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)), "err", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Make sure the result channel is assigned.
|
|
||||||
if results == nil {
|
|
||||||
log.Warn("Ethash result channel is empty, submitted mining result is rejected")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)))
|
|
||||||
|
|
||||||
// Solutions seems to be valid, return to the miner and notify acceptance.
|
|
||||||
solution := block.WithSeal(header)
|
|
||||||
|
|
||||||
// The submitted solution is within the scope of acceptance.
|
|
||||||
if solution.NumberU64()+staleThreshold > currentBlock.NumberU64() {
|
|
||||||
select {
|
|
||||||
case results <- solution:
|
|
||||||
log.Debug("Work submitted is acceptable", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
log.Warn("Sealing result is not read by miner", "mode", "remote", "sealhash", sealhash)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The submitted block is too old to accept, drop it.
|
|
||||||
log.Warn("Work submitted is too old", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
ticker := time.NewTicker(5 * time.Second)
|
ticker := time.NewTicker(5 * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case work := <-ethash.workCh:
|
case work := <-s.workCh:
|
||||||
// Update current work with new received block.
|
// Update current work with new received block.
|
||||||
// Note same work can be past twice, happens when changing CPU threads.
|
// Note same work can be past twice, happens when changing CPU threads.
|
||||||
results = work.results
|
s.results = work.results
|
||||||
|
s.makeWork(work.block)
|
||||||
|
s.notifyWork()
|
||||||
|
|
||||||
makeWork(work.block)
|
case work := <-s.fetchWorkCh:
|
||||||
|
|
||||||
// Notify and requested URLs of the new work availability
|
|
||||||
notifyWork()
|
|
||||||
|
|
||||||
case work := <-ethash.fetchWorkCh:
|
|
||||||
// Return current mining work to remote miner.
|
// Return current mining work to remote miner.
|
||||||
if currentBlock == nil {
|
if s.currentBlock == nil {
|
||||||
work.errc <- errNoMiningWork
|
work.errc <- errNoMiningWork
|
||||||
} else {
|
} else {
|
||||||
work.res <- currentWork
|
work.res <- s.currentWork
|
||||||
}
|
}
|
||||||
|
|
||||||
case result := <-ethash.submitWorkCh:
|
case result := <-s.submitWorkCh:
|
||||||
// Verify submitted PoW solution based on maintained mining blocks.
|
// Verify submitted PoW solution based on maintained mining blocks.
|
||||||
if submitWork(result.nonce, result.mixDigest, result.hash) {
|
if s.submitWork(result.nonce, result.mixDigest, result.hash) {
|
||||||
result.errc <- nil
|
result.errc <- nil
|
||||||
} else {
|
} else {
|
||||||
result.errc <- errInvalidSealResult
|
result.errc <- errInvalidSealResult
|
||||||
}
|
}
|
||||||
|
|
||||||
case result := <-ethash.submitRateCh:
|
case result := <-s.submitRateCh:
|
||||||
// Trace remote sealer's hash rate by submitted value.
|
// Trace remote sealer's hash rate by submitted value.
|
||||||
rates[result.id] = hashrate{rate: result.rate, ping: time.Now()}
|
s.rates[result.id] = hashrate{rate: result.rate, ping: time.Now()}
|
||||||
close(result.done)
|
close(result.done)
|
||||||
|
|
||||||
case req := <-ethash.fetchRateCh:
|
case req := <-s.fetchRateCh:
|
||||||
// Gather all hash rate submitted by remote sealer.
|
// Gather all hash rate submitted by remote sealer.
|
||||||
var total uint64
|
var total uint64
|
||||||
for _, rate := range rates {
|
for _, rate := range s.rates {
|
||||||
// this could overflow
|
// this could overflow
|
||||||
total += rate.rate
|
total += rate.rate
|
||||||
}
|
}
|
||||||
|
|
@ -347,25 +315,126 @@ func (ethash *Ethash) remote(notify []string, noverify bool) {
|
||||||
|
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
// Clear stale submitted hash rate.
|
// Clear stale submitted hash rate.
|
||||||
for id, rate := range rates {
|
for id, rate := range s.rates {
|
||||||
if time.Since(rate.ping) > 10*time.Second {
|
if time.Since(rate.ping) > 10*time.Second {
|
||||||
delete(rates, id)
|
delete(s.rates, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Clear stale pending blocks
|
// Clear stale pending blocks
|
||||||
if currentBlock != nil {
|
if s.currentBlock != nil {
|
||||||
for hash, block := range works {
|
for hash, block := range s.works {
|
||||||
if block.NumberU64()+staleThreshold <= currentBlock.NumberU64() {
|
if block.NumberU64()+staleThreshold <= s.currentBlock.NumberU64() {
|
||||||
delete(works, hash)
|
delete(s.works, hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case errc := <-ethash.exitCh:
|
case <-s.requestExit:
|
||||||
// Exit remote loop if ethash is closed and return relevant error.
|
|
||||||
errc <- nil
|
|
||||||
log.Trace("Ethash remote sealer is exiting")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeWork creates a work package for external miner.
|
||||||
|
//
|
||||||
|
// The work package consists of 3 strings:
|
||||||
|
// result[0], 32 bytes hex encoded current block header pow-hash
|
||||||
|
// result[1], 32 bytes hex encoded seed hash used for DAG
|
||||||
|
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
||||||
|
// result[3], hex encoded block number
|
||||||
|
func (s *remoteSealer) makeWork(block *types.Block) {
|
||||||
|
hash := s.ethash.SealHash(block.Header())
|
||||||
|
s.currentWork[0] = hash.Hex()
|
||||||
|
s.currentWork[1] = common.BytesToHash(SeedHash(block.NumberU64())).Hex()
|
||||||
|
s.currentWork[2] = common.BytesToHash(new(big.Int).Div(two256, block.Difficulty()).Bytes()).Hex()
|
||||||
|
s.currentWork[3] = hexutil.EncodeBig(block.Number())
|
||||||
|
|
||||||
|
// Trace the seal work fetched by remote sealer.
|
||||||
|
s.currentBlock = block
|
||||||
|
s.works[hash] = block
|
||||||
|
}
|
||||||
|
|
||||||
|
// notifyWork notifies all the specified mining endpoints of the availability of
|
||||||
|
// new work to be processed.
|
||||||
|
func (s *remoteSealer) notifyWork() {
|
||||||
|
work := s.currentWork
|
||||||
|
blob, _ := json.Marshal(work)
|
||||||
|
s.reqWG.Add(len(s.notifyURLs))
|
||||||
|
for _, url := range s.notifyURLs {
|
||||||
|
go s.sendNotification(s.notifyCtx, url, blob, work)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *remoteSealer) sendNotification(ctx context.Context, url string, json []byte, work [4]string) {
|
||||||
|
defer s.reqWG.Done()
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", url, bytes.NewReader(json))
|
||||||
|
if err != nil {
|
||||||
|
s.ethash.config.Log.Warn("Can't create remote miner notification", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, remoteSealerTimeout)
|
||||||
|
defer cancel()
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
s.ethash.config.Log.Warn("Failed to notify remote miner", "err", err)
|
||||||
|
} else {
|
||||||
|
s.ethash.config.Log.Trace("Notified remote miner", "miner", url, "hash", work[0], "target", work[2])
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// submitWork verifies the submitted pow solution, returning
|
||||||
|
// whether the solution was accepted or not (not can be both a bad pow as well as
|
||||||
|
// any other error, like no pending work or stale mining result).
|
||||||
|
func (s *remoteSealer) submitWork(nonce types.BlockNonce, mixDigest common.Hash, sealhash common.Hash) bool {
|
||||||
|
if s.currentBlock == nil {
|
||||||
|
s.ethash.config.Log.Error("Pending work without block", "sealhash", sealhash)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Make sure the work submitted is present
|
||||||
|
block := s.works[sealhash]
|
||||||
|
if block == nil {
|
||||||
|
s.ethash.config.Log.Warn("Work submitted but none pending", "sealhash", sealhash, "curnumber", s.currentBlock.NumberU64())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Verify the correctness of submitted result.
|
||||||
|
header := block.Header()
|
||||||
|
header.Nonce = nonce
|
||||||
|
header.MixDigest = mixDigest
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
if !s.noverify {
|
||||||
|
if err := s.ethash.verifySeal(nil, header, true); err != nil {
|
||||||
|
s.ethash.config.Log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)), "err", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make sure the result channel is assigned.
|
||||||
|
if s.results == nil {
|
||||||
|
s.ethash.config.Log.Warn("Ethash result channel is empty, submitted mining result is rejected")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s.ethash.config.Log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
|
||||||
|
// Solutions seems to be valid, return to the miner and notify acceptance.
|
||||||
|
solution := block.WithSeal(header)
|
||||||
|
|
||||||
|
// The submitted solution is within the scope of acceptance.
|
||||||
|
if solution.NumberU64()+staleThreshold > s.currentBlock.NumberU64() {
|
||||||
|
select {
|
||||||
|
case s.results <- solution:
|
||||||
|
s.ethash.config.Log.Debug("Work submitted is acceptable", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
s.ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "remote", "sealhash", sealhash)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The submitted block is too old to accept, drop it.
|
||||||
|
s.ethash.config.Log.Warn("Work submitted is too old", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,59 +20,39 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"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/internal/testlog"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests whether remote HTTP servers are correctly notified of new work.
|
// Tests whether remote HTTP servers are correctly notified of new work.
|
||||||
func TestRemoteNotify(t *testing.T) {
|
func TestRemoteNotify(t *testing.T) {
|
||||||
// Start a simple webserver to capture notifications
|
// Start a simple web server to capture notifications.
|
||||||
sink := make(chan [3]string)
|
sink := make(chan [3]string)
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
server := &http.Server{
|
blob, err := ioutil.ReadAll(req.Body)
|
||||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
if err != nil {
|
||||||
blob, err := ioutil.ReadAll(req.Body)
|
t.Errorf("failed to read miner notification: %v", err)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to read miner notification: %v", err)
|
|
||||||
}
|
|
||||||
var work [3]string
|
|
||||||
if err := json.Unmarshal(blob, &work); err != nil {
|
|
||||||
t.Fatalf("failed to unmarshal miner notification: %v", err)
|
|
||||||
}
|
|
||||||
sink <- work
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
// Open a custom listener to extract its local address
|
|
||||||
listener, err := net.Listen("tcp", "localhost:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to open notification server: %v", err)
|
|
||||||
}
|
|
||||||
defer listener.Close()
|
|
||||||
|
|
||||||
go server.Serve(listener)
|
|
||||||
|
|
||||||
// Wait for server to start listening
|
|
||||||
var tries int
|
|
||||||
for tries = 0; tries < 10; tries++ {
|
|
||||||
conn, _ := net.DialTimeout("tcp", listener.Addr().String(), 1*time.Second)
|
|
||||||
if conn != nil {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
var work [3]string
|
||||||
if tries == 10 {
|
if err := json.Unmarshal(blob, &work); err != nil {
|
||||||
t.Fatal("tcp listener not ready for more than 10 seconds")
|
t.Errorf("failed to unmarshal miner notification: %v", err)
|
||||||
}
|
}
|
||||||
|
sink <- work
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
// Create the custom ethash engine
|
// Create the custom ethash engine.
|
||||||
ethash := NewTester([]string{"http://" + listener.Addr().String()}, false)
|
ethash := NewTester([]string{server.URL}, false)
|
||||||
defer ethash.Close()
|
defer ethash.Close()
|
||||||
|
|
||||||
// Stream a work task and ensure the notification bubbles out
|
// Stream a work task and ensure the notification bubbles out.
|
||||||
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
||||||
block := types.NewBlockWithHeader(header)
|
block := types.NewBlockWithHeader(header)
|
||||||
|
|
||||||
|
|
@ -97,46 +77,37 @@ func TestRemoteNotify(t *testing.T) {
|
||||||
// Tests that pushing work packages fast to the miner doesn't cause any data race
|
// Tests that pushing work packages fast to the miner doesn't cause any data race
|
||||||
// issues in the notifications.
|
// issues in the notifications.
|
||||||
func TestRemoteMultiNotify(t *testing.T) {
|
func TestRemoteMultiNotify(t *testing.T) {
|
||||||
// Start a simple webserver to capture notifications
|
// Start a simple web server to capture notifications.
|
||||||
sink := make(chan [3]string, 64)
|
sink := make(chan [3]string, 64)
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
blob, err := ioutil.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to read miner notification: %v", err)
|
||||||
|
}
|
||||||
|
var work [3]string
|
||||||
|
if err := json.Unmarshal(blob, &work); err != nil {
|
||||||
|
t.Errorf("failed to unmarshal miner notification: %v", err)
|
||||||
|
}
|
||||||
|
sink <- work
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
server := &http.Server{
|
// Create the custom ethash engine.
|
||||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
ethash := NewTester([]string{server.URL}, false)
|
||||||
blob, err := ioutil.ReadAll(req.Body)
|
ethash.config.Log = testlog.Logger(t, log.LvlWarn)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to read miner notification: %v", err)
|
|
||||||
}
|
|
||||||
var work [3]string
|
|
||||||
if err := json.Unmarshal(blob, &work); err != nil {
|
|
||||||
t.Fatalf("failed to unmarshal miner notification: %v", err)
|
|
||||||
}
|
|
||||||
sink <- work
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
// Open a custom listener to extract its local address
|
|
||||||
listener, err := net.Listen("tcp", "localhost:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to open notification server: %v", err)
|
|
||||||
}
|
|
||||||
defer listener.Close()
|
|
||||||
|
|
||||||
go server.Serve(listener)
|
|
||||||
|
|
||||||
// Create the custom ethash engine
|
|
||||||
ethash := NewTester([]string{"http://" + listener.Addr().String()}, false)
|
|
||||||
defer ethash.Close()
|
defer ethash.Close()
|
||||||
|
|
||||||
// Stream a lot of work task and ensure all the notifications bubble out
|
// Stream a lot of work task and ensure all the notifications bubble out.
|
||||||
for i := 0; i < cap(sink); i++ {
|
for i := 0; i < cap(sink); i++ {
|
||||||
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
|
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
|
||||||
block := types.NewBlockWithHeader(header)
|
block := types.NewBlockWithHeader(header)
|
||||||
|
|
||||||
ethash.Seal(nil, block, nil, nil)
|
ethash.Seal(nil, block, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < cap(sink); i++ {
|
for i := 0; i < cap(sink); i++ {
|
||||||
select {
|
select {
|
||||||
case <-sink:
|
case <-sink:
|
||||||
case <-time.After(3 * time.Second):
|
case <-time.After(10 * time.Second):
|
||||||
t.Fatalf("notification %d timed out", i)
|
t.Fatalf("notification %d timed out", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -206,10 +177,10 @@ func TestStaleSubmission(t *testing.T) {
|
||||||
select {
|
select {
|
||||||
case res := <-results:
|
case res := <-results:
|
||||||
if res.Header().Nonce != fakeNonce {
|
if res.Header().Nonce != fakeNonce {
|
||||||
t.Errorf("case %d block nonce mismatch, want %s, get %s", id+1, fakeNonce, res.Header().Nonce)
|
t.Errorf("case %d block nonce mismatch, want %x, get %x", id+1, fakeNonce, res.Header().Nonce)
|
||||||
}
|
}
|
||||||
if res.Header().MixDigest != fakeDigest {
|
if res.Header().MixDigest != fakeDigest {
|
||||||
t.Errorf("case %d block digest mismatch, want %s, get %s", id+1, fakeDigest, res.Header().MixDigest)
|
t.Errorf("case %d block digest mismatch, want %x, get %x", id+1, fakeDigest, res.Header().MixDigest)
|
||||||
}
|
}
|
||||||
if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() {
|
if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() {
|
||||||
t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty)
|
t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty)
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,14 @@ func TestLexer(t *testing.T) {
|
||||||
input: "0123abc",
|
input: "0123abc",
|
||||||
tokens: []token{{typ: lineStart}, {typ: number, text: "0123"}, {typ: element, text: "abc"}, {typ: eof}},
|
tokens: []token{{typ: lineStart}, {typ: number, text: "0123"}, {typ: element, text: "abc"}, {typ: eof}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: "@foo",
|
||||||
|
tokens: []token{{typ: lineStart}, {typ: label, text: "foo"}, {typ: eof}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "@label123",
|
||||||
|
tokens: []token{{typ: lineStart}, {typ: label, text: "label123"}, {typ: eof}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ func lexComment(l *lexer) stateFn {
|
||||||
// the lex text state function to advance the parsing
|
// the lex text state function to advance the parsing
|
||||||
// process.
|
// process.
|
||||||
func lexLabel(l *lexer) stateFn {
|
func lexLabel(l *lexer) stateFn {
|
||||||
l.acceptRun(Alpha + "_")
|
l.acceptRun(Alpha + "_" + Numbers)
|
||||||
|
|
||||||
l.emit(label)
|
l.emit(label)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1260,16 +1260,16 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteBlockWithState writes the block and all associated state to the database.
|
// WriteBlockWithState writes the block and all associated state to the database.
|
||||||
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
|
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||||
bc.chainmu.Lock()
|
bc.chainmu.Lock()
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
return bc.writeBlockWithState(block, receipts, state)
|
return bc.writeBlockWithState(block, receipts, logs, state, emitHeadEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeBlockWithState writes the block and all associated state to the database,
|
// writeBlockWithState writes the block and all associated state to the database,
|
||||||
// but is expects the chain mutex to be held.
|
// but is expects the chain mutex to be held.
|
||||||
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
|
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||||
bc.wg.Add(1)
|
bc.wg.Add(1)
|
||||||
defer bc.wg.Done()
|
defer bc.wg.Done()
|
||||||
|
|
||||||
|
|
@ -1394,6 +1394,23 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
bc.insert(block)
|
bc.insert(block)
|
||||||
}
|
}
|
||||||
bc.futureBlocks.Remove(block.Hash())
|
bc.futureBlocks.Remove(block.Hash())
|
||||||
|
|
||||||
|
if status == CanonStatTy {
|
||||||
|
bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
|
||||||
|
if len(logs) > 0 {
|
||||||
|
bc.logsFeed.Send(logs)
|
||||||
|
}
|
||||||
|
// In theory we should fire a ChainHeadEvent when we inject
|
||||||
|
// a canonical block, but sometimes we can insert a batch of
|
||||||
|
// canonicial blocks. Avoid firing too much ChainHeadEvents,
|
||||||
|
// we will fire an accumulated ChainHeadEvent and disable fire
|
||||||
|
// event here.
|
||||||
|
if emitHeadEvent {
|
||||||
|
bc.chainHeadFeed.Send(ChainHeadEvent{Block: block})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bc.chainSideFeed.Send(ChainSideEvent{Block: block})
|
||||||
|
}
|
||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1444,11 +1461,10 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
// Pre-checks passed, start the full block imports
|
// Pre-checks passed, start the full block imports
|
||||||
bc.wg.Add(1)
|
bc.wg.Add(1)
|
||||||
bc.chainmu.Lock()
|
bc.chainmu.Lock()
|
||||||
n, events, logs, err := bc.insertChain(chain, true)
|
n, err := bc.insertChain(chain, true)
|
||||||
bc.chainmu.Unlock()
|
bc.chainmu.Unlock()
|
||||||
bc.wg.Done()
|
bc.wg.Done()
|
||||||
|
|
||||||
bc.PostChainEvents(events, logs)
|
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1460,23 +1476,24 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
// racey behaviour. If a sidechain import is in progress, and the historic state
|
// racey behaviour. If a sidechain import is in progress, and the historic state
|
||||||
// is imported, but then new canon-head is added before the actual sidechain
|
// is imported, but then new canon-head is added before the actual sidechain
|
||||||
// completes, then the historic state could be pruned again
|
// completes, then the historic state could be pruned again
|
||||||
func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []interface{}, []*types.Log, error) {
|
func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, error) {
|
||||||
// If the chain is terminating, don't even bother starting up
|
// If the chain is terminating, don't even bother starting up
|
||||||
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
|
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
|
||||||
return 0, nil, nil, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
||||||
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
|
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
|
||||||
|
|
||||||
// A queued approach to delivering events. This is generally
|
|
||||||
// faster than direct delivery and requires much less mutex
|
|
||||||
// acquiring.
|
|
||||||
var (
|
var (
|
||||||
stats = insertStats{startTime: mclock.Now()}
|
stats = insertStats{startTime: mclock.Now()}
|
||||||
events = make([]interface{}, 0, len(chain))
|
lastCanon *types.Block
|
||||||
lastCanon *types.Block
|
|
||||||
coalescedLogs []*types.Log
|
|
||||||
)
|
)
|
||||||
|
// Fire a single chain head event if we've progressed the chain
|
||||||
|
defer func() {
|
||||||
|
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
|
||||||
|
bc.chainHeadFeed.Send(ChainHeadEvent{lastCanon})
|
||||||
|
}
|
||||||
|
}()
|
||||||
// Start the parallel header verifier
|
// Start the parallel header verifier
|
||||||
headers := make([]*types.Header, len(chain))
|
headers := make([]*types.Header, len(chain))
|
||||||
seals := make([]bool, len(chain))
|
seals := make([]bool, len(chain))
|
||||||
|
|
@ -1526,7 +1543,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
for block != nil && err == ErrKnownBlock {
|
for block != nil && err == ErrKnownBlock {
|
||||||
log.Debug("Writing previously known block", "number", block.Number(), "hash", block.Hash())
|
log.Debug("Writing previously known block", "number", block.Number(), "hash", block.Hash())
|
||||||
if err := bc.writeKnownBlock(block); err != nil {
|
if err := bc.writeKnownBlock(block); err != nil {
|
||||||
return it.index, nil, nil, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
lastCanon = block
|
lastCanon = block
|
||||||
|
|
||||||
|
|
@ -1545,7 +1562,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
for block != nil && (it.index == 0 || err == consensus.ErrUnknownAncestor) {
|
for block != nil && (it.index == 0 || err == consensus.ErrUnknownAncestor) {
|
||||||
log.Debug("Future block, postponing import", "number", block.Number(), "hash", block.Hash())
|
log.Debug("Future block, postponing import", "number", block.Number(), "hash", block.Hash())
|
||||||
if err := bc.addFutureBlock(block); err != nil {
|
if err := bc.addFutureBlock(block); err != nil {
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
block, err = it.next()
|
block, err = it.next()
|
||||||
}
|
}
|
||||||
|
|
@ -1553,14 +1570,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
stats.ignored += it.remaining()
|
stats.ignored += it.remaining()
|
||||||
|
|
||||||
// If there are any still remaining, mark as ignored
|
// If there are any still remaining, mark as ignored
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, err
|
||||||
|
|
||||||
// Some other error occurred, abort
|
// Some other error occurred, abort
|
||||||
case err != nil:
|
case err != nil:
|
||||||
bc.futureBlocks.Remove(block.Hash())
|
bc.futureBlocks.Remove(block.Hash())
|
||||||
stats.ignored += len(it.chain)
|
stats.ignored += len(it.chain)
|
||||||
bc.reportBlock(block, nil, err)
|
bc.reportBlock(block, nil, err)
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
// No validation errors for the first block (or chain prefix skipped)
|
// No validation errors for the first block (or chain prefix skipped)
|
||||||
for ; block != nil && err == nil || err == ErrKnownBlock; block, err = it.next() {
|
for ; block != nil && err == nil || err == ErrKnownBlock; block, err = it.next() {
|
||||||
|
|
@ -1572,7 +1589,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
// If the header is a banned one, straight out abort
|
// If the header is a banned one, straight out abort
|
||||||
if BadHashes[block.Hash()] {
|
if BadHashes[block.Hash()] {
|
||||||
bc.reportBlock(block, nil, ErrBlacklistedHash)
|
bc.reportBlock(block, nil, ErrBlacklistedHash)
|
||||||
return it.index, events, coalescedLogs, ErrBlacklistedHash
|
return it.index, ErrBlacklistedHash
|
||||||
}
|
}
|
||||||
// If the block is known (in the middle of the chain), it's a special case for
|
// If the block is known (in the middle of the chain), it's a special case for
|
||||||
// Clique blocks where they can share state among each other, so importing an
|
// Clique blocks where they can share state among each other, so importing an
|
||||||
|
|
@ -1589,15 +1606,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
"root", block.Root())
|
"root", block.Root())
|
||||||
|
|
||||||
if err := bc.writeKnownBlock(block); err != nil {
|
if err := bc.writeKnownBlock(block); err != nil {
|
||||||
return it.index, nil, nil, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
stats.processed++
|
stats.processed++
|
||||||
|
|
||||||
// We can assume that logs are empty here, since the only way for consecutive
|
// We can assume that logs are empty here, since the only way for consecutive
|
||||||
// Clique blocks to have the same state is if there are no transactions.
|
// Clique blocks to have the same state is if there are no transactions.
|
||||||
events = append(events, ChainEvent{block, block.Hash(), nil})
|
|
||||||
lastCanon = block
|
lastCanon = block
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Retrieve the parent block and it's state to execute on top
|
// Retrieve the parent block and it's state to execute on top
|
||||||
|
|
@ -1609,7 +1624,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
}
|
}
|
||||||
statedb, err := state.New(parent.Root, bc.stateCache)
|
statedb, err := state.New(parent.Root, bc.stateCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
// If we have a followup block, run that against the current state to pre-cache
|
// If we have a followup block, run that against the current state to pre-cache
|
||||||
// transactions and probabilistically some of the account/storage trie nodes.
|
// transactions and probabilistically some of the account/storage trie nodes.
|
||||||
|
|
@ -1634,7 +1649,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bc.reportBlock(block, receipts, err)
|
bc.reportBlock(block, receipts, err)
|
||||||
atomic.StoreUint32(&followupInterrupt, 1)
|
atomic.StoreUint32(&followupInterrupt, 1)
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
// Update the metrics touched during block processing
|
// Update the metrics touched during block processing
|
||||||
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them
|
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them
|
||||||
|
|
@ -1653,7 +1668,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
|
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
|
||||||
bc.reportBlock(block, receipts, err)
|
bc.reportBlock(block, receipts, err)
|
||||||
atomic.StoreUint32(&followupInterrupt, 1)
|
atomic.StoreUint32(&followupInterrupt, 1)
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
proctime := time.Since(start)
|
proctime := time.Since(start)
|
||||||
|
|
||||||
|
|
@ -1665,10 +1680,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
|
|
||||||
// Write the block to the chain and get the status.
|
// Write the block to the chain and get the status.
|
||||||
substart = time.Now()
|
substart = time.Now()
|
||||||
status, err := bc.writeBlockWithState(block, receipts, statedb)
|
status, err := bc.writeBlockWithState(block, receipts, logs, statedb, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
atomic.StoreUint32(&followupInterrupt, 1)
|
atomic.StoreUint32(&followupInterrupt, 1)
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
atomic.StoreUint32(&followupInterrupt, 1)
|
atomic.StoreUint32(&followupInterrupt, 1)
|
||||||
|
|
||||||
|
|
@ -1686,8 +1701,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
"elapsed", common.PrettyDuration(time.Since(start)),
|
"elapsed", common.PrettyDuration(time.Since(start)),
|
||||||
"root", block.Root())
|
"root", block.Root())
|
||||||
|
|
||||||
coalescedLogs = append(coalescedLogs, logs...)
|
|
||||||
events = append(events, ChainEvent{block, block.Hash(), logs})
|
|
||||||
lastCanon = block
|
lastCanon = block
|
||||||
|
|
||||||
// Only count canonical blocks for GC processing time
|
// Only count canonical blocks for GC processing time
|
||||||
|
|
@ -1698,7 +1711,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
|
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
|
||||||
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
|
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
|
||||||
"root", block.Root())
|
"root", block.Root())
|
||||||
events = append(events, ChainSideEvent{block})
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// This in theory is impossible, but lets be nice to our future selves and leave
|
// This in theory is impossible, but lets be nice to our future selves and leave
|
||||||
|
|
@ -1717,24 +1729,20 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
// Any blocks remaining here? The only ones we care about are the future ones
|
// Any blocks remaining here? The only ones we care about are the future ones
|
||||||
if block != nil && err == consensus.ErrFutureBlock {
|
if block != nil && err == consensus.ErrFutureBlock {
|
||||||
if err := bc.addFutureBlock(block); err != nil {
|
if err := bc.addFutureBlock(block); err != nil {
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
block, err = it.next()
|
block, err = it.next()
|
||||||
|
|
||||||
for ; block != nil && err == consensus.ErrUnknownAncestor; block, err = it.next() {
|
for ; block != nil && err == consensus.ErrUnknownAncestor; block, err = it.next() {
|
||||||
if err := bc.addFutureBlock(block); err != nil {
|
if err := bc.addFutureBlock(block); err != nil {
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
stats.queued++
|
stats.queued++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stats.ignored += it.remaining()
|
stats.ignored += it.remaining()
|
||||||
|
|
||||||
// Append a single chain head event if we've progressed the chain
|
return it.index, err
|
||||||
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
|
|
||||||
events = append(events, ChainHeadEvent{lastCanon})
|
|
||||||
}
|
|
||||||
return it.index, events, coalescedLogs, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
||||||
|
|
@ -1743,7 +1751,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
//
|
//
|
||||||
// The method writes all (header-and-body-valid) blocks to disk, then tries to
|
// The method writes all (header-and-body-valid) blocks to disk, then tries to
|
||||||
// switch over to the new chain if the TD exceeded the current chain.
|
// switch over to the new chain if the TD exceeded the current chain.
|
||||||
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (int, []interface{}, []*types.Log, error) {
|
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (int, error) {
|
||||||
var (
|
var (
|
||||||
externTd *big.Int
|
externTd *big.Int
|
||||||
current = bc.CurrentBlock()
|
current = bc.CurrentBlock()
|
||||||
|
|
@ -1779,7 +1787,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
// If someone legitimately side-mines blocks, they would still be imported as usual. However,
|
// If someone legitimately side-mines blocks, they would still be imported as usual. However,
|
||||||
// we cannot risk writing unverified blocks to disk when they obviously target the pruning
|
// we cannot risk writing unverified blocks to disk when they obviously target the pruning
|
||||||
// mechanism.
|
// mechanism.
|
||||||
return it.index, nil, nil, errors.New("sidechain ghost-state attack")
|
return it.index, errors.New("sidechain ghost-state attack")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if externTd == nil {
|
if externTd == nil {
|
||||||
|
|
@ -1790,7 +1798,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
if !bc.HasBlock(block.Hash(), block.NumberU64()) {
|
if !bc.HasBlock(block.Hash(), block.NumberU64()) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
if err := bc.writeBlockWithoutState(block, externTd); err != nil {
|
if err := bc.writeBlockWithoutState(block, externTd); err != nil {
|
||||||
return it.index, nil, nil, err
|
return it.index, err
|
||||||
}
|
}
|
||||||
log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(),
|
log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(),
|
||||||
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
|
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
|
||||||
|
|
@ -1807,7 +1815,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
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().Number, "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, err
|
||||||
}
|
}
|
||||||
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
||||||
var (
|
var (
|
||||||
|
|
@ -1822,7 +1830,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
parent = bc.GetHeader(parent.ParentHash, parent.Number.Uint64()-1)
|
parent = bc.GetHeader(parent.ParentHash, parent.Number.Uint64()-1)
|
||||||
}
|
}
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return it.index, nil, nil, errors.New("missing parent")
|
return it.index, errors.New("missing parent")
|
||||||
}
|
}
|
||||||
// Import all the pruned blocks to make the state available
|
// Import all the pruned blocks to make the state available
|
||||||
var (
|
var (
|
||||||
|
|
@ -1841,15 +1849,15 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
// memory here.
|
// memory here.
|
||||||
if len(blocks) >= 2048 || memory > 64*1024*1024 {
|
if len(blocks) >= 2048 || memory > 64*1024*1024 {
|
||||||
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
|
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
|
||||||
if _, _, _, err := bc.insertChain(blocks, false); err != nil {
|
if _, err := bc.insertChain(blocks, false); err != nil {
|
||||||
return 0, nil, nil, err
|
return 0, err
|
||||||
}
|
}
|
||||||
blocks, memory = blocks[:0], 0
|
blocks, memory = blocks[:0], 0
|
||||||
|
|
||||||
// If the chain is terminating, stop processing blocks
|
// If the chain is terminating, stop processing blocks
|
||||||
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
|
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
|
||||||
log.Debug("Premature abort during blocks processing")
|
log.Debug("Premature abort during blocks processing")
|
||||||
return 0, nil, nil, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1857,7 +1865,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
|
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
|
||||||
return bc.insertChain(blocks, false)
|
return bc.insertChain(blocks, false)
|
||||||
}
|
}
|
||||||
return 0, nil, nil, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reorg takes two blocks, an old chain and a new chain and will reconstruct the
|
// reorg takes two blocks, an old chain and a new chain and will reconstruct the
|
||||||
|
|
@ -1872,11 +1880,11 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||||
deletedTxs types.Transactions
|
deletedTxs types.Transactions
|
||||||
addedTxs types.Transactions
|
addedTxs types.Transactions
|
||||||
|
|
||||||
deletedLogs []*types.Log
|
deletedLogs [][]*types.Log
|
||||||
rebirthLogs []*types.Log
|
rebirthLogs [][]*types.Log
|
||||||
|
|
||||||
// collectLogs collects the logs that were generated during the
|
// collectLogs collects the logs that were generated or removed during
|
||||||
// processing of the block that corresponds with the given hash.
|
// the processing of the block that corresponds with the given hash.
|
||||||
// These logs are later announced as deleted or reborn
|
// These logs are later announced as deleted or reborn
|
||||||
collectLogs = func(hash common.Hash, removed bool) {
|
collectLogs = func(hash common.Hash, removed bool) {
|
||||||
number := bc.hc.GetBlockNumber(hash)
|
number := bc.hc.GetBlockNumber(hash)
|
||||||
|
|
@ -1884,17 +1892,39 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig)
|
receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig)
|
||||||
|
|
||||||
|
var logs []*types.Log
|
||||||
for _, receipt := range receipts {
|
for _, receipt := range receipts {
|
||||||
for _, log := range receipt.Logs {
|
for _, log := range receipt.Logs {
|
||||||
l := *log
|
l := *log
|
||||||
if removed {
|
if removed {
|
||||||
l.Removed = true
|
l.Removed = true
|
||||||
deletedLogs = append(deletedLogs, &l)
|
|
||||||
} else {
|
} else {
|
||||||
rebirthLogs = append(rebirthLogs, &l)
|
|
||||||
}
|
}
|
||||||
|
logs = append(logs, &l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(logs) > 0 {
|
||||||
|
if removed {
|
||||||
|
deletedLogs = append(deletedLogs, logs)
|
||||||
|
} else {
|
||||||
|
rebirthLogs = append(rebirthLogs, logs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mergeLogs returns a merged log slice with specified sort order.
|
||||||
|
mergeLogs = func(logs [][]*types.Log, reverse bool) []*types.Log {
|
||||||
|
var ret []*types.Log
|
||||||
|
if reverse {
|
||||||
|
for i := len(logs) - 1; i >= 0; i-- {
|
||||||
|
ret = append(ret, logs[i]...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 0; i < len(logs); i++ {
|
||||||
|
ret = append(ret, logs[i]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// Reduce the longer chain to the same number as the shorter one
|
// Reduce the longer chain to the same number as the shorter one
|
||||||
|
|
@ -1990,47 +2020,20 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||||
// this goroutine if there are no events to fire, but realistcally that only
|
// this goroutine if there are no events to fire, but realistcally that only
|
||||||
// ever happens if we're reorging empty blocks, which will only happen on idle
|
// ever happens if we're reorging empty blocks, which will only happen on idle
|
||||||
// networks where performance is not an issue either way.
|
// networks where performance is not an issue either way.
|
||||||
//
|
if len(deletedLogs) > 0 {
|
||||||
// TODO(karalabe): Can we get rid of the goroutine somehow to guarantee correct
|
bc.rmLogsFeed.Send(RemovedLogsEvent{mergeLogs(deletedLogs, true)})
|
||||||
// event ordering?
|
}
|
||||||
go func() {
|
if len(rebirthLogs) > 0 {
|
||||||
if len(deletedLogs) > 0 {
|
bc.logsFeed.Send(mergeLogs(rebirthLogs, false))
|
||||||
bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
|
}
|
||||||
|
if len(oldChain) > 0 {
|
||||||
|
for i := len(oldChain) - 1; i >= 0; i-- {
|
||||||
|
bc.chainSideFeed.Send(ChainSideEvent{Block: oldChain[i]})
|
||||||
}
|
}
|
||||||
if len(rebirthLogs) > 0 {
|
}
|
||||||
bc.logsFeed.Send(rebirthLogs)
|
|
||||||
}
|
|
||||||
if len(oldChain) > 0 {
|
|
||||||
for _, block := range oldChain {
|
|
||||||
bc.chainSideFeed.Send(ChainSideEvent{Block: block})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostChainEvents iterates over the events generated by a chain insertion and
|
|
||||||
// posts them into the event feed.
|
|
||||||
// TODO: Should not expose PostChainEvents. The chain events should be posted in WriteBlock.
|
|
||||||
func (bc *BlockChain) PostChainEvents(events []interface{}, logs []*types.Log) {
|
|
||||||
// post event logs for further processing
|
|
||||||
if logs != nil {
|
|
||||||
bc.logsFeed.Send(logs)
|
|
||||||
}
|
|
||||||
for _, event := range events {
|
|
||||||
switch ev := event.(type) {
|
|
||||||
case ChainEvent:
|
|
||||||
bc.chainFeed.Send(ev)
|
|
||||||
|
|
||||||
case ChainHeadEvent:
|
|
||||||
bc.chainHeadFeed.Send(ev)
|
|
||||||
|
|
||||||
case ChainSideEvent:
|
|
||||||
bc.chainSideFeed.Send(ev)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bc *BlockChain) update() {
|
func (bc *BlockChain) update() {
|
||||||
futureTimer := time.NewTicker(5 * time.Second)
|
futureTimer := time.NewTicker(5 * time.Second)
|
||||||
defer futureTimer.Stop()
|
defer futureTimer.Stop()
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -960,16 +961,20 @@ func TestLogReorgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
chain, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
|
chain, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
|
||||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
done := make(chan struct{})
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
go func() {
|
||||||
}
|
ev := <-rmLogsCh
|
||||||
|
|
||||||
timeout := time.NewTimer(1 * time.Second)
|
|
||||||
select {
|
|
||||||
case ev := <-rmLogsCh:
|
|
||||||
if len(ev.Logs) == 0 {
|
if len(ev.Logs) == 0 {
|
||||||
t.Error("expected logs")
|
t.Error("expected logs")
|
||||||
}
|
}
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||||
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
|
}
|
||||||
|
timeout := time.NewTimer(1 * time.Second)
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
case <-timeout.C:
|
case <-timeout.C:
|
||||||
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
|
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
|
||||||
}
|
}
|
||||||
|
|
@ -982,39 +987,47 @@ func TestLogRebirth(t *testing.T) {
|
||||||
db = rawdb.NewMemoryDatabase()
|
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)}}}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||||
newLogCh = make(chan bool)
|
newLogCh = make(chan bool)
|
||||||
|
removeLogCh = make(chan bool)
|
||||||
)
|
)
|
||||||
|
|
||||||
// listenNewLog checks whether the received logs number is equal with expected.
|
// validateLogEvent checks whether the received logs number is equal with expected.
|
||||||
listenNewLog := func(sink chan []*types.Log, expect int) {
|
validateLogEvent := func(sink interface{}, result chan bool, expect int) {
|
||||||
|
chanval := reflect.ValueOf(sink)
|
||||||
|
chantyp := chanval.Type()
|
||||||
|
if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.RecvDir == 0 {
|
||||||
|
t.Fatalf("invalid channel, given type %v", chantyp)
|
||||||
|
}
|
||||||
cnt := 0
|
cnt := 0
|
||||||
|
var recv []reflect.Value
|
||||||
|
timeout := time.After(1 * time.Second)
|
||||||
|
cases := []reflect.SelectCase{{Chan: chanval, Dir: reflect.SelectRecv}, {Chan: reflect.ValueOf(timeout), Dir: reflect.SelectRecv}}
|
||||||
for {
|
for {
|
||||||
select {
|
chose, v, _ := reflect.Select(cases)
|
||||||
case logs := <-sink:
|
if chose == 1 {
|
||||||
cnt += len(logs)
|
// Not enough event received
|
||||||
case <-time.NewTimer(5 * time.Second).C:
|
result <- false
|
||||||
// new logs timeout
|
|
||||||
newLogCh <- false
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
cnt += 1
|
||||||
|
recv = append(recv, v)
|
||||||
if cnt == expect {
|
if cnt == expect {
|
||||||
break
|
break
|
||||||
} else if cnt > expect {
|
|
||||||
// redundant logs received
|
|
||||||
newLogCh <- false
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select {
|
done := time.After(50 * time.Millisecond)
|
||||||
case <-sink:
|
cases = cases[:1]
|
||||||
// redundant logs received
|
cases = append(cases, reflect.SelectCase{Chan: reflect.ValueOf(done), Dir: reflect.SelectRecv})
|
||||||
newLogCh <- false
|
chose, _, _ := reflect.Select(cases)
|
||||||
case <-time.NewTimer(100 * time.Millisecond).C:
|
// If chose equal 0, it means receiving redundant events.
|
||||||
newLogCh <- true
|
if chose == 1 {
|
||||||
|
result <- true
|
||||||
|
} else {
|
||||||
|
result <- false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1038,12 +1051,12 @@ func TestLogRebirth(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Spawn a goroutine to receive log events
|
// Spawn a goroutine to receive log events
|
||||||
go listenNewLog(logsCh, 1)
|
go validateLogEvent(logsCh, newLogCh, 1)
|
||||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||||
t.Fatalf("failed to insert chain: %v", err)
|
t.Fatalf("failed to insert chain: %v", err)
|
||||||
}
|
}
|
||||||
if !<-newLogCh {
|
if !<-newLogCh {
|
||||||
t.Fatalf("failed to receive new log event")
|
t.Fatal("failed to receive new log event")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate long reorg chain
|
// Generate long reorg chain
|
||||||
|
|
@ -1060,40 +1073,31 @@ func TestLogRebirth(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Spawn a goroutine to receive log events
|
// Spawn a goroutine to receive log events
|
||||||
go listenNewLog(logsCh, 1)
|
go validateLogEvent(logsCh, newLogCh, 1)
|
||||||
|
go validateLogEvent(rmLogsCh, removeLogCh, 1)
|
||||||
if _, err := blockchain.InsertChain(forkChain); err != nil {
|
if _, err := blockchain.InsertChain(forkChain); err != nil {
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
}
|
}
|
||||||
if !<-newLogCh {
|
if !<-newLogCh {
|
||||||
t.Fatalf("failed to receive new log event")
|
t.Fatal("failed to receive new log event")
|
||||||
}
|
}
|
||||||
// Ensure removedLog events received
|
if !<-removeLogCh {
|
||||||
select {
|
t.Fatal("failed to receive removed log event")
|
||||||
case ev := <-rmLogsCh:
|
|
||||||
if len(ev.Logs) == 0 {
|
|
||||||
t.Error("expected logs")
|
|
||||||
}
|
|
||||||
case <-time.NewTimer(1 * time.Second).C:
|
|
||||||
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
go listenNewLog(logsCh, 1)
|
go validateLogEvent(logsCh, newLogCh, 1)
|
||||||
|
go validateLogEvent(rmLogsCh, removeLogCh, 1)
|
||||||
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
}
|
}
|
||||||
// Ensure removedLog events received
|
|
||||||
select {
|
|
||||||
case ev := <-rmLogsCh:
|
|
||||||
if len(ev.Logs) == 0 {
|
|
||||||
t.Error("expected logs")
|
|
||||||
}
|
|
||||||
case <-time.NewTimer(1 * time.Second).C:
|
|
||||||
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
|
|
||||||
}
|
|
||||||
// Rebirth logs should omit a newLogEvent
|
// Rebirth logs should omit a newLogEvent
|
||||||
if !<-newLogCh {
|
if !<-newLogCh {
|
||||||
t.Fatalf("failed to receive new log event")
|
t.Fatal("failed to receive new log event")
|
||||||
|
}
|
||||||
|
// Ensure removedLog events received
|
||||||
|
if !<-removeLogCh {
|
||||||
|
t.Fatal("failed to receive removed log event")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1145,7 +1149,6 @@ func TestSideLogRebirth(t *testing.T) {
|
||||||
|
|
||||||
logsCh := make(chan []*types.Log)
|
logsCh := make(chan []*types.Log)
|
||||||
blockchain.SubscribeLogsEvent(logsCh)
|
blockchain.SubscribeLogsEvent(logsCh)
|
||||||
|
|
||||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
// Higher block difficulty
|
// Higher block difficulty
|
||||||
|
|
|
||||||
|
|
@ -150,11 +150,10 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace st
|
||||||
}
|
}
|
||||||
// Database contains only older data than the freezer, this happens if the
|
// Database contains only older data than the freezer, this happens if the
|
||||||
// state was wiped and reinited from an existing freezer.
|
// state was wiped and reinited from an existing freezer.
|
||||||
} else {
|
|
||||||
// Key-value store continues where the freezer left off, all is fine. We might
|
|
||||||
// have duplicate blocks (crash after freezer write but before kay-value store
|
|
||||||
// deletion, but that's fine).
|
|
||||||
}
|
}
|
||||||
|
// Otherwise, key-value store continues where the freezer left off, all is fine.
|
||||||
|
// We might have duplicate blocks (crash after freezer write but before key-value
|
||||||
|
// store deletion, but that's fine).
|
||||||
} else {
|
} else {
|
||||||
// If the freezer is empty, ensure nothing was moved yet from the key-value
|
// If the freezer is empty, ensure nothing was moved yet from the key-value
|
||||||
// store, otherwise we'll end up missing data. We check block #1 to decide
|
// store, otherwise we'll end up missing data. We check block #1 to decide
|
||||||
|
|
@ -167,9 +166,9 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace st
|
||||||
return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
|
return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
|
||||||
}
|
}
|
||||||
// Block #1 is still in the database, we're allowed to init a new feezer
|
// Block #1 is still in the database, we're allowed to init a new feezer
|
||||||
} else {
|
|
||||||
// The head header is still the genesis, we're allowed to init a new feezer
|
|
||||||
}
|
}
|
||||||
|
// Otherwise, the head header is still the genesis, we're allowed to init a new
|
||||||
|
// feezer.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Freezer is consistent with the key-value database, permit combining the two
|
// Freezer is consistent with the key-value database, permit combining the two
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,10 @@ func InitDatabaseFromFreezer(db ethdb.Database) error {
|
||||||
if n >= frozen {
|
if n >= frozen {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Retrieve the block from the freezer (no need for the hash, we pull by
|
// Retrieve the block from the freezer. If successful, pre-cache
|
||||||
// number from the freezer). If successful, pre-cache the block hash and
|
// the block hash and the individual transaction hashes for storing
|
||||||
// the individual transaction hashes for storing into the database.
|
// into the database.
|
||||||
block := ReadBlock(db, common.Hash{}, n)
|
block := ReadBlock(db, ReadCanonicalHash(db, n), n)
|
||||||
if block != nil {
|
if block != nil {
|
||||||
block.Hash()
|
block.Hash()
|
||||||
for _, tx := range block.Transactions() {
|
for _, tx := range block.Transactions() {
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,9 @@ type Dump struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterativeDump is a 'collector'-implementation which dump output line-by-line iteratively
|
// iterativeDump is a 'collector'-implementation which dump output line-by-line iteratively
|
||||||
type iterativeDump json.Encoder
|
type iterativeDump struct {
|
||||||
|
*json.Encoder
|
||||||
|
}
|
||||||
|
|
||||||
// Collector interface which the state trie calls during iteration
|
// Collector interface which the state trie calls during iteration
|
||||||
type collector interface {
|
type collector interface {
|
||||||
|
|
@ -55,15 +57,15 @@ type collector interface {
|
||||||
onAccount(common.Address, DumpAccount)
|
onAccount(common.Address, DumpAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Dump) onRoot(root common.Hash) {
|
func (d *Dump) onRoot(root common.Hash) {
|
||||||
self.Root = fmt.Sprintf("%x", root)
|
d.Root = fmt.Sprintf("%x", root)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Dump) onAccount(addr common.Address, account DumpAccount) {
|
func (d *Dump) onAccount(addr common.Address, account DumpAccount) {
|
||||||
self.Accounts[addr] = account
|
d.Accounts[addr] = account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self iterativeDump) onAccount(addr common.Address, account DumpAccount) {
|
func (d iterativeDump) onAccount(addr common.Address, account DumpAccount) {
|
||||||
dumpAccount := &DumpAccount{
|
dumpAccount := &DumpAccount{
|
||||||
Balance: account.Balance,
|
Balance: account.Balance,
|
||||||
Nonce: account.Nonce,
|
Nonce: account.Nonce,
|
||||||
|
|
@ -77,25 +79,26 @@ func (self iterativeDump) onAccount(addr common.Address, account DumpAccount) {
|
||||||
if addr != (common.Address{}) {
|
if addr != (common.Address{}) {
|
||||||
dumpAccount.Address = &addr
|
dumpAccount.Address = &addr
|
||||||
}
|
}
|
||||||
(*json.Encoder)(&self).Encode(dumpAccount)
|
d.Encode(dumpAccount)
|
||||||
}
|
}
|
||||||
func (self iterativeDump) onRoot(root common.Hash) {
|
|
||||||
(*json.Encoder)(&self).Encode(struct {
|
func (d iterativeDump) onRoot(root common.Hash) {
|
||||||
|
d.Encode(struct {
|
||||||
Root common.Hash `json:"root"`
|
Root common.Hash `json:"root"`
|
||||||
}{root})
|
}{root})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool) {
|
func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool) {
|
||||||
emptyAddress := (common.Address{})
|
emptyAddress := (common.Address{})
|
||||||
missingPreimages := 0
|
missingPreimages := 0
|
||||||
c.onRoot(self.trie.Hash())
|
c.onRoot(s.trie.Hash())
|
||||||
it := trie.NewIterator(self.trie.NodeIterator(nil))
|
it := trie.NewIterator(s.trie.NodeIterator(nil))
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
var data Account
|
var data Account
|
||||||
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
|
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
addr := common.BytesToAddress(self.trie.GetKey(it.Key))
|
addr := common.BytesToAddress(s.trie.GetKey(it.Key))
|
||||||
obj := newObject(nil, addr, data)
|
obj := newObject(nil, addr, data)
|
||||||
account := DumpAccount{
|
account := DumpAccount{
|
||||||
Balance: data.Balance.String(),
|
Balance: data.Balance.String(),
|
||||||
|
|
@ -112,18 +115,18 @@ func (self *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissi
|
||||||
account.SecureKey = it.Key
|
account.SecureKey = it.Key
|
||||||
}
|
}
|
||||||
if !excludeCode {
|
if !excludeCode {
|
||||||
account.Code = common.Bytes2Hex(obj.Code(self.db))
|
account.Code = common.Bytes2Hex(obj.Code(s.db))
|
||||||
}
|
}
|
||||||
if !excludeStorage {
|
if !excludeStorage {
|
||||||
account.Storage = make(map[common.Hash]string)
|
account.Storage = make(map[common.Hash]string)
|
||||||
storageIt := trie.NewIterator(obj.getTrie(self.db).NodeIterator(nil))
|
storageIt := trie.NewIterator(obj.getTrie(s.db).NodeIterator(nil))
|
||||||
for storageIt.Next() {
|
for storageIt.Next() {
|
||||||
_, content, _, err := rlp.Split(storageIt.Value)
|
_, content, _, err := rlp.Split(storageIt.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to decode the value returned by iterator", "error", err)
|
log.Error("Failed to decode the value returned by iterator", "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
account.Storage[common.BytesToHash(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
|
account.Storage[common.BytesToHash(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.onAccount(addr, account)
|
c.onAccount(addr, account)
|
||||||
|
|
@ -134,17 +137,17 @@ func (self *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissi
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawDump returns the entire state an a single large object
|
// RawDump returns the entire state an a single large object
|
||||||
func (self *StateDB) RawDump(excludeCode, excludeStorage, excludeMissingPreimages bool) Dump {
|
func (s *StateDB) RawDump(excludeCode, excludeStorage, excludeMissingPreimages bool) Dump {
|
||||||
dump := &Dump{
|
dump := &Dump{
|
||||||
Accounts: make(map[common.Address]DumpAccount),
|
Accounts: make(map[common.Address]DumpAccount),
|
||||||
}
|
}
|
||||||
self.dump(dump, excludeCode, excludeStorage, excludeMissingPreimages)
|
s.dump(dump, excludeCode, excludeStorage, excludeMissingPreimages)
|
||||||
return *dump
|
return *dump
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump returns a JSON string representing the entire state as a single json-object
|
// Dump returns a JSON string representing the entire state as a single json-object
|
||||||
func (self *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages bool) []byte {
|
func (s *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages bool) []byte {
|
||||||
dump := self.RawDump(excludeCode, excludeStorage, excludeMissingPreimages)
|
dump := s.RawDump(excludeCode, excludeStorage, excludeMissingPreimages)
|
||||||
json, err := json.MarshalIndent(dump, "", " ")
|
json, err := json.MarshalIndent(dump, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("dump err", err)
|
fmt.Println("dump err", err)
|
||||||
|
|
@ -153,6 +156,6 @@ func (self *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages b
|
||||||
}
|
}
|
||||||
|
|
||||||
// IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout
|
// IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout
|
||||||
func (self *StateDB) IterativeDump(excludeCode, excludeStorage, excludeMissingPreimages bool, output *json.Encoder) {
|
func (s *StateDB) IterativeDump(excludeCode, excludeStorage, excludeMissingPreimages bool, output *json.Encoder) {
|
||||||
self.dump(iterativeDump(*output), excludeCode, excludeStorage, excludeMissingPreimages)
|
s.dump(iterativeDump{output}, excludeCode, excludeStorage, excludeMissingPreimages)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,9 +127,7 @@ type (
|
||||||
hash common.Hash
|
hash common.Hash
|
||||||
}
|
}
|
||||||
touchChange struct {
|
touchChange struct {
|
||||||
account *common.Address
|
account *common.Address
|
||||||
prev bool
|
|
||||||
prevDirty bool
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
// Copyright 2014 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 state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
checker "gopkg.in/check.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test(t *testing.T) { checker.TestingT(t) }
|
|
||||||
|
|
@ -25,19 +25,24 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type StateSuite struct {
|
var toAddr = common.BytesToAddress
|
||||||
|
|
||||||
|
type stateTest struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
state *StateDB
|
state *StateDB
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = checker.Suite(&StateSuite{})
|
func newStateTest() *stateTest {
|
||||||
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
sdb, _ := New(common.Hash{}, NewDatabase(db))
|
||||||
|
return &stateTest{db: db, state: sdb}
|
||||||
|
}
|
||||||
|
|
||||||
var toAddr = common.BytesToAddress
|
func TestDump(t *testing.T) {
|
||||||
|
s := newStateTest()
|
||||||
|
|
||||||
func (s *StateSuite) TestDump(c *checker.C) {
|
|
||||||
// generate a few entries
|
// generate a few entries
|
||||||
obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01}))
|
obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01}))
|
||||||
obj1.AddBalance(big.NewInt(22))
|
obj1.AddBalance(big.NewInt(22))
|
||||||
|
|
@ -78,16 +83,12 @@ func (s *StateSuite) TestDump(c *checker.C) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
if got != want {
|
if got != want {
|
||||||
c.Errorf("dump mismatch:\ngot: %s\nwant: %s\n", got, want)
|
t.Errorf("dump mismatch:\ngot: %s\nwant: %s\n", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateSuite) SetUpTest(c *checker.C) {
|
func TestNull(t *testing.T) {
|
||||||
s.db = rawdb.NewMemoryDatabase()
|
s := newStateTest()
|
||||||
s.state, _ = New(common.Hash{}, NewDatabase(s.db))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StateSuite) TestNull(c *checker.C) {
|
|
||||||
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
|
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
|
||||||
s.state.CreateAccount(address)
|
s.state.CreateAccount(address)
|
||||||
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
|
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
|
||||||
|
|
@ -97,18 +98,19 @@ func (s *StateSuite) TestNull(c *checker.C) {
|
||||||
s.state.Commit(false)
|
s.state.Commit(false)
|
||||||
|
|
||||||
if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
|
if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
|
||||||
c.Errorf("expected empty current value, got %x", value)
|
t.Errorf("expected empty current value, got %x", value)
|
||||||
}
|
}
|
||||||
if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) {
|
if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) {
|
||||||
c.Errorf("expected empty committed value, got %x", value)
|
t.Errorf("expected empty committed value, got %x", value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateSuite) TestSnapshot(c *checker.C) {
|
func TestSnapshot(t *testing.T) {
|
||||||
stateobjaddr := toAddr([]byte("aa"))
|
stateobjaddr := toAddr([]byte("aa"))
|
||||||
var storageaddr common.Hash
|
var storageaddr common.Hash
|
||||||
data1 := common.BytesToHash([]byte{42})
|
data1 := common.BytesToHash([]byte{42})
|
||||||
data2 := common.BytesToHash([]byte{43})
|
data2 := common.BytesToHash([]byte{43})
|
||||||
|
s := newStateTest()
|
||||||
|
|
||||||
// snapshot the genesis state
|
// snapshot the genesis state
|
||||||
genesis := s.state.Snapshot()
|
genesis := s.state.Snapshot()
|
||||||
|
|
@ -121,21 +123,28 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
|
||||||
s.state.SetState(stateobjaddr, storageaddr, data2)
|
s.state.SetState(stateobjaddr, storageaddr, data2)
|
||||||
s.state.RevertToSnapshot(snapshot)
|
s.state.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, data1)
|
if v := s.state.GetState(stateobjaddr, storageaddr); v != data1 {
|
||||||
c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
|
t.Errorf("wrong storage value %v, want %v", v, data1)
|
||||||
|
}
|
||||||
|
if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) {
|
||||||
|
t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{})
|
||||||
|
}
|
||||||
|
|
||||||
// revert up to the genesis state and ensure correct content
|
// revert up to the genesis state and ensure correct content
|
||||||
s.state.RevertToSnapshot(genesis)
|
s.state.RevertToSnapshot(genesis)
|
||||||
c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
|
if v := s.state.GetState(stateobjaddr, storageaddr); v != (common.Hash{}) {
|
||||||
c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
|
t.Errorf("wrong storage value %v, want %v", v, common.Hash{})
|
||||||
|
}
|
||||||
|
if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) {
|
||||||
|
t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateSuite) TestSnapshotEmpty(c *checker.C) {
|
func TestSnapshotEmpty(t *testing.T) {
|
||||||
|
s := newStateTest()
|
||||||
s.state.RevertToSnapshot(s.state.Snapshot())
|
s.state.RevertToSnapshot(s.state.Snapshot())
|
||||||
}
|
}
|
||||||
|
|
||||||
// use testing instead of checker because checker does not support
|
|
||||||
// printing/logging in tests (-check.vv does not work)
|
|
||||||
func TestSnapshot2(t *testing.T) {
|
func TestSnapshot2(t *testing.T) {
|
||||||
state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,115 +124,115 @@ func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// setError remembers the first non-nil error it is called with.
|
// setError remembers the first non-nil error it is called with.
|
||||||
func (self *StateDB) setError(err error) {
|
func (s *StateDB) setError(err error) {
|
||||||
if self.dbErr == nil {
|
if s.dbErr == nil {
|
||||||
self.dbErr = err
|
s.dbErr = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) Error() error {
|
func (s *StateDB) Error() error {
|
||||||
return self.dbErr
|
return s.dbErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset clears out all ephemeral state objects from the state db, but keeps
|
// Reset clears out all ephemeral state objects from the state db, but keeps
|
||||||
// the underlying state trie to avoid reloading data for the next operations.
|
// the underlying state trie to avoid reloading data for the next operations.
|
||||||
func (self *StateDB) Reset(root common.Hash) error {
|
func (s *StateDB) Reset(root common.Hash) error {
|
||||||
tr, err := self.db.OpenTrie(root)
|
tr, err := s.db.OpenTrie(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
self.trie = tr
|
s.trie = tr
|
||||||
self.stateObjects = make(map[common.Address]*stateObject)
|
s.stateObjects = make(map[common.Address]*stateObject)
|
||||||
self.stateObjectsPending = make(map[common.Address]struct{})
|
s.stateObjectsPending = make(map[common.Address]struct{})
|
||||||
self.stateObjectsDirty = make(map[common.Address]struct{})
|
s.stateObjectsDirty = make(map[common.Address]struct{})
|
||||||
self.thash = common.Hash{}
|
s.thash = common.Hash{}
|
||||||
self.bhash = common.Hash{}
|
s.bhash = common.Hash{}
|
||||||
self.txIndex = 0
|
s.txIndex = 0
|
||||||
self.logs = make(map[common.Hash][]*types.Log)
|
s.logs = make(map[common.Hash][]*types.Log)
|
||||||
self.logSize = 0
|
s.logSize = 0
|
||||||
self.preimages = make(map[common.Hash][]byte)
|
s.preimages = make(map[common.Hash][]byte)
|
||||||
self.clearJournalAndRefund()
|
s.clearJournalAndRefund()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) AddLog(log *types.Log) {
|
func (s *StateDB) AddLog(log *types.Log) {
|
||||||
self.journal.append(addLogChange{txhash: self.thash})
|
s.journal.append(addLogChange{txhash: s.thash})
|
||||||
|
|
||||||
log.TxHash = self.thash
|
log.TxHash = s.thash
|
||||||
log.BlockHash = self.bhash
|
log.BlockHash = s.bhash
|
||||||
log.TxIndex = uint(self.txIndex)
|
log.TxIndex = uint(s.txIndex)
|
||||||
log.Index = self.logSize
|
log.Index = s.logSize
|
||||||
self.logs[self.thash] = append(self.logs[self.thash], log)
|
s.logs[s.thash] = append(s.logs[s.thash], log)
|
||||||
self.logSize++
|
s.logSize++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) GetLogs(hash common.Hash) []*types.Log {
|
func (s *StateDB) GetLogs(hash common.Hash) []*types.Log {
|
||||||
return self.logs[hash]
|
return s.logs[hash]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) Logs() []*types.Log {
|
func (s *StateDB) Logs() []*types.Log {
|
||||||
var logs []*types.Log
|
var logs []*types.Log
|
||||||
for _, lgs := range self.logs {
|
for _, lgs := range s.logs {
|
||||||
logs = append(logs, lgs...)
|
logs = append(logs, lgs...)
|
||||||
}
|
}
|
||||||
return logs
|
return logs
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPreimage records a SHA3 preimage seen by the VM.
|
// AddPreimage records a SHA3 preimage seen by the VM.
|
||||||
func (self *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
|
func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
|
||||||
if _, ok := self.preimages[hash]; !ok {
|
if _, ok := s.preimages[hash]; !ok {
|
||||||
self.journal.append(addPreimageChange{hash: hash})
|
s.journal.append(addPreimageChange{hash: hash})
|
||||||
pi := make([]byte, len(preimage))
|
pi := make([]byte, len(preimage))
|
||||||
copy(pi, preimage)
|
copy(pi, preimage)
|
||||||
self.preimages[hash] = pi
|
s.preimages[hash] = pi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preimages returns a list of SHA3 preimages that have been submitted.
|
// Preimages returns a list of SHA3 preimages that have been submitted.
|
||||||
func (self *StateDB) Preimages() map[common.Hash][]byte {
|
func (s *StateDB) Preimages() map[common.Hash][]byte {
|
||||||
return self.preimages
|
return s.preimages
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddRefund adds gas to the refund counter
|
// AddRefund adds gas to the refund counter
|
||||||
func (self *StateDB) AddRefund(gas uint64) {
|
func (s *StateDB) AddRefund(gas uint64) {
|
||||||
self.journal.append(refundChange{prev: self.refund})
|
s.journal.append(refundChange{prev: s.refund})
|
||||||
self.refund += gas
|
s.refund += gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubRefund removes gas from the refund counter.
|
// SubRefund removes gas from the refund counter.
|
||||||
// This method will panic if the refund counter goes below zero
|
// This method will panic if the refund counter goes below zero
|
||||||
func (self *StateDB) SubRefund(gas uint64) {
|
func (s *StateDB) SubRefund(gas uint64) {
|
||||||
self.journal.append(refundChange{prev: self.refund})
|
s.journal.append(refundChange{prev: s.refund})
|
||||||
if gas > self.refund {
|
if gas > s.refund {
|
||||||
panic("Refund counter below zero")
|
panic("Refund counter below zero")
|
||||||
}
|
}
|
||||||
self.refund -= gas
|
s.refund -= gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exist reports whether the given account address exists in the state.
|
// Exist reports whether the given account address exists in the state.
|
||||||
// Notably this also returns true for suicided accounts.
|
// Notably this also returns true for suicided accounts.
|
||||||
func (self *StateDB) Exist(addr common.Address) bool {
|
func (s *StateDB) Exist(addr common.Address) bool {
|
||||||
return self.getStateObject(addr) != nil
|
return s.getStateObject(addr) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty returns whether the state object is either non-existent
|
// Empty returns whether the state object is either non-existent
|
||||||
// or empty according to the EIP161 specification (balance = nonce = code = 0)
|
// or empty according to the EIP161 specification (balance = nonce = code = 0)
|
||||||
func (self *StateDB) Empty(addr common.Address) bool {
|
func (s *StateDB) Empty(addr common.Address) bool {
|
||||||
so := self.getStateObject(addr)
|
so := s.getStateObject(addr)
|
||||||
return so == nil || so.empty()
|
return so == nil || so.empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the balance from the given address or 0 if object not found
|
// Retrieve the balance from the given address or 0 if object not found
|
||||||
func (self *StateDB) GetBalance(addr common.Address) *big.Int {
|
func (s *StateDB) GetBalance(addr common.Address) *big.Int {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.Balance()
|
return stateObject.Balance()
|
||||||
}
|
}
|
||||||
return common.Big0
|
return common.Big0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
func (s *StateDB) GetNonce(addr common.Address) uint64 {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.Nonce()
|
return stateObject.Nonce()
|
||||||
}
|
}
|
||||||
|
|
@ -241,40 +241,40 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxIndex returns the current transaction index set by Prepare.
|
// TxIndex returns the current transaction index set by Prepare.
|
||||||
func (self *StateDB) TxIndex() int {
|
func (s *StateDB) TxIndex() int {
|
||||||
return self.txIndex
|
return s.txIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockHash returns the current block hash set by Prepare.
|
// BlockHash returns the current block hash set by Prepare.
|
||||||
func (self *StateDB) BlockHash() common.Hash {
|
func (s *StateDB) BlockHash() common.Hash {
|
||||||
return self.bhash
|
return s.bhash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) GetCode(addr common.Address) []byte {
|
func (s *StateDB) GetCode(addr common.Address) []byte {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.Code(self.db)
|
return stateObject.Code(s.db)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) GetCodeSize(addr common.Address) int {
|
func (s *StateDB) GetCodeSize(addr common.Address) int {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject == nil {
|
if stateObject == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if stateObject.code != nil {
|
if stateObject.code != nil {
|
||||||
return len(stateObject.code)
|
return len(stateObject.code)
|
||||||
}
|
}
|
||||||
size, err := self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
|
size, err := s.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.setError(err)
|
s.setError(err)
|
||||||
}
|
}
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject == nil {
|
if stateObject == nil {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
@ -282,25 +282,25 @@ func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetState retrieves a value from the given account's storage trie.
|
// GetState retrieves a value from the given account's storage trie.
|
||||||
func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.GetState(self.db, hash)
|
return stateObject.GetState(s.db, hash)
|
||||||
}
|
}
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProof returns the MerkleProof for a given Account
|
// GetProof returns the MerkleProof for a given Account
|
||||||
func (self *StateDB) GetProof(a common.Address) ([][]byte, error) {
|
func (s *StateDB) GetProof(a common.Address) ([][]byte, error) {
|
||||||
var proof proofList
|
var proof proofList
|
||||||
err := self.trie.Prove(crypto.Keccak256(a.Bytes()), 0, &proof)
|
err := s.trie.Prove(crypto.Keccak256(a.Bytes()), 0, &proof)
|
||||||
return [][]byte(proof), err
|
return [][]byte(proof), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProof returns the StorageProof for given key
|
// GetProof returns the StorageProof for given key
|
||||||
func (self *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
|
func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
|
||||||
var proof proofList
|
var proof proofList
|
||||||
trie := self.StorageTrie(a)
|
trie := s.StorageTrie(a)
|
||||||
if trie == nil {
|
if trie == nil {
|
||||||
return proof, errors.New("storage trie for requested address does not exist")
|
return proof, errors.New("storage trie for requested address does not exist")
|
||||||
}
|
}
|
||||||
|
|
@ -309,32 +309,32 @@ func (self *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byt
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommittedState retrieves a value from the given account's committed storage trie.
|
// GetCommittedState retrieves a value from the given account's committed storage trie.
|
||||||
func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.GetCommittedState(self.db, hash)
|
return stateObject.GetCommittedState(s.db, hash)
|
||||||
}
|
}
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Database retrieves the low level database supporting the lower level trie ops.
|
// Database retrieves the low level database supporting the lower level trie ops.
|
||||||
func (self *StateDB) Database() Database {
|
func (s *StateDB) Database() Database {
|
||||||
return self.db
|
return s.db
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageTrie returns the storage trie of an account.
|
// StorageTrie returns the storage trie of an account.
|
||||||
// The return value is a copy and is nil for non-existent accounts.
|
// The return value is a copy and is nil for non-existent accounts.
|
||||||
func (self *StateDB) StorageTrie(addr common.Address) Trie {
|
func (s *StateDB) StorageTrie(addr common.Address) Trie {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject == nil {
|
if stateObject == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cpy := stateObject.deepCopy(self)
|
cpy := stateObject.deepCopy(s)
|
||||||
return cpy.updateTrie(self.db)
|
return cpy.updateTrie(s.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) HasSuicided(addr common.Address) bool {
|
func (s *StateDB) HasSuicided(addr common.Address) bool {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.suicided
|
return stateObject.suicided
|
||||||
}
|
}
|
||||||
|
|
@ -346,53 +346,53 @@ func (self *StateDB) HasSuicided(addr common.Address) bool {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// AddBalance adds amount to the account associated with addr.
|
// AddBalance adds amount to the account associated with addr.
|
||||||
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.AddBalance(amount)
|
stateObject.AddBalance(amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubBalance subtracts amount from the account associated with addr.
|
// SubBalance subtracts amount from the account associated with addr.
|
||||||
func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
|
func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.SubBalance(amount)
|
stateObject.SubBalance(amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
|
func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.SetBalance(amount)
|
stateObject.SetBalance(amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.SetNonce(nonce)
|
stateObject.SetNonce(nonce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) SetCode(addr common.Address, code []byte) {
|
func (s *StateDB) SetCode(addr common.Address, code []byte) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.SetCode(crypto.Keccak256Hash(code), code)
|
stateObject.SetCode(crypto.Keccak256Hash(code), code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) SetState(addr common.Address, key, value common.Hash) {
|
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.SetState(self.db, key, value)
|
stateObject.SetState(s.db, key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetStorage replaces the entire storage for the specified account with given
|
// SetStorage replaces the entire storage for the specified account with given
|
||||||
// storage. This function should only be used for debugging.
|
// storage. This function should only be used for debugging.
|
||||||
func (self *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) {
|
func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.SetStorage(storage)
|
stateObject.SetStorage(storage)
|
||||||
}
|
}
|
||||||
|
|
@ -403,12 +403,12 @@ func (self *StateDB) SetStorage(addr common.Address, storage map[common.Hash]com
|
||||||
//
|
//
|
||||||
// The account's state object is still available until the state is committed,
|
// The account's state object is still available until the state is committed,
|
||||||
// getStateObject will return a non-nil account after Suicide.
|
// getStateObject will return a non-nil account after Suicide.
|
||||||
func (self *StateDB) Suicide(addr common.Address) bool {
|
func (s *StateDB) Suicide(addr common.Address) bool {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject == nil {
|
if stateObject == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
self.journal.append(suicideChange{
|
s.journal.append(suicideChange{
|
||||||
account: &addr,
|
account: &addr,
|
||||||
prev: stateObject.suicided,
|
prev: stateObject.suicided,
|
||||||
prevbalance: new(big.Int).Set(stateObject.Balance()),
|
prevbalance: new(big.Int).Set(stateObject.Balance()),
|
||||||
|
|
@ -462,7 +462,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
||||||
|
|
||||||
// getDeletedStateObject is similar to getStateObject, but instead of returning
|
// getDeletedStateObject is similar to getStateObject, but instead of returning
|
||||||
// nil for a deleted state object, it returns the actual object with the deleted
|
// nil for a deleted state object, it returns the actual object with the deleted
|
||||||
// flag set. This is needed by the state journal to revert to the correct self-
|
// flag set. This is needed by the state journal to revert to the correct s-
|
||||||
// destructed object instead of wiping all knowledge about the state object.
|
// destructed object instead of wiping all knowledge about the state object.
|
||||||
func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
|
func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
|
||||||
// Prefer live objects if any is available
|
// Prefer live objects if any is available
|
||||||
|
|
@ -490,32 +490,32 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) setStateObject(object *stateObject) {
|
func (s *StateDB) setStateObject(object *stateObject) {
|
||||||
self.stateObjects[object.Address()] = object
|
s.stateObjects[object.Address()] = object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve a state object or create a new state object if nil.
|
// Retrieve a state object or create a new state object if nil.
|
||||||
func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
|
func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
|
||||||
stateObject := self.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject == nil {
|
if stateObject == nil {
|
||||||
stateObject, _ = self.createObject(addr)
|
stateObject, _ = s.createObject(addr)
|
||||||
}
|
}
|
||||||
return stateObject
|
return stateObject
|
||||||
}
|
}
|
||||||
|
|
||||||
// createObject creates a new state object. If there is an existing account with
|
// createObject creates a new state object. If there is an existing account with
|
||||||
// the given address, it is overwritten and returned as the second return value.
|
// the given address, it is overwritten and returned as the second return value.
|
||||||
func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
|
func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
|
||||||
prev = self.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that!
|
prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that!
|
||||||
|
|
||||||
newobj = newObject(self, addr, Account{})
|
newobj = newObject(s, addr, Account{})
|
||||||
newobj.setNonce(0) // sets the object to dirty
|
newobj.setNonce(0) // sets the object to dirty
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
self.journal.append(createObjectChange{account: &addr})
|
s.journal.append(createObjectChange{account: &addr})
|
||||||
} else {
|
} else {
|
||||||
self.journal.append(resetObjectChange{prev: prev})
|
s.journal.append(resetObjectChange{prev: prev})
|
||||||
}
|
}
|
||||||
self.setStateObject(newobj)
|
s.setStateObject(newobj)
|
||||||
return newobj, prev
|
return newobj, prev
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -529,8 +529,8 @@ func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObjec
|
||||||
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
|
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
|
||||||
//
|
//
|
||||||
// Carrying over the balance ensures that Ether doesn't disappear.
|
// Carrying over the balance ensures that Ether doesn't disappear.
|
||||||
func (self *StateDB) CreateAccount(addr common.Address) {
|
func (s *StateDB) CreateAccount(addr common.Address) {
|
||||||
newObj, prev := self.createObject(addr)
|
newObj, prev := s.createObject(addr)
|
||||||
if prev != nil {
|
if prev != nil {
|
||||||
newObj.setBalance(prev.data.Balance)
|
newObj.setBalance(prev.data.Balance)
|
||||||
}
|
}
|
||||||
|
|
@ -567,27 +567,27 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
|
||||||
|
|
||||||
// Copy creates a deep, independent copy of the state.
|
// Copy creates a deep, independent copy of the state.
|
||||||
// Snapshots of the copied state cannot be applied to the copy.
|
// Snapshots of the copied state cannot be applied to the copy.
|
||||||
func (self *StateDB) Copy() *StateDB {
|
func (s *StateDB) Copy() *StateDB {
|
||||||
// Copy all the basic fields, initialize the memory ones
|
// Copy all the basic fields, initialize the memory ones
|
||||||
state := &StateDB{
|
state := &StateDB{
|
||||||
db: self.db,
|
db: s.db,
|
||||||
trie: self.db.CopyTrie(self.trie),
|
trie: s.db.CopyTrie(s.trie),
|
||||||
stateObjects: make(map[common.Address]*stateObject, len(self.journal.dirties)),
|
stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)),
|
||||||
stateObjectsPending: make(map[common.Address]struct{}, len(self.stateObjectsPending)),
|
stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
|
||||||
stateObjectsDirty: make(map[common.Address]struct{}, len(self.journal.dirties)),
|
stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
|
||||||
refund: self.refund,
|
refund: s.refund,
|
||||||
logs: make(map[common.Hash][]*types.Log, len(self.logs)),
|
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
||||||
logSize: self.logSize,
|
logSize: s.logSize,
|
||||||
preimages: make(map[common.Hash][]byte, len(self.preimages)),
|
preimages: make(map[common.Hash][]byte, len(s.preimages)),
|
||||||
journal: newJournal(),
|
journal: newJournal(),
|
||||||
}
|
}
|
||||||
// Copy the dirty states, logs, and preimages
|
// Copy the dirty states, logs, and preimages
|
||||||
for addr := range self.journal.dirties {
|
for addr := range s.journal.dirties {
|
||||||
// As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527),
|
// As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527),
|
||||||
// and in the Finalise-method, there is a case where an object is in the journal but not
|
// and in the Finalise-method, there is a case where an object is in the journal but not
|
||||||
// in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for
|
// in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for
|
||||||
// nil
|
// nil
|
||||||
if object, exist := self.stateObjects[addr]; exist {
|
if object, exist := s.stateObjects[addr]; exist {
|
||||||
// Even though the original object is dirty, we are not copying the journal,
|
// Even though the original object is dirty, we are not copying the journal,
|
||||||
// so we need to make sure that anyside effect the journal would have caused
|
// so we need to make sure that anyside effect the journal would have caused
|
||||||
// during a commit (or similar op) is already applied to the copy.
|
// during a commit (or similar op) is already applied to the copy.
|
||||||
|
|
@ -600,19 +600,19 @@ func (self *StateDB) Copy() *StateDB {
|
||||||
// Above, we don't copy the actual journal. This means that if the copy is copied, the
|
// Above, we don't copy the actual journal. This means that if the copy is copied, the
|
||||||
// loop above will be a no-op, since the copy's journal is empty.
|
// loop above will be a no-op, since the copy's journal is empty.
|
||||||
// Thus, here we iterate over stateObjects, to enable copies of copies
|
// Thus, here we iterate over stateObjects, to enable copies of copies
|
||||||
for addr := range self.stateObjectsPending {
|
for addr := range s.stateObjectsPending {
|
||||||
if _, exist := state.stateObjects[addr]; !exist {
|
if _, exist := state.stateObjects[addr]; !exist {
|
||||||
state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state)
|
state.stateObjects[addr] = s.stateObjects[addr].deepCopy(state)
|
||||||
}
|
}
|
||||||
state.stateObjectsPending[addr] = struct{}{}
|
state.stateObjectsPending[addr] = struct{}{}
|
||||||
}
|
}
|
||||||
for addr := range self.stateObjectsDirty {
|
for addr := range s.stateObjectsDirty {
|
||||||
if _, exist := state.stateObjects[addr]; !exist {
|
if _, exist := state.stateObjects[addr]; !exist {
|
||||||
state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state)
|
state.stateObjects[addr] = s.stateObjects[addr].deepCopy(state)
|
||||||
}
|
}
|
||||||
state.stateObjectsDirty[addr] = struct{}{}
|
state.stateObjectsDirty[addr] = struct{}{}
|
||||||
}
|
}
|
||||||
for hash, logs := range self.logs {
|
for hash, logs := range s.logs {
|
||||||
cpy := make([]*types.Log, len(logs))
|
cpy := make([]*types.Log, len(logs))
|
||||||
for i, l := range logs {
|
for i, l := range logs {
|
||||||
cpy[i] = new(types.Log)
|
cpy[i] = new(types.Log)
|
||||||
|
|
@ -620,42 +620,42 @@ func (self *StateDB) Copy() *StateDB {
|
||||||
}
|
}
|
||||||
state.logs[hash] = cpy
|
state.logs[hash] = cpy
|
||||||
}
|
}
|
||||||
for hash, preimage := range self.preimages {
|
for hash, preimage := range s.preimages {
|
||||||
state.preimages[hash] = preimage
|
state.preimages[hash] = preimage
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshot returns an identifier for the current revision of the state.
|
// Snapshot returns an identifier for the current revision of the state.
|
||||||
func (self *StateDB) Snapshot() int {
|
func (s *StateDB) Snapshot() int {
|
||||||
id := self.nextRevisionId
|
id := s.nextRevisionId
|
||||||
self.nextRevisionId++
|
s.nextRevisionId++
|
||||||
self.validRevisions = append(self.validRevisions, revision{id, self.journal.length()})
|
s.validRevisions = append(s.validRevisions, revision{id, s.journal.length()})
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevertToSnapshot reverts all state changes made since the given revision.
|
// RevertToSnapshot reverts all state changes made since the given revision.
|
||||||
func (self *StateDB) RevertToSnapshot(revid int) {
|
func (s *StateDB) RevertToSnapshot(revid int) {
|
||||||
// Find the snapshot in the stack of valid snapshots.
|
// Find the snapshot in the stack of valid snapshots.
|
||||||
idx := sort.Search(len(self.validRevisions), func(i int) bool {
|
idx := sort.Search(len(s.validRevisions), func(i int) bool {
|
||||||
return self.validRevisions[i].id >= revid
|
return s.validRevisions[i].id >= revid
|
||||||
})
|
})
|
||||||
if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
|
if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid {
|
||||||
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
|
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
|
||||||
}
|
}
|
||||||
snapshot := self.validRevisions[idx].journalIndex
|
snapshot := s.validRevisions[idx].journalIndex
|
||||||
|
|
||||||
// Replay the journal to undo changes and remove invalidated snapshots
|
// Replay the journal to undo changes and remove invalidated snapshots
|
||||||
self.journal.revert(self, snapshot)
|
s.journal.revert(s, snapshot)
|
||||||
self.validRevisions = self.validRevisions[:idx]
|
s.validRevisions = s.validRevisions[:idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRefund returns the current value of the refund counter.
|
// GetRefund returns the current value of the refund counter.
|
||||||
func (self *StateDB) GetRefund() uint64 {
|
func (s *StateDB) GetRefund() uint64 {
|
||||||
return self.refund
|
return s.refund
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalise finalises the state by removing the self destructed objects and clears
|
// Finalise finalises the state by removing the s destructed objects and clears
|
||||||
// the journal as well as the refunds. Finalise, however, will not push any updates
|
// the journal as well as the refunds. Finalise, however, will not push any updates
|
||||||
// into the tries just yet. Only IntermediateRoot or Commit will do that.
|
// into the tries just yet. Only IntermediateRoot or Commit will do that.
|
||||||
func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
||||||
|
|
@ -710,10 +710,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
|
|
||||||
// Prepare sets the current transaction hash and index and block hash which is
|
// Prepare sets the current transaction hash and index and block hash which is
|
||||||
// used when the EVM emits new state logs.
|
// used when the EVM emits new state logs.
|
||||||
func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
|
func (s *StateDB) Prepare(thash, bhash common.Hash, ti int) {
|
||||||
self.thash = thash
|
s.thash = thash
|
||||||
self.bhash = bhash
|
s.bhash = bhash
|
||||||
self.txIndex = ti
|
s.txIndex = ti
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) clearJournalAndRefund() {
|
func (s *StateDB) clearJournalAndRefund() {
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"testing/quick"
|
"testing/quick"
|
||||||
|
|
||||||
"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/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -458,7 +456,8 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateSuite) TestTouchDelete(c *check.C) {
|
func TestTouchDelete(t *testing.T) {
|
||||||
|
s := newStateTest()
|
||||||
s.state.GetOrNewStateObject(common.Address{})
|
s.state.GetOrNewStateObject(common.Address{})
|
||||||
root, _ := s.state.Commit(false)
|
root, _ := s.state.Commit(false)
|
||||||
s.state.Reset(root)
|
s.state.Reset(root)
|
||||||
|
|
@ -467,11 +466,11 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
|
||||||
s.state.AddBalance(common.Address{}, new(big.Int))
|
s.state.AddBalance(common.Address{}, new(big.Int))
|
||||||
|
|
||||||
if len(s.state.journal.dirties) != 1 {
|
if len(s.state.journal.dirties) != 1 {
|
||||||
c.Fatal("expected one dirty state object")
|
t.Fatal("expected one dirty state object")
|
||||||
}
|
}
|
||||||
s.state.RevertToSnapshot(snapshot)
|
s.state.RevertToSnapshot(snapshot)
|
||||||
if len(s.state.journal.dirties) != 0 {
|
if len(s.state.journal.dirties) != 0 {
|
||||||
c.Fatal("expected no dirty state object")
|
t.Fatal("expected no dirty state object")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto/blake2b"
|
"github.com/ethereum/go-ethereum/crypto/blake2b"
|
||||||
"github.com/ethereum/go-ethereum/crypto/bn256"
|
"github.com/ethereum/go-ethereum/crypto/bn256"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
||||||
|
//lint:ignore SA1019 Needed for precompile
|
||||||
"golang.org/x/crypto/ripemd160"
|
"golang.org/x/crypto/ripemd160"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import (
|
||||||
// precompiledTest defines the input/output pairs for precompiled contract tests.
|
// precompiledTest defines the input/output pairs for precompiled contract tests.
|
||||||
type precompiledTest struct {
|
type precompiledTest struct {
|
||||||
input, expected string
|
input, expected string
|
||||||
gas uint64
|
|
||||||
name string
|
name string
|
||||||
noBenchmark bool // Benchmark primarily the worst-cases
|
noBenchmark bool // Benchmark primarily the worst-cases
|
||||||
}
|
}
|
||||||
|
|
@ -418,6 +417,24 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
|
||||||
|
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
|
||||||
|
in := common.Hex2Bytes(test.input)
|
||||||
|
contract := NewContract(AccountRef(common.HexToAddress("1337")),
|
||||||
|
nil, new(big.Int), p.RequiredGas(in)-1)
|
||||||
|
t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) {
|
||||||
|
_, err := RunPrecompiledContract(p, in, contract)
|
||||||
|
if err.Error() != "out of gas" {
|
||||||
|
t.Errorf("Expected error [out of gas], got [%v]", err)
|
||||||
|
}
|
||||||
|
// Verify that the precompile did not touch the input buffer
|
||||||
|
exp := common.Hex2Bytes(test.input)
|
||||||
|
if !bytes.Equal(in, exp) {
|
||||||
|
t.Errorf("Precompiled %v modified input data", addr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
|
func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
|
||||||
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
|
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
|
||||||
in := common.Hex2Bytes(test.input)
|
in := common.Hex2Bytes(test.input)
|
||||||
|
|
@ -541,6 +558,13 @@ func BenchmarkPrecompiledBn256Add(bench *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests OOG
|
||||||
|
func TestPrecompiledModExpOOG(t *testing.T) {
|
||||||
|
for _, test := range modexpTests {
|
||||||
|
testPrecompiledOOG("05", test, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tests the sample inputs from the elliptic curve scalar multiplication EIP 213.
|
// Tests the sample inputs from the elliptic curve scalar multiplication EIP 213.
|
||||||
func TestPrecompiledBn256ScalarMul(t *testing.T) {
|
func TestPrecompiledBn256ScalarMul(t *testing.T) {
|
||||||
for _, test := range bn256ScalarMulTests {
|
for _, test := range bn256ScalarMulTests {
|
||||||
|
|
|
||||||
|
|
@ -74,13 +74,6 @@ func (st *Stack) Back(n int) *big.Int {
|
||||||
return st.data[st.len()-n-1]
|
return st.data[st.len()-n-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) require(n int) error {
|
|
||||||
if st.len() < n {
|
|
||||||
return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print dumps the content of the stack
|
// Print dumps the content of the stack
|
||||||
func (st *Stack) Print() {
|
func (st *Stack) Print() {
|
||||||
fmt.Println("### stack ###")
|
fmt.Println("### stack ###")
|
||||||
|
|
|
||||||
|
|
@ -419,17 +419,17 @@ func New(code string) (*Tracer, error) {
|
||||||
tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
|
tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
|
||||||
|
|
||||||
if !tracer.vm.GetPropString(tracer.tracerObject, "step") {
|
if !tracer.vm.GetPropString(tracer.tracerObject, "step") {
|
||||||
return nil, fmt.Errorf("Trace object must expose a function step()")
|
return nil, fmt.Errorf("trace object must expose a function step()")
|
||||||
}
|
}
|
||||||
tracer.vm.Pop()
|
tracer.vm.Pop()
|
||||||
|
|
||||||
if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
|
if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
|
||||||
return nil, fmt.Errorf("Trace object must expose a function fault()")
|
return nil, fmt.Errorf("trace object must expose a function fault()")
|
||||||
}
|
}
|
||||||
tracer.vm.Pop()
|
tracer.vm.Pop()
|
||||||
|
|
||||||
if !tracer.vm.GetPropString(tracer.tracerObject, "result") {
|
if !tracer.vm.GetPropString(tracer.tracerObject, "result") {
|
||||||
return nil, fmt.Errorf("Trace object must expose a function result()")
|
return nil, fmt.Errorf("trace object must expose a function result()")
|
||||||
}
|
}
|
||||||
tracer.vm.Pop()
|
tracer.vm.Pop()
|
||||||
|
|
||||||
|
|
|
||||||
4
go.mod
4
go.mod
|
|
@ -7,10 +7,11 @@ require (
|
||||||
github.com/Azure/azure-storage-blob-go v0.7.0
|
github.com/Azure/azure-storage-blob-go v0.7.0
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.8.0 // indirect
|
github.com/Azure/go-autorest/autorest/adal v0.8.0 // indirect
|
||||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
||||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156
|
github.com/VictoriaMetrics/fastcache v1.5.3
|
||||||
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847
|
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847
|
||||||
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6
|
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6
|
||||||
github.com/cespare/cp v0.1.0
|
github.com/cespare/cp v0.1.0
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9
|
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea
|
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea
|
||||||
|
|
@ -59,7 +60,6 @@ require (
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f
|
||||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7
|
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7
|
||||||
golang.org/x/text v0.3.2
|
golang.org/x/text v0.3.2
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
|
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
|
||||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772
|
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772
|
||||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||||
|
|
|
||||||
12
go.sum
12
go.sum
|
|
@ -21,8 +21,12 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY
|
||||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
|
||||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
|
github.com/VictoriaMetrics/fastcache v1.5.3 h1:2odJnXLbFZcoV9KYtQ+7TH1UOq3dn3AssMgieaezkR4=
|
||||||
|
github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
|
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
|
||||||
|
|
@ -34,7 +38,12 @@ github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Ly
|
||||||
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
|
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
|
||||||
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
|
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
|
||||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||||
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18 h1:pl4eWIqvFe/Kg3zkn7NxevNzILnZYWDCG7qbA1CJik0=
|
||||||
|
github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18/go.mod h1:HD5P3vAIAh+Y2GAxg0PrPN1P8WkepXGpjbUPDHJqqKM=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9 h1:J82+/8rub3qSy0HxEnoYD8cs+HDlHWYrqYXe2Vqxluk=
|
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9 h1:J82+/8rub3qSy0HxEnoYD8cs+HDlHWYrqYXe2Vqxluk=
|
||||||
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
|
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
|
@ -155,6 +164,8 @@ github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubr
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9 h1:5Cp3cVwpQP4aCQ6jx6dNLP3IarbYiuStmIzYu+BjQwY=
|
||||||
|
github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
|
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
|
||||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
|
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
|
||||||
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
|
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
|
||||||
|
|
@ -163,6 +174,7 @@ github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639
|
||||||
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
|
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs=
|
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs=
|
||||||
|
|
|
||||||
|
|
@ -127,12 +127,12 @@ func (tt *TestCmd) matchExactOutput(want []byte) error {
|
||||||
// Find the mismatch position.
|
// Find the mismatch position.
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
if want[i] != buf[i] {
|
if want[i] != buf[i] {
|
||||||
return fmt.Errorf("Output mismatch at ◊:\n---------------- (stdout text)\n%s◊%s\n---------------- (expected text)\n%s",
|
return fmt.Errorf("output mismatch at ◊:\n---------------- (stdout text)\n%s◊%s\n---------------- (expected text)\n%s",
|
||||||
buf[:i], buf[i:n], want)
|
buf[:i], buf[i:n], want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if n < len(want) {
|
if n < len(want) {
|
||||||
return fmt.Errorf("Not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s",
|
return fmt.Errorf("not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s",
|
||||||
buf, want[:n], want[n:])
|
buf, want[:n], want[n:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -486,7 +486,7 @@ func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (s
|
||||||
case *scwallet.Wallet:
|
case *scwallet.Wallet:
|
||||||
return mnemonic, wallet.Initialize(seed)
|
return mnemonic, wallet.Initialize(seed)
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("Specified wallet does not support initialization")
|
return "", fmt.Errorf("specified wallet does not support initialization")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -501,7 +501,7 @@ func (s *PrivateAccountAPI) Unpair(ctx context.Context, url string, pin string)
|
||||||
case *scwallet.Wallet:
|
case *scwallet.Wallet:
|
||||||
return wallet.Unpair([]byte(pin))
|
return wallet.Unpair([]byte(pin))
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Specified wallet does not support pairing")
|
return fmt.Errorf("specified wallet does not support pairing")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1389,7 +1389,7 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
|
||||||
args.Nonce = (*hexutil.Uint64)(&nonce)
|
args.Nonce = (*hexutil.Uint64)(&nonce)
|
||||||
}
|
}
|
||||||
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
|
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
|
||||||
return errors.New(`Both "data" and "input" are set and not equal. Please use "input" to pass transaction call data.`)
|
return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`)
|
||||||
}
|
}
|
||||||
if args.To == nil {
|
if args.To == nil {
|
||||||
// Contract creation
|
// Contract creation
|
||||||
|
|
@ -1645,7 +1645,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return common.Hash{}, fmt.Errorf("Transaction %#x not found", matchTx.Hash())
|
return common.Hash{}, fmt.Errorf("transaction %#x not found", matchTx.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicDebugAPI is the collection of Ethereum APIs exposed over the public
|
// PublicDebugAPI is the collection of Ethereum APIs exposed over the public
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
// Code generated by go-bindata. DO NOT EDIT.
|
// Package deps Code generated by go-bindata. (@generated) DO NOT EDIT.
|
||||||
// sources:
|
// sources:
|
||||||
// bignumber.js
|
// bignumber.js
|
||||||
// web3.js
|
// web3.js
|
||||||
|
|
||||||
package deps
|
package deps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -20,7 +19,7 @@ import (
|
||||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
@ -28,7 +27,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
|
||||||
clErr := gz.Close()
|
clErr := gz.Close()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||||
}
|
}
|
||||||
if clErr != nil {
|
if clErr != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -49,21 +48,32 @@ type bindataFileInfo struct {
|
||||||
modTime time.Time
|
modTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name return file name
|
||||||
func (fi bindataFileInfo) Name() string {
|
func (fi bindataFileInfo) Name() string {
|
||||||
return fi.name
|
return fi.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size return file size
|
||||||
func (fi bindataFileInfo) Size() int64 {
|
func (fi bindataFileInfo) Size() int64 {
|
||||||
return fi.size
|
return fi.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mode return file mode
|
||||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||||
return fi.mode
|
return fi.mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ModTime return file modify time
|
||||||
func (fi bindataFileInfo) ModTime() time.Time {
|
func (fi bindataFileInfo) ModTime() time.Time {
|
||||||
return fi.modTime
|
return fi.modTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDir return file whether a directory
|
||||||
func (fi bindataFileInfo) IsDir() bool {
|
func (fi bindataFileInfo) IsDir() bool {
|
||||||
return false
|
return fi.mode&os.ModeDir != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sys return file is sys mode
|
||||||
func (fi bindataFileInfo) Sys() interface{} {
|
func (fi bindataFileInfo) Sys() interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -161,8 +171,7 @@ func AssetNames() []string {
|
||||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
var _bindata = map[string]func() (*asset, error){
|
var _bindata = map[string]func() (*asset, error){
|
||||||
"bignumber.js": bignumberJs,
|
"bignumber.js": bignumberJs,
|
||||||
|
"web3.js": web3Js,
|
||||||
"web3.js": web3Js,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetDir returns the file names below a certain
|
// AssetDir returns the file names below a certain
|
||||||
|
|
@ -228,7 +237,11 @@ func RestoreAsset(dir, name string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RestoreAssets restores an asset under the given directory recursively
|
// RestoreAssets restores an asset under the given directory recursively
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,11 @@ const DebugJs = `
|
||||||
web3._extend({
|
web3._extend({
|
||||||
property: 'debug',
|
property: 'debug',
|
||||||
methods: [
|
methods: [
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'accountRange',
|
||||||
|
call: 'debug_accountRange',
|
||||||
|
params: 2
|
||||||
|
}),
|
||||||
new web3._extend.Method({
|
new web3._extend.Method({
|
||||||
name: 'printBlock',
|
name: 'printBlock',
|
||||||
call: 'debug_printBlock',
|
call: 'debug_printBlock',
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ func (api *PrivateLightServerAPI) clientInfo(c *clientInfo, id enode.ID) map[str
|
||||||
info["priority"] = pb != 0
|
info["priority"] = pb != 0
|
||||||
} else {
|
} else {
|
||||||
info["isConnected"] = false
|
info["isConnected"] = false
|
||||||
pb := api.server.clientPool.getPosBalance(id)
|
pb := api.server.clientPool.ndb.getOrNewPB(id)
|
||||||
info["pricing/balance"], info["pricing/balanceMeta"] = pb.value, pb.meta
|
info["pricing/balance"], info["pricing/balanceMeta"] = pb.value, pb.meta
|
||||||
info["priority"] = pb.value != 0
|
info["priority"] = pb.value != 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ func (rm *retrieveManager) retrieve(ctx context.Context, reqID uint64, req *dist
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
sentReq.stop(ctx.Err())
|
sentReq.stop(ctx.Err())
|
||||||
case <-shutdown:
|
case <-shutdown:
|
||||||
sentReq.stop(fmt.Errorf("Client is shutting down"))
|
sentReq.stop(fmt.Errorf("client is shutting down"))
|
||||||
}
|
}
|
||||||
return sentReq.getError()
|
return sentReq.getError()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,51 +54,51 @@ func newLesTxRelay(ps *peerSet, retriever *retrieveManager) *lesTxRelay {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *lesTxRelay) Stop() {
|
func (ltrx *lesTxRelay) Stop() {
|
||||||
close(self.stop)
|
close(ltrx.stop)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *lesTxRelay) registerPeer(p *peer) {
|
func (ltrx *lesTxRelay) registerPeer(p *peer) {
|
||||||
self.lock.Lock()
|
ltrx.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer ltrx.lock.Unlock()
|
||||||
|
|
||||||
self.peerList = self.ps.AllPeers()
|
ltrx.peerList = ltrx.ps.AllPeers()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *lesTxRelay) unregisterPeer(p *peer) {
|
func (ltrx *lesTxRelay) unregisterPeer(p *peer) {
|
||||||
self.lock.Lock()
|
ltrx.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer ltrx.lock.Unlock()
|
||||||
|
|
||||||
self.peerList = self.ps.AllPeers()
|
ltrx.peerList = ltrx.ps.AllPeers()
|
||||||
}
|
}
|
||||||
|
|
||||||
// send sends a list of transactions to at most a given number of peers at
|
// send sends a list of transactions to at most a given number of peers at
|
||||||
// once, never resending any particular transaction to the same peer twice
|
// once, never resending any particular transaction to the same peer twice
|
||||||
func (self *lesTxRelay) send(txs types.Transactions, count int) {
|
func (ltrx *lesTxRelay) send(txs types.Transactions, count int) {
|
||||||
sendTo := make(map[*peer]types.Transactions)
|
sendTo := make(map[*peer]types.Transactions)
|
||||||
|
|
||||||
self.peerStartPos++ // rotate the starting position of the peer list
|
ltrx.peerStartPos++ // rotate the starting position of the peer list
|
||||||
if self.peerStartPos >= len(self.peerList) {
|
if ltrx.peerStartPos >= len(ltrx.peerList) {
|
||||||
self.peerStartPos = 0
|
ltrx.peerStartPos = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
ltr, ok := self.txSent[hash]
|
ltr, ok := ltrx.txSent[hash]
|
||||||
if !ok {
|
if !ok {
|
||||||
ltr = <rInfo{
|
ltr = <rInfo{
|
||||||
tx: tx,
|
tx: tx,
|
||||||
sentTo: make(map[*peer]struct{}),
|
sentTo: make(map[*peer]struct{}),
|
||||||
}
|
}
|
||||||
self.txSent[hash] = ltr
|
ltrx.txSent[hash] = ltr
|
||||||
self.txPending[hash] = struct{}{}
|
ltrx.txPending[hash] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(self.peerList) > 0 {
|
if len(ltrx.peerList) > 0 {
|
||||||
cnt := count
|
cnt := count
|
||||||
pos := self.peerStartPos
|
pos := ltrx.peerStartPos
|
||||||
for {
|
for {
|
||||||
peer := self.peerList[pos]
|
peer := ltrx.peerList[pos]
|
||||||
if _, ok := ltr.sentTo[peer]; !ok {
|
if _, ok := ltr.sentTo[peer]; !ok {
|
||||||
sendTo[peer] = append(sendTo[peer], tx)
|
sendTo[peer] = append(sendTo[peer], tx)
|
||||||
ltr.sentTo[peer] = struct{}{}
|
ltr.sentTo[peer] = struct{}{}
|
||||||
|
|
@ -108,10 +108,10 @@ func (self *lesTxRelay) send(txs types.Transactions, count int) {
|
||||||
break // sent it to the desired number of peers
|
break // sent it to the desired number of peers
|
||||||
}
|
}
|
||||||
pos++
|
pos++
|
||||||
if pos == len(self.peerList) {
|
if pos == len(ltrx.peerList) {
|
||||||
pos = 0
|
pos = 0
|
||||||
}
|
}
|
||||||
if pos == self.peerStartPos {
|
if pos == ltrx.peerStartPos {
|
||||||
break // tried all available peers
|
break // tried all available peers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,46 +139,46 @@ func (self *lesTxRelay) send(txs types.Transactions, count int) {
|
||||||
return func() { peer.SendTxs(reqID, cost, enc) }
|
return func() { peer.SendTxs(reqID, cost, enc) }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
go self.retriever.retrieve(context.Background(), reqID, rq, func(p distPeer, msg *Msg) error { return nil }, self.stop)
|
go ltrx.retriever.retrieve(context.Background(), reqID, rq, func(p distPeer, msg *Msg) error { return nil }, ltrx.stop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *lesTxRelay) Send(txs types.Transactions) {
|
func (ltrx *lesTxRelay) Send(txs types.Transactions) {
|
||||||
self.lock.Lock()
|
ltrx.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer ltrx.lock.Unlock()
|
||||||
|
|
||||||
self.send(txs, 3)
|
ltrx.send(txs, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *lesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
|
func (ltrx *lesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
|
||||||
self.lock.Lock()
|
ltrx.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer ltrx.lock.Unlock()
|
||||||
|
|
||||||
for _, hash := range mined {
|
for _, hash := range mined {
|
||||||
delete(self.txPending, hash)
|
delete(ltrx.txPending, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, hash := range rollback {
|
for _, hash := range rollback {
|
||||||
self.txPending[hash] = struct{}{}
|
ltrx.txPending[hash] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(self.txPending) > 0 {
|
if len(ltrx.txPending) > 0 {
|
||||||
txs := make(types.Transactions, len(self.txPending))
|
txs := make(types.Transactions, len(ltrx.txPending))
|
||||||
i := 0
|
i := 0
|
||||||
for hash := range self.txPending {
|
for hash := range ltrx.txPending {
|
||||||
txs[i] = self.txSent[hash].tx
|
txs[i] = ltrx.txSent[hash].tx
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
self.send(txs, 1)
|
ltrx.send(txs, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *lesTxRelay) Discard(hashes []common.Hash) {
|
func (ltrx *lesTxRelay) Discard(hashes []common.Hash) {
|
||||||
self.lock.Lock()
|
ltrx.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer ltrx.lock.Unlock()
|
||||||
|
|
||||||
for _, hash := range hashes {
|
for _, hash := range hashes {
|
||||||
delete(self.txSent, hash)
|
delete(ltrx.txSent, hash)
|
||||||
delete(self.txPending, hash)
|
delete(ltrx.txPending, hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ func (h *GlogHandler) Log(r *Record) error {
|
||||||
}
|
}
|
||||||
// Check callsite cache for previously calculated log levels
|
// Check callsite cache for previously calculated log levels
|
||||||
h.lock.RLock()
|
h.lock.RLock()
|
||||||
lvl, ok := h.siteCache[r.Call.PC()]
|
lvl, ok := h.siteCache[r.Call.Frame().PC]
|
||||||
h.lock.RUnlock()
|
h.lock.RUnlock()
|
||||||
|
|
||||||
// If we didn't cache the callsite yet, calculate it
|
// If we didn't cache the callsite yet, calculate it
|
||||||
|
|
@ -215,13 +215,13 @@ func (h *GlogHandler) Log(r *Record) error {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
for _, rule := range h.patterns {
|
for _, rule := range h.patterns {
|
||||||
if rule.pattern.MatchString(fmt.Sprintf("%+s", r.Call)) {
|
if rule.pattern.MatchString(fmt.Sprintf("%+s", r.Call)) {
|
||||||
h.siteCache[r.Call.PC()], lvl, ok = rule.level, rule.level, true
|
h.siteCache[r.Call.Frame().PC], lvl, ok = rule.level, rule.level, true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If no rule matched, remember to drop log the next time
|
// If no rule matched, remember to drop log the next time
|
||||||
if !ok {
|
if !ok {
|
||||||
h.siteCache[r.Call.PC()] = 0
|
h.siteCache[r.Call.Frame().PC] = 0
|
||||||
}
|
}
|
||||||
h.lock.Unlock()
|
h.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ func LvlFromString(lvlString string) (Lvl, error) {
|
||||||
case "crit":
|
case "crit":
|
||||||
return LvlCrit, nil
|
return LvlCrit, nil
|
||||||
default:
|
default:
|
||||||
return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString)
|
return LvlDebug, fmt.Errorf("unknown level: %v", lvlString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ func TestCounterClear(t *testing.T) {
|
||||||
c := NewCounter()
|
c := NewCounter()
|
||||||
c.Inc(1)
|
c.Inc(1)
|
||||||
c.Clear()
|
c.Clear()
|
||||||
if count := c.Count(); 0 != count {
|
if count := c.Count(); count != 0 {
|
||||||
t.Errorf("c.Count(): 0 != %v\n", count)
|
t.Errorf("c.Count(): 0 != %v\n", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ func TestCounterClear(t *testing.T) {
|
||||||
func TestCounterDec1(t *testing.T) {
|
func TestCounterDec1(t *testing.T) {
|
||||||
c := NewCounter()
|
c := NewCounter()
|
||||||
c.Dec(1)
|
c.Dec(1)
|
||||||
if count := c.Count(); -1 != count {
|
if count := c.Count(); count != -1 {
|
||||||
t.Errorf("c.Count(): -1 != %v\n", count)
|
t.Errorf("c.Count(): -1 != %v\n", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +30,7 @@ func TestCounterDec1(t *testing.T) {
|
||||||
func TestCounterDec2(t *testing.T) {
|
func TestCounterDec2(t *testing.T) {
|
||||||
c := NewCounter()
|
c := NewCounter()
|
||||||
c.Dec(2)
|
c.Dec(2)
|
||||||
if count := c.Count(); -2 != count {
|
if count := c.Count(); count != -2 {
|
||||||
t.Errorf("c.Count(): -2 != %v\n", count)
|
t.Errorf("c.Count(): -2 != %v\n", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ func TestCounterDec2(t *testing.T) {
|
||||||
func TestCounterInc1(t *testing.T) {
|
func TestCounterInc1(t *testing.T) {
|
||||||
c := NewCounter()
|
c := NewCounter()
|
||||||
c.Inc(1)
|
c.Inc(1)
|
||||||
if count := c.Count(); 1 != count {
|
if count := c.Count(); count != 1 {
|
||||||
t.Errorf("c.Count(): 1 != %v\n", count)
|
t.Errorf("c.Count(): 1 != %v\n", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +46,7 @@ func TestCounterInc1(t *testing.T) {
|
||||||
func TestCounterInc2(t *testing.T) {
|
func TestCounterInc2(t *testing.T) {
|
||||||
c := NewCounter()
|
c := NewCounter()
|
||||||
c.Inc(2)
|
c.Inc(2)
|
||||||
if count := c.Count(); 2 != count {
|
if count := c.Count(); count != 2 {
|
||||||
t.Errorf("c.Count(): 2 != %v\n", count)
|
t.Errorf("c.Count(): 2 != %v\n", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,14 +56,14 @@ func TestCounterSnapshot(t *testing.T) {
|
||||||
c.Inc(1)
|
c.Inc(1)
|
||||||
snapshot := c.Snapshot()
|
snapshot := c.Snapshot()
|
||||||
c.Inc(1)
|
c.Inc(1)
|
||||||
if count := snapshot.Count(); 1 != count {
|
if count := snapshot.Count(); count != 1 {
|
||||||
t.Errorf("c.Count(): 1 != %v\n", count)
|
t.Errorf("c.Count(): 1 != %v\n", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCounterZero(t *testing.T) {
|
func TestCounterZero(t *testing.T) {
|
||||||
c := NewCounter()
|
c := NewCounter()
|
||||||
if count := c.Count(); 0 != count {
|
if count := c.Count(); count != 0 {
|
||||||
t.Errorf("c.Count(): 0 != %v\n", count)
|
t.Errorf("c.Count(): 0 != %v\n", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +71,7 @@ func TestCounterZero(t *testing.T) {
|
||||||
func TestGetOrRegisterCounter(t *testing.T) {
|
func TestGetOrRegisterCounter(t *testing.T) {
|
||||||
r := NewRegistry()
|
r := NewRegistry()
|
||||||
NewRegisteredCounter("foo", r).Inc(47)
|
NewRegisteredCounter("foo", r).Inc(47)
|
||||||
if c := GetOrRegisterCounter("foo", r); 47 != c.Count() {
|
if c := GetOrRegisterCounter("foo", r); c.Count() != 47 {
|
||||||
t.Fatal(c)
|
t.Fatal(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ func TestFunctionalGaugeFloat64(t *testing.T) {
|
||||||
func TestGetOrRegisterFunctionalGaugeFloat64(t *testing.T) {
|
func TestGetOrRegisterFunctionalGaugeFloat64(t *testing.T) {
|
||||||
r := NewRegistry()
|
r := NewRegistry()
|
||||||
NewRegisteredFunctionalGaugeFloat64("foo", r, func() float64 { return 47 })
|
NewRegisteredFunctionalGaugeFloat64("foo", r, func() float64 { return 47 })
|
||||||
if g := GetOrRegisterGaugeFloat64("foo", r); 47 != g.Value() {
|
if g := GetOrRegisterGaugeFloat64("foo", r); g.Value() != 47 {
|
||||||
t.Fatal(g)
|
t.Fatal(g)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ func BenchmarkGuage(b *testing.B) {
|
||||||
func TestGauge(t *testing.T) {
|
func TestGauge(t *testing.T) {
|
||||||
g := NewGauge()
|
g := NewGauge()
|
||||||
g.Update(int64(47))
|
g.Update(int64(47))
|
||||||
if v := g.Value(); 47 != v {
|
if v := g.Value(); v != 47 {
|
||||||
t.Errorf("g.Value(): 47 != %v\n", v)
|
t.Errorf("g.Value(): 47 != %v\n", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -26,7 +26,7 @@ func TestGaugeSnapshot(t *testing.T) {
|
||||||
g.Update(int64(47))
|
g.Update(int64(47))
|
||||||
snapshot := g.Snapshot()
|
snapshot := g.Snapshot()
|
||||||
g.Update(int64(0))
|
g.Update(int64(0))
|
||||||
if v := snapshot.Value(); 47 != v {
|
if v := snapshot.Value(); v != 47 {
|
||||||
t.Errorf("g.Value(): 47 != %v\n", v)
|
t.Errorf("g.Value(): 47 != %v\n", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +34,7 @@ func TestGaugeSnapshot(t *testing.T) {
|
||||||
func TestGetOrRegisterGauge(t *testing.T) {
|
func TestGetOrRegisterGauge(t *testing.T) {
|
||||||
r := NewRegistry()
|
r := NewRegistry()
|
||||||
NewRegisteredGauge("foo", r).Update(47)
|
NewRegisteredGauge("foo", r).Update(47)
|
||||||
if g := GetOrRegisterGauge("foo", r); 47 != g.Value() {
|
if g := GetOrRegisterGauge("foo", r); g.Value() != 47 {
|
||||||
t.Fatal(g)
|
t.Fatal(g)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +55,7 @@ func TestFunctionalGauge(t *testing.T) {
|
||||||
func TestGetOrRegisterFunctionalGauge(t *testing.T) {
|
func TestGetOrRegisterFunctionalGauge(t *testing.T) {
|
||||||
r := NewRegistry()
|
r := NewRegistry()
|
||||||
NewRegisteredFunctionalGauge("foo", r, func() int64 { return 47 })
|
NewRegisteredFunctionalGauge("foo", r, func() int64 { return 47 })
|
||||||
if g := GetOrRegisterGauge("foo", r); 47 != g.Value() {
|
if g := GetOrRegisterGauge("foo", r); g.Value() != 47 {
|
||||||
t.Fatal(g)
|
t.Fatal(g)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ func TestGetOrRegisterHistogram(t *testing.T) {
|
||||||
r := NewRegistry()
|
r := NewRegistry()
|
||||||
s := NewUniformSample(100)
|
s := NewUniformSample(100)
|
||||||
NewRegisteredHistogram("foo", r, s).Update(47)
|
NewRegisteredHistogram("foo", r, s).Update(47)
|
||||||
if h := GetOrRegisterHistogram("foo", r, s); 1 != h.Count() {
|
if h := GetOrRegisterHistogram("foo", r, s); h.Count() != 1 {
|
||||||
t.Fatal(h)
|
t.Fatal(h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -29,29 +29,29 @@ func TestHistogram10000(t *testing.T) {
|
||||||
|
|
||||||
func TestHistogramEmpty(t *testing.T) {
|
func TestHistogramEmpty(t *testing.T) {
|
||||||
h := NewHistogram(NewUniformSample(100))
|
h := NewHistogram(NewUniformSample(100))
|
||||||
if count := h.Count(); 0 != count {
|
if count := h.Count(); count != 0 {
|
||||||
t.Errorf("h.Count(): 0 != %v\n", count)
|
t.Errorf("h.Count(): 0 != %v\n", count)
|
||||||
}
|
}
|
||||||
if min := h.Min(); 0 != min {
|
if min := h.Min(); min != 0 {
|
||||||
t.Errorf("h.Min(): 0 != %v\n", min)
|
t.Errorf("h.Min(): 0 != %v\n", min)
|
||||||
}
|
}
|
||||||
if max := h.Max(); 0 != max {
|
if max := h.Max(); max != 0 {
|
||||||
t.Errorf("h.Max(): 0 != %v\n", max)
|
t.Errorf("h.Max(): 0 != %v\n", max)
|
||||||
}
|
}
|
||||||
if mean := h.Mean(); 0.0 != mean {
|
if mean := h.Mean(); mean != 0.0 {
|
||||||
t.Errorf("h.Mean(): 0.0 != %v\n", mean)
|
t.Errorf("h.Mean(): 0.0 != %v\n", mean)
|
||||||
}
|
}
|
||||||
if stdDev := h.StdDev(); 0.0 != stdDev {
|
if stdDev := h.StdDev(); stdDev != 0.0 {
|
||||||
t.Errorf("h.StdDev(): 0.0 != %v\n", stdDev)
|
t.Errorf("h.StdDev(): 0.0 != %v\n", stdDev)
|
||||||
}
|
}
|
||||||
ps := h.Percentiles([]float64{0.5, 0.75, 0.99})
|
ps := h.Percentiles([]float64{0.5, 0.75, 0.99})
|
||||||
if 0.0 != ps[0] {
|
if ps[0] != 0.0 {
|
||||||
t.Errorf("median: 0.0 != %v\n", ps[0])
|
t.Errorf("median: 0.0 != %v\n", ps[0])
|
||||||
}
|
}
|
||||||
if 0.0 != ps[1] {
|
if ps[1] != 0.0 {
|
||||||
t.Errorf("75th percentile: 0.0 != %v\n", ps[1])
|
t.Errorf("75th percentile: 0.0 != %v\n", ps[1])
|
||||||
}
|
}
|
||||||
if 0.0 != ps[2] {
|
if ps[2] != 0.0 {
|
||||||
t.Errorf("99th percentile: 0.0 != %v\n", ps[2])
|
t.Errorf("99th percentile: 0.0 != %v\n", ps[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -67,29 +67,29 @@ func TestHistogramSnapshot(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testHistogram10000(t *testing.T, h Histogram) {
|
func testHistogram10000(t *testing.T, h Histogram) {
|
||||||
if count := h.Count(); 10000 != count {
|
if count := h.Count(); count != 10000 {
|
||||||
t.Errorf("h.Count(): 10000 != %v\n", count)
|
t.Errorf("h.Count(): 10000 != %v\n", count)
|
||||||
}
|
}
|
||||||
if min := h.Min(); 1 != min {
|
if min := h.Min(); min != 1 {
|
||||||
t.Errorf("h.Min(): 1 != %v\n", min)
|
t.Errorf("h.Min(): 1 != %v\n", min)
|
||||||
}
|
}
|
||||||
if max := h.Max(); 10000 != max {
|
if max := h.Max(); max != 10000 {
|
||||||
t.Errorf("h.Max(): 10000 != %v\n", max)
|
t.Errorf("h.Max(): 10000 != %v\n", max)
|
||||||
}
|
}
|
||||||
if mean := h.Mean(); 5000.5 != mean {
|
if mean := h.Mean(); mean != 5000.5 {
|
||||||
t.Errorf("h.Mean(): 5000.5 != %v\n", mean)
|
t.Errorf("h.Mean(): 5000.5 != %v\n", mean)
|
||||||
}
|
}
|
||||||
if stdDev := h.StdDev(); 2886.751331514372 != stdDev {
|
if stdDev := h.StdDev(); stdDev != 2886.751331514372 {
|
||||||
t.Errorf("h.StdDev(): 2886.751331514372 != %v\n", stdDev)
|
t.Errorf("h.StdDev(): 2886.751331514372 != %v\n", stdDev)
|
||||||
}
|
}
|
||||||
ps := h.Percentiles([]float64{0.5, 0.75, 0.99})
|
ps := h.Percentiles([]float64{0.5, 0.75, 0.99})
|
||||||
if 5000.5 != ps[0] {
|
if ps[0] != 5000.5 {
|
||||||
t.Errorf("median: 5000.5 != %v\n", ps[0])
|
t.Errorf("median: 5000.5 != %v\n", ps[0])
|
||||||
}
|
}
|
||||||
if 7500.75 != ps[1] {
|
if ps[1] != 7500.75 {
|
||||||
t.Errorf("75th percentile: 7500.75 != %v\n", ps[1])
|
t.Errorf("75th percentile: 7500.75 != %v\n", ps[1])
|
||||||
}
|
}
|
||||||
if 9900.99 != ps[2] {
|
if ps[2] != 9900.99 {
|
||||||
t.Errorf("99th percentile: 9900.99 != %v\n", ps[2])
|
t.Errorf("99th percentile: 9900.99 != %v\n", ps[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, userna
|
||||||
func InfluxDBWithTagsOnce(r metrics.Registry, url, database, username, password, namespace string, tags map[string]string) error {
|
func InfluxDBWithTagsOnce(r metrics.Registry, url, database, username, password, namespace string, tags map[string]string) error {
|
||||||
u, err := uurl.Parse(url)
|
u, err := uurl.Parse(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Unable to parse InfluxDB. url: %s, err: %v", url, err)
|
return fmt.Errorf("unable to parse InfluxDB. url: %s, err: %v", url, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rep := &reporter{
|
rep := &reporter{
|
||||||
|
|
@ -76,11 +76,11 @@ func InfluxDBWithTagsOnce(r metrics.Registry, url, database, username, password,
|
||||||
cache: make(map[string]int64),
|
cache: make(map[string]int64),
|
||||||
}
|
}
|
||||||
if err := rep.makeClient(); err != nil {
|
if err := rep.makeClient(); err != nil {
|
||||||
return fmt.Errorf("Unable to make InfluxDB client. err: %v", err)
|
return fmt.Errorf("unable to make InfluxDB client. err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rep.send(); err != nil {
|
if err := rep.send(); err != nil {
|
||||||
return fmt.Errorf("Unable to send to InfluxDB. err: %v", err)
|
return fmt.Errorf("unable to send to InfluxDB. err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ func TestRegistryMarshallJSON(t *testing.T) {
|
||||||
r := NewRegistry()
|
r := NewRegistry()
|
||||||
r.Register("counter", NewCounter())
|
r.Register("counter", NewCounter())
|
||||||
enc.Encode(r)
|
enc.Encode(r)
|
||||||
if s := b.String(); "{\"counter\":{\"count\":0}}\n" != s {
|
if s := b.String(); s != "{\"counter\":{\"count\":0}}\n" {
|
||||||
t.Fatalf(s)
|
t.Fatalf(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func (c *LibratoClient) PostMetrics(batch Batch) (err error) {
|
||||||
if body, err = ioutil.ReadAll(resp.Body); err != nil {
|
if body, err = ioutil.ReadAll(resp.Body); err != nil {
|
||||||
body = []byte(fmt.Sprintf("(could not fetch response body for error: %s)", err))
|
body = []byte(fmt.Sprintf("(could not fetch response body for error: %s)", err))
|
||||||
}
|
}
|
||||||
err = fmt.Errorf("Unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body))
|
err = fmt.Errorf("unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,10 @@ func Librato(r metrics.Registry, d time.Duration, e string, t string, s string,
|
||||||
|
|
||||||
func (rep *Reporter) Run() {
|
func (rep *Reporter) Run() {
|
||||||
log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015")
|
log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015")
|
||||||
ticker := time.Tick(rep.Interval)
|
ticker := time.NewTicker(rep.Interval)
|
||||||
|
defer ticker.Stop()
|
||||||
metricsApi := &LibratoClient{rep.Email, rep.Token}
|
metricsApi := &LibratoClient{rep.Email, rep.Token}
|
||||||
for now := range ticker {
|
for now := range ticker.C {
|
||||||
var metrics Batch
|
var metrics Batch
|
||||||
var err error
|
var err error
|
||||||
if metrics, err = rep.BuildRequest(now, rep.Registry); err != nil {
|
if metrics, err = rep.BuildRequest(now, rep.Registry); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ func BenchmarkMeter(b *testing.B) {
|
||||||
func TestGetOrRegisterMeter(t *testing.T) {
|
func TestGetOrRegisterMeter(t *testing.T) {
|
||||||
r := NewRegistry()
|
r := NewRegistry()
|
||||||
NewRegisteredMeter("foo", r).Mark(47)
|
NewRegisteredMeter("foo", r).Mark(47)
|
||||||
if m := GetOrRegisterMeter("foo", r); 47 != m.Count() {
|
if m := GetOrRegisterMeter("foo", r); m.Count() != 47 {
|
||||||
t.Fatal(m)
|
t.Fatal(m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ func TestMeterDecay(t *testing.T) {
|
||||||
func TestMeterNonzero(t *testing.T) {
|
func TestMeterNonzero(t *testing.T) {
|
||||||
m := NewMeter()
|
m := NewMeter()
|
||||||
m.Mark(3)
|
m.Mark(3)
|
||||||
if count := m.Count(); 3 != count {
|
if count := m.Count(); count != 3 {
|
||||||
t.Errorf("m.Count(): 3 != %v\n", count)
|
t.Errorf("m.Count(): 3 != %v\n", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,11 +48,11 @@ func TestMeterNonzero(t *testing.T) {
|
||||||
func TestMeterStop(t *testing.T) {
|
func TestMeterStop(t *testing.T) {
|
||||||
l := len(arbiter.meters)
|
l := len(arbiter.meters)
|
||||||
m := NewMeter()
|
m := NewMeter()
|
||||||
if len(arbiter.meters) != l+1 {
|
if l+1 != len(arbiter.meters) {
|
||||||
t.Errorf("arbiter.meters: %d != %d\n", l+1, len(arbiter.meters))
|
t.Errorf("arbiter.meters: %d != %d\n", l+1, len(arbiter.meters))
|
||||||
}
|
}
|
||||||
m.Stop()
|
m.Stop()
|
||||||
if len(arbiter.meters) != l {
|
if l != len(arbiter.meters) {
|
||||||
t.Errorf("arbiter.meters: %d != %d\n", l, len(arbiter.meters))
|
t.Errorf("arbiter.meters: %d != %d\n", l, len(arbiter.meters))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +67,7 @@ func TestMeterSnapshot(t *testing.T) {
|
||||||
|
|
||||||
func TestMeterZero(t *testing.T) {
|
func TestMeterZero(t *testing.T) {
|
||||||
m := NewMeter()
|
m := NewMeter()
|
||||||
if count := m.Count(); 0 != count {
|
if count := m.Count(); count != 0 {
|
||||||
t.Errorf("m.Count(): 0 != %v\n", count)
|
t.Errorf("m.Count(): 0 != %v\n", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,20 +19,20 @@ func TestRegistry(t *testing.T) {
|
||||||
i := 0
|
i := 0
|
||||||
r.Each(func(name string, iface interface{}) {
|
r.Each(func(name string, iface interface{}) {
|
||||||
i++
|
i++
|
||||||
if "foo" != name {
|
if name != "foo" {
|
||||||
t.Fatal(name)
|
t.Fatal(name)
|
||||||
}
|
}
|
||||||
if _, ok := iface.(Counter); !ok {
|
if _, ok := iface.(Counter); !ok {
|
||||||
t.Fatal(iface)
|
t.Fatal(iface)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if 1 != i {
|
if i != 1 {
|
||||||
t.Fatal(i)
|
t.Fatal(i)
|
||||||
}
|
}
|
||||||
r.Unregister("foo")
|
r.Unregister("foo")
|
||||||
i = 0
|
i = 0
|
||||||
r.Each(func(string, interface{}) { i++ })
|
r.Each(func(string, interface{}) { i++ })
|
||||||
if 0 != i {
|
if i != 0 {
|
||||||
t.Fatal(i)
|
t.Fatal(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +52,7 @@ func TestRegistryDuplicate(t *testing.T) {
|
||||||
t.Fatal(iface)
|
t.Fatal(iface)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if 1 != i {
|
if i != 1 {
|
||||||
t.Fatal(i)
|
t.Fatal(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,11 +60,11 @@ func TestRegistryDuplicate(t *testing.T) {
|
||||||
func TestRegistryGet(t *testing.T) {
|
func TestRegistryGet(t *testing.T) {
|
||||||
r := NewRegistry()
|
r := NewRegistry()
|
||||||
r.Register("foo", NewCounter())
|
r.Register("foo", NewCounter())
|
||||||
if count := r.Get("foo").(Counter).Count(); 0 != count {
|
if count := r.Get("foo").(Counter).Count(); count != 0 {
|
||||||
t.Fatal(count)
|
t.Fatal(count)
|
||||||
}
|
}
|
||||||
r.Get("foo").(Counter).Inc(1)
|
r.Get("foo").(Counter).Inc(1)
|
||||||
if count := r.Get("foo").(Counter).Count(); 1 != count {
|
if count := r.Get("foo").(Counter).Count(); count != 1 {
|
||||||
t.Fatal(count)
|
t.Fatal(count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -271,6 +271,9 @@ func TestChildPrefixedRegistryOfChildRegister(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
err = r2.Register("baz", NewCounter())
|
err = r2.Register("baz", NewCounter())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
c := NewCounter()
|
c := NewCounter()
|
||||||
Register("bars", c)
|
Register("bars", c)
|
||||||
|
|
||||||
|
|
@ -278,7 +281,7 @@ func TestChildPrefixedRegistryOfChildRegister(t *testing.T) {
|
||||||
r2.Each(func(name string, m interface{}) {
|
r2.Each(func(name string, m interface{}) {
|
||||||
i++
|
i++
|
||||||
if name != "prefix.prefix2.baz" {
|
if name != "prefix.prefix2.baz" {
|
||||||
//t.Fatal(name)
|
t.Fatal(name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if i != 1 {
|
if i != 1 {
|
||||||
|
|
@ -294,11 +297,14 @@ func TestWalkRegistries(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
err = r2.Register("baz", NewCounter())
|
err = r2.Register("baz", NewCounter())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
c := NewCounter()
|
c := NewCounter()
|
||||||
Register("bars", c)
|
Register("bars", c)
|
||||||
|
|
||||||
_, prefix := findPrefix(r2, "")
|
_, prefix := findPrefix(r2, "")
|
||||||
if "prefix.prefix2." != prefix {
|
if prefix != "prefix.prefix2." {
|
||||||
t.Fatal(prefix)
|
t.Fatal(prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,27 +22,27 @@ func TestRuntimeMemStats(t *testing.T) {
|
||||||
zero := runtimeMetrics.MemStats.PauseNs.Count() // Get a "zero" since GC may have run before these tests.
|
zero := runtimeMetrics.MemStats.PauseNs.Count() // Get a "zero" since GC may have run before these tests.
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
CaptureRuntimeMemStatsOnce(r)
|
CaptureRuntimeMemStatsOnce(r)
|
||||||
if count := runtimeMetrics.MemStats.PauseNs.Count(); 1 != count-zero {
|
if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 1 {
|
||||||
t.Fatal(count - zero)
|
t.Fatal(count - zero)
|
||||||
}
|
}
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
CaptureRuntimeMemStatsOnce(r)
|
CaptureRuntimeMemStatsOnce(r)
|
||||||
if count := runtimeMetrics.MemStats.PauseNs.Count(); 3 != count-zero {
|
if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 3 {
|
||||||
t.Fatal(count - zero)
|
t.Fatal(count - zero)
|
||||||
}
|
}
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
}
|
}
|
||||||
CaptureRuntimeMemStatsOnce(r)
|
CaptureRuntimeMemStatsOnce(r)
|
||||||
if count := runtimeMetrics.MemStats.PauseNs.Count(); 259 != count-zero {
|
if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 259 {
|
||||||
t.Fatal(count - zero)
|
t.Fatal(count - zero)
|
||||||
}
|
}
|
||||||
for i := 0; i < 257; i++ {
|
for i := 0; i < 257; i++ {
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
}
|
}
|
||||||
CaptureRuntimeMemStatsOnce(r)
|
CaptureRuntimeMemStatsOnce(r)
|
||||||
if count := runtimeMetrics.MemStats.PauseNs.Count(); 515 != count-zero { // We lost one because there were too many GCs between captures.
|
if count := runtimeMetrics.MemStats.PauseNs.Count(); count-zero != 515 { // We lost one because there were too many GCs between captures.
|
||||||
t.Fatal(count - zero)
|
t.Fatal(count - zero)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ func (NilSample) Variance() float64 { return 0.0 }
|
||||||
|
|
||||||
// SampleMax returns the maximum value of the slice of int64.
|
// SampleMax returns the maximum value of the slice of int64.
|
||||||
func SampleMax(values []int64) int64 {
|
func SampleMax(values []int64) int64 {
|
||||||
if 0 == len(values) {
|
if len(values) == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
var max int64 = math.MinInt64
|
var max int64 = math.MinInt64
|
||||||
|
|
@ -248,7 +248,7 @@ func SampleMax(values []int64) int64 {
|
||||||
|
|
||||||
// SampleMean returns the mean value of the slice of int64.
|
// SampleMean returns the mean value of the slice of int64.
|
||||||
func SampleMean(values []int64) float64 {
|
func SampleMean(values []int64) float64 {
|
||||||
if 0 == len(values) {
|
if len(values) == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
return float64(SampleSum(values)) / float64(len(values))
|
return float64(SampleSum(values)) / float64(len(values))
|
||||||
|
|
@ -256,7 +256,7 @@ func SampleMean(values []int64) float64 {
|
||||||
|
|
||||||
// SampleMin returns the minimum value of the slice of int64.
|
// SampleMin returns the minimum value of the slice of int64.
|
||||||
func SampleMin(values []int64) int64 {
|
func SampleMin(values []int64) int64 {
|
||||||
if 0 == len(values) {
|
if len(values) == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
var min int64 = math.MaxInt64
|
var min int64 = math.MaxInt64
|
||||||
|
|
@ -382,7 +382,7 @@ func SampleSum(values []int64) int64 {
|
||||||
|
|
||||||
// SampleVariance returns the variance of the slice of int64.
|
// SampleVariance returns the variance of the slice of int64.
|
||||||
func SampleVariance(values []int64) float64 {
|
func SampleVariance(values []int64) float64 {
|
||||||
if 0 == len(values) {
|
if len(values) == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
m := SampleMean(values)
|
m := SampleMean(values)
|
||||||
|
|
|
||||||
|
|
@ -85,13 +85,13 @@ func TestExpDecaySample10(t *testing.T) {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
s.Update(int64(i))
|
s.Update(int64(i))
|
||||||
}
|
}
|
||||||
if size := s.Count(); 10 != size {
|
if size := s.Count(); size != 10 {
|
||||||
t.Errorf("s.Count(): 10 != %v\n", size)
|
t.Errorf("s.Count(): 10 != %v\n", size)
|
||||||
}
|
}
|
||||||
if size := s.Size(); 10 != size {
|
if size := s.Size(); size != 10 {
|
||||||
t.Errorf("s.Size(): 10 != %v\n", size)
|
t.Errorf("s.Size(): 10 != %v\n", size)
|
||||||
}
|
}
|
||||||
if l := len(s.Values()); 10 != l {
|
if l := len(s.Values()); l != 10 {
|
||||||
t.Errorf("len(s.Values()): 10 != %v\n", l)
|
t.Errorf("len(s.Values()): 10 != %v\n", l)
|
||||||
}
|
}
|
||||||
for _, v := range s.Values() {
|
for _, v := range s.Values() {
|
||||||
|
|
@ -107,13 +107,13 @@ func TestExpDecaySample100(t *testing.T) {
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
s.Update(int64(i))
|
s.Update(int64(i))
|
||||||
}
|
}
|
||||||
if size := s.Count(); 100 != size {
|
if size := s.Count(); size != 100 {
|
||||||
t.Errorf("s.Count(): 100 != %v\n", size)
|
t.Errorf("s.Count(): 100 != %v\n", size)
|
||||||
}
|
}
|
||||||
if size := s.Size(); 100 != size {
|
if size := s.Size(); size != 100 {
|
||||||
t.Errorf("s.Size(): 100 != %v\n", size)
|
t.Errorf("s.Size(): 100 != %v\n", size)
|
||||||
}
|
}
|
||||||
if l := len(s.Values()); 100 != l {
|
if l := len(s.Values()); l != 100 {
|
||||||
t.Errorf("len(s.Values()): 100 != %v\n", l)
|
t.Errorf("len(s.Values()): 100 != %v\n", l)
|
||||||
}
|
}
|
||||||
for _, v := range s.Values() {
|
for _, v := range s.Values() {
|
||||||
|
|
@ -129,13 +129,13 @@ func TestExpDecaySample1000(t *testing.T) {
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
s.Update(int64(i))
|
s.Update(int64(i))
|
||||||
}
|
}
|
||||||
if size := s.Count(); 1000 != size {
|
if size := s.Count(); size != 1000 {
|
||||||
t.Errorf("s.Count(): 1000 != %v\n", size)
|
t.Errorf("s.Count(): 1000 != %v\n", size)
|
||||||
}
|
}
|
||||||
if size := s.Size(); 100 != size {
|
if size := s.Size(); size != 100 {
|
||||||
t.Errorf("s.Size(): 100 != %v\n", size)
|
t.Errorf("s.Size(): 100 != %v\n", size)
|
||||||
}
|
}
|
||||||
if l := len(s.Values()); 100 != l {
|
if l := len(s.Values()); l != 100 {
|
||||||
t.Errorf("len(s.Values()): 100 != %v\n", l)
|
t.Errorf("len(s.Values()): 100 != %v\n", l)
|
||||||
}
|
}
|
||||||
for _, v := range s.Values() {
|
for _, v := range s.Values() {
|
||||||
|
|
@ -209,13 +209,13 @@ func TestUniformSample(t *testing.T) {
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
s.Update(int64(i))
|
s.Update(int64(i))
|
||||||
}
|
}
|
||||||
if size := s.Count(); 1000 != size {
|
if size := s.Count(); size != 1000 {
|
||||||
t.Errorf("s.Count(): 1000 != %v\n", size)
|
t.Errorf("s.Count(): 1000 != %v\n", size)
|
||||||
}
|
}
|
||||||
if size := s.Size(); 100 != size {
|
if size := s.Size(); size != 100 {
|
||||||
t.Errorf("s.Size(): 100 != %v\n", size)
|
t.Errorf("s.Size(): 100 != %v\n", size)
|
||||||
}
|
}
|
||||||
if l := len(s.Values()); 100 != l {
|
if l := len(s.Values()); l != 100 {
|
||||||
t.Errorf("len(s.Values()): 100 != %v\n", l)
|
t.Errorf("len(s.Values()): 100 != %v\n", l)
|
||||||
}
|
}
|
||||||
for _, v := range s.Values() {
|
for _, v := range s.Values() {
|
||||||
|
|
@ -277,54 +277,54 @@ func benchmarkSample(b *testing.B, s Sample) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testExpDecaySampleStatistics(t *testing.T, s Sample) {
|
func testExpDecaySampleStatistics(t *testing.T, s Sample) {
|
||||||
if count := s.Count(); 10000 != count {
|
if count := s.Count(); count != 10000 {
|
||||||
t.Errorf("s.Count(): 10000 != %v\n", count)
|
t.Errorf("s.Count(): 10000 != %v\n", count)
|
||||||
}
|
}
|
||||||
if min := s.Min(); 107 != min {
|
if min := s.Min(); min != 107 {
|
||||||
t.Errorf("s.Min(): 107 != %v\n", min)
|
t.Errorf("s.Min(): 107 != %v\n", min)
|
||||||
}
|
}
|
||||||
if max := s.Max(); 10000 != max {
|
if max := s.Max(); max != 10000 {
|
||||||
t.Errorf("s.Max(): 10000 != %v\n", max)
|
t.Errorf("s.Max(): 10000 != %v\n", max)
|
||||||
}
|
}
|
||||||
if mean := s.Mean(); 4965.98 != mean {
|
if mean := s.Mean(); mean != 4965.98 {
|
||||||
t.Errorf("s.Mean(): 4965.98 != %v\n", mean)
|
t.Errorf("s.Mean(): 4965.98 != %v\n", mean)
|
||||||
}
|
}
|
||||||
if stdDev := s.StdDev(); 2959.825156930727 != stdDev {
|
if stdDev := s.StdDev(); stdDev != 2959.825156930727 {
|
||||||
t.Errorf("s.StdDev(): 2959.825156930727 != %v\n", stdDev)
|
t.Errorf("s.StdDev(): 2959.825156930727 != %v\n", stdDev)
|
||||||
}
|
}
|
||||||
ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
|
ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
|
||||||
if 4615 != ps[0] {
|
if ps[0] != 4615 {
|
||||||
t.Errorf("median: 4615 != %v\n", ps[0])
|
t.Errorf("median: 4615 != %v\n", ps[0])
|
||||||
}
|
}
|
||||||
if 7672 != ps[1] {
|
if ps[1] != 7672 {
|
||||||
t.Errorf("75th percentile: 7672 != %v\n", ps[1])
|
t.Errorf("75th percentile: 7672 != %v\n", ps[1])
|
||||||
}
|
}
|
||||||
if 9998.99 != ps[2] {
|
if ps[2] != 9998.99 {
|
||||||
t.Errorf("99th percentile: 9998.99 != %v\n", ps[2])
|
t.Errorf("99th percentile: 9998.99 != %v\n", ps[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testUniformSampleStatistics(t *testing.T, s Sample) {
|
func testUniformSampleStatistics(t *testing.T, s Sample) {
|
||||||
if count := s.Count(); 10000 != count {
|
if count := s.Count(); count != 10000 {
|
||||||
t.Errorf("s.Count(): 10000 != %v\n", count)
|
t.Errorf("s.Count(): 10000 != %v\n", count)
|
||||||
}
|
}
|
||||||
if min := s.Min(); 37 != min {
|
if min := s.Min(); min != 37 {
|
||||||
t.Errorf("s.Min(): 37 != %v\n", min)
|
t.Errorf("s.Min(): 37 != %v\n", min)
|
||||||
}
|
}
|
||||||
if max := s.Max(); 9989 != max {
|
if max := s.Max(); max != 9989 {
|
||||||
t.Errorf("s.Max(): 9989 != %v\n", max)
|
t.Errorf("s.Max(): 9989 != %v\n", max)
|
||||||
}
|
}
|
||||||
if mean := s.Mean(); 4748.14 != mean {
|
if mean := s.Mean(); mean != 4748.14 {
|
||||||
t.Errorf("s.Mean(): 4748.14 != %v\n", mean)
|
t.Errorf("s.Mean(): 4748.14 != %v\n", mean)
|
||||||
}
|
}
|
||||||
if stdDev := s.StdDev(); 2826.684117548333 != stdDev {
|
if stdDev := s.StdDev(); stdDev != 2826.684117548333 {
|
||||||
t.Errorf("s.StdDev(): 2826.684117548333 != %v\n", stdDev)
|
t.Errorf("s.StdDev(): 2826.684117548333 != %v\n", stdDev)
|
||||||
}
|
}
|
||||||
ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
|
ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
|
||||||
if 4599 != ps[0] {
|
if ps[0] != 4599 {
|
||||||
t.Errorf("median: 4599 != %v\n", ps[0])
|
t.Errorf("median: 4599 != %v\n", ps[0])
|
||||||
}
|
}
|
||||||
if 7380.5 != ps[1] {
|
if ps[1] != 7380.5 {
|
||||||
t.Errorf("75th percentile: 7380.5 != %v\n", ps[1])
|
t.Errorf("75th percentile: 7380.5 != %v\n", ps[1])
|
||||||
}
|
}
|
||||||
if math.Abs(9986.429999999998-ps[2]) > epsilonPercentile {
|
if math.Abs(9986.429999999998-ps[2]) > epsilonPercentile {
|
||||||
|
|
|
||||||
|
|
@ -76,10 +76,7 @@ func NewTimer() Timer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NilTimer is a no-op Timer.
|
// NilTimer is a no-op Timer.
|
||||||
type NilTimer struct {
|
type NilTimer struct{}
|
||||||
h Histogram
|
|
||||||
m Meter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count is a no-op.
|
// Count is a no-op.
|
||||||
func (NilTimer) Count() int64 { return 0 }
|
func (NilTimer) Count() int64 { return 0 }
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ func BenchmarkTimer(b *testing.B) {
|
||||||
func TestGetOrRegisterTimer(t *testing.T) {
|
func TestGetOrRegisterTimer(t *testing.T) {
|
||||||
r := NewRegistry()
|
r := NewRegistry()
|
||||||
NewRegisteredTimer("foo", r).Update(47)
|
NewRegisteredTimer("foo", r).Update(47)
|
||||||
if tm := GetOrRegisterTimer("foo", r); 1 != tm.Count() {
|
if tm := GetOrRegisterTimer("foo", r); tm.Count() != 1 {
|
||||||
t.Fatal(tm)
|
t.Fatal(tm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +27,7 @@ func TestTimerExtremes(t *testing.T) {
|
||||||
tm := NewTimer()
|
tm := NewTimer()
|
||||||
tm.Update(math.MaxInt64)
|
tm.Update(math.MaxInt64)
|
||||||
tm.Update(0)
|
tm.Update(0)
|
||||||
if stdDev := tm.StdDev(); 4.611686018427388e+18 != stdDev {
|
if stdDev := tm.StdDev(); stdDev != 4.611686018427388e+18 {
|
||||||
t.Errorf("tm.StdDev(): 4.611686018427388e+18 != %v\n", stdDev)
|
t.Errorf("tm.StdDev(): 4.611686018427388e+18 != %v\n", stdDev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -35,11 +35,11 @@ func TestTimerExtremes(t *testing.T) {
|
||||||
func TestTimerStop(t *testing.T) {
|
func TestTimerStop(t *testing.T) {
|
||||||
l := len(arbiter.meters)
|
l := len(arbiter.meters)
|
||||||
tm := NewTimer()
|
tm := NewTimer()
|
||||||
if len(arbiter.meters) != l+1 {
|
if l+1 != len(arbiter.meters) {
|
||||||
t.Errorf("arbiter.meters: %d != %d\n", l+1, len(arbiter.meters))
|
t.Errorf("arbiter.meters: %d != %d\n", l+1, len(arbiter.meters))
|
||||||
}
|
}
|
||||||
tm.Stop()
|
tm.Stop()
|
||||||
if len(arbiter.meters) != l {
|
if l != len(arbiter.meters) {
|
||||||
t.Errorf("arbiter.meters: %d != %d\n", l, len(arbiter.meters))
|
t.Errorf("arbiter.meters: %d != %d\n", l, len(arbiter.meters))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -54,41 +54,41 @@ func TestTimerFunc(t *testing.T) {
|
||||||
|
|
||||||
func TestTimerZero(t *testing.T) {
|
func TestTimerZero(t *testing.T) {
|
||||||
tm := NewTimer()
|
tm := NewTimer()
|
||||||
if count := tm.Count(); 0 != count {
|
if count := tm.Count(); count != 0 {
|
||||||
t.Errorf("tm.Count(): 0 != %v\n", count)
|
t.Errorf("tm.Count(): 0 != %v\n", count)
|
||||||
}
|
}
|
||||||
if min := tm.Min(); 0 != min {
|
if min := tm.Min(); min != 0 {
|
||||||
t.Errorf("tm.Min(): 0 != %v\n", min)
|
t.Errorf("tm.Min(): 0 != %v\n", min)
|
||||||
}
|
}
|
||||||
if max := tm.Max(); 0 != max {
|
if max := tm.Max(); max != 0 {
|
||||||
t.Errorf("tm.Max(): 0 != %v\n", max)
|
t.Errorf("tm.Max(): 0 != %v\n", max)
|
||||||
}
|
}
|
||||||
if mean := tm.Mean(); 0.0 != mean {
|
if mean := tm.Mean(); mean != 0.0 {
|
||||||
t.Errorf("tm.Mean(): 0.0 != %v\n", mean)
|
t.Errorf("tm.Mean(): 0.0 != %v\n", mean)
|
||||||
}
|
}
|
||||||
if stdDev := tm.StdDev(); 0.0 != stdDev {
|
if stdDev := tm.StdDev(); stdDev != 0.0 {
|
||||||
t.Errorf("tm.StdDev(): 0.0 != %v\n", stdDev)
|
t.Errorf("tm.StdDev(): 0.0 != %v\n", stdDev)
|
||||||
}
|
}
|
||||||
ps := tm.Percentiles([]float64{0.5, 0.75, 0.99})
|
ps := tm.Percentiles([]float64{0.5, 0.75, 0.99})
|
||||||
if 0.0 != ps[0] {
|
if ps[0] != 0.0 {
|
||||||
t.Errorf("median: 0.0 != %v\n", ps[0])
|
t.Errorf("median: 0.0 != %v\n", ps[0])
|
||||||
}
|
}
|
||||||
if 0.0 != ps[1] {
|
if ps[1] != 0.0 {
|
||||||
t.Errorf("75th percentile: 0.0 != %v\n", ps[1])
|
t.Errorf("75th percentile: 0.0 != %v\n", ps[1])
|
||||||
}
|
}
|
||||||
if 0.0 != ps[2] {
|
if ps[2] != 0.0 {
|
||||||
t.Errorf("99th percentile: 0.0 != %v\n", ps[2])
|
t.Errorf("99th percentile: 0.0 != %v\n", ps[2])
|
||||||
}
|
}
|
||||||
if rate1 := tm.Rate1(); 0.0 != rate1 {
|
if rate1 := tm.Rate1(); rate1 != 0.0 {
|
||||||
t.Errorf("tm.Rate1(): 0.0 != %v\n", rate1)
|
t.Errorf("tm.Rate1(): 0.0 != %v\n", rate1)
|
||||||
}
|
}
|
||||||
if rate5 := tm.Rate5(); 0.0 != rate5 {
|
if rate5 := tm.Rate5(); rate5 != 0.0 {
|
||||||
t.Errorf("tm.Rate5(): 0.0 != %v\n", rate5)
|
t.Errorf("tm.Rate5(): 0.0 != %v\n", rate5)
|
||||||
}
|
}
|
||||||
if rate15 := tm.Rate15(); 0.0 != rate15 {
|
if rate15 := tm.Rate15(); rate15 != 0.0 {
|
||||||
t.Errorf("tm.Rate15(): 0.0 != %v\n", rate15)
|
t.Errorf("tm.Rate15(): 0.0 != %v\n", rate15)
|
||||||
}
|
}
|
||||||
if rateMean := tm.RateMean(); 0.0 != rateMean {
|
if rateMean := tm.RateMean(); rateMean != 0.0 {
|
||||||
t.Errorf("tm.RateMean(): 0.0 != %v\n", rateMean)
|
t.Errorf("tm.RateMean(): 0.0 != %v\n", rateMean)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
|
||||||
// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
|
// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
|
||||||
// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
|
// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
|
||||||
// and halt your mining operation for as long as the DOS continues.
|
// and halt your mining operation for as long as the DOS continues.
|
||||||
func (self *Miner) update() {
|
func (miner *Miner) update() {
|
||||||
events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
|
events := miner.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
|
||||||
defer events.Unsubscribe()
|
defer events.Unsubscribe()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
@ -96,77 +96,77 @@ func (self *Miner) update() {
|
||||||
}
|
}
|
||||||
switch ev.Data.(type) {
|
switch ev.Data.(type) {
|
||||||
case downloader.StartEvent:
|
case downloader.StartEvent:
|
||||||
atomic.StoreInt32(&self.canStart, 0)
|
atomic.StoreInt32(&miner.canStart, 0)
|
||||||
if self.Mining() {
|
if miner.Mining() {
|
||||||
self.Stop()
|
miner.Stop()
|
||||||
atomic.StoreInt32(&self.shouldStart, 1)
|
atomic.StoreInt32(&miner.shouldStart, 1)
|
||||||
log.Info("Mining aborted due to sync")
|
log.Info("Mining aborted due to sync")
|
||||||
}
|
}
|
||||||
case downloader.DoneEvent, downloader.FailedEvent:
|
case downloader.DoneEvent, downloader.FailedEvent:
|
||||||
shouldStart := atomic.LoadInt32(&self.shouldStart) == 1
|
shouldStart := atomic.LoadInt32(&miner.shouldStart) == 1
|
||||||
|
|
||||||
atomic.StoreInt32(&self.canStart, 1)
|
atomic.StoreInt32(&miner.canStart, 1)
|
||||||
atomic.StoreInt32(&self.shouldStart, 0)
|
atomic.StoreInt32(&miner.shouldStart, 0)
|
||||||
if shouldStart {
|
if shouldStart {
|
||||||
self.Start(self.coinbase)
|
miner.Start(miner.coinbase)
|
||||||
}
|
}
|
||||||
// stop immediately and ignore all further pending events
|
// stop immediately and ignore all further pending events
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case <-self.exitCh:
|
case <-miner.exitCh:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Miner) Start(coinbase common.Address) {
|
func (miner *Miner) Start(coinbase common.Address) {
|
||||||
atomic.StoreInt32(&self.shouldStart, 1)
|
atomic.StoreInt32(&miner.shouldStart, 1)
|
||||||
self.SetEtherbase(coinbase)
|
miner.SetEtherbase(coinbase)
|
||||||
|
|
||||||
if atomic.LoadInt32(&self.canStart) == 0 {
|
if atomic.LoadInt32(&miner.canStart) == 0 {
|
||||||
log.Info("Network syncing, will start miner afterwards")
|
log.Info("Network syncing, will start miner afterwards")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.worker.start()
|
miner.worker.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Miner) Stop() {
|
func (miner *Miner) Stop() {
|
||||||
self.worker.stop()
|
miner.worker.stop()
|
||||||
atomic.StoreInt32(&self.shouldStart, 0)
|
atomic.StoreInt32(&miner.shouldStart, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Miner) Close() {
|
func (miner *Miner) Close() {
|
||||||
self.worker.close()
|
miner.worker.close()
|
||||||
close(self.exitCh)
|
close(miner.exitCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Miner) Mining() bool {
|
func (miner *Miner) Mining() bool {
|
||||||
return self.worker.isRunning()
|
return miner.worker.isRunning()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Miner) HashRate() uint64 {
|
func (miner *Miner) HashRate() uint64 {
|
||||||
if pow, ok := self.engine.(consensus.PoW); ok {
|
if pow, ok := miner.engine.(consensus.PoW); ok {
|
||||||
return uint64(pow.Hashrate())
|
return uint64(pow.Hashrate())
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Miner) SetExtra(extra []byte) error {
|
func (miner *Miner) SetExtra(extra []byte) error {
|
||||||
if uint64(len(extra)) > params.MaximumExtraDataSize {
|
if uint64(len(extra)) > params.MaximumExtraDataSize {
|
||||||
return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize)
|
return fmt.Errorf("extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize)
|
||||||
}
|
}
|
||||||
self.worker.setExtra(extra)
|
miner.worker.setExtra(extra)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRecommitInterval sets the interval for sealing work resubmitting.
|
// SetRecommitInterval sets the interval for sealing work resubmitting.
|
||||||
func (self *Miner) SetRecommitInterval(interval time.Duration) {
|
func (miner *Miner) SetRecommitInterval(interval time.Duration) {
|
||||||
self.worker.setRecommitInterval(interval)
|
miner.worker.setRecommitInterval(interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pending returns the currently pending block and associated state.
|
// Pending returns the currently pending block and associated state.
|
||||||
func (self *Miner) Pending() (*types.Block, *state.StateDB) {
|
func (miner *Miner) Pending() (*types.Block, *state.StateDB) {
|
||||||
return self.worker.pending()
|
return miner.worker.pending()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingBlock returns the currently pending block.
|
// PendingBlock returns the currently pending block.
|
||||||
|
|
@ -174,11 +174,11 @@ func (self *Miner) Pending() (*types.Block, *state.StateDB) {
|
||||||
// Note, to access both the pending block and the pending state
|
// Note, to access both the pending block and the pending state
|
||||||
// simultaneously, please use Pending(), as the pending state can
|
// simultaneously, please use Pending(), as the pending state can
|
||||||
// change between multiple method calls
|
// change between multiple method calls
|
||||||
func (self *Miner) PendingBlock() *types.Block {
|
func (miner *Miner) PendingBlock() *types.Block {
|
||||||
return self.worker.pendingBlock()
|
return miner.worker.pendingBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Miner) SetEtherbase(addr common.Address) {
|
func (miner *Miner) SetEtherbase(addr common.Address) {
|
||||||
self.coinbase = addr
|
miner.coinbase = addr
|
||||||
self.worker.setEtherbase(addr)
|
miner.worker.setEtherbase(addr)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -590,7 +590,7 @@ func (w *worker) resultLoop() {
|
||||||
logs = append(logs, receipt.Logs...)
|
logs = append(logs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
// Commit block and state to database.
|
// Commit block and state to database.
|
||||||
stat, err := w.chain.WriteBlockWithState(block, receipts, task.state)
|
_, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed writing block to chain", "err", err)
|
log.Error("Failed writing block to chain", "err", err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -601,16 +601,6 @@ func (w *worker) resultLoop() {
|
||||||
// Broadcast the block and announce chain insertion event
|
// Broadcast the block and announce chain insertion event
|
||||||
w.mux.Post(core.NewMinedBlockEvent{Block: block})
|
w.mux.Post(core.NewMinedBlockEvent{Block: block})
|
||||||
|
|
||||||
var events []interface{}
|
|
||||||
switch stat {
|
|
||||||
case core.CanonStatTy:
|
|
||||||
events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
|
|
||||||
events = append(events, core.ChainHeadEvent{Block: block})
|
|
||||||
case core.SideStatTy:
|
|
||||||
events = append(events, core.ChainSideEvent{Block: block})
|
|
||||||
}
|
|
||||||
w.chain.PostChainEvents(events, logs)
|
|
||||||
|
|
||||||
// Insert the block into the set of pending ones to resultLoop for confirmations
|
// Insert the block into the set of pending ones to resultLoop for confirmations
|
||||||
w.unconfirmed.Insert(block.NumberU64(), block.Hash())
|
w.unconfirmed.Insert(block.NumberU64(), block.Hash())
|
||||||
|
|
||||||
|
|
@ -996,3 +986,11 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// postSideBlock fires a side chain event, only use it for testing.
|
||||||
|
func (w *worker) postSideBlock(event core.ChainSideEvent) {
|
||||||
|
select {
|
||||||
|
case w.chainSideCh <- event:
|
||||||
|
case <-w.exitCh:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package miner
|
package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -148,9 +149,6 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
|
||||||
|
|
||||||
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
|
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
|
||||||
func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool }
|
func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool }
|
||||||
func (b *testWorkerBackend) PostChainEvents(events []interface{}) {
|
|
||||||
b.chain.PostChainEvents(events, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *testWorkerBackend) newRandomUncle() *types.Block {
|
func (b *testWorkerBackend) newRandomUncle() *types.Block {
|
||||||
var parent *types.Block
|
var parent *types.Block
|
||||||
|
|
@ -217,6 +215,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) {
|
||||||
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil)
|
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
|
loopErr := make(chan error)
|
||||||
newBlock := make(chan struct{})
|
newBlock := make(chan struct{})
|
||||||
listenNewBlock := func() {
|
listenNewBlock := func() {
|
||||||
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
|
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
|
||||||
|
|
@ -226,7 +225,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) {
|
||||||
block := item.Data.(core.NewMinedBlockEvent).Block
|
block := item.Data.(core.NewMinedBlockEvent).Block
|
||||||
_, err := chain.InsertChain([]*types.Block{block})
|
_, err := chain.InsertChain([]*types.Block{block})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to insert new mined block:%d, error:%v", block.NumberU64(), err)
|
loopErr <- fmt.Errorf("failed to insert new mined block:%d, error:%v", block.NumberU64(), err)
|
||||||
}
|
}
|
||||||
newBlock <- struct{}{}
|
newBlock <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
@ -241,9 +240,11 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) {
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
b.txPool.AddLocal(b.newRandomTx(true))
|
b.txPool.AddLocal(b.newRandomTx(true))
|
||||||
b.txPool.AddLocal(b.newRandomTx(false))
|
b.txPool.AddLocal(b.newRandomTx(false))
|
||||||
b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.newRandomUncle()}})
|
w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()})
|
||||||
b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.newRandomUncle()}})
|
w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()})
|
||||||
select {
|
select {
|
||||||
|
case e := <-loopErr:
|
||||||
|
t.Fatal(e)
|
||||||
case <-newBlock:
|
case <-newBlock:
|
||||||
case <-time.NewTimer(3 * time.Second).C: // Worker needs 1s to include new changes.
|
case <-time.NewTimer(3 * time.Second).C: // Worker needs 1s to include new changes.
|
||||||
t.Fatalf("timeout")
|
t.Fatalf("timeout")
|
||||||
|
|
@ -291,7 +292,7 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens
|
||||||
}
|
}
|
||||||
w.skipSealHook = func(task *task) bool { return true }
|
w.skipSealHook = func(task *task) bool { return true }
|
||||||
w.fullTaskHook = func() {
|
w.fullTaskHook = func() {
|
||||||
// Aarch64 unit tests are running in a VM on travis, they must
|
// Arch64 unit tests are running in a VM on travis, they must
|
||||||
// be given more time to execute.
|
// be given more time to execute.
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
|
|
@ -347,7 +348,8 @@ func TestStreamUncleBlock(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.uncleBlock}})
|
w.postSideBlock(core.ChainSideEvent{Block: b.uncleBlock})
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-taskCh:
|
case <-taskCh:
|
||||||
case <-time.NewTimer(time.Second).C:
|
case <-time.NewTimer(time.Second).C:
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package discover
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
)
|
)
|
||||||
|
|
@ -106,6 +107,12 @@ func (it *lookup) startQueries() bool {
|
||||||
it.tab.mutex.Lock()
|
it.tab.mutex.Lock()
|
||||||
closest := it.tab.closest(it.result.target, bucketSize, false)
|
closest := it.tab.closest(it.result.target, bucketSize, false)
|
||||||
it.tab.mutex.Unlock()
|
it.tab.mutex.Unlock()
|
||||||
|
// Avoid finishing the lookup too quickly if table is empty. It'd be better to wait
|
||||||
|
// for the table to fill in this case, but there is no good mechanism for that
|
||||||
|
// yet.
|
||||||
|
if len(closest.entries) == 0 {
|
||||||
|
it.slowdown()
|
||||||
|
}
|
||||||
it.queries = 1
|
it.queries = 1
|
||||||
it.replyCh <- closest.entries
|
it.replyCh <- closest.entries
|
||||||
return true
|
return true
|
||||||
|
|
@ -124,6 +131,15 @@ func (it *lookup) startQueries() bool {
|
||||||
return it.queries > 0
|
return it.queries > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *lookup) slowdown() {
|
||||||
|
sleep := time.NewTimer(1 * time.Second)
|
||||||
|
defer sleep.Stop()
|
||||||
|
select {
|
||||||
|
case <-sleep.C:
|
||||||
|
case <-it.tab.closeReq:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (it *lookup) query(n *node, reply chan<- []*node) {
|
func (it *lookup) query(n *node, reply chan<- []*node) {
|
||||||
fails := it.tab.db.FindFails(n.ID(), n.IP())
|
fails := it.tab.db.FindFails(n.ID(), n.IP())
|
||||||
r, err := it.queryfunc(n)
|
r, err := it.queryfunc(n)
|
||||||
|
|
|
||||||
4
p2p/discv5/README
Normal file
4
p2p/discv5/README
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
This package is an early prototype of Discovery v5. Do not use this code.
|
||||||
|
|
||||||
|
See https://github.com/ethereum/devp2p/blob/master/discv5/discv5.md for the
|
||||||
|
current Discovery v5 specification.
|
||||||
|
|
@ -58,12 +58,11 @@ var (
|
||||||
nodeDBVersionKey = []byte("version") // Version of the database to flush if changes
|
nodeDBVersionKey = []byte("version") // Version of the database to flush if changes
|
||||||
nodeDBItemPrefix = []byte("n:") // Identifier to prefix node entries with
|
nodeDBItemPrefix = []byte("n:") // Identifier to prefix node entries with
|
||||||
|
|
||||||
nodeDBDiscoverRoot = ":discover"
|
nodeDBDiscoverRoot = ":discover"
|
||||||
nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping"
|
nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping"
|
||||||
nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong"
|
nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong"
|
||||||
nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail"
|
nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail"
|
||||||
nodeDBDiscoverLocalEndpoint = nodeDBDiscoverRoot + ":localendpoint"
|
nodeDBTopicRegTickets = ":tickets"
|
||||||
nodeDBTopicRegTickets = ":tickets"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// newNodeDB creates a new node database for storing and retrieving infos about
|
// newNodeDB creates a new node database for storing and retrieving infos about
|
||||||
|
|
@ -311,20 +310,6 @@ func (db *nodeDB) updateFindFails(id NodeID, fails int) error {
|
||||||
return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails))
|
return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails))
|
||||||
}
|
}
|
||||||
|
|
||||||
// localEndpoint returns the last local endpoint communicated to the
|
|
||||||
// given remote node.
|
|
||||||
func (db *nodeDB) localEndpoint(id NodeID) *rpcEndpoint {
|
|
||||||
var ep rpcEndpoint
|
|
||||||
if err := db.fetchRLP(makeKey(id, nodeDBDiscoverLocalEndpoint), &ep); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &ep
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *nodeDB) updateLocalEndpoint(id NodeID, ep rpcEndpoint) error {
|
|
||||||
return db.storeRLP(makeKey(id, nodeDBDiscoverLocalEndpoint), &ep)
|
|
||||||
}
|
|
||||||
|
|
||||||
// querySeeds retrieves random nodes to be used as potential seed nodes
|
// querySeeds retrieves random nodes to be used as potential seed nodes
|
||||||
// for bootstrapping.
|
// for bootstrapping.
|
||||||
func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node {
|
func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node {
|
||||||
|
|
|
||||||
|
|
@ -77,14 +77,6 @@ type Network struct {
|
||||||
nursery []*Node
|
nursery []*Node
|
||||||
nodes map[NodeID]*Node // tracks active nodes with state != known
|
nodes map[NodeID]*Node // tracks active nodes with state != known
|
||||||
timeoutTimers map[timeoutEvent]*time.Timer
|
timeoutTimers map[timeoutEvent]*time.Timer
|
||||||
|
|
||||||
// Revalidation queues.
|
|
||||||
// Nodes put on these queues will be pinged eventually.
|
|
||||||
slowRevalidateQueue []*Node
|
|
||||||
fastRevalidateQueue []*Node
|
|
||||||
|
|
||||||
// Buffers for state transition.
|
|
||||||
sendBuf []*ingressPacket
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// transport is implemented by the UDP transport.
|
// transport is implemented by the UDP transport.
|
||||||
|
|
@ -104,10 +96,9 @@ type transport interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type findnodeQuery struct {
|
type findnodeQuery struct {
|
||||||
remote *Node
|
remote *Node
|
||||||
target common.Hash
|
target common.Hash
|
||||||
reply chan<- []*Node
|
reply chan<- []*Node
|
||||||
nresults int // counter for received nodes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type topicRegisterReq struct {
|
type topicRegisterReq struct {
|
||||||
|
|
@ -650,10 +641,10 @@ loop:
|
||||||
if net.conn != nil {
|
if net.conn != nil {
|
||||||
net.conn.Close()
|
net.conn.Close()
|
||||||
}
|
}
|
||||||
if refreshDone != nil {
|
// TODO: wait for pending refresh.
|
||||||
// TODO: wait for pending refresh.
|
// if refreshDone != nil {
|
||||||
//<-refreshResults
|
// <-refreshResults
|
||||||
}
|
// }
|
||||||
// Cancel all pending timeouts.
|
// Cancel all pending timeouts.
|
||||||
for _, timer := range net.timeoutTimers {
|
for _, timer := range net.timeoutTimers {
|
||||||
timer.Stop()
|
timer.Stop()
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package discv5
|
package discv5
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -265,10 +264,6 @@ type preminedTestnet struct {
|
||||||
net *Network
|
net *Network
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tn *preminedTestnet) sendFindnode(to *Node, target NodeID) {
|
|
||||||
panic("sendFindnode called")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tn *preminedTestnet) sendFindnodeHash(to *Node, target common.Hash) {
|
func (tn *preminedTestnet) sendFindnodeHash(to *Node, target common.Hash) {
|
||||||
// current log distance is encoded in port number
|
// current log distance is encoded in port number
|
||||||
// fmt.Println("findnode query at dist", toaddr.Port)
|
// fmt.Println("findnode query at dist", toaddr.Port)
|
||||||
|
|
@ -316,10 +311,6 @@ func (tn *preminedTestnet) sendNeighbours(to *Node, nodes []*Node) {
|
||||||
panic("sendNeighbours called")
|
panic("sendNeighbours called")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tn *preminedTestnet) sendTopicQuery(to *Node, topic Topic) {
|
|
||||||
panic("sendTopicQuery called")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tn *preminedTestnet) sendTopicNodes(to *Node, queryHash common.Hash, nodes []*Node) {
|
func (tn *preminedTestnet) sendTopicNodes(to *Node, queryHash common.Hash, nodes []*Node) {
|
||||||
panic("sendTopicNodes called")
|
panic("sendTopicNodes called")
|
||||||
}
|
}
|
||||||
|
|
@ -334,41 +325,6 @@ func (*preminedTestnet) localAddr() *net.UDPAddr {
|
||||||
return &net.UDPAddr{IP: net.ParseIP("10.0.1.1"), Port: 40000}
|
return &net.UDPAddr{IP: net.ParseIP("10.0.1.1"), Port: 40000}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mine generates a testnet struct literal with nodes at
|
|
||||||
// various distances to the given target.
|
|
||||||
func (tn *preminedTestnet) mine(target NodeID) {
|
|
||||||
tn.target = target
|
|
||||||
tn.targetSha = crypto.Keccak256Hash(tn.target[:])
|
|
||||||
found := 0
|
|
||||||
for found < bucketSize*10 {
|
|
||||||
k := newkey()
|
|
||||||
id := PubkeyID(&k.PublicKey)
|
|
||||||
sha := crypto.Keccak256Hash(id[:])
|
|
||||||
ld := logdist(tn.targetSha, sha)
|
|
||||||
if len(tn.dists[ld]) < bucketSize {
|
|
||||||
tn.dists[ld] = append(tn.dists[ld], id)
|
|
||||||
fmt.Println("found ID with ld", ld)
|
|
||||||
found++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("&preminedTestnet{")
|
|
||||||
fmt.Printf(" target: %#v,\n", tn.target)
|
|
||||||
fmt.Printf(" targetSha: %#v,\n", tn.targetSha)
|
|
||||||
fmt.Printf(" dists: [%d][]NodeID{\n", len(tn.dists))
|
|
||||||
for ld, ns := range &tn.dists {
|
|
||||||
if len(ns) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Printf(" %d: []NodeID{\n", ld)
|
|
||||||
for _, n := range ns {
|
|
||||||
fmt.Printf(" MustHexID(\"%x\"),\n", n[:])
|
|
||||||
}
|
|
||||||
fmt.Println(" },")
|
|
||||||
}
|
|
||||||
fmt.Println(" },")
|
|
||||||
fmt.Println("}")
|
|
||||||
}
|
|
||||||
|
|
||||||
func injectResponse(net *Network, from *Node, ev nodeEvent, packet interface{}) {
|
func injectResponse(net *Network, from *Node, ev nodeEvent, packet interface{}) {
|
||||||
go net.reqReadPacket(ingressPacket{remoteID: from.ID, remoteAddr: from.addr(), ev: ev, data: packet})
|
go net.reqReadPacket(ingressPacket{remoteID: from.ID, remoteAddr: from.addr(), ev: ev, data: packet})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,23 +66,6 @@ func (n *Node) addr() *net.UDPAddr {
|
||||||
return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)}
|
return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) setAddr(a *net.UDPAddr) {
|
|
||||||
n.IP = a.IP
|
|
||||||
if ipv4 := a.IP.To4(); ipv4 != nil {
|
|
||||||
n.IP = ipv4
|
|
||||||
}
|
|
||||||
n.UDP = uint16(a.Port)
|
|
||||||
}
|
|
||||||
|
|
||||||
// compares the given address against the stored values.
|
|
||||||
func (n *Node) addrEqual(a *net.UDPAddr) bool {
|
|
||||||
ip := a.IP
|
|
||||||
if ipv4 := a.IP.To4(); ipv4 != nil {
|
|
||||||
ip = ipv4
|
|
||||||
}
|
|
||||||
return n.UDP == uint16(a.Port) && n.IP.Equal(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Incomplete returns true for nodes with no IP address.
|
// Incomplete returns true for nodes with no IP address.
|
||||||
func (n *Node) Incomplete() bool {
|
func (n *Node) Incomplete() bool {
|
||||||
return n.IP == nil
|
return n.IP == nil
|
||||||
|
|
@ -326,14 +309,6 @@ func (n NodeID) Pubkey() (*ecdsa.PublicKey, error) {
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (id NodeID) mustPubkey() ecdsa.PublicKey {
|
|
||||||
pk, err := id.Pubkey()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return *pk
|
|
||||||
}
|
|
||||||
|
|
||||||
// recoverNodeID computes the public key used to sign the
|
// recoverNodeID computes the public key used to sign the
|
||||||
// given hash from the signature.
|
// given hash from the signature.
|
||||||
func recoverNodeID(hash, sig []byte) (id NodeID, err error) {
|
func recoverNodeID(hash, sig []byte) (id NodeID, err error) {
|
||||||
|
|
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
// Copyright 2016 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// Contains the NTP time drift detection via the SNTP protocol:
|
|
||||||
// https://tools.ietf.org/html/rfc4330
|
|
||||||
|
|
||||||
package discv5
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ntpPool = "pool.ntp.org" // ntpPool is the NTP server to query for the current time
|
|
||||||
ntpChecks = 3 // Number of measurements to do against the NTP server
|
|
||||||
)
|
|
||||||
|
|
||||||
// durationSlice attaches the methods of sort.Interface to []time.Duration,
|
|
||||||
// sorting in increasing order.
|
|
||||||
type durationSlice []time.Duration
|
|
||||||
|
|
||||||
func (s durationSlice) Len() int { return len(s) }
|
|
||||||
func (s durationSlice) Less(i, j int) bool { return s[i] < s[j] }
|
|
||||||
func (s durationSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
|
|
||||||
// checkClockDrift queries an NTP server for clock drifts and warns the user if
|
|
||||||
// one large enough is detected.
|
|
||||||
func checkClockDrift() {
|
|
||||||
drift, err := sntpDrift(ntpChecks)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if drift < -driftThreshold || drift > driftThreshold {
|
|
||||||
warning := fmt.Sprintf("System clock seems off by %v, which can prevent network connectivity", drift)
|
|
||||||
howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings")
|
|
||||||
separator := strings.Repeat("-", len(warning))
|
|
||||||
|
|
||||||
log.Warn(separator)
|
|
||||||
log.Warn(warning)
|
|
||||||
log.Warn(howtofix)
|
|
||||||
log.Warn(separator)
|
|
||||||
} else {
|
|
||||||
log.Debug(fmt.Sprintf("Sanity NTP check reported %v drift, all ok", drift))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sntpDrift does a naive time resolution against an NTP server and returns the
|
|
||||||
// measured drift. This method uses the simple version of NTP. It's not precise
|
|
||||||
// but should be fine for these purposes.
|
|
||||||
//
|
|
||||||
// Note, it executes two extra measurements compared to the number of requested
|
|
||||||
// ones to be able to discard the two extremes as outliers.
|
|
||||||
func sntpDrift(measurements int) (time.Duration, error) {
|
|
||||||
// Resolve the address of the NTP server
|
|
||||||
addr, err := net.ResolveUDPAddr("udp", ntpPool+":123")
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
// Construct the time request (empty package with only 2 fields set):
|
|
||||||
// Bits 3-5: Protocol version, 3
|
|
||||||
// Bits 6-8: Mode of operation, client, 3
|
|
||||||
request := make([]byte, 48)
|
|
||||||
request[0] = 3<<3 | 3
|
|
||||||
|
|
||||||
// Execute each of the measurements
|
|
||||||
drifts := []time.Duration{}
|
|
||||||
for i := 0; i < measurements+2; i++ {
|
|
||||||
// Dial the NTP server and send the time retrieval request
|
|
||||||
conn, err := net.DialUDP("udp", nil, addr)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
sent := time.Now()
|
|
||||||
if _, err = conn.Write(request); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
// Retrieve the reply and calculate the elapsed time
|
|
||||||
conn.SetDeadline(time.Now().Add(5 * time.Second))
|
|
||||||
|
|
||||||
reply := make([]byte, 48)
|
|
||||||
if _, err = conn.Read(reply); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
elapsed := time.Since(sent)
|
|
||||||
|
|
||||||
// Reconstruct the time from the reply data
|
|
||||||
sec := uint64(reply[43]) | uint64(reply[42])<<8 | uint64(reply[41])<<16 | uint64(reply[40])<<24
|
|
||||||
frac := uint64(reply[47]) | uint64(reply[46])<<8 | uint64(reply[45])<<16 | uint64(reply[44])<<24
|
|
||||||
|
|
||||||
nanosec := sec*1e9 + (frac*1e9)>>32
|
|
||||||
|
|
||||||
t := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nanosec)).Local()
|
|
||||||
|
|
||||||
// Calculate the drift based on an assumed answer time of RRT/2
|
|
||||||
drifts = append(drifts, sent.Sub(t)+elapsed/2)
|
|
||||||
}
|
|
||||||
// Calculate average drif (drop two extremities to avoid outliers)
|
|
||||||
sort.Sort(durationSlice(drifts))
|
|
||||||
|
|
||||||
drift := time.Duration(0)
|
|
||||||
for i := 1; i < len(drifts)-1; i++ {
|
|
||||||
drift += drifts[i]
|
|
||||||
}
|
|
||||||
return drift / time.Duration(measurements), nil
|
|
||||||
}
|
|
||||||
|
|
@ -294,15 +294,6 @@ func (s *simulation) launchNode(log bool) *Network {
|
||||||
return net
|
return net
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *simulation) dropNode(id NodeID) {
|
|
||||||
s.mu.Lock()
|
|
||||||
n := s.nodes[id]
|
|
||||||
delete(s.nodes, id)
|
|
||||||
s.mu.Unlock()
|
|
||||||
|
|
||||||
n.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
type simTransport struct {
|
type simTransport struct {
|
||||||
joinTime time.Time
|
joinTime time.Time
|
||||||
sender NodeID
|
sender NodeID
|
||||||
|
|
@ -358,22 +349,6 @@ func (st *simTransport) sendPing(remote *Node, remoteAddr *net.UDPAddr, topics [
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *simTransport) sendPong(remote *Node, pingHash []byte) {
|
|
||||||
raddr := remote.addr()
|
|
||||||
|
|
||||||
st.sendPacket(remote.ID, ingressPacket{
|
|
||||||
remoteID: st.sender,
|
|
||||||
remoteAddr: st.senderAddr,
|
|
||||||
hash: st.nextHash(),
|
|
||||||
ev: pongPacket,
|
|
||||||
data: &pong{
|
|
||||||
To: rpcEndpoint{IP: raddr.IP, UDP: uint16(raddr.Port), TCP: 30303},
|
|
||||||
ReplyTok: pingHash,
|
|
||||||
Expiration: uint64(time.Now().Unix() + int64(expiration)),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *simTransport) sendFindnodeHash(remote *Node, target common.Hash) {
|
func (st *simTransport) sendFindnodeHash(remote *Node, target common.Hash) {
|
||||||
st.sendPacket(remote.ID, ingressPacket{
|
st.sendPacket(remote.ID, ingressPacket{
|
||||||
remoteID: st.sender,
|
remoteID: st.sender,
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,8 @@
|
||||||
// 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 discv5 implements the RLPx v5 Topic Discovery Protocol.
|
// Package discv5 is a prototype implementation of Discvery v5.
|
||||||
//
|
// Deprecated: do not use this package.
|
||||||
// The Topic Discovery protocol provides a way to find RLPx nodes that
|
|
||||||
// can be connected to. It uses a Kademlia-like protocol to maintain a
|
|
||||||
// distributed database of the IDs and endpoints of all listening
|
|
||||||
// nodes.
|
|
||||||
package discv5
|
package discv5
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -31,72 +31,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type nullTransport struct{}
|
|
||||||
|
|
||||||
func (nullTransport) sendPing(remote *Node, remoteAddr *net.UDPAddr) []byte { return []byte{1} }
|
|
||||||
func (nullTransport) sendPong(remote *Node, pingHash []byte) {}
|
|
||||||
func (nullTransport) sendFindnode(remote *Node, target NodeID) {}
|
|
||||||
func (nullTransport) sendNeighbours(remote *Node, nodes []*Node) {}
|
|
||||||
func (nullTransport) localAddr() *net.UDPAddr { return new(net.UDPAddr) }
|
|
||||||
func (nullTransport) Close() {}
|
|
||||||
|
|
||||||
// func TestTable_pingReplace(t *testing.T) {
|
|
||||||
// doit := func(newNodeIsResponding, lastInBucketIsResponding bool) {
|
|
||||||
// transport := newPingRecorder()
|
|
||||||
// tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{})
|
|
||||||
// defer tab.Close()
|
|
||||||
// pingSender := NewNode(MustHexID("a502af0f59b2aab7746995408c79e9ca312d2793cc997e44fc55eda62f0150bbb8c59a6f9269ba3a081518b62699ee807c7c19c20125ddfccca872608af9e370"), net.IP{}, 99, 99)
|
|
||||||
//
|
|
||||||
// // fill up the sender's bucket.
|
|
||||||
// last := fillBucket(tab, 253)
|
|
||||||
//
|
|
||||||
// // this call to bond should replace the last node
|
|
||||||
// // in its bucket if the node is not responding.
|
|
||||||
// transport.responding[last.ID] = lastInBucketIsResponding
|
|
||||||
// transport.responding[pingSender.ID] = newNodeIsResponding
|
|
||||||
// tab.bond(true, pingSender.ID, &net.UDPAddr{}, 0)
|
|
||||||
//
|
|
||||||
// // first ping goes to sender (bonding pingback)
|
|
||||||
// if !transport.pinged[pingSender.ID] {
|
|
||||||
// t.Error("table did not ping back sender")
|
|
||||||
// }
|
|
||||||
// if newNodeIsResponding {
|
|
||||||
// // second ping goes to oldest node in bucket
|
|
||||||
// // to see whether it is still alive.
|
|
||||||
// if !transport.pinged[last.ID] {
|
|
||||||
// t.Error("table did not ping last node in bucket")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// tab.mutex.Lock()
|
|
||||||
// defer tab.mutex.Unlock()
|
|
||||||
// if l := len(tab.buckets[253].entries); l != bucketSize {
|
|
||||||
// t.Errorf("wrong bucket size after bond: got %d, want %d", l, bucketSize)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if lastInBucketIsResponding || !newNodeIsResponding {
|
|
||||||
// if !contains(tab.buckets[253].entries, last.ID) {
|
|
||||||
// t.Error("last entry was removed")
|
|
||||||
// }
|
|
||||||
// if contains(tab.buckets[253].entries, pingSender.ID) {
|
|
||||||
// t.Error("new entry was added")
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// if contains(tab.buckets[253].entries, last.ID) {
|
|
||||||
// t.Error("last entry was not removed")
|
|
||||||
// }
|
|
||||||
// if !contains(tab.buckets[253].entries, pingSender.ID) {
|
|
||||||
// t.Error("new entry was not added")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// doit(true, true)
|
|
||||||
// doit(false, true)
|
|
||||||
// doit(true, false)
|
|
||||||
// doit(false, false)
|
|
||||||
// }
|
|
||||||
|
|
||||||
func TestBucket_bumpNoDuplicates(t *testing.T) {
|
func TestBucket_bumpNoDuplicates(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
cfg := &quick.Config{
|
cfg := &quick.Config{
|
||||||
|
|
@ -139,17 +73,6 @@ func TestBucket_bumpNoDuplicates(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fillBucket inserts nodes into the given bucket until
|
|
||||||
// it is full. The node's IDs dont correspond to their
|
|
||||||
// hashes.
|
|
||||||
func fillBucket(tab *Table, ld int) (last *Node) {
|
|
||||||
b := tab.buckets[ld]
|
|
||||||
for len(b.entries) < bucketSize {
|
|
||||||
b.entries = append(b.entries, nodeAtDistance(tab.self.sha, ld))
|
|
||||||
}
|
|
||||||
return b.entries[bucketSize-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// nodeAtDistance creates a node for which logdist(base, n.sha) == ld.
|
// nodeAtDistance creates a node for which logdist(base, n.sha) == ld.
|
||||||
// The node's ID does not correspond to n.sha.
|
// The node's ID does not correspond to n.sha.
|
||||||
func nodeAtDistance(base common.Hash, ld int) (n *Node) {
|
func nodeAtDistance(base common.Hash, ld int) (n *Node) {
|
||||||
|
|
@ -159,28 +82,6 @@ func nodeAtDistance(base common.Hash, ld int) (n *Node) {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
type pingRecorder struct{ responding, pinged map[NodeID]bool }
|
|
||||||
|
|
||||||
func newPingRecorder() *pingRecorder {
|
|
||||||
return &pingRecorder{make(map[NodeID]bool), make(map[NodeID]bool)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *pingRecorder) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
|
|
||||||
panic("findnode called on pingRecorder")
|
|
||||||
}
|
|
||||||
func (t *pingRecorder) close() {}
|
|
||||||
func (t *pingRecorder) waitping(from NodeID) error {
|
|
||||||
return nil // remote always pings
|
|
||||||
}
|
|
||||||
func (t *pingRecorder) ping(toid NodeID, toaddr *net.UDPAddr) error {
|
|
||||||
t.pinged[toid] = true
|
|
||||||
if t.responding[toid] {
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return errTimeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTable_closest(t *testing.T) {
|
func TestTable_closest(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sort"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -33,8 +32,6 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ticketTimeBucketLen = time.Minute
|
ticketTimeBucketLen = time.Minute
|
||||||
timeWindow = 10 // * ticketTimeBucketLen
|
|
||||||
wantTicketsInWindow = 10
|
|
||||||
collectFrequency = time.Second * 30
|
collectFrequency = time.Second * 30
|
||||||
registerFrequency = time.Second * 60
|
registerFrequency = time.Second * 60
|
||||||
maxCollectDebt = 10
|
maxCollectDebt = 10
|
||||||
|
|
@ -139,7 +136,6 @@ type ticketStore struct {
|
||||||
|
|
||||||
lastBucketFetched timeBucket
|
lastBucketFetched timeBucket
|
||||||
nextTicketCached *ticketRef
|
nextTicketCached *ticketRef
|
||||||
nextTicketReg mclock.AbsTime
|
|
||||||
|
|
||||||
searchTopicMap map[Topic]searchTopic
|
searchTopicMap map[Topic]searchTopic
|
||||||
nextTopicQueryCleanup mclock.AbsTime
|
nextTopicQueryCleanup mclock.AbsTime
|
||||||
|
|
@ -268,57 +264,6 @@ func (s *ticketStore) nextSearchLookup(topic Topic) lookupInfo {
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
|
|
||||||
// ticketsInWindow returns the tickets of a given topic in the registration window.
|
|
||||||
func (s *ticketStore) ticketsInWindow(topic Topic) []ticketRef {
|
|
||||||
// Sanity check that the topic still exists before operating on it
|
|
||||||
if s.tickets[topic] == nil {
|
|
||||||
log.Warn("Listing non-existing discovery tickets", "topic", topic)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Gather all the tickers in the next time window
|
|
||||||
var tickets []ticketRef
|
|
||||||
|
|
||||||
buckets := s.tickets[topic].buckets
|
|
||||||
for idx := timeBucket(0); idx < timeWindow; idx++ {
|
|
||||||
tickets = append(tickets, buckets[s.lastBucketFetched+idx]...)
|
|
||||||
}
|
|
||||||
log.Trace("Retrieved discovery registration tickets", "topic", topic, "from", s.lastBucketFetched, "tickets", len(tickets))
|
|
||||||
return tickets
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ticketStore) removeExcessTickets(t Topic) {
|
|
||||||
tickets := s.ticketsInWindow(t)
|
|
||||||
if len(tickets) <= wantTicketsInWindow {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sort.Sort(ticketRefByWaitTime(tickets))
|
|
||||||
for _, r := range tickets[wantTicketsInWindow:] {
|
|
||||||
s.removeTicketRef(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ticketRefByWaitTime []ticketRef
|
|
||||||
|
|
||||||
// Len is the number of elements in the collection.
|
|
||||||
func (s ticketRefByWaitTime) Len() int {
|
|
||||||
return len(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref ticketRef) waitTime() mclock.AbsTime {
|
|
||||||
return ref.t.regTime[ref.idx] - ref.t.issueTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// Less reports whether the element with
|
|
||||||
// index i should sort before the element with index j.
|
|
||||||
func (s ticketRefByWaitTime) Less(i, j int) bool {
|
|
||||||
return s[i].waitTime() < s[j].waitTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap swaps the elements with indexes i and j.
|
|
||||||
func (s ticketRefByWaitTime) Swap(i, j int) {
|
|
||||||
s[i], s[j] = s[j], s[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ticketStore) addTicketRef(r ticketRef) {
|
func (s *ticketStore) addTicketRef(r ticketRef) {
|
||||||
topic := r.t.topics[r.idx]
|
topic := r.t.topics[r.idx]
|
||||||
tickets := s.tickets[topic]
|
tickets := s.tickets[topic]
|
||||||
|
|
@ -565,15 +510,6 @@ func (s *ticketStore) addTicket(localTime mclock.AbsTime, pingHash []byte, ticke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ticketStore) getNodeTicket(node *Node) *ticket {
|
|
||||||
if s.nodes[node] == nil {
|
|
||||||
log.Trace("Retrieving node ticket", "node", node.ID, "serial", nil)
|
|
||||||
} else {
|
|
||||||
log.Trace("Retrieving node ticket", "node", node.ID, "serial", s.nodes[node].serial)
|
|
||||||
}
|
|
||||||
return s.nodes[node]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ticketStore) canQueryTopic(node *Node, topic Topic) bool {
|
func (s *ticketStore) canQueryTopic(node *Node, topic Topic) bool {
|
||||||
qq := s.queriesSent[node]
|
qq := s.queriesSent[node]
|
||||||
if qq != nil {
|
if qq != nil {
|
||||||
|
|
@ -770,12 +706,6 @@ func globalRandRead(b []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *topicRadius) isInRadius(addrHash common.Hash) bool {
|
|
||||||
nodePrefix := binary.BigEndian.Uint64(addrHash[0:8])
|
|
||||||
dist := nodePrefix ^ r.topicHashPrefix
|
|
||||||
return dist < r.radius
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *topicRadius) chooseLookupBucket(a, b int) int {
|
func (r *topicRadius) chooseLookupBucket(a, b int) int {
|
||||||
if a < 0 {
|
if a < 0 {
|
||||||
a = 0
|
a = 0
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
@ -38,15 +37,12 @@ const Version = 4
|
||||||
var (
|
var (
|
||||||
errPacketTooSmall = errors.New("too small")
|
errPacketTooSmall = errors.New("too small")
|
||||||
errBadPrefix = errors.New("bad prefix")
|
errBadPrefix = errors.New("bad prefix")
|
||||||
errTimeout = errors.New("RPC timeout")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Timeouts
|
// Timeouts
|
||||||
const (
|
const (
|
||||||
respTimeout = 500 * time.Millisecond
|
respTimeout = 500 * time.Millisecond
|
||||||
expiration = 20 * time.Second
|
expiration = 20 * time.Second
|
||||||
|
|
||||||
driftThreshold = 10 * time.Second // Allowed clock drift before warning user
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RPC request structures
|
// RPC request structures
|
||||||
|
|
@ -187,10 +183,6 @@ func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint {
|
||||||
return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort}
|
return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e1 rpcEndpoint) equal(e2 rpcEndpoint) bool {
|
|
||||||
return e1.UDP == e2.UDP && e1.TCP == e2.TCP && e1.IP.Equal(e2.IP)
|
|
||||||
}
|
|
||||||
|
|
||||||
func nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) {
|
func nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) {
|
||||||
if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil {
|
if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -225,7 +217,6 @@ type udp struct {
|
||||||
conn conn
|
conn conn
|
||||||
priv *ecdsa.PrivateKey
|
priv *ecdsa.PrivateKey
|
||||||
ourEndpoint rpcEndpoint
|
ourEndpoint rpcEndpoint
|
||||||
nat nat.Interface
|
|
||||||
net *Network
|
net *Network
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,13 +265,6 @@ func (t *udp) sendPing(remote *Node, toaddr *net.UDPAddr, topics []Topic) (hash
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *udp) sendFindnode(remote *Node, target NodeID) {
|
|
||||||
t.sendPacket(remote.ID, remote.addr(), byte(findnodePacket), findnode{
|
|
||||||
Target: target,
|
|
||||||
Expiration: uint64(time.Now().Add(expiration).Unix()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *udp) sendNeighbours(remote *Node, results []*Node) {
|
func (t *udp) sendNeighbours(remote *Node, results []*Node) {
|
||||||
// Send neighbors in chunks with at most maxNeighbors per packet
|
// Send neighbors in chunks with at most maxNeighbors per packet
|
||||||
// to stay below the 1280 byte limit.
|
// to stay below the 1280 byte limit.
|
||||||
|
|
|
||||||
|
|
@ -1,450 +0,0 @@
|
||||||
// Copyright 2016 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package discv5
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
spew.Config.DisableMethods = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// shared test variables
|
|
||||||
var (
|
|
||||||
testLocal = rpcEndpoint{IP: net.ParseIP("3.3.3.3").To4(), UDP: 5, TCP: 6}
|
|
||||||
)
|
|
||||||
|
|
||||||
// type udpTest struct {
|
|
||||||
// t *testing.T
|
|
||||||
// pipe *dgramPipe
|
|
||||||
// table *Table
|
|
||||||
// udp *udp
|
|
||||||
// sent [][]byte
|
|
||||||
// localkey, remotekey *ecdsa.PrivateKey
|
|
||||||
// remoteaddr *net.UDPAddr
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func newUDPTest(t *testing.T) *udpTest {
|
|
||||||
// test := &udpTest{
|
|
||||||
// t: t,
|
|
||||||
// pipe: newpipe(),
|
|
||||||
// localkey: newkey(),
|
|
||||||
// remotekey: newkey(),
|
|
||||||
// remoteaddr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 30303},
|
|
||||||
// }
|
|
||||||
// test.table, test.udp, _ = newUDP(test.localkey, test.pipe, nil, "")
|
|
||||||
// return test
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // handles a packet as if it had been sent to the transport.
|
|
||||||
// func (test *udpTest) packetIn(wantError error, ptype byte, data packet) error {
|
|
||||||
// enc, err := encodePacket(test.remotekey, ptype, data)
|
|
||||||
// if err != nil {
|
|
||||||
// return test.errorf("packet (%d) encode error: %v", ptype, err)
|
|
||||||
// }
|
|
||||||
// test.sent = append(test.sent, enc)
|
|
||||||
// if err = test.udp.handlePacket(test.remoteaddr, enc); err != wantError {
|
|
||||||
// return test.errorf("error mismatch: got %q, want %q", err, wantError)
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // waits for a packet to be sent by the transport.
|
|
||||||
// // validate should have type func(*udpTest, X) error, where X is a packet type.
|
|
||||||
// func (test *udpTest) waitPacketOut(validate interface{}) error {
|
|
||||||
// dgram := test.pipe.waitPacketOut()
|
|
||||||
// p, _, _, err := decodePacket(dgram)
|
|
||||||
// if err != nil {
|
|
||||||
// return test.errorf("sent packet decode error: %v", err)
|
|
||||||
// }
|
|
||||||
// fn := reflect.ValueOf(validate)
|
|
||||||
// exptype := fn.Type().In(0)
|
|
||||||
// if reflect.TypeOf(p) != exptype {
|
|
||||||
// return test.errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype)
|
|
||||||
// }
|
|
||||||
// fn.Call([]reflect.Value{reflect.ValueOf(p)})
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func (test *udpTest) errorf(format string, args ...interface{}) error {
|
|
||||||
// _, file, line, ok := runtime.Caller(2) // errorf + waitPacketOut
|
|
||||||
// if ok {
|
|
||||||
// file = filepath.Base(file)
|
|
||||||
// } else {
|
|
||||||
// file = "???"
|
|
||||||
// line = 1
|
|
||||||
// }
|
|
||||||
// err := fmt.Errorf(format, args...)
|
|
||||||
// fmt.Printf("\t%s:%d: %v\n", file, line, err)
|
|
||||||
// test.t.Fail()
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func TestUDP_packetErrors(t *testing.T) {
|
|
||||||
// test := newUDPTest(t)
|
|
||||||
// defer test.table.Close()
|
|
||||||
//
|
|
||||||
// test.packetIn(errExpired, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: Version})
|
|
||||||
// test.packetIn(errUnsolicitedReply, pongPacket, &pong{ReplyTok: []byte{}, Expiration: futureExp})
|
|
||||||
// test.packetIn(errUnknownNode, findnodePacket, &findnode{Expiration: futureExp})
|
|
||||||
// test.packetIn(errUnsolicitedReply, neighborsPacket, &neighbors{Expiration: futureExp})
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func TestUDP_findnode(t *testing.T) {
|
|
||||||
// test := newUDPTest(t)
|
|
||||||
// defer test.table.Close()
|
|
||||||
//
|
|
||||||
// // put a few nodes into the table. their exact
|
|
||||||
// // distribution shouldn't matter much, although we need to
|
|
||||||
// // take care not to overflow any bucket.
|
|
||||||
// targetHash := crypto.Keccak256Hash(testTarget[:])
|
|
||||||
// nodes := &nodesByDistance{target: targetHash}
|
|
||||||
// for i := 0; i < bucketSize; i++ {
|
|
||||||
// nodes.push(nodeAtDistance(test.table.self.sha, i+2), bucketSize)
|
|
||||||
// }
|
|
||||||
// test.table.stuff(nodes.entries)
|
|
||||||
//
|
|
||||||
// // ensure there's a bond with the test node,
|
|
||||||
// // findnode won't be accepted otherwise.
|
|
||||||
// test.table.db.updateNode(NewNode(
|
|
||||||
// PubkeyID(&test.remotekey.PublicKey),
|
|
||||||
// test.remoteaddr.IP,
|
|
||||||
// uint16(test.remoteaddr.Port),
|
|
||||||
// 99,
|
|
||||||
// ))
|
|
||||||
// // check that closest neighbors are returned.
|
|
||||||
// test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp})
|
|
||||||
// expected := test.table.closest(targetHash, bucketSize)
|
|
||||||
//
|
|
||||||
// waitNeighbors := func(want []*Node) {
|
|
||||||
// test.waitPacketOut(func(p *neighbors) {
|
|
||||||
// if len(p.Nodes) != len(want) {
|
|
||||||
// t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize)
|
|
||||||
// }
|
|
||||||
// for i := range p.Nodes {
|
|
||||||
// if p.Nodes[i].ID != want[i].ID {
|
|
||||||
// t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, p.Nodes[i], expected.entries[i])
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// waitNeighbors(expected.entries[:maxNeighbors])
|
|
||||||
// waitNeighbors(expected.entries[maxNeighbors:])
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func TestUDP_findnodeMultiReply(t *testing.T) {
|
|
||||||
// test := newUDPTest(t)
|
|
||||||
// defer test.table.Close()
|
|
||||||
//
|
|
||||||
// // queue a pending findnode request
|
|
||||||
// resultc, errc := make(chan []*Node), make(chan error)
|
|
||||||
// go func() {
|
|
||||||
// rid := PubkeyID(&test.remotekey.PublicKey)
|
|
||||||
// ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
|
|
||||||
// if err != nil && len(ns) == 0 {
|
|
||||||
// errc <- err
|
|
||||||
// } else {
|
|
||||||
// resultc <- ns
|
|
||||||
// }
|
|
||||||
// }()
|
|
||||||
//
|
|
||||||
// // wait for the findnode to be sent.
|
|
||||||
// // after it is sent, the transport is waiting for a reply
|
|
||||||
// test.waitPacketOut(func(p *findnode) {
|
|
||||||
// if p.Target != testTarget {
|
|
||||||
// t.Errorf("wrong target: got %v, want %v", p.Target, testTarget)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// // send the reply as two packets.
|
|
||||||
// list := []*Node{
|
|
||||||
// MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"),
|
|
||||||
// MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"),
|
|
||||||
// MustParseNode("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17"),
|
|
||||||
// MustParseNode("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"),
|
|
||||||
// }
|
|
||||||
// rpclist := make([]rpcNode, len(list))
|
|
||||||
// for i := range list {
|
|
||||||
// rpclist[i] = nodeToRPC(list[i])
|
|
||||||
// }
|
|
||||||
// test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[:2]})
|
|
||||||
// test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[2:]})
|
|
||||||
//
|
|
||||||
// // check that the sent neighbors are all returned by findnode
|
|
||||||
// select {
|
|
||||||
// case result := <-resultc:
|
|
||||||
// if !reflect.DeepEqual(result, list) {
|
|
||||||
// t.Errorf("neighbors mismatch:\n got: %v\n want: %v", result, list)
|
|
||||||
// }
|
|
||||||
// case err := <-errc:
|
|
||||||
// t.Errorf("findnode error: %v", err)
|
|
||||||
// case <-time.After(5 * time.Second):
|
|
||||||
// t.Error("findnode did not return within 5 seconds")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func TestUDP_successfulPing(t *testing.T) {
|
|
||||||
// test := newUDPTest(t)
|
|
||||||
// added := make(chan *Node, 1)
|
|
||||||
// test.table.nodeAddedHook = func(n *Node) { added <- n }
|
|
||||||
// defer test.table.Close()
|
|
||||||
//
|
|
||||||
// // The remote side sends a ping packet to initiate the exchange.
|
|
||||||
// go test.packetIn(nil, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: Version, Expiration: futureExp})
|
|
||||||
//
|
|
||||||
// // the ping is replied to.
|
|
||||||
// test.waitPacketOut(func(p *pong) {
|
|
||||||
// pinghash := test.sent[0][:macSize]
|
|
||||||
// if !bytes.Equal(p.ReplyTok, pinghash) {
|
|
||||||
// t.Errorf("got pong.ReplyTok %x, want %x", p.ReplyTok, pinghash)
|
|
||||||
// }
|
|
||||||
// wantTo := rpcEndpoint{
|
|
||||||
// // The mirrored UDP address is the UDP packet sender
|
|
||||||
// IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port),
|
|
||||||
// // The mirrored TCP port is the one from the ping packet
|
|
||||||
// TCP: testRemote.TCP,
|
|
||||||
// }
|
|
||||||
// if !reflect.DeepEqual(p.To, wantTo) {
|
|
||||||
// t.Errorf("got pong.To %v, want %v", p.To, wantTo)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// // remote is unknown, the table pings back.
|
|
||||||
// test.waitPacketOut(func(p *ping) error {
|
|
||||||
// if !reflect.DeepEqual(p.From, test.udp.ourEndpoint) {
|
|
||||||
// t.Errorf("got ping.From %v, want %v", p.From, test.udp.ourEndpoint)
|
|
||||||
// }
|
|
||||||
// wantTo := rpcEndpoint{
|
|
||||||
// // The mirrored UDP address is the UDP packet sender.
|
|
||||||
// IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port),
|
|
||||||
// TCP: 0,
|
|
||||||
// }
|
|
||||||
// if !reflect.DeepEqual(p.To, wantTo) {
|
|
||||||
// t.Errorf("got ping.To %v, want %v", p.To, wantTo)
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// })
|
|
||||||
// test.packetIn(nil, pongPacket, &pong{Expiration: futureExp})
|
|
||||||
//
|
|
||||||
// // the node should be added to the table shortly after getting the
|
|
||||||
// // pong packet.
|
|
||||||
// select {
|
|
||||||
// case n := <-added:
|
|
||||||
// rid := PubkeyID(&test.remotekey.PublicKey)
|
|
||||||
// if n.ID != rid {
|
|
||||||
// t.Errorf("node has wrong ID: got %v, want %v", n.ID, rid)
|
|
||||||
// }
|
|
||||||
// if !bytes.Equal(n.IP, test.remoteaddr.IP) {
|
|
||||||
// t.Errorf("node has wrong IP: got %v, want: %v", n.IP, test.remoteaddr.IP)
|
|
||||||
// }
|
|
||||||
// if int(n.UDP) != test.remoteaddr.Port {
|
|
||||||
// t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP, test.remoteaddr.Port)
|
|
||||||
// }
|
|
||||||
// if n.TCP != testRemote.TCP {
|
|
||||||
// t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP, testRemote.TCP)
|
|
||||||
// }
|
|
||||||
// case <-time.After(2 * time.Second):
|
|
||||||
// t.Errorf("node was not added within 2 seconds")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
var testPackets = []struct {
|
|
||||||
input string
|
|
||||||
wantPacket interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
input: "71dbda3a79554728d4f94411e42ee1f8b0d561c10e1e5f5893367948c6a7d70bb87b235fa28a77070271b6c164a2dce8c7e13a5739b53b5e96f2e5acb0e458a02902f5965d55ecbeb2ebb6cabb8b2b232896a36b737666c55265ad0a68412f250001ea04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a355",
|
|
||||||
wantPacket: &ping{
|
|
||||||
Version: 4,
|
|
||||||
From: rpcEndpoint{net.ParseIP("127.0.0.1").To4(), 3322, 5544},
|
|
||||||
To: rpcEndpoint{net.ParseIP("::1"), 2222, 3333},
|
|
||||||
Expiration: 1136239445,
|
|
||||||
Rest: []rlp.RawValue{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "e9614ccfd9fc3e74360018522d30e1419a143407ffcce748de3e22116b7e8dc92ff74788c0b6663aaa3d67d641936511c8f8d6ad8698b820a7cf9e1be7155e9a241f556658c55428ec0563514365799a4be2be5a685a80971ddcfa80cb422cdd0101ec04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a3550102",
|
|
||||||
wantPacket: &ping{
|
|
||||||
Version: 4,
|
|
||||||
From: rpcEndpoint{net.ParseIP("127.0.0.1").To4(), 3322, 5544},
|
|
||||||
To: rpcEndpoint{net.ParseIP("::1"), 2222, 3333},
|
|
||||||
Expiration: 1136239445,
|
|
||||||
Rest: []rlp.RawValue{{0x01}, {0x02}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "577be4349c4dd26768081f58de4c6f375a7a22f3f7adda654d1428637412c3d7fe917cadc56d4e5e7ffae1dbe3efffb9849feb71b262de37977e7c7a44e677295680e9e38ab26bee2fcbae207fba3ff3d74069a50b902a82c9903ed37cc993c50001f83e82022bd79020010db83c4d001500000000abcdef12820cfa8215a8d79020010db885a308d313198a2e037073488208ae82823a8443b9a355c5010203040531b9019afde696e582a78fa8d95ea13ce3297d4afb8ba6433e4154caa5ac6431af1b80ba76023fa4090c408f6b4bc3701562c031041d4702971d102c9ab7fa5eed4cd6bab8f7af956f7d565ee1917084a95398b6a21eac920fe3dd1345ec0a7ef39367ee69ddf092cbfe5b93e5e568ebc491983c09c76d922dc3",
|
|
||||||
wantPacket: &ping{
|
|
||||||
Version: 555,
|
|
||||||
From: rpcEndpoint{net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 3322, 5544},
|
|
||||||
To: rpcEndpoint{net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 2222, 33338},
|
|
||||||
Expiration: 1136239445,
|
|
||||||
Rest: []rlp.RawValue{{0xC5, 0x01, 0x02, 0x03, 0x04, 0x05}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "09b2428d83348d27cdf7064ad9024f526cebc19e4958f0fdad87c15eb598dd61d08423e0bf66b2069869e1724125f820d851c136684082774f870e614d95a2855d000f05d1648b2d5945470bc187c2d2216fbe870f43ed0909009882e176a46b0102f846d79020010db885a308d313198a2e037073488208ae82823aa0fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c9548443b9a355c6010203c2040506a0c969a58f6f9095004c0177a6b47f451530cab38966a25cca5cb58f055542124e",
|
|
||||||
wantPacket: &pong{
|
|
||||||
To: rpcEndpoint{net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 2222, 33338},
|
|
||||||
ReplyTok: common.Hex2Bytes("fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c954"),
|
|
||||||
Expiration: 1136239445,
|
|
||||||
Rest: []rlp.RawValue{{0xC6, 0x01, 0x02, 0x03, 0xC2, 0x04, 0x05}, {0x06}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260add7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396",
|
|
||||||
wantPacket: &findnode{
|
|
||||||
Target: MustHexID("ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f"),
|
|
||||||
Expiration: 1136239445,
|
|
||||||
Rest: []rlp.RawValue{{0x82, 0x99, 0x99}, {0x83, 0x99, 0x99, 0x99}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "c679fc8fe0b8b12f06577f2e802d34f6fa257e6137a995f6f4cbfc9ee50ed3710faf6e66f932c4c8d81d64343f429651328758b47d3dbc02c4042f0fff6946a50f4a49037a72bb550f3a7872363a83e1b9ee6469856c24eb4ef80b7535bcf99c0004f9015bf90150f84d846321163782115c82115db8403155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32f84984010203040101b840312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069dbf8599020010db83c4d001500000000abcdef12820d05820d05b84038643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aacf8599020010db885a308d313198a2e037073488203e78203e8b8408dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df738443b9a355010203b525a138aa34383fec3d2719a0",
|
|
||||||
wantPacket: &neighbors{
|
|
||||||
Nodes: []rpcNode{
|
|
||||||
{
|
|
||||||
ID: MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"),
|
|
||||||
IP: net.ParseIP("99.33.22.55").To4(),
|
|
||||||
UDP: 4444,
|
|
||||||
TCP: 4445,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ID: MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"),
|
|
||||||
IP: net.ParseIP("1.2.3.4").To4(),
|
|
||||||
UDP: 1,
|
|
||||||
TCP: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ID: MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"),
|
|
||||||
IP: net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
|
|
||||||
UDP: 3333,
|
|
||||||
TCP: 3333,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ID: MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"),
|
|
||||||
IP: net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"),
|
|
||||||
UDP: 999,
|
|
||||||
TCP: 1000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Expiration: 1136239445,
|
|
||||||
Rest: []rlp.RawValue{{0x01}, {0x02}, {0x03}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestForwardCompatibility(t *testing.T) {
|
|
||||||
t.Skip("skipped while working on discovery v5")
|
|
||||||
|
|
||||||
testkey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
||||||
wantNodeID := PubkeyID(&testkey.PublicKey)
|
|
||||||
|
|
||||||
for _, test := range testPackets {
|
|
||||||
input, err := hex.DecodeString(test.input)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("invalid hex: %s", test.input)
|
|
||||||
}
|
|
||||||
var pkt ingressPacket
|
|
||||||
if err := decodePacket(input, &pkt); err != nil {
|
|
||||||
t.Errorf("did not accept packet %s\n%v", test.input, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(pkt.data, test.wantPacket) {
|
|
||||||
t.Errorf("got %s\nwant %s", spew.Sdump(pkt.data), spew.Sdump(test.wantPacket))
|
|
||||||
}
|
|
||||||
if pkt.remoteID != wantNodeID {
|
|
||||||
t.Errorf("got id %v\nwant id %v", pkt.remoteID, wantNodeID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dgramPipe is a fake UDP socket. It queues all sent datagrams.
|
|
||||||
type dgramPipe struct {
|
|
||||||
mu *sync.Mutex
|
|
||||||
cond *sync.Cond
|
|
||||||
closing chan struct{}
|
|
||||||
closed bool
|
|
||||||
queue [][]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func newpipe() *dgramPipe {
|
|
||||||
mu := new(sync.Mutex)
|
|
||||||
return &dgramPipe{
|
|
||||||
closing: make(chan struct{}),
|
|
||||||
cond: &sync.Cond{L: mu},
|
|
||||||
mu: mu,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteToUDP queues a datagram.
|
|
||||||
func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) {
|
|
||||||
msg := make([]byte, len(b))
|
|
||||||
copy(msg, b)
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
if c.closed {
|
|
||||||
return 0, errors.New("closed")
|
|
||||||
}
|
|
||||||
c.queue = append(c.queue, msg)
|
|
||||||
c.cond.Signal()
|
|
||||||
return len(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadFromUDP just hangs until the pipe is closed.
|
|
||||||
func (c *dgramPipe) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
|
|
||||||
<-c.closing
|
|
||||||
return 0, nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dgramPipe) Close() error {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
if !c.closed {
|
|
||||||
close(c.closing)
|
|
||||||
c.closed = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dgramPipe) LocalAddr() net.Addr {
|
|
||||||
return &net.UDPAddr{IP: testLocal.IP, Port: int(testLocal.UDP)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dgramPipe) waitPacketOut() []byte {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
for len(c.queue) == 0 {
|
|
||||||
c.cond.Wait()
|
|
||||||
}
|
|
||||||
p := c.queue[0]
|
|
||||||
copy(c.queue, c.queue[1:])
|
|
||||||
c.queue = c.queue[:len(c.queue)-1]
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
@ -88,6 +88,8 @@ func (it *sliceIter) Next() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *sliceIter) Node() *Node {
|
func (it *sliceIter) Node() *Node {
|
||||||
|
it.mu.Lock()
|
||||||
|
defer it.mu.Unlock()
|
||||||
if len(it.nodes) == 0 {
|
if len(it.nodes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,10 @@ var (
|
||||||
|
|
||||||
// MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network.
|
// MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network.
|
||||||
MainnetTrustedCheckpoint = &TrustedCheckpoint{
|
MainnetTrustedCheckpoint = &TrustedCheckpoint{
|
||||||
SectionIndex: 270,
|
SectionIndex: 275,
|
||||||
SectionHead: common.HexToHash("0xb67c33d838a60c282c2fb49b188fbbac1ef8565ffb4a1c4909b0a05885e72e40"),
|
SectionHead: common.HexToHash("0x03159234a3699e31d27e5d83a55cbcf8ceb1f2d90855c219c55d79089b61abd4"),
|
||||||
CHTRoot: common.HexToHash("0x781daa4607782300da85d440df3813ba38a1262585231e35e9480726de81dbfc"),
|
CHTRoot: common.HexToHash("0xd0c1f3828a4dcb2ee76625fdbea85afeabfb61c04adf07439d2fc1cf00469f76"),
|
||||||
BloomRoot: common.HexToHash("0xfd8951fa6d779cbc981df40dc31056ed1a549db529349d7dfae016f9d96cae72"),
|
BloomRoot: common.HexToHash("0xab8ea2be8aa24703208fee3fc0afdbb536301013f412a7282b2692d6d68f92c5"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// MainnetCheckpointOracle contains a set of configs for the main network oracle.
|
// MainnetCheckpointOracle contains a set of configs for the main network oracle.
|
||||||
|
|
@ -109,10 +109,10 @@ var (
|
||||||
|
|
||||||
// TestnetTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network.
|
// TestnetTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network.
|
||||||
TestnetTrustedCheckpoint = &TrustedCheckpoint{
|
TestnetTrustedCheckpoint = &TrustedCheckpoint{
|
||||||
SectionIndex: 204,
|
SectionIndex: 209,
|
||||||
SectionHead: common.HexToHash("0xa39168b51c3205456f30ce6a91f3590a43295b15a1c8c2ab86bb8c06b8ad1808"),
|
SectionHead: common.HexToHash("0x8037eb6872b69397d434121424ed8d6ab74be32bf3cb3f12dc5d9657fc146860"),
|
||||||
CHTRoot: common.HexToHash("0x9a3654147b79882bfc4e16fbd3421512aa7e4dfadc6c511923980e0877bdf3b4"),
|
CHTRoot: common.HexToHash("0xe64b7d6324e5cbdcbbc250adf4cf24a639a665aa83ccfd6a0b84a80faaaa0d41"),
|
||||||
BloomRoot: common.HexToHash("0xe72b979522d94fa45c1331639316da234a9bb85062d64d72e13afe1d3f5c17d5"),
|
BloomRoot: common.HexToHash("0x80fedbef680cd70d3dc4b50b14480fba82c74361a35e8dc7be9f11e03077c840"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestnetCheckpointOracle contains a set of configs for the Ropsten test network oracle.
|
// TestnetCheckpointOracle contains a set of configs for the Ropsten test network oracle.
|
||||||
|
|
@ -150,10 +150,10 @@ var (
|
||||||
|
|
||||||
// RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network.
|
// RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network.
|
||||||
RinkebyTrustedCheckpoint = &TrustedCheckpoint{
|
RinkebyTrustedCheckpoint = &TrustedCheckpoint{
|
||||||
SectionIndex: 163,
|
SectionIndex: 168,
|
||||||
SectionHead: common.HexToHash("0x36e5deaa46f258bece94b05d8e10f1ef68f422fb62ed47a2b6e616aa26e84997"),
|
SectionHead: common.HexToHash("0x87301279595b16ac59360c839ef86b159e21fedbfcc8847d727ef446a14cf334"),
|
||||||
CHTRoot: common.HexToHash("0x829b9feca1c2cdf5a4cf3efac554889e438ee4df8718c2ce3e02555a02d9e9e5"),
|
CHTRoot: common.HexToHash("0x00f522dd0705ff647cebdd36707d6779caaf77f5fe8f958aae85f36aa88e3f9c"),
|
||||||
BloomRoot: common.HexToHash("0x58c01de24fdae7c082ebbe7665f189d0aa4d90ee10e72086bf56651c63269e54"),
|
BloomRoot: common.HexToHash("0xc908547a6b01c47c65a4581c68090e5602308d39e893f7c0ae3e16c52ce2abf2"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// RinkebyCheckpointOracle contains a set of configs for the Rinkeby test network oracle.
|
// RinkebyCheckpointOracle contains a set of configs for the Rinkeby test network oracle.
|
||||||
|
|
@ -189,10 +189,10 @@ var (
|
||||||
|
|
||||||
// GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network.
|
// GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network.
|
||||||
GoerliTrustedCheckpoint = &TrustedCheckpoint{
|
GoerliTrustedCheckpoint = &TrustedCheckpoint{
|
||||||
SectionIndex: 47,
|
SectionIndex: 52,
|
||||||
SectionHead: common.HexToHash("0x00c5b54c6c9a73660501fd9273ccdb4c5bbdbe5d7b8b650e28f881ec9d2337f6"),
|
SectionHead: common.HexToHash("0x64c3bbc896578cbf782e343db48e334177e87fb8b16106b75e1dcebf59ca59dc"),
|
||||||
CHTRoot: common.HexToHash("0xef35caa155fd659f57167e7d507de2f8132cbb31f771526481211d8a977d704c"),
|
CHTRoot: common.HexToHash("0x5d092e644f3815de40b8c4196698d3e34a9097cf3066a499c96e83e3927d8b8d"),
|
||||||
BloomRoot: common.HexToHash("0xbda330402f66008d52e7adc748da28535b1212a7912a21244acd2ba77ff0ff06"),
|
BloomRoot: common.HexToHash("0xb2ceb966b499dd9e6e5bf6adbf35440a0e15cbccc0f527f89a1c522a9f36250a"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoerliCheckpointOracle contains a set of configs for the Goerli test network oracle.
|
// GoerliCheckpointOracle contains a set of configs for the Goerli test network oracle.
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 1 // Major version component of the current release
|
VersionMajor = 1 // Major version component of the current release
|
||||||
VersionMinor = 9 // Minor version component of the current release
|
VersionMinor = 9 // Minor version component of the current release
|
||||||
VersionPatch = 8 // Patch version component of the current release
|
VersionPatch = 9 // Patch version component of the current release
|
||||||
VersionMeta = "unstable" // Version metadata to append to the version string
|
VersionMeta = "unstable" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,13 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
//lint:ignore ST1012 EOL is not an error.
|
||||||
// EOL is returned when the end of the current list
|
|
||||||
// has been reached during streaming.
|
|
||||||
EOL = errors.New("rlp: end of list")
|
|
||||||
|
|
||||||
// Actual Errors
|
// EOL is returned when the end of the current list
|
||||||
|
// has been reached during streaming.
|
||||||
|
var EOL = errors.New("rlp: end of list")
|
||||||
|
|
||||||
|
var (
|
||||||
ErrExpectedString = errors.New("rlp: expected String or Byte")
|
ErrExpectedString = errors.New("rlp: expected String or Byte")
|
||||||
ErrExpectedList = errors.New("rlp: expected List")
|
ErrExpectedList = errors.New("rlp: expected List")
|
||||||
ErrCanonInt = errors.New("rlp: non-canonical integer format")
|
ErrCanonInt = errors.New("rlp: non-canonical integer format")
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,7 @@ type tailUint struct {
|
||||||
type tailPrivateFields struct {
|
type tailPrivateFields struct {
|
||||||
A uint
|
A uint
|
||||||
Tail []uint `rlp:"tail"`
|
Tail []uint `rlp:"tail"`
|
||||||
x, y bool
|
x, y bool //lint:ignore U1000 unused fields required for testing purposes.
|
||||||
}
|
}
|
||||||
|
|
||||||
type nilListUint struct {
|
type nilListUint struct {
|
||||||
|
|
@ -806,9 +806,8 @@ func ExampleDecode() {
|
||||||
input, _ := hex.DecodeString("C90A1486666F6F626172")
|
input, _ := hex.DecodeString("C90A1486666F6F626172")
|
||||||
|
|
||||||
type example struct {
|
type example struct {
|
||||||
A, B uint
|
A, B uint
|
||||||
private uint // private fields are ignored
|
String string
|
||||||
String string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var s example
|
var s example
|
||||||
|
|
@ -819,7 +818,7 @@ func ExampleDecode() {
|
||||||
fmt.Printf("Decoded value: %#v\n", s)
|
fmt.Printf("Decoded value: %#v\n", s)
|
||||||
}
|
}
|
||||||
// Output:
|
// Output:
|
||||||
// Decoded value: rlp.example{A:0xa, B:0x14, private:0x0, String:"foobar"}
|
// Decoded value: rlp.example{A:0xa, B:0x14, String:"foobar"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleDecode_structTagNil() {
|
func ExampleDecode_structTagNil() {
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,7 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrRequestDenied = errors.New("Request denied")
|
var ErrRequestDenied = errors.New("request denied")
|
||||||
|
|
||||||
// NewSignerAPI creates a new API that can be used for Account management.
|
// NewSignerAPI creates a new API that can be used for Account management.
|
||||||
// ksLocation specifies the directory where to store the password protected private
|
// ksLocation specifies the directory where to store the password protected private
|
||||||
|
|
@ -552,6 +552,9 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
|
||||||
}
|
}
|
||||||
|
|
||||||
rlpdata, err := rlp.EncodeToBytes(signedTx)
|
rlpdata, err := rlp.EncodeToBytes(signedTx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
response := ethapi.SignTransactionResult{Raw: rlpdata, Tx: signedTx}
|
response := ethapi.SignTransactionResult{Raw: rlpdata, Tx: signedTx}
|
||||||
|
|
||||||
// Finally, send the signed tx to the UI
|
// Finally, send the signed tx to the UI
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ func (ui *headlessUi) ApproveTx(request *core.SignTxRequest) (core.SignTxRespons
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *headlessUi) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
|
func (ui *headlessUi) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
|
||||||
approved := "Y" == <-ui.approveCh
|
approved := (<-ui.approveCh == "Y")
|
||||||
return core.SignDataResponse{approved}, nil
|
return core.SignDataResponse{approved}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ func (ui *headlessUi) ApproveListing(request *core.ListRequest) (core.ListRespon
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *headlessUi) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
|
func (ui *headlessUi) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
|
||||||
if "Y" == <-ui.approveCh {
|
if <-ui.approveCh == "Y" {
|
||||||
return core.NewAccountResponse{true}, nil
|
return core.NewAccountResponse{true}, nil
|
||||||
}
|
}
|
||||||
return core.NewAccountResponse{false}, nil
|
return core.NewAccountResponse{false}, nil
|
||||||
|
|
|
||||||
|
|
@ -58,34 +58,6 @@ func (ui *CommandlineUI) readString() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// readPassword reads a single line from stdin, trimming it from the trailing new
|
|
||||||
// line and returns it. The input will not be echoed.
|
|
||||||
func (ui *CommandlineUI) readPassword() string {
|
|
||||||
fmt.Printf("Enter password to approve:\n")
|
|
||||||
fmt.Printf("> ")
|
|
||||||
|
|
||||||
text, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read password", "err", err)
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Println("-----------------------")
|
|
||||||
return string(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
// readPassword reads a single line from stdin, trimming it from the trailing new
|
|
||||||
// line and returns it. The input will not be echoed.
|
|
||||||
func (ui *CommandlineUI) readPasswordText(inputstring string) string {
|
|
||||||
fmt.Printf("Enter %s:\n", inputstring)
|
|
||||||
fmt.Printf("> ")
|
|
||||||
text, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
|
||||||
if err != nil {
|
|
||||||
log.Crit("Failed to read password", "err", err)
|
|
||||||
}
|
|
||||||
fmt.Println("-----------------------")
|
|
||||||
return string(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ui *CommandlineUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
|
func (ui *CommandlineUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
|
||||||
|
|
||||||
fmt.Printf("## %s\n\n%s\n", info.Title, info.Prompt)
|
fmt.Printf("## %s\n\n%s\n", info.Title, info.Prompt)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -27,7 +26,6 @@ import (
|
||||||
|
|
||||||
type StdIOUI struct {
|
type StdIOUI struct {
|
||||||
client rpc.Client
|
client rpc.Client
|
||||||
mu sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStdIOUI() *StdIOUI {
|
func NewStdIOUI() *StdIOUI {
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ func (v *ValidationMessages) getWarnings() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(messages) > 0 {
|
if len(messages) > 0 {
|
||||||
return fmt.Errorf("Validation failed: %s", strings.Join(messages, ","))
|
return fmt.Errorf("validation failed: %s", strings.Join(messages, ","))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ func (s *UIServerAPI) Export(ctx context.Context, addr common.Address) (json.Raw
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if wallet.URL().Scheme != keystore.KeyStoreScheme {
|
if wallet.URL().Scheme != keystore.KeyStoreScheme {
|
||||||
return nil, fmt.Errorf("Account is not a keystore-account")
|
return nil, fmt.Errorf("account is not a keystore-account")
|
||||||
}
|
}
|
||||||
return ioutil.ReadFile(wallet.URL().Path)
|
return ioutil.ReadFile(wallet.URL().Path)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
// Code generated by go-bindata.
|
// Package deps Code generated by go-bindata. (@generated) DO NOT EDIT.
|
||||||
// sources:
|
// sources:
|
||||||
// bignumber.js
|
// bignumber.js
|
||||||
// DO NOT EDIT!
|
|
||||||
|
|
||||||
package deps
|
package deps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -20,7 +18,7 @@ import (
|
||||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
@ -28,7 +26,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
|
||||||
clErr := gz.Close()
|
clErr := gz.Close()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||||
}
|
}
|
||||||
if clErr != nil {
|
if clErr != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -49,21 +47,32 @@ type bindataFileInfo struct {
|
||||||
modTime time.Time
|
modTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name return file name
|
||||||
func (fi bindataFileInfo) Name() string {
|
func (fi bindataFileInfo) Name() string {
|
||||||
return fi.name
|
return fi.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size return file size
|
||||||
func (fi bindataFileInfo) Size() int64 {
|
func (fi bindataFileInfo) Size() int64 {
|
||||||
return fi.size
|
return fi.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mode return file mode
|
||||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||||
return fi.mode
|
return fi.mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ModTime return file modify time
|
||||||
func (fi bindataFileInfo) ModTime() time.Time {
|
func (fi bindataFileInfo) ModTime() time.Time {
|
||||||
return fi.modTime
|
return fi.modTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDir return file whether a directory
|
||||||
func (fi bindataFileInfo) IsDir() bool {
|
func (fi bindataFileInfo) IsDir() bool {
|
||||||
return false
|
return fi.mode&os.ModeDir != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sys return file is sys mode
|
||||||
func (fi bindataFileInfo) Sys() interface{} {
|
func (fi bindataFileInfo) Sys() interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ func (r *rulesetUI) checkApproval(jsfunc string, jsarg []byte, err error) (bool,
|
||||||
log.Info("Op rejected")
|
log.Info("Op rejected")
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, fmt.Errorf("Unknown response")
|
return false, fmt.Errorf("unknown response")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rulesetUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
|
func (r *rulesetUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,7 @@ type Storage interface {
|
||||||
// EphemeralStorage is an in-memory storage that does
|
// EphemeralStorage is an in-memory storage that does
|
||||||
// not persist values to disk. Mainly used for testing
|
// not persist values to disk. Mainly used for testing
|
||||||
type EphemeralStorage struct {
|
type EphemeralStorage struct {
|
||||||
data map[string]string
|
data map[string]string
|
||||||
namespace string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put stores a value by key. 0-length keys results in noop.
|
// Put stores a value by key. 0-length keys results in noop.
|
||||||
|
|
@ -83,5 +82,5 @@ type NoStorage struct{}
|
||||||
func (s *NoStorage) Put(key, value string) {}
|
func (s *NoStorage) Put(key, value string) {}
|
||||||
func (s *NoStorage) Del(key string) {}
|
func (s *NoStorage) Del(key string) {}
|
||||||
func (s *NoStorage) Get(key string) (string, error) {
|
func (s *NoStorage) Get(key string) (string, error) {
|
||||||
return "", errors.New("I forgot")
|
return "", errors.New("missing key, I probably forgot")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error)
|
||||||
if b.BlockHeader == nil {
|
if b.BlockHeader == nil {
|
||||||
continue // OK - block is supposed to be invalid, continue with next block
|
continue // OK - block is supposed to be invalid, continue with next block
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
|
return nil, fmt.Errorf("block RLP decoding failed when expected to succeed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// RLP decoding worked, try to insert into chain:
|
// RLP decoding worked, try to insert into chain:
|
||||||
|
|
@ -189,16 +189,16 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error)
|
||||||
if b.BlockHeader == nil {
|
if b.BlockHeader == nil {
|
||||||
continue // OK - block is supposed to be invalid, continue with next block
|
continue // OK - block is supposed to be invalid, continue with next block
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("Block #%v insertion into chain failed: %v", blocks[i].Number(), err)
|
return nil, fmt.Errorf("block #%v insertion into chain failed: %v", blocks[i].Number(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if b.BlockHeader == nil {
|
if b.BlockHeader == nil {
|
||||||
return nil, fmt.Errorf("Block insertion should have failed")
|
return nil, fmt.Errorf("block insertion should have failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate RLP decoding by checking all values against test file JSON
|
// validate RLP decoding by checking all values against test file JSON
|
||||||
if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
|
if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
|
||||||
return nil, fmt.Errorf("Deserialised block header validation failed: %v", err)
|
return nil, fmt.Errorf("deserialised block header validation failed: %v", err)
|
||||||
}
|
}
|
||||||
validBlocks = append(validBlocks, b)
|
validBlocks = append(validBlocks, b)
|
||||||
}
|
}
|
||||||
|
|
@ -207,49 +207,49 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error)
|
||||||
|
|
||||||
func validateHeader(h *btHeader, h2 *types.Header) error {
|
func validateHeader(h *btHeader, h2 *types.Header) error {
|
||||||
if h.Bloom != h2.Bloom {
|
if h.Bloom != h2.Bloom {
|
||||||
return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom)
|
return fmt.Errorf("bloom: want: %x have: %x", h.Bloom, h2.Bloom)
|
||||||
}
|
}
|
||||||
if h.Coinbase != h2.Coinbase {
|
if h.Coinbase != h2.Coinbase {
|
||||||
return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
|
return fmt.Errorf("coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
|
||||||
}
|
}
|
||||||
if h.MixHash != h2.MixDigest {
|
if h.MixHash != h2.MixDigest {
|
||||||
return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest)
|
return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest)
|
||||||
}
|
}
|
||||||
if h.Nonce != h2.Nonce {
|
if h.Nonce != h2.Nonce {
|
||||||
return fmt.Errorf("Nonce: want: %x have: %x", h.Nonce, h2.Nonce)
|
return fmt.Errorf("nonce: want: %x have: %x", h.Nonce, h2.Nonce)
|
||||||
}
|
}
|
||||||
if h.Number.Cmp(h2.Number) != 0 {
|
if h.Number.Cmp(h2.Number) != 0 {
|
||||||
return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number)
|
return fmt.Errorf("number: want: %v have: %v", h.Number, h2.Number)
|
||||||
}
|
}
|
||||||
if h.ParentHash != h2.ParentHash {
|
if h.ParentHash != h2.ParentHash {
|
||||||
return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
|
return fmt.Errorf("parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
|
||||||
}
|
}
|
||||||
if h.ReceiptTrie != h2.ReceiptHash {
|
if h.ReceiptTrie != h2.ReceiptHash {
|
||||||
return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
|
return fmt.Errorf("receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
|
||||||
}
|
}
|
||||||
if h.TransactionsTrie != h2.TxHash {
|
if h.TransactionsTrie != h2.TxHash {
|
||||||
return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
|
return fmt.Errorf("tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
|
||||||
}
|
}
|
||||||
if h.StateRoot != h2.Root {
|
if h.StateRoot != h2.Root {
|
||||||
return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root)
|
return fmt.Errorf("state hash: want: %x have: %x", h.StateRoot, h2.Root)
|
||||||
}
|
}
|
||||||
if h.UncleHash != h2.UncleHash {
|
if h.UncleHash != h2.UncleHash {
|
||||||
return fmt.Errorf("Uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash)
|
return fmt.Errorf("uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(h.ExtraData, h2.Extra) {
|
if !bytes.Equal(h.ExtraData, h2.Extra) {
|
||||||
return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra)
|
return fmt.Errorf("extra data: want: %x have: %x", h.ExtraData, h2.Extra)
|
||||||
}
|
}
|
||||||
if h.Difficulty.Cmp(h2.Difficulty) != 0 {
|
if h.Difficulty.Cmp(h2.Difficulty) != 0 {
|
||||||
return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
|
return fmt.Errorf("difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
|
||||||
}
|
}
|
||||||
if h.GasLimit != h2.GasLimit {
|
if h.GasLimit != h2.GasLimit {
|
||||||
return fmt.Errorf("GasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit)
|
return fmt.Errorf("gasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit)
|
||||||
}
|
}
|
||||||
if h.GasUsed != h2.GasUsed {
|
if h.GasUsed != h2.GasUsed {
|
||||||
return fmt.Errorf("GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
|
return fmt.Errorf("gasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
|
||||||
}
|
}
|
||||||
if h.Timestamp != h2.Time {
|
if h.Timestamp != h2.Time {
|
||||||
return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time)
|
return fmt.Errorf("timestamp: want: %v have: %v", h.Timestamp, h2.Time)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -287,7 +287,7 @@ func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []b
|
||||||
// be part of the longest chain until last block is imported.
|
// be part of the longest chain until last block is imported.
|
||||||
for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) {
|
for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) {
|
||||||
if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil {
|
if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil {
|
||||||
return fmt.Errorf("Imported block header validation failed: %v", err)
|
return fmt.Errorf("imported block header validation failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -289,3 +289,14 @@ func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value
|
||||||
m.MapIndex(reflect.ValueOf(key)),
|
m.MapIndex(reflect.ValueOf(key)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMatcherWhitelist(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
tm := new(testMatcher)
|
||||||
|
tm.whitelist("invalid*")
|
||||||
|
tm.walk(t, rlpTestDir, func(t *testing.T, name string, test *RLPTest) {
|
||||||
|
if name[:len("invalidRLPTest.json")] != "invalidRLPTest.json" {
|
||||||
|
t.Fatalf("invalid test found: %s != invalidRLPTest.json", name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,25 +86,25 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error {
|
||||||
|
|
||||||
if testcase.fork.Sender == (common.UnprefixedAddress{}) {
|
if testcase.fork.Sender == (common.UnprefixedAddress{}) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Expected error, got none (address %v)[%v]", sender.String(), testcase.name)
|
return fmt.Errorf("expected error, got none (address %v)[%v]", sender.String(), testcase.name)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Should resolve the right address
|
// Should resolve the right address
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Got error, expected none: %v", err)
|
return fmt.Errorf("got error, expected none: %v", err)
|
||||||
}
|
}
|
||||||
if sender == nil {
|
if sender == nil {
|
||||||
return fmt.Errorf("sender was nil, should be %x", common.Address(testcase.fork.Sender))
|
return fmt.Errorf("sender was nil, should be %x", common.Address(testcase.fork.Sender))
|
||||||
}
|
}
|
||||||
if *sender != common.Address(testcase.fork.Sender) {
|
if *sender != common.Address(testcase.fork.Sender) {
|
||||||
return fmt.Errorf("Sender mismatch: got %x, want %x", sender, testcase.fork.Sender)
|
return fmt.Errorf("sender mismatch: got %x, want %x", sender, testcase.fork.Sender)
|
||||||
}
|
}
|
||||||
if txhash == nil {
|
if txhash == nil {
|
||||||
return fmt.Errorf("txhash was nil, should be %x", common.Hash(testcase.fork.Hash))
|
return fmt.Errorf("txhash was nil, should be %x", common.Hash(testcase.fork.Hash))
|
||||||
}
|
}
|
||||||
if *txhash != common.Hash(testcase.fork.Hash) {
|
if *txhash != common.Hash(testcase.fork.Hash) {
|
||||||
return fmt.Errorf("Hash mismatch: got %x, want %x", *txhash, testcase.fork.Hash)
|
return fmt.Errorf("hash mismatch: got %x, want %x", *txhash, testcase.fork.Hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
114
trie/database.go
114
trie/database.go
|
|
@ -17,7 +17,6 @@
|
||||||
package trie
|
package trie
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -25,7 +24,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/allegro/bigcache"
|
"github.com/VictoriaMetrics/fastcache"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -39,6 +38,11 @@ var (
|
||||||
memcacheCleanReadMeter = metrics.NewRegisteredMeter("trie/memcache/clean/read", nil)
|
memcacheCleanReadMeter = metrics.NewRegisteredMeter("trie/memcache/clean/read", nil)
|
||||||
memcacheCleanWriteMeter = metrics.NewRegisteredMeter("trie/memcache/clean/write", nil)
|
memcacheCleanWriteMeter = metrics.NewRegisteredMeter("trie/memcache/clean/write", nil)
|
||||||
|
|
||||||
|
memcacheDirtyHitMeter = metrics.NewRegisteredMeter("trie/memcache/dirty/hit", nil)
|
||||||
|
memcacheDirtyMissMeter = metrics.NewRegisteredMeter("trie/memcache/dirty/miss", nil)
|
||||||
|
memcacheDirtyReadMeter = metrics.NewRegisteredMeter("trie/memcache/dirty/read", nil)
|
||||||
|
memcacheDirtyWriteMeter = metrics.NewRegisteredMeter("trie/memcache/dirty/write", nil)
|
||||||
|
|
||||||
memcacheFlushTimeTimer = metrics.NewRegisteredResettingTimer("trie/memcache/flush/time", nil)
|
memcacheFlushTimeTimer = metrics.NewRegisteredResettingTimer("trie/memcache/flush/time", nil)
|
||||||
memcacheFlushNodesMeter = metrics.NewRegisteredMeter("trie/memcache/flush/nodes", nil)
|
memcacheFlushNodesMeter = metrics.NewRegisteredMeter("trie/memcache/flush/nodes", nil)
|
||||||
memcacheFlushSizeMeter = metrics.NewRegisteredMeter("trie/memcache/flush/size", nil)
|
memcacheFlushSizeMeter = metrics.NewRegisteredMeter("trie/memcache/flush/size", nil)
|
||||||
|
|
@ -69,7 +73,7 @@ const secureKeyLength = 11 + 32
|
||||||
type Database struct {
|
type Database struct {
|
||||||
diskdb ethdb.KeyValueStore // Persistent storage for matured trie nodes
|
diskdb ethdb.KeyValueStore // Persistent storage for matured trie nodes
|
||||||
|
|
||||||
cleans *bigcache.BigCache // GC friendly memory cache of clean node RLPs
|
cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs
|
||||||
dirties map[common.Hash]*cachedNode // Data and references relationships of dirty nodes
|
dirties map[common.Hash]*cachedNode // Data and references relationships of dirty nodes
|
||||||
oldest common.Hash // Oldest tracked node, flush-list head
|
oldest common.Hash // Oldest tracked node, flush-list head
|
||||||
newest common.Hash // Newest tracked node, flush-list tail
|
newest common.Hash // Newest tracked node, flush-list tail
|
||||||
|
|
@ -97,18 +101,16 @@ type Database struct {
|
||||||
// in the same cache fields).
|
// in the same cache fields).
|
||||||
type rawNode []byte
|
type rawNode []byte
|
||||||
|
|
||||||
func (n rawNode) canUnload(uint16, uint16) bool { panic("this should never end up in a live trie") }
|
func (n rawNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") }
|
||||||
func (n rawNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") }
|
func (n rawNode) fstring(ind string) string { panic("this should never end up in a live trie") }
|
||||||
func (n rawNode) fstring(ind string) string { panic("this should never end up in a live trie") }
|
|
||||||
|
|
||||||
// rawFullNode represents only the useful data content of a full node, with the
|
// rawFullNode represents only the useful data content of a full node, with the
|
||||||
// caches and flags stripped out to minimize its data storage. This type honors
|
// caches and flags stripped out to minimize its data storage. This type honors
|
||||||
// the same RLP encoding as the original parent.
|
// the same RLP encoding as the original parent.
|
||||||
type rawFullNode [17]node
|
type rawFullNode [17]node
|
||||||
|
|
||||||
func (n rawFullNode) canUnload(uint16, uint16) bool { panic("this should never end up in a live trie") }
|
func (n rawFullNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") }
|
||||||
func (n rawFullNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") }
|
func (n rawFullNode) fstring(ind string) string { panic("this should never end up in a live trie") }
|
||||||
func (n rawFullNode) fstring(ind string) string { panic("this should never end up in a live trie") }
|
|
||||||
|
|
||||||
func (n rawFullNode) EncodeRLP(w io.Writer) error {
|
func (n rawFullNode) EncodeRLP(w io.Writer) error {
|
||||||
var nodes [17]node
|
var nodes [17]node
|
||||||
|
|
@ -131,9 +133,8 @@ type rawShortNode struct {
|
||||||
Val node
|
Val node
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n rawShortNode) canUnload(uint16, uint16) bool { panic("this should never end up in a live trie") }
|
func (n rawShortNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") }
|
||||||
func (n rawShortNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") }
|
func (n rawShortNode) fstring(ind string) string { panic("this should never end up in a live trie") }
|
||||||
func (n rawShortNode) fstring(ind string) string { panic("this should never end up in a live trie") }
|
|
||||||
|
|
||||||
// cachedNode is all the information we know about a single cached node in the
|
// cachedNode is all the information we know about a single cached node in the
|
||||||
// memory database write layer.
|
// memory database write layer.
|
||||||
|
|
@ -275,19 +276,6 @@ func expandNode(hash hashNode, n node) node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// trienodeHasher is a struct to be used with BigCache, which uses a Hasher to
|
|
||||||
// determine which shard to place an entry into. It's not a cryptographic hash,
|
|
||||||
// just to provide a bit of anti-collision (default is FNV64a).
|
|
||||||
//
|
|
||||||
// Since trie keys are already hashes, we can just use the key directly to
|
|
||||||
// map shard id.
|
|
||||||
type trienodeHasher struct{}
|
|
||||||
|
|
||||||
// Sum64 implements the bigcache.Hasher interface.
|
|
||||||
func (t trienodeHasher) Sum64(key string) uint64 {
|
|
||||||
return binary.BigEndian.Uint64([]byte(key))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDatabase creates a new trie database to store ephemeral trie content before
|
// NewDatabase creates a new trie database to store ephemeral trie content before
|
||||||
// its written out to disk or garbage collected. No read cache is created, so all
|
// its written out to disk or garbage collected. No read cache is created, so all
|
||||||
// data retrievals will hit the underlying disk database.
|
// data retrievals will hit the underlying disk database.
|
||||||
|
|
@ -299,16 +287,9 @@ func NewDatabase(diskdb ethdb.KeyValueStore) *Database {
|
||||||
// before its written out to disk or garbage collected. It also acts as a read cache
|
// before its written out to disk or garbage collected. It also acts as a read cache
|
||||||
// for nodes loaded from disk.
|
// for nodes loaded from disk.
|
||||||
func NewDatabaseWithCache(diskdb ethdb.KeyValueStore, cache int) *Database {
|
func NewDatabaseWithCache(diskdb ethdb.KeyValueStore, cache int) *Database {
|
||||||
var cleans *bigcache.BigCache
|
var cleans *fastcache.Cache
|
||||||
if cache > 0 {
|
if cache > 0 {
|
||||||
cleans, _ = bigcache.NewBigCache(bigcache.Config{
|
cleans = fastcache.New(cache * 1024 * 1024)
|
||||||
Shards: 1024,
|
|
||||||
LifeWindow: time.Hour,
|
|
||||||
MaxEntriesInWindow: cache * 1024,
|
|
||||||
MaxEntrySize: 512,
|
|
||||||
HardMaxCacheSize: cache,
|
|
||||||
Hasher: trienodeHasher{},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return &Database{
|
return &Database{
|
||||||
diskdb: diskdb,
|
diskdb: diskdb,
|
||||||
|
|
@ -345,6 +326,8 @@ func (db *Database) insert(hash common.Hash, blob []byte, node node) {
|
||||||
if _, ok := db.dirties[hash]; ok {
|
if _, ok := db.dirties[hash]; ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
memcacheDirtyWriteMeter.Mark(int64(len(blob)))
|
||||||
|
|
||||||
// Create the cached entry for this node
|
// Create the cached entry for this node
|
||||||
entry := &cachedNode{
|
entry := &cachedNode{
|
||||||
node: simplifyNode(node),
|
node: simplifyNode(node),
|
||||||
|
|
@ -384,7 +367,7 @@ func (db *Database) insertPreimage(hash common.Hash, preimage []byte) {
|
||||||
func (db *Database) node(hash common.Hash) node {
|
func (db *Database) node(hash common.Hash) node {
|
||||||
// Retrieve the node from the clean cache if available
|
// Retrieve the node from the clean cache if available
|
||||||
if db.cleans != nil {
|
if db.cleans != nil {
|
||||||
if enc, err := db.cleans.Get(string(hash[:])); err == nil && enc != nil {
|
if enc := db.cleans.Get(nil, hash[:]); enc != nil {
|
||||||
memcacheCleanHitMeter.Mark(1)
|
memcacheCleanHitMeter.Mark(1)
|
||||||
memcacheCleanReadMeter.Mark(int64(len(enc)))
|
memcacheCleanReadMeter.Mark(int64(len(enc)))
|
||||||
return mustDecodeNode(hash[:], enc)
|
return mustDecodeNode(hash[:], enc)
|
||||||
|
|
@ -396,15 +379,19 @@ func (db *Database) node(hash common.Hash) node {
|
||||||
db.lock.RUnlock()
|
db.lock.RUnlock()
|
||||||
|
|
||||||
if dirty != nil {
|
if dirty != nil {
|
||||||
|
memcacheDirtyHitMeter.Mark(1)
|
||||||
|
memcacheDirtyReadMeter.Mark(int64(dirty.size))
|
||||||
return dirty.obj(hash)
|
return dirty.obj(hash)
|
||||||
}
|
}
|
||||||
|
memcacheDirtyMissMeter.Mark(1)
|
||||||
|
|
||||||
// Content unavailable in memory, attempt to retrieve from disk
|
// Content unavailable in memory, attempt to retrieve from disk
|
||||||
enc, err := db.diskdb.Get(hash[:])
|
enc, err := db.diskdb.Get(hash[:])
|
||||||
if err != nil || enc == nil {
|
if err != nil || enc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if db.cleans != nil {
|
if db.cleans != nil {
|
||||||
db.cleans.Set(string(hash[:]), enc)
|
db.cleans.Set(hash[:], enc)
|
||||||
memcacheCleanMissMeter.Mark(1)
|
memcacheCleanMissMeter.Mark(1)
|
||||||
memcacheCleanWriteMeter.Mark(int64(len(enc)))
|
memcacheCleanWriteMeter.Mark(int64(len(enc)))
|
||||||
}
|
}
|
||||||
|
|
@ -420,7 +407,7 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
|
||||||
}
|
}
|
||||||
// Retrieve the node from the clean cache if available
|
// Retrieve the node from the clean cache if available
|
||||||
if db.cleans != nil {
|
if db.cleans != nil {
|
||||||
if enc, err := db.cleans.Get(string(hash[:])); err == nil && enc != nil {
|
if enc := db.cleans.Get(nil, hash[:]); enc != nil {
|
||||||
memcacheCleanHitMeter.Mark(1)
|
memcacheCleanHitMeter.Mark(1)
|
||||||
memcacheCleanReadMeter.Mark(int64(len(enc)))
|
memcacheCleanReadMeter.Mark(int64(len(enc)))
|
||||||
return enc, nil
|
return enc, nil
|
||||||
|
|
@ -432,13 +419,17 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
|
||||||
db.lock.RUnlock()
|
db.lock.RUnlock()
|
||||||
|
|
||||||
if dirty != nil {
|
if dirty != nil {
|
||||||
|
memcacheDirtyHitMeter.Mark(1)
|
||||||
|
memcacheDirtyReadMeter.Mark(int64(dirty.size))
|
||||||
return dirty.rlp(), nil
|
return dirty.rlp(), nil
|
||||||
}
|
}
|
||||||
|
memcacheDirtyMissMeter.Mark(1)
|
||||||
|
|
||||||
// Content unavailable in memory, attempt to retrieve from disk
|
// Content unavailable in memory, attempt to retrieve from disk
|
||||||
enc, err := db.diskdb.Get(hash[:])
|
enc, err := db.diskdb.Get(hash[:])
|
||||||
if err == nil && enc != nil {
|
if err == nil && enc != nil {
|
||||||
if db.cleans != nil {
|
if db.cleans != nil {
|
||||||
db.cleans.Set(string(hash[:]), enc)
|
db.cleans.Set(hash[:], enc)
|
||||||
memcacheCleanMissMeter.Mark(1)
|
memcacheCleanMissMeter.Mark(1)
|
||||||
memcacheCleanWriteMeter.Mark(int64(len(enc)))
|
memcacheCleanWriteMeter.Mark(int64(len(enc)))
|
||||||
}
|
}
|
||||||
|
|
@ -835,13 +826,14 @@ func (c *cleaner) Put(key []byte, rlp []byte) error {
|
||||||
}
|
}
|
||||||
// Move the flushed node into the clean cache to prevent insta-reloads
|
// Move the flushed node into the clean cache to prevent insta-reloads
|
||||||
if c.db.cleans != nil {
|
if c.db.cleans != nil {
|
||||||
c.db.cleans.Set(string(hash[:]), rlp)
|
c.db.cleans.Set(hash[:], rlp)
|
||||||
|
memcacheCleanWriteMeter.Mark(int64(len(rlp)))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cleaner) Delete(key []byte) error {
|
func (c *cleaner) Delete(key []byte) error {
|
||||||
panic("Not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the current storage size of the memory cache in front of the
|
// Size returns the current storage size of the memory cache in front of the
|
||||||
|
|
@ -857,45 +849,3 @@ func (db *Database) Size() (common.StorageSize, common.StorageSize) {
|
||||||
var metarootRefs = common.StorageSize(len(db.dirties[common.Hash{}].children) * (common.HashLength + 2))
|
var metarootRefs = common.StorageSize(len(db.dirties[common.Hash{}].children) * (common.HashLength + 2))
|
||||||
return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, db.preimagesSize
|
return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, db.preimagesSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifyIntegrity is a debug method to iterate over the entire trie stored in
|
|
||||||
// memory and check whether every node is reachable from the meta root. The goal
|
|
||||||
// is to find any errors that might cause memory leaks and or trie nodes to go
|
|
||||||
// missing.
|
|
||||||
//
|
|
||||||
// This method is extremely CPU and memory intensive, only use when must.
|
|
||||||
func (db *Database) verifyIntegrity() {
|
|
||||||
// Iterate over all the cached nodes and accumulate them into a set
|
|
||||||
reachable := map[common.Hash]struct{}{{}: {}}
|
|
||||||
|
|
||||||
for child := range db.dirties[common.Hash{}].children {
|
|
||||||
db.accumulate(child, reachable)
|
|
||||||
}
|
|
||||||
// Find any unreachable but cached nodes
|
|
||||||
var unreachable []string
|
|
||||||
for hash, node := range db.dirties {
|
|
||||||
if _, ok := reachable[hash]; !ok {
|
|
||||||
unreachable = append(unreachable, fmt.Sprintf("%x: {Node: %v, Parents: %d, Prev: %x, Next: %x}",
|
|
||||||
hash, node.node, node.parents, node.flushPrev, node.flushNext))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(unreachable) != 0 {
|
|
||||||
panic(fmt.Sprintf("trie cache memory leak: %v", unreachable))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// accumulate iterates over the trie defined by hash and accumulates all the
|
|
||||||
// cached children found in memory.
|
|
||||||
func (db *Database) accumulate(hash common.Hash, reachable map[common.Hash]struct{}) {
|
|
||||||
// Mark the node reachable if present in the memory cache
|
|
||||||
node, ok := db.dirties[hash]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reachable[hash] = struct{}{}
|
|
||||||
|
|
||||||
// Iterate over all the children and accumulate them too
|
|
||||||
for _, child := range node.childs() {
|
|
||||||
db.accumulate(child, reachable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue