My Merge
Commit
This commit is contained in:
Abeseba 2019-12-05 23:18:43 +01:00
commit e95388dda6
101 changed files with 1277 additions and 2121 deletions

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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}}";

View file

@ -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"}]}]`

View file

@ -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")
} }

View file

@ -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?

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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 {

View file

@ -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.

View file

@ -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() {

View file

@ -43,6 +43,7 @@ func init() {
commandSignMessage, commandSignMessage,
commandVerifyMessage, commandVerifyMessage,
} }
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
} }
// Commonly used command line flags. // Commonly used command line flags.

View file

@ -152,6 +152,7 @@ func init() {
runCommand, runCommand,
stateTestCommand, stateTestCommand,
} }
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
} }
func main() { func main() {

View file

@ -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

View file

@ -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"))

View file

@ -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

View file

@ -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() {

View file

@ -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)

View file

@ -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
} }

View file

@ -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,13 +418,7 @@ 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
@ -463,22 +427,24 @@ type Ethash struct {
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,
@ -486,14 +452,8 @@ func New(config Config, notify []string, noverify bool) *Ethash {
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()
} }

View file

@ -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,80 +186,219 @@ 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 {
works map[common.Hash]*types.Block
rates map[common.Hash]hashrate
currentBlock *types.Block currentBlock *types.Block
currentWork [4]string 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
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
requestExit chan struct{}
exitCh chan struct{}
}
// sealTask wraps a seal block with relative result channel for remote sealer thread.
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
}
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{}),
} }
notifyReqs = make([]*http.Request, len(notify)) go s.loop()
) return s
// notifyWork notifies all the specified mining endpoints of the availability of }
// new work to be processed.
notifyWork := func() {
work := currentWork
blob, _ := json.Marshal(work)
for i, url := range notify { func (s *remoteSealer) loop() {
// Terminate any previously pending request and create the new work defer func() {
if notifyReqs[i] != nil { s.ethash.config.Log.Trace("Ethash remote sealer is exiting")
notifyTransport.CancelRequest(notifyReqs[i]) s.cancelNotify()
} s.reqWG.Wait()
notifyReqs[i], _ = http.NewRequest("POST", url, bytes.NewReader(blob)) close(s.exitCh)
notifyReqs[i].Header.Set("Content-Type", "application/json") }()
// Push the new work concurrently to all the remote nodes ticker := time.NewTicker(5 * time.Second)
go func(req *http.Request, url string) { defer ticker.Stop()
res, err := notifyClient.Do(req)
if err != nil { for {
log.Warn("Failed to notify remote miner", "err", err) select {
case work := <-s.workCh:
// Update current work with new received block.
// Note same work can be past twice, happens when changing CPU threads.
s.results = work.results
s.makeWork(work.block)
s.notifyWork()
case work := <-s.fetchWorkCh:
// Return current mining work to remote miner.
if s.currentBlock == nil {
work.errc <- errNoMiningWork
} else { } else {
log.Trace("Notified remote miner", "miner", url, "hash", log.Lazy{Fn: func() common.Hash { return common.HexToHash(work[0]) }}, "target", work[2]) work.res <- s.currentWork
res.Body.Close()
} }
}(notifyReqs[i], url)
}
}
// 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
makeWork := func(block *types.Block) {
hash := ethash.SealHash(block.Header())
currentWork[0] = hash.Hex() case result := <-s.submitWorkCh:
currentWork[1] = common.BytesToHash(SeedHash(block.NumberU64())).Hex() // Verify submitted PoW solution based on maintained mining blocks.
currentWork[2] = common.BytesToHash(new(big.Int).Div(two256, block.Difficulty()).Bytes()).Hex() if s.submitWork(result.nonce, result.mixDigest, result.hash) {
currentWork[3] = hexutil.EncodeBig(block.Number()) result.errc <- nil
} else {
result.errc <- errInvalidSealResult
}
case result := <-s.submitRateCh:
// Trace remote sealer's hash rate by submitted value.
s.rates[result.id] = hashrate{rate: result.rate, ping: time.Now()}
close(result.done)
case req := <-s.fetchRateCh:
// Gather all hash rate submitted by remote sealer.
var total uint64
for _, rate := range s.rates {
// this could overflow
total += rate.rate
}
req <- total
case <-ticker.C:
// Clear stale submitted hash rate.
for id, rate := range s.rates {
if time.Since(rate.ping) > 10*time.Second {
delete(s.rates, id)
}
}
// Clear stale pending blocks
if s.currentBlock != nil {
for hash, block := range s.works {
if block.NumberU64()+staleThreshold <= s.currentBlock.NumberU64() {
delete(s.works, hash)
}
}
}
case <-s.requestExit:
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. // Trace the seal work fetched by remote sealer.
currentBlock = block s.currentBlock = block
works[hash] = 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)
} }
// 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) sendNotification(ctx context.Context, url string, json []byte, work [4]string) {
submitWork := func(nonce types.BlockNonce, mixDigest common.Hash, sealhash common.Hash) bool { defer s.reqWG.Done()
if currentBlock == nil {
log.Error("Pending work without block", "sealhash", sealhash) 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 return false
} }
// Make sure the work submitted is present // Make sure the work submitted is present
block := works[sealhash] block := s.works[sealhash]
if block == nil { if block == nil {
log.Warn("Work submitted but none pending", "sealhash", sealhash, "curnumber", currentBlock.NumberU64()) s.ethash.config.Log.Warn("Work submitted but none pending", "sealhash", sealhash, "curnumber", s.currentBlock.NumberU64())
return false return false
} }
// Verify the correctness of submitted result. // Verify the correctness of submitted result.
@ -268,104 +407,34 @@ func (ethash *Ethash) remote(notify []string, noverify bool) {
header.MixDigest = mixDigest header.MixDigest = mixDigest
start := time.Now() start := time.Now()
if !noverify { if !s.noverify {
if err := ethash.verifySeal(nil, header, true); err != nil { if err := s.ethash.verifySeal(nil, header, true); err != nil {
log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)), "err", err) s.ethash.config.Log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)), "err", err)
return false return false
} }
} }
// Make sure the result channel is assigned. // Make sure the result channel is assigned.
if results == nil { if s.results == nil {
log.Warn("Ethash result channel is empty, submitted mining result is rejected") s.ethash.config.Log.Warn("Ethash result channel is empty, submitted mining result is rejected")
return false return false
} }
log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start))) 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. // Solutions seems to be valid, return to the miner and notify acceptance.
solution := block.WithSeal(header) solution := block.WithSeal(header)
// The submitted solution is within the scope of acceptance. // The submitted solution is within the scope of acceptance.
if solution.NumberU64()+staleThreshold > currentBlock.NumberU64() { if solution.NumberU64()+staleThreshold > s.currentBlock.NumberU64() {
select { select {
case results <- solution: case s.results <- solution:
log.Debug("Work submitted is acceptable", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash()) s.ethash.config.Log.Debug("Work submitted is acceptable", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
return true return true
default: default:
log.Warn("Sealing result is not read by miner", "mode", "remote", "sealhash", sealhash) s.ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "remote", "sealhash", sealhash)
return false return false
} }
} }
// The submitted block is too old to accept, drop it. // 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()) s.ethash.config.Log.Warn("Work submitted is too old", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
return false return false
}
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case work := <-ethash.workCh:
// Update current work with new received block.
// Note same work can be past twice, happens when changing CPU threads.
results = work.results
makeWork(work.block)
// Notify and requested URLs of the new work availability
notifyWork()
case work := <-ethash.fetchWorkCh:
// Return current mining work to remote miner.
if currentBlock == nil {
work.errc <- errNoMiningWork
} else {
work.res <- currentWork
}
case result := <-ethash.submitWorkCh:
// Verify submitted PoW solution based on maintained mining blocks.
if submitWork(result.nonce, result.mixDigest, result.hash) {
result.errc <- nil
} else {
result.errc <- errInvalidSealResult
}
case result := <-ethash.submitRateCh:
// Trace remote sealer's hash rate by submitted value.
rates[result.id] = hashrate{rate: result.rate, ping: time.Now()}
close(result.done)
case req := <-ethash.fetchRateCh:
// Gather all hash rate submitted by remote sealer.
var total uint64
for _, rate := range rates {
// this could overflow
total += rate.rate
}
req <- total
case <-ticker.C:
// Clear stale submitted hash rate.
for id, rate := range rates {
if time.Since(rate.ping) > 10*time.Second {
delete(rates, id)
}
}
// Clear stale pending blocks
if currentBlock != nil {
for hash, block := range works {
if block.NumberU64()+staleThreshold <= currentBlock.NumberU64() {
delete(works, hash)
}
}
}
case errc := <-ethash.exitCh:
// Exit remote loop if ethash is closed and return relevant error.
errc <- nil
log.Trace("Ethash remote sealer is exiting")
return
}
}
} }

View file

@ -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{
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
blob, err := ioutil.ReadAll(req.Body) blob, err := ioutil.ReadAll(req.Body)
if err != nil { if err != nil {
t.Fatalf("failed to read miner notification: %v", err) t.Errorf("failed to read miner notification: %v", err)
} }
var work [3]string var work [3]string
if err := json.Unmarshal(blob, &work); err != nil { if err := json.Unmarshal(blob, &work); err != nil {
t.Fatalf("failed to unmarshal miner notification: %v", err) t.Errorf("failed to unmarshal miner notification: %v", err)
} }
sink <- work sink <- work
}), }))
} defer server.Close()
// 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{server.URL}, false)
// 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
}
}
if tries == 10 {
t.Fatal("tcp listener not ready for more than 10 seconds")
}
// Create the custom ethash engine
ethash := NewTester([]string{"http://" + listener.Addr().String()}, 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) {
server := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
blob, err := ioutil.ReadAll(req.Body) blob, err := ioutil.ReadAll(req.Body)
if err != nil { if err != nil {
t.Fatalf("failed to read miner notification: %v", err) t.Errorf("failed to read miner notification: %v", err)
} }
var work [3]string var work [3]string
if err := json.Unmarshal(blob, &work); err != nil { if err := json.Unmarshal(blob, &work); err != nil {
t.Fatalf("failed to unmarshal miner notification: %v", err) t.Errorf("failed to unmarshal miner notification: %v", err)
} }
sink <- work sink <- work
}), }))
} defer server.Close()
// 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{server.URL}, false)
// Create the custom ethash engine ethash.config.Log = testlog.Logger(t, log.LvlWarn)
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)

View file

@ -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 {

View file

@ -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)

View file

@ -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.
//
// TODO(karalabe): Can we get rid of the goroutine somehow to guarantee correct
// event ordering?
go func() {
if len(deletedLogs) > 0 { if len(deletedLogs) > 0 {
bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) bc.rmLogsFeed.Send(RemovedLogsEvent{mergeLogs(deletedLogs, true)})
} }
if len(rebirthLogs) > 0 { if len(rebirthLogs) > 0 {
bc.logsFeed.Send(rebirthLogs) bc.logsFeed.Send(mergeLogs(rebirthLogs, false))
} }
if len(oldChain) > 0 { if len(oldChain) > 0 {
for _, block := range oldChain { for i := len(oldChain) - 1; i >= 0; i-- {
bc.chainSideFeed.Send(ChainSideEvent{Block: block}) bc.chainSideFeed.Send(ChainSideEvent{Block: oldChain[i]})
} }
} }
}()
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()

View file

@ -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.")
} }
@ -987,34 +992,42 @@ func TestLogRebirth(t *testing.T) {
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

View file

@ -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

View file

@ -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() {

View file

@ -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)
} }

View file

@ -128,8 +128,6 @@ type (
} }
touchChange struct { touchChange struct {
account *common.Address account *common.Address
prev bool
prevDirty bool
} }
) )

View file

@ -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) }

View file

@ -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()))

View file

@ -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() {

View file

@ -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")
} }
} }

View file

@ -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"
) )

View file

@ -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 {

View file

@ -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 ###")

View file

@ -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
View file

@ -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
View file

@ -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=

View file

@ -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:])
} }
} }

View file

@ -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

View file

@ -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,7 +171,6 @@ 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,
} }
@ -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

View file

@ -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',

View file

@ -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
} }

View file

@ -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()
} }

View file

@ -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 = &ltrInfo{ ltr = &ltrInfo{
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)
} }
} }

View file

@ -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()
} }

View file

@ -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)
} }
} }

View file

@ -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)
} }
} }

View file

@ -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)
} }
} }

View file

@ -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)
} }
} }

View file

@ -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])
} }
} }

View file

@ -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

View file

@ -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)
} }
} }

View file

@ -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
} }

View file

@ -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 {

View file

@ -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)
} }
} }

View file

@ -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)
} }

View file

@ -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)
} }
} }

View file

@ -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)

View file

@ -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 {

View file

@ -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 }

View file

@ -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)
} }
} }

View file

@ -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)
} }

View file

@ -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:
}
}

View file

@ -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:

View file

@ -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
View 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.

View file

@ -62,7 +62,6 @@ var (
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"
) )
@ -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 {

View file

@ -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.
@ -107,7 +99,6 @@ 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.
//<-refreshResults // if refreshDone != nil {
} // <-refreshResults
// }
// Cancel all pending timeouts. // Cancel all pending timeouts.
for _, timer := range net.timeoutTimers { for _, timer := range net.timeoutTimers {
timer.Stop() timer.Stop()

View file

@ -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})
} }

View file

@ -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) {

View file

@ -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
}

View file

@ -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,

View file

@ -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 (

View file

@ -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()

View file

@ -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

View file

@ -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.

View file

@ -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
}

View file

@ -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
} }

View file

@ -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.

View file

@ -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
) )

View file

@ -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")

View file

@ -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 {
@ -807,7 +807,6 @@ func ExampleDecode() {
type example struct { type example struct {
A, B uint A, B uint
private uint // private fields are ignored
String string String string
} }
@ -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() {

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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 {

View file

@ -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
} }

View file

@ -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)
} }

View file

@ -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
} }

View file

@ -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) {

View file

@ -42,7 +42,6 @@ type Storage interface {
// 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")
} }

View file

@ -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

View file

@ -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)
}
})
}

View file

@ -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

View file

@ -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,7 +101,6 @@ 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") }
@ -106,7 +109,6 @@ func (n rawNode) fstring(ind string) string { panic("this should never end u
// 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") }
@ -131,7 +133,6 @@ 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") }
@ -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