mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
Merge commit 'eae63c511ceafab14b92e274c1b18bf1700e2d3d'
This commit is contained in:
commit
d3b90bfa81
70 changed files with 886 additions and 530 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
1.8.9
|
1.8.10
|
||||||
|
|
|
||||||
|
|
@ -454,7 +454,7 @@ func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*ty
|
||||||
return logs, nil
|
return logs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
<-quit
|
<-quit
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,20 @@ package common
|
||||||
|
|
||||||
import "encoding/hex"
|
import "encoding/hex"
|
||||||
|
|
||||||
|
// ToHex returns the hex representation of b, prefixed with '0x'.
|
||||||
|
// For empty slices, the return value is "0x0".
|
||||||
|
//
|
||||||
|
// Deprecated: use hexutil.Encode instead.
|
||||||
func ToHex(b []byte) string {
|
func ToHex(b []byte) string {
|
||||||
hex := Bytes2Hex(b)
|
hex := Bytes2Hex(b)
|
||||||
// Prefer output of "0x0" instead of "0x"
|
|
||||||
if len(hex) == 0 {
|
if len(hex) == 0 {
|
||||||
hex = "0"
|
hex = "0"
|
||||||
}
|
}
|
||||||
return "0x" + hex
|
return "0x" + hex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromHex returns the bytes represented by the hexadecimal string s.
|
||||||
|
// s may be prefixed with "0x".
|
||||||
func FromHex(s string) []byte {
|
func FromHex(s string) []byte {
|
||||||
if len(s) > 1 {
|
if len(s) > 1 {
|
||||||
if s[0:2] == "0x" || s[0:2] == "0X" {
|
if s[0:2] == "0x" || s[0:2] == "0X" {
|
||||||
|
|
@ -40,9 +45,7 @@ func FromHex(s string) []byte {
|
||||||
return Hex2Bytes(s)
|
return Hex2Bytes(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy bytes
|
// CopyBytes returns an exact copy of the provided bytes.
|
||||||
//
|
|
||||||
// Returns an exact copy of the provided bytes
|
|
||||||
func CopyBytes(b []byte) (copiedBytes []byte) {
|
func CopyBytes(b []byte) (copiedBytes []byte) {
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -53,14 +56,17 @@ func CopyBytes(b []byte) (copiedBytes []byte) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasHexPrefix validates str begins with '0x' or '0X'.
|
||||||
func hasHexPrefix(str string) bool {
|
func hasHexPrefix(str string) bool {
|
||||||
return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
|
return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isHexCharacter returns bool of c being a valid hexadecimal.
|
||||||
func isHexCharacter(c byte) bool {
|
func isHexCharacter(c byte) bool {
|
||||||
return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
|
return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isHex validates whether each byte is valid hexadecimal string.
|
||||||
func isHex(str string) bool {
|
func isHex(str string) bool {
|
||||||
if len(str)%2 != 0 {
|
if len(str)%2 != 0 {
|
||||||
return false
|
return false
|
||||||
|
|
@ -73,16 +79,18 @@ func isHex(str string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bytes2Hex returns the hexadecimal encoding of d.
|
||||||
func Bytes2Hex(d []byte) string {
|
func Bytes2Hex(d []byte) string {
|
||||||
return hex.EncodeToString(d)
|
return hex.EncodeToString(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hex2Bytes returns the bytes represented by the hexadecimal string str.
|
||||||
func Hex2Bytes(str string) []byte {
|
func Hex2Bytes(str string) []byte {
|
||||||
h, _ := hex.DecodeString(str)
|
h, _ := hex.DecodeString(str)
|
||||||
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hex2BytesFixed returns bytes of a specified fixed length flen.
|
||||||
func Hex2BytesFixed(str string, flen int) []byte {
|
func Hex2BytesFixed(str string, flen int) []byte {
|
||||||
h, _ := hex.DecodeString(str)
|
h, _ := hex.DecodeString(str)
|
||||||
if len(h) == flen {
|
if len(h) == flen {
|
||||||
|
|
@ -96,6 +104,7 @@ func Hex2BytesFixed(str string, flen int) []byte {
|
||||||
return hh
|
return hh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RightPadBytes zero-pads slice to the right up to length l.
|
||||||
func RightPadBytes(slice []byte, l int) []byte {
|
func RightPadBytes(slice []byte, l int) []byte {
|
||||||
if l <= len(slice) {
|
if l <= len(slice) {
|
||||||
return slice
|
return slice
|
||||||
|
|
@ -107,6 +116,7 @@ func RightPadBytes(slice []byte, l int) []byte {
|
||||||
return padded
|
return padded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LeftPadBytes zero-pads slice to the left up to length l.
|
||||||
func LeftPadBytes(slice []byte, l int) []byte {
|
func LeftPadBytes(slice []byte, l int) []byte {
|
||||||
if l <= len(slice) {
|
if l <= len(slice) {
|
||||||
return slice
|
return slice
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ func ParseBig256(s string) (*big.Int, bool) {
|
||||||
return bigint, ok
|
return bigint, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustParseBig parses s as a 256 bit big integer and panics if the string is invalid.
|
// MustParseBig256 parses s as a 256 bit big integer and panics if the string is invalid.
|
||||||
func MustParseBig256(s string) *big.Int {
|
func MustParseBig256(s string) *big.Int {
|
||||||
v, ok := ParseBig256(s)
|
v, ok := ParseBig256(s)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -186,9 +186,8 @@ func U256(x *big.Int) *big.Int {
|
||||||
func S256(x *big.Int) *big.Int {
|
func S256(x *big.Int) *big.Int {
|
||||||
if x.Cmp(tt255) < 0 {
|
if x.Cmp(tt255) < 0 {
|
||||||
return x
|
return x
|
||||||
} else {
|
|
||||||
return new(big.Int).Sub(x, tt256)
|
|
||||||
}
|
}
|
||||||
|
return new(big.Int).Sub(x, tt256)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exp implements exponentiation by squaring.
|
// Exp implements exponentiation by squaring.
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,12 @@ func limitUnsigned256(x *Number) *Number {
|
||||||
func limitSigned256(x *Number) *Number {
|
func limitSigned256(x *Number) *Number {
|
||||||
if x.num.Cmp(tt255) < 0 {
|
if x.num.Cmp(tt255) < 0 {
|
||||||
return x
|
return x
|
||||||
} else {
|
}
|
||||||
x.num.Sub(x.num, tt256)
|
x.num.Sub(x.num, tt256)
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Number function
|
// Initialiser is a Number function
|
||||||
type Initialiser func(n int64) *Number
|
type Initialiser func(n int64) *Number
|
||||||
|
|
||||||
// A Number represents a generic integer with a bounding function limiter. Limit is called after each operations
|
// A Number represents a generic integer with a bounding function limiter. Limit is called after each operations
|
||||||
|
|
@ -51,65 +50,65 @@ type Number struct {
|
||||||
limit func(n *Number) *Number
|
limit func(n *Number) *Number
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a new initialiser for a new *Number without having to expose certain fields
|
// NewInitialiser returns a new initialiser for a new *Number without having to expose certain fields
|
||||||
func NewInitialiser(limiter func(*Number) *Number) Initialiser {
|
func NewInitialiser(limiter func(*Number) *Number) Initialiser {
|
||||||
return func(n int64) *Number {
|
return func(n int64) *Number {
|
||||||
return &Number{big.NewInt(n), limiter}
|
return &Number{big.NewInt(n), limiter}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a Number with a UNSIGNED limiter up to 256 bits
|
// Uint256 returns a Number with a UNSIGNED limiter up to 256 bits
|
||||||
func Uint256(n int64) *Number {
|
func Uint256(n int64) *Number {
|
||||||
return &Number{big.NewInt(n), limitUnsigned256}
|
return &Number{big.NewInt(n), limitUnsigned256}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a Number with a SIGNED limiter up to 256 bits
|
// Int256 returns Number with a SIGNED limiter up to 256 bits
|
||||||
func Int256(n int64) *Number {
|
func Int256(n int64) *Number {
|
||||||
return &Number{big.NewInt(n), limitSigned256}
|
return &Number{big.NewInt(n), limitSigned256}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a Number with a SIGNED unlimited size
|
// Big returns a Number with a SIGNED unlimited size
|
||||||
func Big(n int64) *Number {
|
func Big(n int64) *Number {
|
||||||
return &Number{big.NewInt(n), func(x *Number) *Number { return x }}
|
return &Number{big.NewInt(n), func(x *Number) *Number { return x }}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets i to sum of x+y
|
// Add sets i to sum of x+y
|
||||||
func (i *Number) Add(x, y *Number) *Number {
|
func (i *Number) Add(x, y *Number) *Number {
|
||||||
i.num.Add(x.num, y.num)
|
i.num.Add(x.num, y.num)
|
||||||
return i.limit(i)
|
return i.limit(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets i to difference of x-y
|
// Sub sets i to difference of x-y
|
||||||
func (i *Number) Sub(x, y *Number) *Number {
|
func (i *Number) Sub(x, y *Number) *Number {
|
||||||
i.num.Sub(x.num, y.num)
|
i.num.Sub(x.num, y.num)
|
||||||
return i.limit(i)
|
return i.limit(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets i to product of x*y
|
// Mul sets i to product of x*y
|
||||||
func (i *Number) Mul(x, y *Number) *Number {
|
func (i *Number) Mul(x, y *Number) *Number {
|
||||||
i.num.Mul(x.num, y.num)
|
i.num.Mul(x.num, y.num)
|
||||||
return i.limit(i)
|
return i.limit(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets i to the quotient prodject of x/y
|
// Div sets i to the quotient prodject of x/y
|
||||||
func (i *Number) Div(x, y *Number) *Number {
|
func (i *Number) Div(x, y *Number) *Number {
|
||||||
i.num.Div(x.num, y.num)
|
i.num.Div(x.num, y.num)
|
||||||
return i.limit(i)
|
return i.limit(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets i to x % y
|
// Mod sets i to x % y
|
||||||
func (i *Number) Mod(x, y *Number) *Number {
|
func (i *Number) Mod(x, y *Number) *Number {
|
||||||
i.num.Mod(x.num, y.num)
|
i.num.Mod(x.num, y.num)
|
||||||
return i.limit(i)
|
return i.limit(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets i to x << s
|
// Lsh sets i to x << s
|
||||||
func (i *Number) Lsh(x *Number, s uint) *Number {
|
func (i *Number) Lsh(x *Number, s uint) *Number {
|
||||||
i.num.Lsh(x.num, s)
|
i.num.Lsh(x.num, s)
|
||||||
return i.limit(i)
|
return i.limit(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets i to x^y
|
// Pow sets i to x^y
|
||||||
func (i *Number) Pow(x, y *Number) *Number {
|
func (i *Number) Pow(x, y *Number) *Number {
|
||||||
i.num.Exp(x.num, y.num, big.NewInt(0))
|
i.num.Exp(x.num, y.num, big.NewInt(0))
|
||||||
return i.limit(i)
|
return i.limit(i)
|
||||||
|
|
@ -117,13 +116,13 @@ func (i *Number) Pow(x, y *Number) *Number {
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
|
|
||||||
// Set x to i
|
// Set sets x to i
|
||||||
func (i *Number) Set(x *Number) *Number {
|
func (i *Number) Set(x *Number) *Number {
|
||||||
i.num.Set(x.num)
|
i.num.Set(x.num)
|
||||||
return i.limit(i)
|
return i.limit(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set x bytes to i
|
// SetBytes sets x bytes to i
|
||||||
func (i *Number) SetBytes(x []byte) *Number {
|
func (i *Number) SetBytes(x []byte) *Number {
|
||||||
i.num.SetBytes(x)
|
i.num.SetBytes(x)
|
||||||
return i.limit(i)
|
return i.limit(i)
|
||||||
|
|
@ -140,12 +139,12 @@ func (i *Number) Cmp(x *Number) int {
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
// Returns the string representation of i
|
// String returns the string representation of i
|
||||||
func (i *Number) String() string {
|
func (i *Number) String() string {
|
||||||
return i.num.String()
|
return i.num.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the byte representation of i
|
// Bytes returns the byte representation of i
|
||||||
func (i *Number) Bytes() []byte {
|
func (i *Number) Bytes() []byte {
|
||||||
return i.num.Bytes()
|
return i.num.Bytes()
|
||||||
}
|
}
|
||||||
|
|
@ -160,17 +159,17 @@ func (i *Number) Int64() int64 {
|
||||||
return i.num.Int64()
|
return i.num.Int64()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the signed version of i
|
// Int256 returns the signed version of i
|
||||||
func (i *Number) Int256() *Number {
|
func (i *Number) Int256() *Number {
|
||||||
return Int(0).Set(i)
|
return Int(0).Set(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the unsigned version of i
|
// Uint256 returns the unsigned version of i
|
||||||
func (i *Number) Uint256() *Number {
|
func (i *Number) Uint256() *Number {
|
||||||
return Uint(0).Set(i)
|
return Uint(0).Set(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the index of the first bit that's set to 1
|
// FirstBitSet returns the index of the first bit that's set to 1
|
||||||
func (i *Number) FirstBitSet() int {
|
func (i *Number) FirstBitSet() int {
|
||||||
for j := 0; j < i.num.BitLen(); j++ {
|
for j := 0; j < i.num.BitLen(); j++ {
|
||||||
if i.num.Bit(j) > 0 {
|
if i.num.Bit(j) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ func MakeName(name, version string) string {
|
||||||
return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version())
|
return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileExist checks if a file exists at filePath.
|
||||||
func FileExist(filePath string) bool {
|
func FileExist(filePath string) bool {
|
||||||
_, err := os.Stat(filePath)
|
_, err := os.Stat(filePath)
|
||||||
if err != nil && os.IsNotExist(err) {
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
|
@ -39,9 +40,10 @@ func FileExist(filePath string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func AbsolutePath(Datadir string, filename string) string {
|
// AbsolutePath returns datadir + filename, or filename if it is absolute.
|
||||||
|
func AbsolutePath(datadir string, filename string) string {
|
||||||
if filepath.IsAbs(filename) {
|
if filepath.IsAbs(filename) {
|
||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
return filepath.Join(Datadir, filename)
|
return filepath.Join(datadir, filename)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,18 +42,29 @@ var (
|
||||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||||
type Hash [HashLength]byte
|
type Hash [HashLength]byte
|
||||||
|
|
||||||
|
// BytesToHash sets b to hash.
|
||||||
|
// If b is larger than len(h), b will be cropped from the left.
|
||||||
func BytesToHash(b []byte) Hash {
|
func BytesToHash(b []byte) Hash {
|
||||||
var h Hash
|
var h Hash
|
||||||
h.SetBytes(b)
|
h.SetBytes(b)
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BigToHash sets byte representation of b to hash.
|
||||||
|
// If b is larger than len(h), b will be cropped from the left.
|
||||||
func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
|
func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
|
||||||
|
|
||||||
|
// HexToHash sets byte representation of s to hash.
|
||||||
|
// If b is larger than len(h), b will be cropped from the left.
|
||||||
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
||||||
|
|
||||||
// Get the string representation of the underlying hash
|
// Bytes gets the byte representation of the underlying hash.
|
||||||
func (h Hash) Str() string { return string(h[:]) }
|
|
||||||
func (h Hash) Bytes() []byte { return h[:] }
|
func (h Hash) Bytes() []byte { return h[:] }
|
||||||
|
|
||||||
|
// Big converts a hash to a big integer.
|
||||||
func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) }
|
func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) }
|
||||||
|
|
||||||
|
// Hex converts a hash to a hex string.
|
||||||
func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
|
func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
|
||||||
|
|
||||||
// TerminalString implements log.TerminalStringer, formatting a string for console
|
// TerminalString implements log.TerminalStringer, formatting a string for console
|
||||||
|
|
@ -89,7 +100,8 @@ func (h Hash) MarshalText() ([]byte, error) {
|
||||||
return hexutil.Bytes(h[:]).MarshalText()
|
return hexutil.Bytes(h[:]).MarshalText()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the hash to the value of b. If b is larger than len(h), 'b' will be cropped (from the left).
|
// SetBytes sets the hash to the value of b.
|
||||||
|
// If b is larger than len(h), b will be cropped from the left.
|
||||||
func (h *Hash) SetBytes(b []byte) {
|
func (h *Hash) SetBytes(b []byte) {
|
||||||
if len(b) > len(h) {
|
if len(b) > len(h) {
|
||||||
b = b[len(b)-HashLength:]
|
b = b[len(b)-HashLength:]
|
||||||
|
|
@ -98,16 +110,6 @@ func (h *Hash) SetBytes(b []byte) {
|
||||||
copy(h[HashLength-len(b):], b)
|
copy(h[HashLength-len(b):], b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set string `s` to h. If s is larger than len(h) s will be cropped (from left) to fit.
|
|
||||||
func (h *Hash) SetString(s string) { h.SetBytes([]byte(s)) }
|
|
||||||
|
|
||||||
// Sets h to other
|
|
||||||
func (h *Hash) Set(other Hash) {
|
|
||||||
for i, v := range other {
|
|
||||||
h[i] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate implements testing/quick.Generator.
|
// Generate implements testing/quick.Generator.
|
||||||
func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value {
|
func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||||
m := rand.Intn(len(h))
|
m := rand.Intn(len(h))
|
||||||
|
|
@ -117,10 +119,6 @@ func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||||
return reflect.ValueOf(h)
|
return reflect.ValueOf(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
func EmptyHash(h Hash) bool {
|
|
||||||
return h == Hash{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnprefixedHash allows marshaling a Hash without 0x prefix.
|
// UnprefixedHash allows marshaling a Hash without 0x prefix.
|
||||||
type UnprefixedHash Hash
|
type UnprefixedHash Hash
|
||||||
|
|
||||||
|
|
@ -139,12 +137,20 @@ func (h UnprefixedHash) MarshalText() ([]byte, error) {
|
||||||
// Address represents the 20 byte address of an Ethereum account.
|
// Address represents the 20 byte address of an Ethereum account.
|
||||||
type Address [AddressLength]byte
|
type Address [AddressLength]byte
|
||||||
|
|
||||||
|
// BytesToAddress returns Address with value b.
|
||||||
|
// If b is larger than len(h), b will be cropped from the left.
|
||||||
func BytesToAddress(b []byte) Address {
|
func BytesToAddress(b []byte) Address {
|
||||||
var a Address
|
var a Address
|
||||||
a.SetBytes(b)
|
a.SetBytes(b)
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BigToAddress returns Address with byte values of b.
|
||||||
|
// If b is larger than len(h), b will be cropped from the left.
|
||||||
func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) }
|
func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) }
|
||||||
|
|
||||||
|
// HexToAddress returns Address with byte values of s.
|
||||||
|
// If s is larger than len(h), s will be cropped from the left.
|
||||||
func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) }
|
func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) }
|
||||||
|
|
||||||
// IsHexAddress verifies whether a string can represent a valid hex-encoded
|
// IsHexAddress verifies whether a string can represent a valid hex-encoded
|
||||||
|
|
@ -156,10 +162,13 @@ func IsHexAddress(s string) bool {
|
||||||
return len(s) == 2*AddressLength && isHex(s)
|
return len(s) == 2*AddressLength && isHex(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the string representation of the underlying address
|
// Bytes gets the string representation of the underlying address.
|
||||||
func (a Address) Str() string { return string(a[:]) }
|
|
||||||
func (a Address) Bytes() []byte { return a[:] }
|
func (a Address) Bytes() []byte { return a[:] }
|
||||||
|
|
||||||
|
// Big converts an address to a big integer.
|
||||||
func (a Address) Big() *big.Int { return new(big.Int).SetBytes(a[:]) }
|
func (a Address) Big() *big.Int { return new(big.Int).SetBytes(a[:]) }
|
||||||
|
|
||||||
|
// Hash converts an address to a hash by left-padding it with zeros.
|
||||||
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
||||||
|
|
||||||
// Hex returns an EIP55-compliant hex string representation of the address.
|
// Hex returns an EIP55-compliant hex string representation of the address.
|
||||||
|
|
@ -184,7 +193,7 @@ func (a Address) Hex() string {
|
||||||
return "0x" + string(result)
|
return "0x" + string(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements the stringer interface and is used also by the logger.
|
// String implements fmt.Stringer.
|
||||||
func (a Address) String() string {
|
func (a Address) String() string {
|
||||||
return a.Hex()
|
return a.Hex()
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +204,8 @@ func (a Address) Format(s fmt.State, c rune) {
|
||||||
fmt.Fprintf(s, "%"+string(c), a[:])
|
fmt.Fprintf(s, "%"+string(c), a[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the address to the value of b. If b is larger than len(a) it will panic
|
// SetBytes sets the address to the value of b.
|
||||||
|
// If b is larger than len(a) it will panic.
|
||||||
func (a *Address) SetBytes(b []byte) {
|
func (a *Address) SetBytes(b []byte) {
|
||||||
if len(b) > len(a) {
|
if len(b) > len(a) {
|
||||||
b = b[len(b)-AddressLength:]
|
b = b[len(b)-AddressLength:]
|
||||||
|
|
@ -203,16 +213,6 @@ func (a *Address) SetBytes(b []byte) {
|
||||||
copy(a[AddressLength-len(b):], b)
|
copy(a[AddressLength-len(b):], b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set string `s` to a. If s is larger than len(a) it will panic
|
|
||||||
func (a *Address) SetString(s string) { a.SetBytes([]byte(s)) }
|
|
||||||
|
|
||||||
// Sets a to other
|
|
||||||
func (a *Address) Set(other Address) {
|
|
||||||
for i, v := range other {
|
|
||||||
a[i] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalText returns the hex representation of a.
|
// MarshalText returns the hex representation of a.
|
||||||
func (a Address) MarshalText() ([]byte, error) {
|
func (a Address) MarshalText() ([]byte, error) {
|
||||||
return hexutil.Bytes(a[:]).MarshalText()
|
return hexutil.Bytes(a[:]).MarshalText()
|
||||||
|
|
@ -228,7 +228,7 @@ func (a *Address) UnmarshalJSON(input []byte) error {
|
||||||
return hexutil.UnmarshalFixedJSON(addressT, input, a[:])
|
return hexutil.UnmarshalFixedJSON(addressT, input, a[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnprefixedHash allows marshaling an Address without 0x prefix.
|
// UnprefixedAddress allows marshaling an Address without 0x prefix.
|
||||||
type UnprefixedAddress Address
|
type UnprefixedAddress Address
|
||||||
|
|
||||||
// UnmarshalText decodes the address from hex. The 0x prefix is optional.
|
// UnmarshalText decodes the address from hex. The 0x prefix is optional.
|
||||||
|
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
// Copyright 2015 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/>.
|
|
||||||
|
|
||||||
// +build none
|
|
||||||
//sed -e 's/_N_/Hash/g' -e 's/_S_/32/g' -e '1d' types_template.go | gofmt -w hash.go
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import "math/big"
|
|
||||||
|
|
||||||
type _N_ [_S_]byte
|
|
||||||
|
|
||||||
func BytesTo_N_(b []byte) _N_ {
|
|
||||||
var h _N_
|
|
||||||
h.SetBytes(b)
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
func StringTo_N_(s string) _N_ { return BytesTo_N_([]byte(s)) }
|
|
||||||
func BigTo_N_(b *big.Int) _N_ { return BytesTo_N_(b.Bytes()) }
|
|
||||||
func HexTo_N_(s string) _N_ { return BytesTo_N_(FromHex(s)) }
|
|
||||||
|
|
||||||
// Don't use the default 'String' method in case we want to overwrite
|
|
||||||
|
|
||||||
// Get the string representation of the underlying hash
|
|
||||||
func (h _N_) Str() string { return string(h[:]) }
|
|
||||||
func (h _N_) Bytes() []byte { return h[:] }
|
|
||||||
func (h _N_) Big() *big.Int { return new(big.Int).SetBytes(h[:]) }
|
|
||||||
func (h _N_) Hex() string { return "0x" + Bytes2Hex(h[:]) }
|
|
||||||
|
|
||||||
// Sets the hash to the value of b. If b is larger than len(h) it will panic
|
|
||||||
func (h *_N_) SetBytes(b []byte) {
|
|
||||||
// Use the right most bytes
|
|
||||||
if len(b) > len(h) {
|
|
||||||
b = b[len(b)-_S_:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse the loop
|
|
||||||
for i := len(b) - 1; i >= 0; i-- {
|
|
||||||
h[_S_-len(b)+i] = b[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set string `s` to h. If s is larger than len(h) it will panic
|
|
||||||
func (h *_N_) SetString(s string) { h.SetBytes([]byte(s)) }
|
|
||||||
|
|
||||||
// Sets h to other
|
|
||||||
func (h *_N_) Set(other _N_) {
|
|
||||||
for i, v := range other {
|
|
||||||
h[i] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -383,7 +383,7 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo
|
||||||
// If an on-disk checkpoint snapshot can be found, use that
|
// If an on-disk checkpoint snapshot can be found, use that
|
||||||
if number%checkpointInterval == 0 {
|
if number%checkpointInterval == 0 {
|
||||||
if s, err := loadSnapshot(c.config, c.signatures, c.db, hash); err == nil {
|
if s, err := loadSnapshot(c.config, c.signatures, c.db, hash); err == nil {
|
||||||
log.Trace("Loaded voting snapshot form disk", "number", number, "hash", hash)
|
log.Trace("Loaded voting snapshot from disk", "number", number, "hash", hash)
|
||||||
snap = s
|
snap = s
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TxPreEvent is posted when a transaction enters the transaction pool.
|
// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
|
||||||
type TxPreEvent struct{ Tx *types.Transaction }
|
type NewTxsEvent struct{ Txs []*types.Transaction }
|
||||||
|
|
||||||
// PendingLogsEvent is posted pre mining and notifies of pending logs.
|
// PendingLogsEvent is posted pre mining and notifies of pending logs.
|
||||||
type PendingLogsEvent struct {
|
type PendingLogsEvent struct {
|
||||||
|
|
@ -35,9 +35,6 @@ type PendingStateEvent struct{}
|
||||||
// NewMinedBlockEvent is posted when a block has been imported.
|
// NewMinedBlockEvent is posted when a block has been imported.
|
||||||
type NewMinedBlockEvent struct{ Block *types.Block }
|
type NewMinedBlockEvent struct{ Block *types.Block }
|
||||||
|
|
||||||
// RemovedTransactionEvent is posted when a reorg happens
|
|
||||||
type RemovedTransactionEvent struct{ Txs types.Transactions }
|
|
||||||
|
|
||||||
// RemovedLogsEvent is posted when a reorg happens
|
// RemovedLogsEvent is posted when a reorg happens
|
||||||
type RemovedLogsEvent struct{ Logs []*types.Log }
|
type RemovedLogsEvent struct{ Logs []*types.Log }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ func (s *StateSuite) TestNull(c *checker.C) {
|
||||||
s.state.SetState(address, common.Hash{}, value)
|
s.state.SetState(address, common.Hash{}, value)
|
||||||
s.state.Commit(false)
|
s.state.Commit(false)
|
||||||
value = s.state.GetState(address, common.Hash{})
|
value = s.state.GetState(address, common.Hash{})
|
||||||
if !common.EmptyHash(value) {
|
if value != (common.Hash{}) {
|
||||||
c.Errorf("expected empty hash. got %x", value)
|
c.Errorf("expected empty hash. got %x", value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,7 @@ func (self *StateDB) deleteStateObject(stateObject *stateObject) {
|
||||||
self.setError(self.trie.TryDelete(addr[:]))
|
self.setError(self.trie.TryDelete(addr[:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve a state object given my the address. Returns nil if not found.
|
// Retrieve a state object given by the address. Returns nil if not found.
|
||||||
func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
|
func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
|
||||||
// Prefer 'live' objects.
|
// Prefer 'live' objects.
|
||||||
if obj := self.stateObjects[addr]; obj != nil {
|
if obj := self.stateObjects[addr]; obj != nil {
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewStateSync create a new state trie download scheduler.
|
// NewStateSync create a new state trie download scheduler.
|
||||||
func NewStateSync(root common.Hash, database trie.DatabaseReader) *trie.TrieSync {
|
func NewStateSync(root common.Hash, database trie.DatabaseReader) *trie.Sync {
|
||||||
var syncer *trie.TrieSync
|
var syncer *trie.Sync
|
||||||
callback := func(leaf []byte, parent common.Hash) error {
|
callback := func(leaf []byte, parent common.Hash) error {
|
||||||
var obj Account
|
var obj Account
|
||||||
if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil {
|
if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil {
|
||||||
|
|
@ -36,6 +36,6 @@ func NewStateSync(root common.Hash, database trie.DatabaseReader) *trie.TrieSync
|
||||||
syncer.AddRawEntry(common.BytesToHash(obj.CodeHash), 64, parent)
|
syncer.AddRawEntry(common.BytesToHash(obj.CodeHash), 64, parent)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
syncer = trie.NewTrieSync(root, database, callback)
|
syncer = trie.NewSync(root, database, callback)
|
||||||
return syncer
|
return syncer
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func newTxJournal(path string) *txJournal {
|
||||||
|
|
||||||
// load parses a transaction journal dump from disk, loading its contents into
|
// load parses a transaction journal dump from disk, loading its contents into
|
||||||
// the specified pool.
|
// the specified pool.
|
||||||
func (journal *txJournal) load(add func(*types.Transaction) error) error {
|
func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
|
||||||
// Skip the parsing if the journal file doens't exist at all
|
// Skip the parsing if the journal file doens't exist at all
|
||||||
if _, err := os.Stat(journal.path); os.IsNotExist(err) {
|
if _, err := os.Stat(journal.path); os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -76,7 +76,21 @@ func (journal *txJournal) load(add func(*types.Transaction) error) error {
|
||||||
stream := rlp.NewStream(input, 0)
|
stream := rlp.NewStream(input, 0)
|
||||||
total, dropped := 0, 0
|
total, dropped := 0, 0
|
||||||
|
|
||||||
var failure error
|
// Create a method to load a limited batch of transactions and bump the
|
||||||
|
// appropriate progress counters. Then use this method to load all the
|
||||||
|
// journalled transactions in small-ish batches.
|
||||||
|
loadBatch := func(txs types.Transactions) {
|
||||||
|
for _, err := range add(txs) {
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to add journaled transaction", "err", err)
|
||||||
|
dropped++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
failure error
|
||||||
|
batch types.Transactions
|
||||||
|
)
|
||||||
for {
|
for {
|
||||||
// Parse the next transaction and terminate on error
|
// Parse the next transaction and terminate on error
|
||||||
tx := new(types.Transaction)
|
tx := new(types.Transaction)
|
||||||
|
|
@ -84,14 +98,17 @@ func (journal *txJournal) load(add func(*types.Transaction) error) error {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
failure = err
|
failure = err
|
||||||
}
|
}
|
||||||
|
if batch.Len() > 0 {
|
||||||
|
loadBatch(batch)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Import the transaction and bump the appropriate progress counters
|
// New transaction parsed, queue up for later, import if threnshold is reached
|
||||||
total++
|
total++
|
||||||
if err = add(tx); err != nil {
|
|
||||||
log.Debug("Failed to add journaled transaction", "err", err)
|
if batch = append(batch, tx); batch.Len() > 1024 {
|
||||||
dropped++
|
loadBatch(batch)
|
||||||
continue
|
batch = batch[:0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped)
|
log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped)
|
||||||
|
|
|
||||||
|
|
@ -397,13 +397,13 @@ func (h *priceHeap) Pop() interface{} {
|
||||||
// txPricedList is a price-sorted heap to allow operating on transactions pool
|
// txPricedList is a price-sorted heap to allow operating on transactions pool
|
||||||
// contents in a price-incrementing way.
|
// contents in a price-incrementing way.
|
||||||
type txPricedList struct {
|
type txPricedList struct {
|
||||||
all *map[common.Hash]*types.Transaction // Pointer to the map of all transactions
|
all *txLookup // Pointer to the map of all transactions
|
||||||
items *priceHeap // Heap of prices of all the stored transactions
|
items *priceHeap // Heap of prices of all the stored transactions
|
||||||
stales int // Number of stale price points to (re-heap trigger)
|
stales int // Number of stale price points to (re-heap trigger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTxPricedList creates a new price-sorted transaction heap.
|
// newTxPricedList creates a new price-sorted transaction heap.
|
||||||
func newTxPricedList(all *map[common.Hash]*types.Transaction) *txPricedList {
|
func newTxPricedList(all *txLookup) *txPricedList {
|
||||||
return &txPricedList{
|
return &txPricedList{
|
||||||
all: all,
|
all: all,
|
||||||
items: new(priceHeap),
|
items: new(priceHeap),
|
||||||
|
|
@ -425,12 +425,13 @@ func (l *txPricedList) Removed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Seems we've reached a critical number of stale transactions, reheap
|
// Seems we've reached a critical number of stale transactions, reheap
|
||||||
reheap := make(priceHeap, 0, len(*l.all))
|
reheap := make(priceHeap, 0, l.all.Count())
|
||||||
|
|
||||||
l.stales, l.items = 0, &reheap
|
l.stales, l.items = 0, &reheap
|
||||||
for _, tx := range *l.all {
|
l.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
|
||||||
*l.items = append(*l.items, tx)
|
*l.items = append(*l.items, tx)
|
||||||
}
|
return true
|
||||||
|
})
|
||||||
heap.Init(l.items)
|
heap.Init(l.items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,7 +444,7 @@ func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transact
|
||||||
for len(*l.items) > 0 {
|
for len(*l.items) > 0 {
|
||||||
// Discard stale transactions if found during cleanup
|
// Discard stale transactions if found during cleanup
|
||||||
tx := heap.Pop(l.items).(*types.Transaction)
|
tx := heap.Pop(l.items).(*types.Transaction)
|
||||||
if _, ok := (*l.all)[tx.Hash()]; !ok {
|
if l.all.Get(tx.Hash()) == nil {
|
||||||
l.stales--
|
l.stales--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -475,7 +476,7 @@ func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) boo
|
||||||
// Discard stale price points if found at the heap start
|
// Discard stale price points if found at the heap start
|
||||||
for len(*l.items) > 0 {
|
for len(*l.items) > 0 {
|
||||||
head := []*types.Transaction(*l.items)[0]
|
head := []*types.Transaction(*l.items)[0]
|
||||||
if _, ok := (*l.all)[head.Hash()]; !ok {
|
if l.all.Get(head.Hash()) == nil {
|
||||||
l.stales--
|
l.stales--
|
||||||
heap.Pop(l.items)
|
heap.Pop(l.items)
|
||||||
continue
|
continue
|
||||||
|
|
@ -500,7 +501,7 @@ func (l *txPricedList) Discard(count int, local *accountSet) types.Transactions
|
||||||
for len(*l.items) > 0 && count > 0 {
|
for len(*l.items) > 0 && count > 0 {
|
||||||
// Discard stale transactions if found during cleanup
|
// Discard stale transactions if found during cleanup
|
||||||
tx := heap.Pop(l.items).(*types.Transaction)
|
tx := heap.Pop(l.items).(*types.Transaction)
|
||||||
if _, ok := (*l.all)[tx.Hash()]; !ok {
|
if l.all.Get(tx.Hash()) == nil {
|
||||||
l.stales--
|
l.stales--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
150
core/tx_pool.go
150
core/tx_pool.go
|
|
@ -203,7 +203,7 @@ type TxPool struct {
|
||||||
pending map[common.Address]*txList // All currently processable transactions
|
pending map[common.Address]*txList // All currently processable transactions
|
||||||
queue map[common.Address]*txList // Queued but non-processable transactions
|
queue map[common.Address]*txList // Queued but non-processable transactions
|
||||||
beats map[common.Address]time.Time // Last heartbeat from each known account
|
beats map[common.Address]time.Time // Last heartbeat from each known account
|
||||||
all map[common.Hash]*types.Transaction // All transactions to allow lookups
|
all *txLookup // All transactions to allow lookups
|
||||||
priced *txPricedList // All transactions sorted by price
|
priced *txPricedList // All transactions sorted by price
|
||||||
|
|
||||||
wg sync.WaitGroup // for shutdown sync
|
wg sync.WaitGroup // for shutdown sync
|
||||||
|
|
@ -226,19 +226,19 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
|
||||||
pending: make(map[common.Address]*txList),
|
pending: make(map[common.Address]*txList),
|
||||||
queue: make(map[common.Address]*txList),
|
queue: make(map[common.Address]*txList),
|
||||||
beats: make(map[common.Address]time.Time),
|
beats: make(map[common.Address]time.Time),
|
||||||
all: make(map[common.Hash]*types.Transaction),
|
all: newTxLookup(),
|
||||||
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
||||||
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
|
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
|
||||||
}
|
}
|
||||||
pool.locals = newAccountSet(pool.signer)
|
pool.locals = newAccountSet(pool.signer)
|
||||||
pool.priced = newTxPricedList(&pool.all)
|
pool.priced = newTxPricedList(pool.all)
|
||||||
pool.reset(nil, chain.CurrentBlock().Header())
|
pool.reset(nil, chain.CurrentBlock().Header())
|
||||||
|
|
||||||
// If local transactions and journaling is enabled, load from disk
|
// If local transactions and journaling is enabled, load from disk
|
||||||
if !config.NoLocals && config.Journal != "" {
|
if !config.NoLocals && config.Journal != "" {
|
||||||
pool.journal = newTxJournal(config.Journal)
|
pool.journal = newTxJournal(config.Journal)
|
||||||
|
|
||||||
if err := pool.journal.load(pool.AddLocal); err != nil {
|
if err := pool.journal.load(pool.AddLocals); err != nil {
|
||||||
log.Warn("Failed to load transaction journal", "err", err)
|
log.Warn("Failed to load transaction journal", "err", err)
|
||||||
}
|
}
|
||||||
if err := pool.journal.rotate(pool.local()); err != nil {
|
if err := pool.journal.rotate(pool.local()); err != nil {
|
||||||
|
|
@ -444,9 +444,9 @@ func (pool *TxPool) Stop() {
|
||||||
log.Info("Transaction pool stopped")
|
log.Info("Transaction pool stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribeTxPreEvent registers a subscription of TxPreEvent and
|
// SubscribeNewTxsEvent registers a subscription of NewTxsEvent and
|
||||||
// starts sending event to the given channel.
|
// starts sending event to the given channel.
|
||||||
func (pool *TxPool) SubscribeTxPreEvent(ch chan<- TxPreEvent) event.Subscription {
|
func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- NewTxsEvent) event.Subscription {
|
||||||
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -605,7 +605,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
||||||
// If the transaction is already known, discard it
|
// If the transaction is already known, discard it
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
if pool.all[hash] != nil {
|
if pool.all.Get(hash) != nil {
|
||||||
log.Trace("Discarding already known transaction", "hash", hash)
|
log.Trace("Discarding already known transaction", "hash", hash)
|
||||||
return false, fmt.Errorf("known transaction: %x", hash)
|
return false, fmt.Errorf("known transaction: %x", hash)
|
||||||
}
|
}
|
||||||
|
|
@ -616,7 +616,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
// If the transaction pool is full, discard underpriced transactions
|
// If the transaction pool is full, discard underpriced transactions
|
||||||
if uint64(len(pool.all)) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
|
if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
|
||||||
// If the new transaction is underpriced, don't accept it
|
// If the new transaction is underpriced, don't accept it
|
||||||
if !local && pool.priced.Underpriced(tx, pool.locals) {
|
if !local && pool.priced.Underpriced(tx, pool.locals) {
|
||||||
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
|
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
|
||||||
|
|
@ -624,7 +624,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
||||||
return false, ErrUnderpriced
|
return false, ErrUnderpriced
|
||||||
}
|
}
|
||||||
// New transaction is better than our worse ones, make room for it
|
// New transaction is better than our worse ones, make room for it
|
||||||
drop := pool.priced.Discard(len(pool.all)-int(pool.config.GlobalSlots+pool.config.GlobalQueue-1), pool.locals)
|
drop := pool.priced.Discard(pool.all.Count()-int(pool.config.GlobalSlots+pool.config.GlobalQueue-1), pool.locals)
|
||||||
for _, tx := range drop {
|
for _, tx := range drop {
|
||||||
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
|
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
|
||||||
underpricedTxCounter.Inc(1)
|
underpricedTxCounter.Inc(1)
|
||||||
|
|
@ -642,18 +642,18 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
||||||
}
|
}
|
||||||
// New transaction is better, replace old one
|
// New transaction is better, replace old one
|
||||||
if old != nil {
|
if old != nil {
|
||||||
delete(pool.all, old.Hash())
|
pool.all.Remove(old.Hash())
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
pendingReplaceCounter.Inc(1)
|
pendingReplaceCounter.Inc(1)
|
||||||
}
|
}
|
||||||
pool.all[tx.Hash()] = tx
|
pool.all.Add(tx)
|
||||||
pool.priced.Put(tx)
|
pool.priced.Put(tx)
|
||||||
pool.journalTx(from, tx)
|
pool.journalTx(from, tx)
|
||||||
|
|
||||||
log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())
|
log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())
|
||||||
|
|
||||||
// We've directly injected a replacement transaction, notify subsystems
|
// We've directly injected a replacement transaction, notify subsystems
|
||||||
go pool.txFeed.Send(TxPreEvent{tx})
|
go pool.txFeed.Send(NewTxsEvent{types.Transactions{tx}})
|
||||||
|
|
||||||
return old != nil, nil
|
return old != nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -689,12 +689,12 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er
|
||||||
}
|
}
|
||||||
// Discard any previous transaction and mark this
|
// Discard any previous transaction and mark this
|
||||||
if old != nil {
|
if old != nil {
|
||||||
delete(pool.all, old.Hash())
|
pool.all.Remove(old.Hash())
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
queuedReplaceCounter.Inc(1)
|
queuedReplaceCounter.Inc(1)
|
||||||
}
|
}
|
||||||
if pool.all[hash] == nil {
|
if pool.all.Get(hash) == nil {
|
||||||
pool.all[hash] = tx
|
pool.all.Add(tx)
|
||||||
pool.priced.Put(tx)
|
pool.priced.Put(tx)
|
||||||
}
|
}
|
||||||
return old != nil, nil
|
return old != nil, nil
|
||||||
|
|
@ -712,10 +712,11 @@ func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// promoteTx adds a transaction to the pending (processable) list of transactions.
|
// promoteTx adds a transaction to the pending (processable) list of transactions
|
||||||
|
// and returns whether it was inserted or an older was better.
|
||||||
//
|
//
|
||||||
// Note, this method assumes the pool lock is held!
|
// Note, this method assumes the pool lock is held!
|
||||||
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) {
|
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool {
|
||||||
// Try to insert the transaction into the pending queue
|
// Try to insert the transaction into the pending queue
|
||||||
if pool.pending[addr] == nil {
|
if pool.pending[addr] == nil {
|
||||||
pool.pending[addr] = newTxList(true)
|
pool.pending[addr] = newTxList(true)
|
||||||
|
|
@ -725,29 +726,29 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
|
||||||
inserted, old := list.Add(tx, pool.config.PriceBump)
|
inserted, old := list.Add(tx, pool.config.PriceBump)
|
||||||
if !inserted {
|
if !inserted {
|
||||||
// An older transaction was better, discard this
|
// An older transaction was better, discard this
|
||||||
delete(pool.all, hash)
|
pool.all.Remove(hash)
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
|
|
||||||
pendingDiscardCounter.Inc(1)
|
pendingDiscardCounter.Inc(1)
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
// Otherwise discard any previous transaction and mark this
|
// Otherwise discard any previous transaction and mark this
|
||||||
if old != nil {
|
if old != nil {
|
||||||
delete(pool.all, old.Hash())
|
pool.all.Remove(old.Hash())
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
|
|
||||||
pendingReplaceCounter.Inc(1)
|
pendingReplaceCounter.Inc(1)
|
||||||
}
|
}
|
||||||
// Failsafe to work around direct pending inserts (tests)
|
// Failsafe to work around direct pending inserts (tests)
|
||||||
if pool.all[hash] == nil {
|
if pool.all.Get(hash) == nil {
|
||||||
pool.all[hash] = tx
|
pool.all.Add(tx)
|
||||||
pool.priced.Put(tx)
|
pool.priced.Put(tx)
|
||||||
}
|
}
|
||||||
// Set the potentially new pending nonce and notify any subsystems of the new tx
|
// Set the potentially new pending nonce and notify any subsystems of the new tx
|
||||||
pool.beats[addr] = time.Now()
|
pool.beats[addr] = time.Now()
|
||||||
pool.pendingState.SetNonce(addr, tx.Nonce()+1)
|
pool.pendingState.SetNonce(addr, tx.Nonce()+1)
|
||||||
|
|
||||||
go pool.txFeed.Send(TxPreEvent{tx})
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLocal enqueues a single transaction into the pool if it is valid, marking
|
// AddLocal enqueues a single transaction into the pool if it is valid, marking
|
||||||
|
|
@ -839,7 +840,7 @@ func (pool *TxPool) Status(hashes []common.Hash) []TxStatus {
|
||||||
|
|
||||||
status := make([]TxStatus, len(hashes))
|
status := make([]TxStatus, len(hashes))
|
||||||
for i, hash := range hashes {
|
for i, hash := range hashes {
|
||||||
if tx := pool.all[hash]; tx != nil {
|
if tx := pool.all.Get(hash); tx != nil {
|
||||||
from, _ := types.Sender(pool.signer, tx) // already validated
|
from, _ := types.Sender(pool.signer, tx) // already validated
|
||||||
if pool.pending[from] != nil && pool.pending[from].txs.items[tx.Nonce()] != nil {
|
if pool.pending[from] != nil && pool.pending[from].txs.items[tx.Nonce()] != nil {
|
||||||
status[i] = TxStatusPending
|
status[i] = TxStatusPending
|
||||||
|
|
@ -854,24 +855,21 @@ func (pool *TxPool) Status(hashes []common.Hash) []TxStatus {
|
||||||
// Get returns a transaction if it is contained in the pool
|
// Get returns a transaction if it is contained in the pool
|
||||||
// and nil otherwise.
|
// and nil otherwise.
|
||||||
func (pool *TxPool) Get(hash common.Hash) *types.Transaction {
|
func (pool *TxPool) Get(hash common.Hash) *types.Transaction {
|
||||||
pool.mu.RLock()
|
return pool.all.Get(hash)
|
||||||
defer pool.mu.RUnlock()
|
|
||||||
|
|
||||||
return pool.all[hash]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeTx removes a single transaction from the queue, moving all subsequent
|
// removeTx removes a single transaction from the queue, moving all subsequent
|
||||||
// transactions back to the future queue.
|
// transactions back to the future queue.
|
||||||
func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
|
func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
|
||||||
// Fetch the transaction we wish to delete
|
// Fetch the transaction we wish to delete
|
||||||
tx, ok := pool.all[hash]
|
tx := pool.all.Get(hash)
|
||||||
if !ok {
|
if tx == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addr, _ := types.Sender(pool.signer, tx) // already validated during insertion
|
addr, _ := types.Sender(pool.signer, tx) // already validated during insertion
|
||||||
|
|
||||||
// Remove it from the list of known transactions
|
// Remove it from the list of known transactions
|
||||||
delete(pool.all, hash)
|
pool.all.Remove(hash)
|
||||||
if outofbound {
|
if outofbound {
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
}
|
}
|
||||||
|
|
@ -907,6 +905,9 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
|
||||||
// future queue to the set of pending transactions. During this process, all
|
// future queue to the set of pending transactions. During this process, all
|
||||||
// invalidated transactions (low nonce, low balance) are deleted.
|
// invalidated transactions (low nonce, low balance) are deleted.
|
||||||
func (pool *TxPool) promoteExecutables(accounts []common.Address) {
|
func (pool *TxPool) promoteExecutables(accounts []common.Address) {
|
||||||
|
// Track the promoted transactions to broadcast them at once
|
||||||
|
var promoted []*types.Transaction
|
||||||
|
|
||||||
// Gather all the accounts potentially needing updates
|
// Gather all the accounts potentially needing updates
|
||||||
if accounts == nil {
|
if accounts == nil {
|
||||||
accounts = make([]common.Address, 0, len(pool.queue))
|
accounts = make([]common.Address, 0, len(pool.queue))
|
||||||
|
|
@ -924,7 +925,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
|
||||||
for _, tx := range list.Forward(pool.currentState.GetNonce(addr)) {
|
for _, tx := range list.Forward(pool.currentState.GetNonce(addr)) {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
log.Trace("Removed old queued transaction", "hash", hash)
|
log.Trace("Removed old queued transaction", "hash", hash)
|
||||||
delete(pool.all, hash)
|
pool.all.Remove(hash)
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
}
|
}
|
||||||
// Drop all transactions that are too costly (low balance or out of gas)
|
// Drop all transactions that are too costly (low balance or out of gas)
|
||||||
|
|
@ -932,21 +933,23 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
|
||||||
for _, tx := range drops {
|
for _, tx := range drops {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
log.Trace("Removed unpayable queued transaction", "hash", hash)
|
log.Trace("Removed unpayable queued transaction", "hash", hash)
|
||||||
delete(pool.all, hash)
|
pool.all.Remove(hash)
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
queuedNofundsCounter.Inc(1)
|
queuedNofundsCounter.Inc(1)
|
||||||
}
|
}
|
||||||
// Gather all executable transactions and promote them
|
// Gather all executable transactions and promote them
|
||||||
for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) {
|
for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
|
if pool.promoteTx(addr, hash, tx) {
|
||||||
log.Trace("Promoting queued transaction", "hash", hash)
|
log.Trace("Promoting queued transaction", "hash", hash)
|
||||||
pool.promoteTx(addr, hash, tx)
|
promoted = append(promoted, tx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Drop all transactions over the allowed limit
|
// Drop all transactions over the allowed limit
|
||||||
if !pool.locals.contains(addr) {
|
if !pool.locals.contains(addr) {
|
||||||
for _, tx := range list.Cap(int(pool.config.AccountQueue)) {
|
for _, tx := range list.Cap(int(pool.config.AccountQueue)) {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
delete(pool.all, hash)
|
pool.all.Remove(hash)
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
queuedRateLimitCounter.Inc(1)
|
queuedRateLimitCounter.Inc(1)
|
||||||
log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
|
log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
|
||||||
|
|
@ -957,6 +960,10 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
|
||||||
delete(pool.queue, addr)
|
delete(pool.queue, addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Notify subsystem for new promoted transactions.
|
||||||
|
if len(promoted) > 0 {
|
||||||
|
go pool.txFeed.Send(NewTxsEvent{promoted})
|
||||||
|
}
|
||||||
// If the pending limit is overflown, start equalizing allowances
|
// If the pending limit is overflown, start equalizing allowances
|
||||||
pending := uint64(0)
|
pending := uint64(0)
|
||||||
for _, list := range pool.pending {
|
for _, list := range pool.pending {
|
||||||
|
|
@ -991,7 +998,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
|
||||||
for _, tx := range list.Cap(list.Len() - 1) {
|
for _, tx := range list.Cap(list.Len() - 1) {
|
||||||
// Drop the transaction from the global pools too
|
// Drop the transaction from the global pools too
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
delete(pool.all, hash)
|
pool.all.Remove(hash)
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
|
|
||||||
// Update the account nonce to the dropped transaction
|
// Update the account nonce to the dropped transaction
|
||||||
|
|
@ -1013,7 +1020,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
|
||||||
for _, tx := range list.Cap(list.Len() - 1) {
|
for _, tx := range list.Cap(list.Len() - 1) {
|
||||||
// Drop the transaction from the global pools too
|
// Drop the transaction from the global pools too
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
delete(pool.all, hash)
|
pool.all.Remove(hash)
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
|
|
||||||
// Update the account nonce to the dropped transaction
|
// Update the account nonce to the dropped transaction
|
||||||
|
|
@ -1082,7 +1089,7 @@ func (pool *TxPool) demoteUnexecutables() {
|
||||||
for _, tx := range list.Forward(nonce) {
|
for _, tx := range list.Forward(nonce) {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
log.Trace("Removed old pending transaction", "hash", hash)
|
log.Trace("Removed old pending transaction", "hash", hash)
|
||||||
delete(pool.all, hash)
|
pool.all.Remove(hash)
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
}
|
}
|
||||||
// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
|
// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
|
||||||
|
|
@ -1090,7 +1097,7 @@ func (pool *TxPool) demoteUnexecutables() {
|
||||||
for _, tx := range drops {
|
for _, tx := range drops {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
log.Trace("Removed unpayable pending transaction", "hash", hash)
|
log.Trace("Removed unpayable pending transaction", "hash", hash)
|
||||||
delete(pool.all, hash)
|
pool.all.Remove(hash)
|
||||||
pool.priced.Removed()
|
pool.priced.Removed()
|
||||||
pendingNofundsCounter.Inc(1)
|
pendingNofundsCounter.Inc(1)
|
||||||
}
|
}
|
||||||
|
|
@ -1162,3 +1169,68 @@ func (as *accountSet) containsTx(tx *types.Transaction) bool {
|
||||||
func (as *accountSet) add(addr common.Address) {
|
func (as *accountSet) add(addr common.Address) {
|
||||||
as.accounts[addr] = struct{}{}
|
as.accounts[addr] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// txLookup is used internally by TxPool to track transactions while allowing lookup without
|
||||||
|
// mutex contention.
|
||||||
|
//
|
||||||
|
// Note, although this type is properly protected against concurrent access, it
|
||||||
|
// is **not** a type that should ever be mutated or even exposed outside of the
|
||||||
|
// transaction pool, since its internal state is tightly coupled with the pools
|
||||||
|
// internal mechanisms. The sole purpose of the type is to permit out-of-bound
|
||||||
|
// peeking into the pool in TxPool.Get without having to acquire the widely scoped
|
||||||
|
// TxPool.mu mutex.
|
||||||
|
type txLookup struct {
|
||||||
|
all map[common.Hash]*types.Transaction
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTxLookup returns a new txLookup structure.
|
||||||
|
func newTxLookup() *txLookup {
|
||||||
|
return &txLookup{
|
||||||
|
all: make(map[common.Hash]*types.Transaction),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range calls f on each key and value present in the map.
|
||||||
|
func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction) bool) {
|
||||||
|
t.lock.RLock()
|
||||||
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
for key, value := range t.all {
|
||||||
|
if !f(key, value) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a transaction if it exists in the lookup, or nil if not found.
|
||||||
|
func (t *txLookup) Get(hash common.Hash) *types.Transaction {
|
||||||
|
t.lock.RLock()
|
||||||
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
return t.all[hash]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the current number of items in the lookup.
|
||||||
|
func (t *txLookup) Count() int {
|
||||||
|
t.lock.RLock()
|
||||||
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
return len(t.all)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a transaction to the lookup.
|
||||||
|
func (t *txLookup) Add(tx *types.Transaction) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
t.all[tx.Hash()] = tx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes a transaction from the lookup.
|
||||||
|
func (t *txLookup) Remove(hash common.Hash) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
delete(t.all, hash)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ func validateTxPoolInternals(pool *TxPool) error {
|
||||||
|
|
||||||
// Ensure the total transaction set is consistent with pending + queued
|
// Ensure the total transaction set is consistent with pending + queued
|
||||||
pending, queued := pool.stats()
|
pending, queued := pool.stats()
|
||||||
if total := len(pool.all); total != pending+queued {
|
if total := pool.all.Count(); total != pending+queued {
|
||||||
return fmt.Errorf("total transaction count %d != %d pending + %d queued", total, pending, queued)
|
return fmt.Errorf("total transaction count %d != %d pending + %d queued", total, pending, queued)
|
||||||
}
|
}
|
||||||
if priced := pool.priced.items.Len() - pool.priced.stales; priced != pending+queued {
|
if priced := pool.priced.items.Len() - pool.priced.stales; priced != pending+queued {
|
||||||
|
|
@ -118,21 +118,27 @@ func validateTxPoolInternals(pool *TxPool) error {
|
||||||
|
|
||||||
// validateEvents checks that the correct number of transaction addition events
|
// validateEvents checks that the correct number of transaction addition events
|
||||||
// were fired on the pool's event feed.
|
// were fired on the pool's event feed.
|
||||||
func validateEvents(events chan TxPreEvent, count int) error {
|
func validateEvents(events chan NewTxsEvent, count int) error {
|
||||||
for i := 0; i < count; i++ {
|
var received []*types.Transaction
|
||||||
|
|
||||||
|
for len(received) < count {
|
||||||
select {
|
select {
|
||||||
case <-events:
|
case ev := <-events:
|
||||||
|
received = append(received, ev.Txs...)
|
||||||
case <-time.After(time.Second):
|
case <-time.After(time.Second):
|
||||||
return fmt.Errorf("event #%d not fired", i)
|
return fmt.Errorf("event #%d not fired", received)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(received) > count {
|
||||||
|
return fmt.Errorf("more than %d events fired: %v", count, received[count:])
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case tx := <-events:
|
case ev := <-events:
|
||||||
return fmt.Errorf("more than %d events fired: %v", count, tx.Tx)
|
return fmt.Errorf("more than %d events fired: %v", count, ev.Txs)
|
||||||
|
|
||||||
case <-time.After(50 * time.Millisecond):
|
case <-time.After(50 * time.Millisecond):
|
||||||
// This branch should be "default", but it's a data race between goroutines,
|
// This branch should be "default", but it's a data race between goroutines,
|
||||||
// reading the event channel and pushng into it, so better wait a bit ensuring
|
// reading the event channel and pushing into it, so better wait a bit ensuring
|
||||||
// really nothing gets injected.
|
// really nothing gets injected.
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -395,8 +401,8 @@ func TestTransactionDoubleNonce(t *testing.T) {
|
||||||
t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash())
|
t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash())
|
||||||
}
|
}
|
||||||
// Ensure the total transaction count is correct
|
// Ensure the total transaction count is correct
|
||||||
if len(pool.all) != 1 {
|
if pool.all.Count() != 1 {
|
||||||
t.Error("expected 1 total transactions, got", len(pool.all))
|
t.Error("expected 1 total transactions, got", pool.all.Count())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -418,8 +424,8 @@ func TestTransactionMissingNonce(t *testing.T) {
|
||||||
if pool.queue[addr].Len() != 1 {
|
if pool.queue[addr].Len() != 1 {
|
||||||
t.Error("expected 1 queued transaction, got", pool.queue[addr].Len())
|
t.Error("expected 1 queued transaction, got", pool.queue[addr].Len())
|
||||||
}
|
}
|
||||||
if len(pool.all) != 1 {
|
if pool.all.Count() != 1 {
|
||||||
t.Error("expected 1 total transactions, got", len(pool.all))
|
t.Error("expected 1 total transactions, got", pool.all.Count())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -482,8 +488,8 @@ func TestTransactionDropping(t *testing.T) {
|
||||||
if pool.queue[account].Len() != 3 {
|
if pool.queue[account].Len() != 3 {
|
||||||
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
|
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
|
||||||
}
|
}
|
||||||
if len(pool.all) != 6 {
|
if pool.all.Count() != 6 {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 6)
|
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 6)
|
||||||
}
|
}
|
||||||
pool.lockedReset(nil, nil)
|
pool.lockedReset(nil, nil)
|
||||||
if pool.pending[account].Len() != 3 {
|
if pool.pending[account].Len() != 3 {
|
||||||
|
|
@ -492,8 +498,8 @@ func TestTransactionDropping(t *testing.T) {
|
||||||
if pool.queue[account].Len() != 3 {
|
if pool.queue[account].Len() != 3 {
|
||||||
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
|
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
|
||||||
}
|
}
|
||||||
if len(pool.all) != 6 {
|
if pool.all.Count() != 6 {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 6)
|
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 6)
|
||||||
}
|
}
|
||||||
// Reduce the balance of the account, and check that invalidated transactions are dropped
|
// Reduce the balance of the account, and check that invalidated transactions are dropped
|
||||||
pool.currentState.AddBalance(account, big.NewInt(-650))
|
pool.currentState.AddBalance(account, big.NewInt(-650))
|
||||||
|
|
@ -517,8 +523,8 @@ func TestTransactionDropping(t *testing.T) {
|
||||||
if _, ok := pool.queue[account].txs.items[tx12.Nonce()]; ok {
|
if _, ok := pool.queue[account].txs.items[tx12.Nonce()]; ok {
|
||||||
t.Errorf("out-of-fund queued transaction present: %v", tx11)
|
t.Errorf("out-of-fund queued transaction present: %v", tx11)
|
||||||
}
|
}
|
||||||
if len(pool.all) != 4 {
|
if pool.all.Count() != 4 {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
|
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 4)
|
||||||
}
|
}
|
||||||
// Reduce the block gas limit, check that invalidated transactions are dropped
|
// Reduce the block gas limit, check that invalidated transactions are dropped
|
||||||
pool.chain.(*testBlockChain).gasLimit = 100
|
pool.chain.(*testBlockChain).gasLimit = 100
|
||||||
|
|
@ -536,8 +542,8 @@ func TestTransactionDropping(t *testing.T) {
|
||||||
if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok {
|
if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok {
|
||||||
t.Errorf("over-gased queued transaction present: %v", tx11)
|
t.Errorf("over-gased queued transaction present: %v", tx11)
|
||||||
}
|
}
|
||||||
if len(pool.all) != 2 {
|
if pool.all.Count() != 2 {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 2)
|
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -590,8 +596,8 @@ func TestTransactionPostponing(t *testing.T) {
|
||||||
if len(pool.queue) != 0 {
|
if len(pool.queue) != 0 {
|
||||||
t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
|
t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
|
||||||
}
|
}
|
||||||
if len(pool.all) != len(txs) {
|
if pool.all.Count() != len(txs) {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs))
|
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs))
|
||||||
}
|
}
|
||||||
pool.lockedReset(nil, nil)
|
pool.lockedReset(nil, nil)
|
||||||
if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) {
|
if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) {
|
||||||
|
|
@ -600,8 +606,8 @@ func TestTransactionPostponing(t *testing.T) {
|
||||||
if len(pool.queue) != 0 {
|
if len(pool.queue) != 0 {
|
||||||
t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
|
t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
|
||||||
}
|
}
|
||||||
if len(pool.all) != len(txs) {
|
if pool.all.Count() != len(txs) {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs))
|
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs))
|
||||||
}
|
}
|
||||||
// Reduce the balance of the account, and check that transactions are reorganised
|
// Reduce the balance of the account, and check that transactions are reorganised
|
||||||
for _, addr := range accs {
|
for _, addr := range accs {
|
||||||
|
|
@ -650,8 +656,8 @@ func TestTransactionPostponing(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(pool.all) != len(txs)/2 {
|
if pool.all.Count() != len(txs)/2 {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs)/2)
|
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs)/2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -669,7 +675,7 @@ func TestTransactionGapFilling(t *testing.T) {
|
||||||
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
||||||
|
|
||||||
// Keep track of transaction events to ensure all executables get announced
|
// Keep track of transaction events to ensure all executables get announced
|
||||||
events := make(chan TxPreEvent, testTxPoolConfig.AccountQueue+5)
|
events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue+5)
|
||||||
sub := pool.txFeed.Subscribe(events)
|
sub := pool.txFeed.Subscribe(events)
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
|
|
@ -742,8 +748,8 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(pool.all) != int(testTxPoolConfig.AccountQueue) {
|
if pool.all.Count() != int(testTxPoolConfig.AccountQueue) {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), testTxPoolConfig.AccountQueue)
|
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), testTxPoolConfig.AccountQueue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -920,7 +926,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
|
||||||
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
||||||
|
|
||||||
// Keep track of transaction events to ensure all executables get announced
|
// Keep track of transaction events to ensure all executables get announced
|
||||||
events := make(chan TxPreEvent, testTxPoolConfig.AccountQueue+5)
|
events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue+5)
|
||||||
sub := pool.txFeed.Subscribe(events)
|
sub := pool.txFeed.Subscribe(events)
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
|
|
@ -936,8 +942,8 @@ func TestTransactionPendingLimiting(t *testing.T) {
|
||||||
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), 0)
|
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(pool.all) != int(testTxPoolConfig.AccountQueue+5) {
|
if pool.all.Count() != int(testTxPoolConfig.AccountQueue+5) {
|
||||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), testTxPoolConfig.AccountQueue+5)
|
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), testTxPoolConfig.AccountQueue+5)
|
||||||
}
|
}
|
||||||
if err := validateEvents(events, int(testTxPoolConfig.AccountQueue+5)); err != nil {
|
if err := validateEvents(events, int(testTxPoolConfig.AccountQueue+5)); err != nil {
|
||||||
t.Fatalf("event firing failed: %v", err)
|
t.Fatalf("event firing failed: %v", err)
|
||||||
|
|
@ -987,8 +993,8 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
|
||||||
if len(pool1.queue) != len(pool2.queue) {
|
if len(pool1.queue) != len(pool2.queue) {
|
||||||
t.Errorf("queued transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.queue), len(pool2.queue))
|
t.Errorf("queued transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.queue), len(pool2.queue))
|
||||||
}
|
}
|
||||||
if len(pool1.all) != len(pool2.all) {
|
if pool1.all.Count() != pool2.all.Count() {
|
||||||
t.Errorf("total transaction count mismatch: one-by-one algo %d, batch algo %d", len(pool1.all), len(pool2.all))
|
t.Errorf("total transaction count mismatch: one-by-one algo %d, batch algo %d", pool1.all.Count(), pool2.all.Count())
|
||||||
}
|
}
|
||||||
if err := validateTxPoolInternals(pool1); err != nil {
|
if err := validateTxPoolInternals(pool1); err != nil {
|
||||||
t.Errorf("pool 1 internal state corrupted: %v", err)
|
t.Errorf("pool 1 internal state corrupted: %v", err)
|
||||||
|
|
@ -1140,7 +1146,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
|
|
||||||
// Keep track of transaction events to ensure all executables get announced
|
// Keep track of transaction events to ensure all executables get announced
|
||||||
events := make(chan TxPreEvent, 32)
|
events := make(chan NewTxsEvent, 32)
|
||||||
sub := pool.txFeed.Subscribe(events)
|
sub := pool.txFeed.Subscribe(events)
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
|
|
@ -1327,7 +1333,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
|
|
||||||
// Keep track of transaction events to ensure all executables get announced
|
// Keep track of transaction events to ensure all executables get announced
|
||||||
events := make(chan TxPreEvent, 32)
|
events := make(chan NewTxsEvent, 32)
|
||||||
sub := pool.txFeed.Subscribe(events)
|
sub := pool.txFeed.Subscribe(events)
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
|
|
@ -1433,7 +1439,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
|
|
||||||
// Keep track of transaction events to ensure all executables get announced
|
// Keep track of transaction events to ensure all executables get announced
|
||||||
events := make(chan TxPreEvent, 32)
|
events := make(chan NewTxsEvent, 32)
|
||||||
sub := pool.txFeed.Subscribe(events)
|
sub := pool.txFeed.Subscribe(events)
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
|
|
@ -1495,7 +1501,7 @@ func TestTransactionReplacement(t *testing.T) {
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
|
|
||||||
// Keep track of transaction events to ensure all executables get announced
|
// Keep track of transaction events to ensure all executables get announced
|
||||||
events := make(chan TxPreEvent, 32)
|
events := make(chan NewTxsEvent, 32)
|
||||||
sub := pool.txFeed.Subscribe(events)
|
sub := pool.txFeed.Subscribe(events)
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,11 @@ import (
|
||||||
|
|
||||||
var _ = (*receiptMarshaling)(nil)
|
var _ = (*receiptMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
func (r Receipt) MarshalJSON() ([]byte, error) {
|
func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||||
type Receipt struct {
|
type Receipt struct {
|
||||||
PostState hexutil.Bytes `json:"root"`
|
PostState hexutil.Bytes `json:"root"`
|
||||||
Status hexutil.Uint `json:"status"`
|
Status hexutil.Uint64 `json:"status"`
|
||||||
CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||||
Logs []*Log `json:"logs" gencodec:"required"`
|
Logs []*Log `json:"logs" gencodec:"required"`
|
||||||
|
|
@ -25,7 +26,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
var enc Receipt
|
var enc Receipt
|
||||||
enc.PostState = r.PostState
|
enc.PostState = r.PostState
|
||||||
enc.Status = hexutil.Uint(r.Status)
|
enc.Status = hexutil.Uint64(r.Status)
|
||||||
enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed)
|
enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed)
|
||||||
enc.Bloom = r.Bloom
|
enc.Bloom = r.Bloom
|
||||||
enc.Logs = r.Logs
|
enc.Logs = r.Logs
|
||||||
|
|
@ -35,10 +36,11 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (r *Receipt) UnmarshalJSON(input []byte) error {
|
func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||||
type Receipt struct {
|
type Receipt struct {
|
||||||
PostState *hexutil.Bytes `json:"root"`
|
PostState *hexutil.Bytes `json:"root"`
|
||||||
Status *hexutil.Uint `json:"status"`
|
Status *hexutil.Uint64 `json:"status"`
|
||||||
CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||||
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
|
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
|
||||||
Logs []*Log `json:"logs" gencodec:"required"`
|
Logs []*Log `json:"logs" gencodec:"required"`
|
||||||
|
|
@ -54,7 +56,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||||
r.PostState = *dec.PostState
|
r.PostState = *dec.PostState
|
||||||
}
|
}
|
||||||
if dec.Status != nil {
|
if dec.Status != nil {
|
||||||
r.Status = uint(*dec.Status)
|
r.Status = uint64(*dec.Status)
|
||||||
}
|
}
|
||||||
if dec.CumulativeGasUsed == nil {
|
if dec.CumulativeGasUsed == nil {
|
||||||
return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
|
return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
|
||||||
|
|
|
||||||
|
|
@ -36,17 +36,17 @@ var (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ReceiptStatusFailed is the status code of a transaction if execution failed.
|
// ReceiptStatusFailed is the status code of a transaction if execution failed.
|
||||||
ReceiptStatusFailed = uint(0)
|
ReceiptStatusFailed = uint64(0)
|
||||||
|
|
||||||
// ReceiptStatusSuccessful is the status code of a transaction if execution succeeded.
|
// ReceiptStatusSuccessful is the status code of a transaction if execution succeeded.
|
||||||
ReceiptStatusSuccessful = uint(1)
|
ReceiptStatusSuccessful = uint64(1)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Receipt represents the results of a transaction.
|
// Receipt represents the results of a transaction.
|
||||||
type Receipt struct {
|
type Receipt struct {
|
||||||
// Consensus fields
|
// Consensus fields
|
||||||
PostState []byte `json:"root"`
|
PostState []byte `json:"root"`
|
||||||
Status uint `json:"status"`
|
Status uint64 `json:"status"`
|
||||||
CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||||
Logs []*Log `json:"logs" gencodec:"required"`
|
Logs []*Log `json:"logs" gencodec:"required"`
|
||||||
|
|
@ -59,7 +59,7 @@ type Receipt struct {
|
||||||
|
|
||||||
type receiptMarshaling struct {
|
type receiptMarshaling struct {
|
||||||
PostState hexutil.Bytes
|
PostState hexutil.Bytes
|
||||||
Status hexutil.Uint
|
Status hexutil.Uint64
|
||||||
CumulativeGasUsed hexutil.Uint64
|
CumulativeGasUsed hexutil.Uint64
|
||||||
GasUsed hexutil.Uint64
|
GasUsed hexutil.Uint64
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,28 +165,13 @@ func TestTransactionPriceNonceSort(t *testing.T) {
|
||||||
t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce())
|
t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Find the previous and next nonce of this account
|
|
||||||
prev, next := i-1, i+1
|
// If the next tx has different from account, the price must be lower than the current one
|
||||||
for j := i - 1; j >= 0; j-- {
|
if i+1 < len(txs) {
|
||||||
if fromj, _ := Sender(signer, txs[j]); fromi == fromj {
|
next := txs[i+1]
|
||||||
prev = j
|
fromNext, _ := Sender(signer, next)
|
||||||
break
|
if fromi != fromNext && txi.GasPrice().Cmp(next.GasPrice()) < 0 {
|
||||||
}
|
t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", i, fromi[:4], txi.GasPrice(), i+1, fromNext[:4], next.GasPrice())
|
||||||
}
|
|
||||||
for j := i + 1; j < len(txs); j++ {
|
|
||||||
if fromj, _ := Sender(signer, txs[j]); fromi == fromj {
|
|
||||||
next = j
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Make sure that in between the neighbor nonces, the transaction is correctly positioned price wise
|
|
||||||
for j := prev + 1; j < next; j++ {
|
|
||||||
fromj, _ := Sender(signer, txs[j])
|
|
||||||
if j < i && txs[j].GasPrice().Cmp(txi.GasPrice()) < 0 {
|
|
||||||
t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", j, fromj[:4], txs[j].GasPrice(), i, fromi[:4], txi.GasPrice())
|
|
||||||
}
|
|
||||||
if j > i && txs[j].GasPrice().Cmp(txi.GasPrice()) > 0 {
|
|
||||||
t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) > tx #%d (A=%x P=%v)", j, fromj[:4], txs[j].GasPrice(), i, fromi[:4], txi.GasPrice())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,12 +124,12 @@ func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m
|
||||||
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||||
// 2. From a non-zero value address to a zero-value address (DELETE)
|
// 2. From a non-zero value address to a zero-value address (DELETE)
|
||||||
// 3. From a non-zero to a non-zero (CHANGE)
|
// 3. From a non-zero to a non-zero (CHANGE)
|
||||||
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
if val == (common.Hash{}) && y.Sign() != 0 {
|
||||||
// 0 => non 0
|
// 0 => non 0
|
||||||
return params.SstoreSetGas, nil
|
return params.SstoreSetGas, nil
|
||||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
} else if val != (common.Hash{}) && y.Sign() == 0 {
|
||||||
|
// non 0 => 0
|
||||||
evm.StateDB.AddRefund(params.SstoreRefundGas)
|
evm.StateDB.AddRefund(params.SstoreRefundGas)
|
||||||
|
|
||||||
return params.SstoreClearGas, nil
|
return params.SstoreClearGas, nil
|
||||||
} else {
|
} else {
|
||||||
// non 0 => non 0 (or 0 => 0)
|
// non 0 => non 0 (or 0 => 0)
|
||||||
|
|
|
||||||
|
|
@ -850,7 +850,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make push instruction function
|
// make dup instruction function
|
||||||
func makeDup(size int64) executionFunc {
|
func makeDup(size int64) executionFunc {
|
||||||
return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
stack.dup(evm.interpreter.intPool, int(size))
|
stack.dup(evm.interpreter.intPool, int(size))
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ type (
|
||||||
var errGasUintOverflow = errors.New("gas uint64 overflow")
|
var errGasUintOverflow = errors.New("gas uint64 overflow")
|
||||||
|
|
||||||
type operation struct {
|
type operation struct {
|
||||||
// op is the operation function
|
// execute is the operation function
|
||||||
execute executionFunc
|
execute executionFunc
|
||||||
// gasCost is the gas function and returns the gas required for execution
|
// gasCost is the gas function and returns the gas required for execution
|
||||||
gasCost gasFunc
|
gasCost gasFunc
|
||||||
|
|
|
||||||
|
|
@ -188,8 +188,8 @@ func (b *EthAPIBackend) TxPoolContent() (map[common.Address]types.Transactions,
|
||||||
return b.eth.TxPool().Content()
|
return b.eth.TxPool().Content()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||||
return b.eth.TxPool().SubscribeTxPreEvent(ch)
|
return b.eth.TxPool().SubscribeNewTxsEvent(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) Downloader() *downloader.Downloader {
|
func (b *EthAPIBackend) Downloader() *downloader.Downloader {
|
||||||
|
|
|
||||||
|
|
@ -215,14 +215,14 @@ func CreateConsensusEngine(ctx *node.ServiceContext, config *ethash.Config, chai
|
||||||
return clique.New(chainConfig.Clique, db)
|
return clique.New(chainConfig.Clique, db)
|
||||||
}
|
}
|
||||||
// Otherwise assume proof-of-work
|
// Otherwise assume proof-of-work
|
||||||
switch {
|
switch config.PowMode {
|
||||||
case config.PowMode == ethash.ModeFake:
|
case ethash.ModeFake:
|
||||||
log.Warn("Ethash used in fake mode")
|
log.Warn("Ethash used in fake mode")
|
||||||
return ethash.NewFaker()
|
return ethash.NewFaker()
|
||||||
case config.PowMode == ethash.ModeTest:
|
case ethash.ModeTest:
|
||||||
log.Warn("Ethash used in test mode")
|
log.Warn("Ethash used in test mode")
|
||||||
return ethash.NewTester()
|
return ethash.NewTester()
|
||||||
case config.PowMode == ethash.ModeShared:
|
case ethash.ModeShared:
|
||||||
log.Warn("Ethash used in shared mode")
|
log.Warn("Ethash used in shared mode")
|
||||||
return ethash.NewShared()
|
return ethash.NewShared()
|
||||||
default:
|
default:
|
||||||
|
|
@ -239,7 +239,7 @@ func CreateConsensusEngine(ctx *node.ServiceContext, config *ethash.Config, chai
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIs returns the collection of RPC services the ethereum package offers.
|
// APIs return the collection of RPC services the ethereum package offers.
|
||||||
// NOTE, some of these services probably need to be moved to somewhere else.
|
// NOTE, some of these services probably need to be moved to somewhere else.
|
||||||
func (s *Ethereum) APIs() []rpc.API {
|
func (s *Ethereum) APIs() []rpc.API {
|
||||||
apis := ethapi.GetAPIs(s.APIBackend)
|
apis := ethapi.GetAPIs(s.APIBackend)
|
||||||
|
|
|
||||||
|
|
@ -680,7 +680,7 @@ func (d *Downloader) findAncestor(p *peerConnection, height uint64) (uint64, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the head fetch already found an ancestor, return
|
// If the head fetch already found an ancestor, return
|
||||||
if !common.EmptyHash(hash) {
|
if hash != (common.Hash{}) {
|
||||||
if int64(number) <= floor {
|
if int64(number) <= floor {
|
||||||
p.log.Warn("Ancestor below allowance", "number", number, "hash", hash, "allowance", floor)
|
p.log.Warn("Ancestor below allowance", "number", number, "hash", hash, "allowance", floor)
|
||||||
return 0, errInvalidAncestor
|
return 0, errInvalidAncestor
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync {
|
||||||
type stateSync struct {
|
type stateSync struct {
|
||||||
d *Downloader // Downloader instance to access and manage current peerset
|
d *Downloader // Downloader instance to access and manage current peerset
|
||||||
|
|
||||||
sched *trie.TrieSync // State trie sync scheduler defining the tasks
|
sched *trie.Sync // State trie sync scheduler defining the tasks
|
||||||
keccak hash.Hash // Keccak256 hasher to verify deliveries with
|
keccak hash.Hash // Keccak256 hasher to verify deliveries with
|
||||||
tasks map[common.Hash]*stateTask // Set of tasks currently queued for retrieval
|
tasks map[common.Hash]*stateTask // Set of tasks currently queued for retrieval
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -292,20 +292,20 @@ func (f *Fetcher) loop() {
|
||||||
height := f.chainHeight()
|
height := f.chainHeight()
|
||||||
for !f.queue.Empty() {
|
for !f.queue.Empty() {
|
||||||
op := f.queue.PopItem().(*inject)
|
op := f.queue.PopItem().(*inject)
|
||||||
|
hash := op.block.Hash()
|
||||||
if f.queueChangeHook != nil {
|
if f.queueChangeHook != nil {
|
||||||
f.queueChangeHook(op.block.Hash(), false)
|
f.queueChangeHook(hash, false)
|
||||||
}
|
}
|
||||||
// If too high up the chain or phase, continue later
|
// If too high up the chain or phase, continue later
|
||||||
number := op.block.NumberU64()
|
number := op.block.NumberU64()
|
||||||
if number > height+1 {
|
if number > height+1 {
|
||||||
f.queue.Push(op, -float32(op.block.NumberU64()))
|
f.queue.Push(op, -float32(number))
|
||||||
if f.queueChangeHook != nil {
|
if f.queueChangeHook != nil {
|
||||||
f.queueChangeHook(op.block.Hash(), true)
|
f.queueChangeHook(hash, true)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Otherwise if fresh and still unknown, try and import
|
// Otherwise if fresh and still unknown, try and import
|
||||||
hash := op.block.Hash()
|
|
||||||
if number+maxUncleDist < height || f.getBlock(hash) != nil {
|
if number+maxUncleDist < height || f.getBlock(hash) != nil {
|
||||||
f.forgetBlock(hash)
|
f.forgetBlock(hash)
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,8 @@ func (api *PublicFilterAPI) timeoutLoop() {
|
||||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter
|
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter
|
||||||
func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID {
|
func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID {
|
||||||
var (
|
var (
|
||||||
pendingTxs = make(chan common.Hash)
|
pendingTxs = make(chan []common.Hash)
|
||||||
pendingTxSub = api.events.SubscribePendingTxEvents(pendingTxs)
|
pendingTxSub = api.events.SubscribePendingTxs(pendingTxs)
|
||||||
)
|
)
|
||||||
|
|
||||||
api.filtersMu.Lock()
|
api.filtersMu.Lock()
|
||||||
|
|
@ -118,7 +118,7 @@ func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID {
|
||||||
case ph := <-pendingTxs:
|
case ph := <-pendingTxs:
|
||||||
api.filtersMu.Lock()
|
api.filtersMu.Lock()
|
||||||
if f, found := api.filters[pendingTxSub.ID]; found {
|
if f, found := api.filters[pendingTxSub.ID]; found {
|
||||||
f.hashes = append(f.hashes, ph)
|
f.hashes = append(f.hashes, ph...)
|
||||||
}
|
}
|
||||||
api.filtersMu.Unlock()
|
api.filtersMu.Unlock()
|
||||||
case <-pendingTxSub.Err():
|
case <-pendingTxSub.Err():
|
||||||
|
|
@ -144,13 +144,17 @@ func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Su
|
||||||
rpcSub := notifier.CreateSubscription()
|
rpcSub := notifier.CreateSubscription()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
txHashes := make(chan common.Hash)
|
txHashes := make(chan []common.Hash, 128)
|
||||||
pendingTxSub := api.events.SubscribePendingTxEvents(txHashes)
|
pendingTxSub := api.events.SubscribePendingTxs(txHashes)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case h := <-txHashes:
|
case hashes := <-txHashes:
|
||||||
|
// To keep the original behaviour, send a single tx hash in one notification.
|
||||||
|
// TODO(rjl493456442) Send a batch of tx hashes in one notification
|
||||||
|
for _, h := range hashes {
|
||||||
notifier.Notify(rpcSub.ID, h)
|
notifier.Notify(rpcSub.ID, h)
|
||||||
|
}
|
||||||
case <-rpcSub.Err():
|
case <-rpcSub.Err():
|
||||||
pendingTxSub.Unsubscribe()
|
pendingTxSub.Unsubscribe()
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ type Backend interface {
|
||||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
||||||
GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error)
|
GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error)
|
||||||
|
|
||||||
SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription
|
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
||||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
||||||
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
|
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
|
||||||
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
|
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ const (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
||||||
// txChanSize is the size of channel listening to TxPreEvent.
|
// txChanSize is the size of channel listening to NewTxsEvent.
|
||||||
// The number is referenced from the size of tx pool.
|
// The number is referenced from the size of tx pool.
|
||||||
txChanSize = 4096
|
txChanSize = 4096
|
||||||
// rmLogsChanSize is the size of channel listening to RemovedLogsEvent.
|
// rmLogsChanSize is the size of channel listening to RemovedLogsEvent.
|
||||||
|
|
@ -80,7 +80,7 @@ type subscription struct {
|
||||||
created time.Time
|
created time.Time
|
||||||
logsCrit ethereum.FilterQuery
|
logsCrit ethereum.FilterQuery
|
||||||
logs chan []*types.Log
|
logs chan []*types.Log
|
||||||
hashes chan common.Hash
|
hashes chan []common.Hash
|
||||||
headers chan *types.Header
|
headers chan *types.Header
|
||||||
installed chan struct{} // closed when the filter is installed
|
installed chan struct{} // closed when the filter is installed
|
||||||
err chan error // closed when the filter is uninstalled
|
err chan error // closed when the filter is uninstalled
|
||||||
|
|
@ -95,7 +95,7 @@ type EventSystem struct {
|
||||||
lastHead *types.Header
|
lastHead *types.Header
|
||||||
|
|
||||||
// Subscriptions
|
// Subscriptions
|
||||||
txSub event.Subscription // Subscription for new transaction event
|
txsSub event.Subscription // Subscription for new transaction event
|
||||||
logsSub event.Subscription // Subscription for new log event
|
logsSub event.Subscription // Subscription for new log event
|
||||||
rmLogsSub event.Subscription // Subscription for removed log event
|
rmLogsSub event.Subscription // Subscription for removed log event
|
||||||
chainSub event.Subscription // Subscription for new chain event
|
chainSub event.Subscription // Subscription for new chain event
|
||||||
|
|
@ -104,7 +104,7 @@ type EventSystem struct {
|
||||||
// Channels
|
// Channels
|
||||||
install chan *subscription // install filter for event notification
|
install chan *subscription // install filter for event notification
|
||||||
uninstall chan *subscription // remove filter for event notification
|
uninstall chan *subscription // remove filter for event notification
|
||||||
txCh chan core.TxPreEvent // Channel to receive new transaction event
|
txsCh chan core.NewTxsEvent // Channel to receive new transactions event
|
||||||
logsCh chan []*types.Log // Channel to receive new log event
|
logsCh chan []*types.Log // Channel to receive new log event
|
||||||
rmLogsCh chan core.RemovedLogsEvent // Channel to receive removed log event
|
rmLogsCh chan core.RemovedLogsEvent // Channel to receive removed log event
|
||||||
chainCh chan core.ChainEvent // Channel to receive new chain event
|
chainCh chan core.ChainEvent // Channel to receive new chain event
|
||||||
|
|
@ -123,14 +123,14 @@ func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventS
|
||||||
lightMode: lightMode,
|
lightMode: lightMode,
|
||||||
install: make(chan *subscription),
|
install: make(chan *subscription),
|
||||||
uninstall: make(chan *subscription),
|
uninstall: make(chan *subscription),
|
||||||
txCh: make(chan core.TxPreEvent, txChanSize),
|
txsCh: make(chan core.NewTxsEvent, txChanSize),
|
||||||
logsCh: make(chan []*types.Log, logsChanSize),
|
logsCh: make(chan []*types.Log, logsChanSize),
|
||||||
rmLogsCh: make(chan core.RemovedLogsEvent, rmLogsChanSize),
|
rmLogsCh: make(chan core.RemovedLogsEvent, rmLogsChanSize),
|
||||||
chainCh: make(chan core.ChainEvent, chainEvChanSize),
|
chainCh: make(chan core.ChainEvent, chainEvChanSize),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe events
|
// Subscribe events
|
||||||
m.txSub = m.backend.SubscribeTxPreEvent(m.txCh)
|
m.txsSub = m.backend.SubscribeNewTxsEvent(m.txsCh)
|
||||||
m.logsSub = m.backend.SubscribeLogsEvent(m.logsCh)
|
m.logsSub = m.backend.SubscribeLogsEvent(m.logsCh)
|
||||||
m.rmLogsSub = m.backend.SubscribeRemovedLogsEvent(m.rmLogsCh)
|
m.rmLogsSub = m.backend.SubscribeRemovedLogsEvent(m.rmLogsCh)
|
||||||
m.chainSub = m.backend.SubscribeChainEvent(m.chainCh)
|
m.chainSub = m.backend.SubscribeChainEvent(m.chainCh)
|
||||||
|
|
@ -138,7 +138,7 @@ func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventS
|
||||||
m.pendingLogSub = m.mux.Subscribe(core.PendingLogsEvent{})
|
m.pendingLogSub = m.mux.Subscribe(core.PendingLogsEvent{})
|
||||||
|
|
||||||
// Make sure none of the subscriptions are empty
|
// Make sure none of the subscriptions are empty
|
||||||
if m.txSub == nil || m.logsSub == nil || m.rmLogsSub == nil || m.chainSub == nil ||
|
if m.txsSub == nil || m.logsSub == nil || m.rmLogsSub == nil || m.chainSub == nil ||
|
||||||
m.pendingLogSub.Closed() {
|
m.pendingLogSub.Closed() {
|
||||||
log.Crit("Subscribe for event system failed")
|
log.Crit("Subscribe for event system failed")
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +240,7 @@ func (es *EventSystem) subscribeMinedPendingLogs(crit ethereum.FilterQuery, logs
|
||||||
logsCrit: crit,
|
logsCrit: crit,
|
||||||
created: time.Now(),
|
created: time.Now(),
|
||||||
logs: logs,
|
logs: logs,
|
||||||
hashes: make(chan common.Hash),
|
hashes: make(chan []common.Hash),
|
||||||
headers: make(chan *types.Header),
|
headers: make(chan *types.Header),
|
||||||
installed: make(chan struct{}),
|
installed: make(chan struct{}),
|
||||||
err: make(chan error),
|
err: make(chan error),
|
||||||
|
|
@ -257,7 +257,7 @@ func (es *EventSystem) subscribeLogs(crit ethereum.FilterQuery, logs chan []*typ
|
||||||
logsCrit: crit,
|
logsCrit: crit,
|
||||||
created: time.Now(),
|
created: time.Now(),
|
||||||
logs: logs,
|
logs: logs,
|
||||||
hashes: make(chan common.Hash),
|
hashes: make(chan []common.Hash),
|
||||||
headers: make(chan *types.Header),
|
headers: make(chan *types.Header),
|
||||||
installed: make(chan struct{}),
|
installed: make(chan struct{}),
|
||||||
err: make(chan error),
|
err: make(chan error),
|
||||||
|
|
@ -274,7 +274,7 @@ func (es *EventSystem) subscribePendingLogs(crit ethereum.FilterQuery, logs chan
|
||||||
logsCrit: crit,
|
logsCrit: crit,
|
||||||
created: time.Now(),
|
created: time.Now(),
|
||||||
logs: logs,
|
logs: logs,
|
||||||
hashes: make(chan common.Hash),
|
hashes: make(chan []common.Hash),
|
||||||
headers: make(chan *types.Header),
|
headers: make(chan *types.Header),
|
||||||
installed: make(chan struct{}),
|
installed: make(chan struct{}),
|
||||||
err: make(chan error),
|
err: make(chan error),
|
||||||
|
|
@ -290,7 +290,7 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti
|
||||||
typ: BlocksSubscription,
|
typ: BlocksSubscription,
|
||||||
created: time.Now(),
|
created: time.Now(),
|
||||||
logs: make(chan []*types.Log),
|
logs: make(chan []*types.Log),
|
||||||
hashes: make(chan common.Hash),
|
hashes: make(chan []common.Hash),
|
||||||
headers: headers,
|
headers: headers,
|
||||||
installed: make(chan struct{}),
|
installed: make(chan struct{}),
|
||||||
err: make(chan error),
|
err: make(chan error),
|
||||||
|
|
@ -298,9 +298,9 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti
|
||||||
return es.subscribe(sub)
|
return es.subscribe(sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribePendingTxEvents creates a subscription that writes transaction hashes for
|
// SubscribePendingTxs creates a subscription that writes transaction hashes for
|
||||||
// transactions that enter the transaction pool.
|
// transactions that enter the transaction pool.
|
||||||
func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription {
|
func (es *EventSystem) SubscribePendingTxs(hashes chan []common.Hash) *Subscription {
|
||||||
sub := &subscription{
|
sub := &subscription{
|
||||||
id: rpc.NewID(),
|
id: rpc.NewID(),
|
||||||
typ: PendingTransactionsSubscription,
|
typ: PendingTransactionsSubscription,
|
||||||
|
|
@ -348,9 +348,13 @@ func (es *EventSystem) broadcast(filters filterIndex, ev interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case core.TxPreEvent:
|
case core.NewTxsEvent:
|
||||||
|
hashes := make([]common.Hash, 0, len(e.Txs))
|
||||||
|
for _, tx := range e.Txs {
|
||||||
|
hashes = append(hashes, tx.Hash())
|
||||||
|
}
|
||||||
for _, f := range filters[PendingTransactionsSubscription] {
|
for _, f := range filters[PendingTransactionsSubscription] {
|
||||||
f.hashes <- e.Tx.Hash()
|
f.hashes <- hashes
|
||||||
}
|
}
|
||||||
case core.ChainEvent:
|
case core.ChainEvent:
|
||||||
for _, f := range filters[BlocksSubscription] {
|
for _, f := range filters[BlocksSubscription] {
|
||||||
|
|
@ -446,7 +450,7 @@ func (es *EventSystem) eventLoop() {
|
||||||
// Ensure all subscriptions get cleaned up
|
// Ensure all subscriptions get cleaned up
|
||||||
defer func() {
|
defer func() {
|
||||||
es.pendingLogSub.Unsubscribe()
|
es.pendingLogSub.Unsubscribe()
|
||||||
es.txSub.Unsubscribe()
|
es.txsSub.Unsubscribe()
|
||||||
es.logsSub.Unsubscribe()
|
es.logsSub.Unsubscribe()
|
||||||
es.rmLogsSub.Unsubscribe()
|
es.rmLogsSub.Unsubscribe()
|
||||||
es.chainSub.Unsubscribe()
|
es.chainSub.Unsubscribe()
|
||||||
|
|
@ -460,7 +464,7 @@ func (es *EventSystem) eventLoop() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
// Handle subscribed events
|
// Handle subscribed events
|
||||||
case ev := <-es.txCh:
|
case ev := <-es.txsCh:
|
||||||
es.broadcast(index, ev)
|
es.broadcast(index, ev)
|
||||||
case ev := <-es.logsCh:
|
case ev := <-es.logsCh:
|
||||||
es.broadcast(index, ev)
|
es.broadcast(index, ev)
|
||||||
|
|
@ -495,7 +499,7 @@ func (es *EventSystem) eventLoop() {
|
||||||
close(f.err)
|
close(f.err)
|
||||||
|
|
||||||
// System stopped
|
// System stopped
|
||||||
case <-es.txSub.Err():
|
case <-es.txsSub.Err():
|
||||||
return
|
return
|
||||||
case <-es.logsSub.Err():
|
case <-es.logsSub.Err():
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func (b *testBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types
|
||||||
return logs, nil
|
return logs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
func (b *testBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||||
return b.txFeed.Subscribe(ch)
|
return b.txFeed.Subscribe(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,10 +232,7 @@ func TestPendingTxFilter(t *testing.T) {
|
||||||
fid0 := api.NewPendingTransactionFilter()
|
fid0 := api.NewPendingTransactionFilter()
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
for _, tx := range transactions {
|
txFeed.Send(core.NewTxsEvent{Txs: transactions})
|
||||||
ev := core.TxPreEvent{Tx: tx}
|
|
||||||
txFeed.Send(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
timeout := time.Now().Add(1 * time.Second)
|
timeout := time.Now().Add(1 * time.Second)
|
||||||
for {
|
for {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ const (
|
||||||
softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data.
|
softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data.
|
||||||
estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header
|
estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header
|
||||||
|
|
||||||
// txChanSize is the size of channel listening to TxPreEvent.
|
// txChanSize is the size of channel listening to NewTxsEvent.
|
||||||
// The number is referenced from the size of tx pool.
|
// The number is referenced from the size of tx pool.
|
||||||
txChanSize = 4096
|
txChanSize = 4096
|
||||||
)
|
)
|
||||||
|
|
@ -81,8 +81,8 @@ type ProtocolManager struct {
|
||||||
SubProtocols []p2p.Protocol
|
SubProtocols []p2p.Protocol
|
||||||
|
|
||||||
eventMux *event.TypeMux
|
eventMux *event.TypeMux
|
||||||
txCh chan core.TxPreEvent
|
txsCh chan core.NewTxsEvent
|
||||||
txSub event.Subscription
|
txsSub event.Subscription
|
||||||
minedBlockSub *event.TypeMuxSubscription
|
minedBlockSub *event.TypeMuxSubscription
|
||||||
|
|
||||||
// channels for fetcher, syncer, txsyncLoop
|
// channels for fetcher, syncer, txsyncLoop
|
||||||
|
|
@ -204,8 +204,8 @@ func (pm *ProtocolManager) Start(maxPeers int) {
|
||||||
pm.maxPeers = maxPeers
|
pm.maxPeers = maxPeers
|
||||||
|
|
||||||
// broadcast transactions
|
// broadcast transactions
|
||||||
pm.txCh = make(chan core.TxPreEvent, txChanSize)
|
pm.txsCh = make(chan core.NewTxsEvent, txChanSize)
|
||||||
pm.txSub = pm.txpool.SubscribeTxPreEvent(pm.txCh)
|
pm.txsSub = pm.txpool.SubscribeNewTxsEvent(pm.txsCh)
|
||||||
go pm.txBroadcastLoop()
|
go pm.txBroadcastLoop()
|
||||||
|
|
||||||
// broadcast mined blocks
|
// broadcast mined blocks
|
||||||
|
|
@ -220,7 +220,7 @@ func (pm *ProtocolManager) Start(maxPeers int) {
|
||||||
func (pm *ProtocolManager) Stop() {
|
func (pm *ProtocolManager) Stop() {
|
||||||
log.Info("Stopping Ethereum protocol")
|
log.Info("Stopping Ethereum protocol")
|
||||||
|
|
||||||
pm.txSub.Unsubscribe() // quits txBroadcastLoop
|
pm.txsSub.Unsubscribe() // quits txBroadcastLoop
|
||||||
pm.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop
|
pm.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop
|
||||||
|
|
||||||
// Quit the sync loop.
|
// Quit the sync loop.
|
||||||
|
|
@ -698,7 +698,7 @@ func (pm *ProtocolManager) BroadcastBlock(block *types.Block, propagate bool) {
|
||||||
// Send the block to a subset of our peers
|
// Send the block to a subset of our peers
|
||||||
transfer := peers[:int(math.Sqrt(float64(len(peers))))]
|
transfer := peers[:int(math.Sqrt(float64(len(peers))))]
|
||||||
for _, peer := range transfer {
|
for _, peer := range transfer {
|
||||||
peer.SendNewBlock(block, td)
|
peer.AsyncSendNewBlock(block, td)
|
||||||
}
|
}
|
||||||
log.Trace("Propagated block", "hash", hash, "recipients", len(transfer), "duration", common.PrettyDuration(time.Since(block.ReceivedAt)))
|
log.Trace("Propagated block", "hash", hash, "recipients", len(transfer), "duration", common.PrettyDuration(time.Since(block.ReceivedAt)))
|
||||||
return
|
return
|
||||||
|
|
@ -706,22 +706,29 @@ func (pm *ProtocolManager) BroadcastBlock(block *types.Block, propagate bool) {
|
||||||
// Otherwise if the block is indeed in out own chain, announce it
|
// Otherwise if the block is indeed in out own chain, announce it
|
||||||
if pm.blockchain.HasBlock(hash, block.NumberU64()) {
|
if pm.blockchain.HasBlock(hash, block.NumberU64()) {
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
peer.SendNewBlockHashes([]common.Hash{hash}, []uint64{block.NumberU64()})
|
peer.AsyncSendNewBlockHash(block)
|
||||||
}
|
}
|
||||||
log.Trace("Announced block", "hash", hash, "recipients", len(peers), "duration", common.PrettyDuration(time.Since(block.ReceivedAt)))
|
log.Trace("Announced block", "hash", hash, "recipients", len(peers), "duration", common.PrettyDuration(time.Since(block.ReceivedAt)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BroadcastTx will propagate a transaction to all peers which are not known to
|
// BroadcastTxs will propagate a batch of transactions to all peers which are not known to
|
||||||
// already have the given transaction.
|
// already have the given transaction.
|
||||||
func (pm *ProtocolManager) BroadcastTx(hash common.Hash, tx *types.Transaction) {
|
func (pm *ProtocolManager) BroadcastTxs(txs types.Transactions) {
|
||||||
// Broadcast transaction to a batch of peers not knowing about it
|
var txset = make(map[*peer]types.Transactions)
|
||||||
peers := pm.peers.PeersWithoutTx(hash)
|
|
||||||
//FIXME include this again: peers = peers[:int(math.Sqrt(float64(len(peers))))]
|
// Broadcast transactions to a batch of peers not knowing about it
|
||||||
|
for _, tx := range txs {
|
||||||
|
peers := pm.peers.PeersWithoutTx(tx.Hash())
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
peer.SendTransactions(types.Transactions{tx})
|
txset[peer] = append(txset[peer], tx)
|
||||||
|
}
|
||||||
|
log.Trace("Broadcast transaction", "hash", tx.Hash(), "recipients", len(peers))
|
||||||
|
}
|
||||||
|
// FIXME include this again: peers = peers[:int(math.Sqrt(float64(len(peers))))]
|
||||||
|
for peer, txs := range txset {
|
||||||
|
peer.AsyncSendTransactions(txs)
|
||||||
}
|
}
|
||||||
log.Trace("Broadcast transaction", "hash", hash, "recipients", len(peers))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mined broadcast loop
|
// Mined broadcast loop
|
||||||
|
|
@ -739,11 +746,11 @@ func (pm *ProtocolManager) minedBroadcastLoop() {
|
||||||
func (pm *ProtocolManager) txBroadcastLoop() {
|
func (pm *ProtocolManager) txBroadcastLoop() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event := <-pm.txCh:
|
case event := <-pm.txsCh:
|
||||||
pm.BroadcastTx(event.Tx.Hash(), event.Tx)
|
pm.BroadcastTxs(event.Txs)
|
||||||
|
|
||||||
// Err() channel will be closed when unsubscribing.
|
// Err() channel will be closed when unsubscribing.
|
||||||
case <-pm.txSub.Err():
|
case <-pm.txsSub.Err():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) {
|
||||||
return batches, nil
|
return batches, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *testTxPool) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||||
return p.txFeed.Subscribe(ch)
|
return p.txFeed.Subscribe(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
115
eth/peer.go
115
eth/peer.go
|
|
@ -39,6 +39,22 @@ var (
|
||||||
const (
|
const (
|
||||||
maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
|
maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
|
||||||
maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS)
|
maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS)
|
||||||
|
|
||||||
|
// maxQueuedTxs is the maximum number of transaction lists to queue up before
|
||||||
|
// dropping broadcasts. This is a sensitive number as a transaction list might
|
||||||
|
// contain a single transaction, or thousands.
|
||||||
|
maxQueuedTxs = 128
|
||||||
|
|
||||||
|
// maxQueuedProps is the maximum number of block propagations to queue up before
|
||||||
|
// dropping broadcasts. There's not much point in queueing stale blocks, so a few
|
||||||
|
// that might cover uncles should be enough.
|
||||||
|
maxQueuedProps = 4
|
||||||
|
|
||||||
|
// maxQueuedAnns is the maximum number of block announcements to queue up before
|
||||||
|
// dropping broadcasts. Similarly to block propagations, there's no point to queue
|
||||||
|
// above some healthy uncle limit, so use that.
|
||||||
|
maxQueuedAnns = 4
|
||||||
|
|
||||||
handshakeTimeout = 5 * time.Second
|
handshakeTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -50,6 +66,12 @@ type PeerInfo struct {
|
||||||
Head string `json:"head"` // SHA3 hash of the peer's best owned block
|
Head string `json:"head"` // SHA3 hash of the peer's best owned block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// propEvent is a block propagation, waiting for its turn in the broadcast queue.
|
||||||
|
type propEvent struct {
|
||||||
|
block *types.Block
|
||||||
|
td *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
type peer struct {
|
type peer struct {
|
||||||
id string
|
id string
|
||||||
|
|
||||||
|
|
@ -65,21 +87,62 @@ type peer struct {
|
||||||
|
|
||||||
knownTxs *set.Set // Set of transaction hashes known to be known by this peer
|
knownTxs *set.Set // Set of transaction hashes known to be known by this peer
|
||||||
knownBlocks *set.Set // Set of block hashes known to be known by this peer
|
knownBlocks *set.Set // Set of block hashes known to be known by this peer
|
||||||
|
queuedTxs chan []*types.Transaction // Queue of transactions to broadcast to the peer
|
||||||
|
queuedProps chan *propEvent // Queue of blocks to broadcast to the peer
|
||||||
|
queuedAnns chan *types.Block // Queue of blocks to announce to the peer
|
||||||
|
term chan struct{} // Termination channel to stop the broadcaster
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||||
id := p.ID()
|
|
||||||
|
|
||||||
return &peer{
|
return &peer{
|
||||||
Peer: p,
|
Peer: p,
|
||||||
rw: rw,
|
rw: rw,
|
||||||
version: version,
|
version: version,
|
||||||
id: fmt.Sprintf("%x", id[:8]),
|
id: fmt.Sprintf("%x", p.ID().Bytes()[:8]),
|
||||||
knownTxs: set.New(),
|
knownTxs: set.New(),
|
||||||
knownBlocks: set.New(),
|
knownBlocks: set.New(),
|
||||||
|
queuedTxs: make(chan []*types.Transaction, maxQueuedTxs),
|
||||||
|
queuedProps: make(chan *propEvent, maxQueuedProps),
|
||||||
|
queuedAnns: make(chan *types.Block, maxQueuedAnns),
|
||||||
|
term: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// broadcast is a write loop that multiplexes block propagations, announcements
|
||||||
|
// and transaction broadcasts into the remote peer. The goal is to have an async
|
||||||
|
// writer that does not lock up node internals.
|
||||||
|
func (p *peer) broadcast() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case txs := <-p.queuedTxs:
|
||||||
|
if err := p.SendTransactions(txs); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Log().Trace("Broadcast transactions", "count", len(txs))
|
||||||
|
|
||||||
|
case prop := <-p.queuedProps:
|
||||||
|
if err := p.SendNewBlock(prop.block, prop.td); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Log().Trace("Propagated block", "number", prop.block.Number(), "hash", prop.block.Hash(), "td", prop.td)
|
||||||
|
|
||||||
|
case block := <-p.queuedAnns:
|
||||||
|
if err := p.SendNewBlockHashes([]common.Hash{block.Hash()}, []uint64{block.NumberU64()}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Log().Trace("Announced block", "number", block.Number(), "hash", block.Hash())
|
||||||
|
|
||||||
|
case <-p.term:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close signals the broadcast goroutine to terminate.
|
||||||
|
func (p *peer) close() {
|
||||||
|
close(p.term)
|
||||||
|
}
|
||||||
|
|
||||||
// Info gathers and returns a collection of metadata known about a peer.
|
// Info gathers and returns a collection of metadata known about a peer.
|
||||||
func (p *peer) Info() *PeerInfo {
|
func (p *peer) Info() *PeerInfo {
|
||||||
hash, td := p.Head()
|
hash, td := p.Head()
|
||||||
|
|
@ -139,6 +202,19 @@ func (p *peer) SendTransactions(txs types.Transactions) error {
|
||||||
return p2p.Send(p.rw, TxMsg, txs)
|
return p2p.Send(p.rw, TxMsg, txs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsyncSendTransactions queues list of transactions propagation to a remote
|
||||||
|
// peer. If the peer's broadcast queue is full, the event is silently dropped.
|
||||||
|
func (p *peer) AsyncSendTransactions(txs []*types.Transaction) {
|
||||||
|
select {
|
||||||
|
case p.queuedTxs <- txs:
|
||||||
|
for _, tx := range txs {
|
||||||
|
p.knownTxs.Add(tx.Hash())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
p.Log().Debug("Dropping transaction propagation", "count", len(txs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SendNewBlockHashes announces the availability of a number of blocks through
|
// SendNewBlockHashes announces the availability of a number of blocks through
|
||||||
// a hash notification.
|
// a hash notification.
|
||||||
func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error {
|
func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error {
|
||||||
|
|
@ -153,12 +229,35 @@ func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error
|
||||||
return p2p.Send(p.rw, NewBlockHashesMsg, request)
|
return p2p.Send(p.rw, NewBlockHashesMsg, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsyncSendNewBlockHash queues the availability of a block for propagation to a
|
||||||
|
// remote peer. If the peer's broadcast queue is full, the event is silently
|
||||||
|
// dropped.
|
||||||
|
func (p *peer) AsyncSendNewBlockHash(block *types.Block) {
|
||||||
|
select {
|
||||||
|
case p.queuedAnns <- block:
|
||||||
|
p.knownBlocks.Add(block.Hash())
|
||||||
|
default:
|
||||||
|
p.Log().Debug("Dropping block announcement", "number", block.NumberU64(), "hash", block.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SendNewBlock propagates an entire block to a remote peer.
|
// SendNewBlock propagates an entire block to a remote peer.
|
||||||
func (p *peer) SendNewBlock(block *types.Block, td *big.Int) error {
|
func (p *peer) SendNewBlock(block *types.Block, td *big.Int) error {
|
||||||
p.knownBlocks.Add(block.Hash())
|
p.knownBlocks.Add(block.Hash())
|
||||||
return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td})
|
return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsyncSendNewBlock queues an entire block for propagation to a remote peer. If
|
||||||
|
// the peer's broadcast queue is full, the event is silently dropped.
|
||||||
|
func (p *peer) AsyncSendNewBlock(block *types.Block, td *big.Int) {
|
||||||
|
select {
|
||||||
|
case p.queuedProps <- &propEvent{block: block, td: td}:
|
||||||
|
p.knownBlocks.Add(block.Hash())
|
||||||
|
default:
|
||||||
|
p.Log().Debug("Dropping block propagation", "number", block.NumberU64(), "hash", block.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SendBlockHeaders sends a batch of block headers to the remote peer.
|
// SendBlockHeaders sends a batch of block headers to the remote peer.
|
||||||
func (p *peer) SendBlockHeaders(headers []*types.Header) error {
|
func (p *peer) SendBlockHeaders(headers []*types.Header) error {
|
||||||
return p2p.Send(p.rw, BlockHeadersMsg, headers)
|
return p2p.Send(p.rw, BlockHeadersMsg, headers)
|
||||||
|
|
@ -313,7 +412,8 @@ func newPeerSet() *peerSet {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register injects a new peer into the working set, or returns an error if the
|
// Register injects a new peer into the working set, or returns an error if the
|
||||||
// peer is already known.
|
// peer is already known. If a new peer it registered, its broadcast loop is also
|
||||||
|
// started.
|
||||||
func (ps *peerSet) Register(p *peer) error {
|
func (ps *peerSet) Register(p *peer) error {
|
||||||
ps.lock.Lock()
|
ps.lock.Lock()
|
||||||
defer ps.lock.Unlock()
|
defer ps.lock.Unlock()
|
||||||
|
|
@ -325,6 +425,8 @@ func (ps *peerSet) Register(p *peer) error {
|
||||||
return errAlreadyRegistered
|
return errAlreadyRegistered
|
||||||
}
|
}
|
||||||
ps.peers[p.id] = p
|
ps.peers[p.id] = p
|
||||||
|
go p.broadcast()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,10 +436,13 @@ func (ps *peerSet) Unregister(id string) error {
|
||||||
ps.lock.Lock()
|
ps.lock.Lock()
|
||||||
defer ps.lock.Unlock()
|
defer ps.lock.Unlock()
|
||||||
|
|
||||||
if _, ok := ps.peers[id]; !ok {
|
p, ok := ps.peers[id]
|
||||||
|
if !ok {
|
||||||
return errNotRegistered
|
return errNotRegistered
|
||||||
}
|
}
|
||||||
delete(ps.peers, id)
|
delete(ps.peers, id)
|
||||||
|
p.close()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,9 +103,9 @@ type txPool interface {
|
||||||
// The slice should be modifiable by the caller.
|
// The slice should be modifiable by the caller.
|
||||||
Pending() (map[common.Address]types.Transactions, error)
|
Pending() (map[common.Address]types.Transactions, error)
|
||||||
|
|
||||||
// SubscribeTxPreEvent should return an event subscription of
|
// SubscribeNewTxsEvent should return an event subscription of
|
||||||
// TxPreEvent and send events to the given channel.
|
// NewTxsEvent and send events to the given channel.
|
||||||
SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription
|
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
// statusData is the network packet for the status message.
|
// statusData is the network packet for the status message.
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ func testRecvTransactions(t *testing.T, protocol int) {
|
||||||
t.Errorf("added wrong tx hash: got %v, want %v", added[0].Hash(), tx.Hash())
|
t.Errorf("added wrong tx hash: got %v, want %v", added[0].Hash(), tx.Hash())
|
||||||
}
|
}
|
||||||
case <-time.After(2 * time.Second):
|
case <-time.After(2 * time.Second):
|
||||||
t.Errorf("no TxPreEvent received within 2 seconds")
|
t.Errorf("no NewTxsEvent received within 2 seconds")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,7 @@ func (db *LDBDatabase) meter(refresh time.Duration) {
|
||||||
delaystats [2]int64
|
delaystats [2]int64
|
||||||
lastWriteDelay time.Time
|
lastWriteDelay time.Time
|
||||||
lastWriteDelayN time.Time
|
lastWriteDelayN time.Time
|
||||||
|
lastWritePaused time.Time
|
||||||
)
|
)
|
||||||
|
|
||||||
// Iterate ad infinitum and collect the stats
|
// Iterate ad infinitum and collect the stats
|
||||||
|
|
@ -267,8 +268,9 @@ func (db *LDBDatabase) meter(refresh time.Duration) {
|
||||||
delayN int64
|
delayN int64
|
||||||
delayDuration string
|
delayDuration string
|
||||||
duration time.Duration
|
duration time.Duration
|
||||||
|
paused bool
|
||||||
)
|
)
|
||||||
if n, err := fmt.Sscanf(writedelay, "DelayN:%d Delay:%s", &delayN, &delayDuration); n != 2 || err != nil {
|
if n, err := fmt.Sscanf(writedelay, "DelayN:%d Delay:%s Paused:%t", &delayN, &delayDuration, &paused); n != 3 || err != nil {
|
||||||
db.log.Error("Write delay statistic not found")
|
db.log.Error("Write delay statistic not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -301,6 +303,14 @@ func (db *LDBDatabase) meter(refresh time.Duration) {
|
||||||
lastWriteDelay = time.Now()
|
lastWriteDelay = time.Now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If a warning that db is performing compaction has been displayed, any subsequent
|
||||||
|
// warnings will be withheld for one minute not to overwhelm the user.
|
||||||
|
if paused && delayN-delaystats[0] == 0 && duration.Nanoseconds()-delaystats[1] == 0 &&
|
||||||
|
time.Now().After(lastWritePaused.Add(writeDelayWarningThrottler)) {
|
||||||
|
db.log.Warn("Database compacting, degraded performance")
|
||||||
|
lastWritePaused = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
delaystats[0], delaystats[1] = delayN, duration.Nanoseconds()
|
delaystats[0], delaystats[1] = delayN, duration.Nanoseconds()
|
||||||
|
|
||||||
// Retrieve the database iostats.
|
// Retrieve the database iostats.
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ const (
|
||||||
// history request.
|
// history request.
|
||||||
historyUpdateRange = 50
|
historyUpdateRange = 50
|
||||||
|
|
||||||
// txChanSize is the size of channel listening to TxPreEvent.
|
// txChanSize is the size of channel listening to NewTxsEvent.
|
||||||
// The number is referenced from the size of tx pool.
|
// The number is referenced from the size of tx pool.
|
||||||
txChanSize = 4096
|
txChanSize = 4096
|
||||||
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
|
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
|
||||||
|
|
@ -57,9 +57,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type txPool interface {
|
type txPool interface {
|
||||||
// SubscribeTxPreEvent should return an event subscription of
|
// SubscribeNewTxsEvent should return an event subscription of
|
||||||
// TxPreEvent and send events to the given channel.
|
// NewTxsEvent and send events to the given channel.
|
||||||
SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription
|
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
type blockChain interface {
|
type blockChain interface {
|
||||||
|
|
@ -150,8 +150,8 @@ func (s *Service) loop() {
|
||||||
headSub := blockchain.SubscribeChainHeadEvent(chainHeadCh)
|
headSub := blockchain.SubscribeChainHeadEvent(chainHeadCh)
|
||||||
defer headSub.Unsubscribe()
|
defer headSub.Unsubscribe()
|
||||||
|
|
||||||
txEventCh := make(chan core.TxPreEvent, txChanSize)
|
txEventCh := make(chan core.NewTxsEvent, txChanSize)
|
||||||
txSub := txpool.SubscribeTxPreEvent(txEventCh)
|
txSub := txpool.SubscribeNewTxsEvent(txEventCh)
|
||||||
defer txSub.Unsubscribe()
|
defer txSub.Unsubscribe()
|
||||||
|
|
||||||
// Start a goroutine that exhausts the subsciptions to avoid events piling up
|
// Start a goroutine that exhausts the subsciptions to avoid events piling up
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ type FilterQuery struct {
|
||||||
// {} or nil matches any topic list
|
// {} or nil matches any topic list
|
||||||
// {{A}} matches topic A in first position
|
// {{A}} matches topic A in first position
|
||||||
// {{}, {B}} matches any topic in first position, B in second position
|
// {{}, {B}} matches any topic in first position, B in second position
|
||||||
// {{A}}, {B}} matches topic A in first position, B in second position
|
// {{A}, {B}} matches topic A in first position, B in second position
|
||||||
// {{A, B}}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position
|
// {{A, B}}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position
|
||||||
Topics [][]common.Hash
|
Topics [][]common.Hash
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ type Backend interface {
|
||||||
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
||||||
Stats() (pending int, queued int)
|
Stats() (pending int, queued int)
|
||||||
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
|
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
|
||||||
SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription
|
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
||||||
|
|
||||||
ChainConfig() *params.ChainConfig
|
ChainConfig() *params.ChainConfig
|
||||||
CurrentBlock() *types.Block
|
CurrentBlock() *types.Block
|
||||||
|
|
|
||||||
|
|
@ -136,8 +136,8 @@ func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions,
|
||||||
return b.eth.txPool.Content()
|
return b.eth.txPool.Content()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
func (b *LesApiBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||||
return b.eth.txPool.SubscribeTxPreEvent(ch)
|
return b.eth.txPool.SubscribeNewTxsEvent(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
func (b *LesApiBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,7 @@ func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
}
|
}
|
||||||
nodeSet := proofs[0].NodeSet()
|
nodeSet := proofs[0].NodeSet()
|
||||||
// Verify the proof and store if checks out
|
// Verify the proof and store if checks out
|
||||||
if _, err, _ := trie.VerifyProof(r.Id.Root, r.Key, nodeSet); err != nil {
|
if _, _, err := trie.VerifyProof(r.Id.Root, r.Key, nodeSet); err != nil {
|
||||||
return fmt.Errorf("merkle proof verification failed: %v", err)
|
return fmt.Errorf("merkle proof verification failed: %v", err)
|
||||||
}
|
}
|
||||||
r.Proof = nodeSet
|
r.Proof = nodeSet
|
||||||
|
|
@ -241,7 +241,7 @@ func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
// Verify the proof and store if checks out
|
// Verify the proof and store if checks out
|
||||||
nodeSet := proofs.NodeSet()
|
nodeSet := proofs.NodeSet()
|
||||||
reads := &readTraceDB{db: nodeSet}
|
reads := &readTraceDB{db: nodeSet}
|
||||||
if _, err, _ := trie.VerifyProof(r.Id.Root, r.Key, reads); err != nil {
|
if _, _, err := trie.VerifyProof(r.Id.Root, r.Key, reads); err != nil {
|
||||||
return fmt.Errorf("merkle proof verification failed: %v", err)
|
return fmt.Errorf("merkle proof verification failed: %v", err)
|
||||||
}
|
}
|
||||||
// check if all nodes have been read by VerifyProof
|
// check if all nodes have been read by VerifyProof
|
||||||
|
|
@ -400,7 +400,7 @@ func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
var encNumber [8]byte
|
var encNumber [8]byte
|
||||||
binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
|
binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
|
||||||
|
|
||||||
value, err, _ := trie.VerifyProof(r.ChtRoot, encNumber[:], light.NodeList(proof.Proof).NodeSet())
|
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], light.NodeList(proof.Proof).NodeSet())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -435,7 +435,7 @@ func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
|
binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
|
||||||
|
|
||||||
reads := &readTraceDB{db: nodeSet}
|
reads := &readTraceDB{db: nodeSet}
|
||||||
value, err, _ := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)
|
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("merkle proof verification failed: %v", err)
|
return fmt.Errorf("merkle proof verification failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -529,7 +529,7 @@ func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
|
|
||||||
for i, idx := range r.SectionIdxList {
|
for i, idx := range r.SectionIdxList {
|
||||||
binary.BigEndian.PutUint64(encNumber[2:], idx)
|
binary.BigEndian.PutUint64(encNumber[2:], idx)
|
||||||
value, err, _ := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads)
|
value, _, err := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -321,9 +321,9 @@ func (pool *TxPool) Stop() {
|
||||||
log.Info("Transaction pool stopped")
|
log.Info("Transaction pool stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribeTxPreEvent registers a subscription of core.TxPreEvent and
|
// SubscribeNewTxsEvent registers a subscription of core.NewTxsEvent and
|
||||||
// starts sending event to the given channel.
|
// starts sending event to the given channel.
|
||||||
func (pool *TxPool) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||||
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -412,7 +412,7 @@ func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error {
|
||||||
// Notify the subscribers. This event is posted in a goroutine
|
// Notify the subscribers. This event is posted in a goroutine
|
||||||
// because it's possible that somewhere during the post "Remove transaction"
|
// because it's possible that somewhere during the post "Remove transaction"
|
||||||
// gets called which will then wait for the global tx pool lock and deadlock.
|
// gets called which will then wait for the global tx pool lock and deadlock.
|
||||||
go self.txFeed.Send(core.TxPreEvent{Tx: tx})
|
go self.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print a log message if low enough level is set
|
// Print a log message if low enough level is set
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ srvlog.SetHandler(log.MultiHandler(
|
||||||
log.StreamHandler(os.Stderr, log.LogfmtFormat()),
|
log.StreamHandler(os.Stderr, log.LogfmtFormat()),
|
||||||
log.LvlFilterHandler(
|
log.LvlFilterHandler(
|
||||||
log.LvlError,
|
log.LvlError,
|
||||||
log.Must.FileHandler("errors.json", log.JsonFormat()))))
|
log.Must.FileHandler("errors.json", log.JSONFormat()))))
|
||||||
```
|
```
|
||||||
|
|
||||||
Will result in output that looks like this:
|
Will result in output that looks like this:
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ from the rpc package in logfmt to standard out. The other prints records at Erro
|
||||||
or above in JSON formatted output to the file /var/log/service.json
|
or above in JSON formatted output to the file /var/log/service.json
|
||||||
|
|
||||||
handler := log.MultiHandler(
|
handler := log.MultiHandler(
|
||||||
log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JsonFormat())),
|
log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JSONFormat())),
|
||||||
log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler())
|
log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -304,8 +304,8 @@ For all Handler functions which can return an error, there is a version of that
|
||||||
function which will return no error but panics on failure. They are all available
|
function which will return no error but panics on failure. They are all available
|
||||||
on the Must object. For example:
|
on the Must object. For example:
|
||||||
|
|
||||||
log.Must.FileHandler("/path", log.JsonFormat)
|
log.Must.FileHandler("/path", log.JSONFormat)
|
||||||
log.Must.NetHandler("tcp", ":1234", log.JsonFormat)
|
log.Must.NetHandler("tcp", ":1234", log.JSONFormat)
|
||||||
|
|
||||||
Inspiration and Credit
|
Inspiration and Credit
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,16 +196,16 @@ func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) {
|
||||||
buf.WriteByte('\n')
|
buf.WriteByte('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
// JsonFormat formats log records as JSON objects separated by newlines.
|
// JSONFormat formats log records as JSON objects separated by newlines.
|
||||||
// It is the equivalent of JsonFormatEx(false, true).
|
// It is the equivalent of JSONFormatEx(false, true).
|
||||||
func JsonFormat() Format {
|
func JSONFormat() Format {
|
||||||
return JsonFormatEx(false, true)
|
return JSONFormatEx(false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JsonFormatEx formats log records as JSON objects. If pretty is true,
|
// JSONFormatEx formats log records as JSON objects. If pretty is true,
|
||||||
// records will be pretty-printed. If lineSeparated is true, records
|
// records will be pretty-printed. If lineSeparated is true, records
|
||||||
// will be logged with a new line between each record.
|
// will be logged with a new line between each record.
|
||||||
func JsonFormatEx(pretty, lineSeparated bool) Format {
|
func JSONFormatEx(pretty, lineSeparated bool) Format {
|
||||||
jsonMarshal := json.Marshal
|
jsonMarshal := json.Marshal
|
||||||
if pretty {
|
if pretty {
|
||||||
jsonMarshal = func(v interface{}) ([]byte, error) {
|
jsonMarshal = func(v interface{}) ([]byte, error) {
|
||||||
|
|
@ -225,7 +225,7 @@ func JsonFormatEx(pretty, lineSeparated bool) Format {
|
||||||
if !ok {
|
if !ok {
|
||||||
props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i])
|
props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i])
|
||||||
}
|
}
|
||||||
props[k] = formatJsonValue(r.Ctx[i+1])
|
props[k] = formatJSONValue(r.Ctx[i+1])
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := jsonMarshal(props)
|
b, err := jsonMarshal(props)
|
||||||
|
|
@ -270,7 +270,7 @@ func formatShared(value interface{}) (result interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatJsonValue(value interface{}) interface{} {
|
func formatJSONValue(value interface{}) interface{} {
|
||||||
value = formatShared(value)
|
value = formatShared(value)
|
||||||
switch value.(type) {
|
switch value.(type) {
|
||||||
case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string:
|
case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string:
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ import (
|
||||||
"github.com/go-stack/stack"
|
"github.com/go-stack/stack"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Handler defines where and how log records are written.
|
||||||
// A Logger prints its log records by writing to a Handler.
|
// A Logger prints its log records by writing to a Handler.
|
||||||
// The Handler interface defines where and how log records are written.
|
|
||||||
// Handlers are composable, providing you great flexibility in combining
|
// Handlers are composable, providing you great flexibility in combining
|
||||||
// them to achieve the logging structure that suits your applications.
|
// them to achieve the logging structure that suits your applications.
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
|
|
@ -193,7 +193,7 @@ func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
|
||||||
}, h)
|
}, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A MultiHandler dispatches any write to each of its handlers.
|
// MultiHandler dispatches any write to each of its handlers.
|
||||||
// This is useful for writing different types of log information
|
// This is useful for writing different types of log information
|
||||||
// to different locations. For example, to log to a file and
|
// to different locations. For example, to log to a file and
|
||||||
// standard error:
|
// standard error:
|
||||||
|
|
@ -212,7 +212,7 @@ func MultiHandler(hs ...Handler) Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// A FailoverHandler writes all log records to the first handler
|
// FailoverHandler writes all log records to the first handler
|
||||||
// specified, but will failover and write to the second handler if
|
// specified, but will failover and write to the second handler if
|
||||||
// the first handler has failed, and so on for all handlers specified.
|
// the first handler has failed, and so on for all handlers specified.
|
||||||
// For example you might want to log to a network socket, but failover
|
// For example you might want to log to a network socket, but failover
|
||||||
|
|
@ -220,7 +220,7 @@ func MultiHandler(hs ...Handler) Handler {
|
||||||
// standard out if the file write fails:
|
// standard out if the file write fails:
|
||||||
//
|
//
|
||||||
// log.FailoverHandler(
|
// log.FailoverHandler(
|
||||||
// log.Must.NetHandler("tcp", ":9090", log.JsonFormat()),
|
// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()),
|
||||||
// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
|
// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
|
||||||
// log.StdoutHandler)
|
// log.StdoutHandler)
|
||||||
//
|
//
|
||||||
|
|
@ -336,7 +336,7 @@ func DiscardHandler() Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Must object provides the following Handler creation functions
|
// Must provides the following Handler creation functions
|
||||||
// which instead of returning an error parameter only return a Handler
|
// which instead of returning an error parameter only return a Handler
|
||||||
// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
|
// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
|
||||||
var Must muster
|
var Must muster
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const (
|
||||||
LvlTrace
|
LvlTrace
|
||||||
)
|
)
|
||||||
|
|
||||||
// Aligned returns a 5-character string containing the name of a Lvl.
|
// AlignedString returns a 5-character string containing the name of a Lvl.
|
||||||
func (l Lvl) AlignedString() string {
|
func (l Lvl) AlignedString() string {
|
||||||
switch l {
|
switch l {
|
||||||
case LvlTrace:
|
case LvlTrace:
|
||||||
|
|
@ -64,7 +64,7 @@ func (l Lvl) String() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the appropriate Lvl from a string name.
|
// LvlFromString returns the appropriate Lvl from a string name.
|
||||||
// Useful for parsing command line args and configuration files.
|
// Useful for parsing command line args and configuration files.
|
||||||
func LvlFromString(lvlString string) (Lvl, error) {
|
func LvlFromString(lvlString string) (Lvl, error) {
|
||||||
switch lvlString {
|
switch lvlString {
|
||||||
|
|
@ -95,6 +95,7 @@ type Record struct {
|
||||||
KeyNames RecordKeyNames
|
KeyNames RecordKeyNames
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordKeyNames gets stored in a Record when the write function is executed.
|
||||||
type RecordKeyNames struct {
|
type RecordKeyNames struct {
|
||||||
Time string
|
Time string
|
||||||
Msg string
|
Msg string
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ const (
|
||||||
resultQueueSize = 10
|
resultQueueSize = 10
|
||||||
miningLogAtDepth = 5
|
miningLogAtDepth = 5
|
||||||
|
|
||||||
// txChanSize is the size of channel listening to TxPreEvent.
|
// txChanSize is the size of channel listening to NewTxsEvent.
|
||||||
// The number is referenced from the size of tx pool.
|
// The number is referenced from the size of tx pool.
|
||||||
txChanSize = 4096
|
txChanSize = 4096
|
||||||
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
|
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
|
||||||
|
|
@ -71,6 +71,7 @@ type Work struct {
|
||||||
family *set.Set // family set (used for checking uncle invalidity)
|
family *set.Set // family set (used for checking uncle invalidity)
|
||||||
uncles *set.Set // uncle set
|
uncles *set.Set // uncle set
|
||||||
tcount int // tx count in cycle
|
tcount int // tx count in cycle
|
||||||
|
gasPool *core.GasPool // available gas used to pack transactions
|
||||||
|
|
||||||
Block *types.Block // the new block
|
Block *types.Block // the new block
|
||||||
|
|
||||||
|
|
@ -95,8 +96,8 @@ type worker struct {
|
||||||
|
|
||||||
// update loop
|
// update loop
|
||||||
mux *event.TypeMux
|
mux *event.TypeMux
|
||||||
txCh chan core.TxPreEvent
|
txsCh chan core.NewTxsEvent
|
||||||
txSub event.Subscription
|
txsSub event.Subscription
|
||||||
chainHeadCh chan core.ChainHeadEvent
|
chainHeadCh chan core.ChainHeadEvent
|
||||||
chainHeadSub event.Subscription
|
chainHeadSub event.Subscription
|
||||||
chainSideCh chan core.ChainSideEvent
|
chainSideCh chan core.ChainSideEvent
|
||||||
|
|
@ -137,7 +138,7 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase com
|
||||||
engine: engine,
|
engine: engine,
|
||||||
eth: eth,
|
eth: eth,
|
||||||
mux: mux,
|
mux: mux,
|
||||||
txCh: make(chan core.TxPreEvent, txChanSize),
|
txsCh: make(chan core.NewTxsEvent, txChanSize),
|
||||||
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||||
chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize),
|
chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize),
|
||||||
chainDb: eth.ChainDb(),
|
chainDb: eth.ChainDb(),
|
||||||
|
|
@ -149,8 +150,8 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase com
|
||||||
agents: make(map[Agent]struct{}),
|
agents: make(map[Agent]struct{}),
|
||||||
unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth),
|
unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth),
|
||||||
}
|
}
|
||||||
// Subscribe TxPreEvent for tx pool
|
// Subscribe NewTxsEvent for tx pool
|
||||||
worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh)
|
worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh)
|
||||||
// Subscribe events for blockchain
|
// Subscribe events for blockchain
|
||||||
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
|
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
|
||||||
worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
|
worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
|
||||||
|
|
@ -241,7 +242,7 @@ func (self *worker) unregister(agent Agent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *worker) update() {
|
func (self *worker) update() {
|
||||||
defer self.txSub.Unsubscribe()
|
defer self.txsSub.Unsubscribe()
|
||||||
defer self.chainHeadSub.Unsubscribe()
|
defer self.chainHeadSub.Unsubscribe()
|
||||||
defer self.chainSideSub.Unsubscribe()
|
defer self.chainSideSub.Unsubscribe()
|
||||||
|
|
||||||
|
|
@ -258,15 +259,21 @@ func (self *worker) update() {
|
||||||
self.possibleUncles[ev.Block.Hash()] = ev.Block
|
self.possibleUncles[ev.Block.Hash()] = ev.Block
|
||||||
self.uncleMu.Unlock()
|
self.uncleMu.Unlock()
|
||||||
|
|
||||||
// Handle TxPreEvent
|
// Handle NewTxsEvent
|
||||||
case ev := <-self.txCh:
|
case ev := <-self.txsCh:
|
||||||
// Apply transaction to the pending state if we're not mining
|
// Apply transactions to the pending state if we're not mining.
|
||||||
|
//
|
||||||
|
// Note all transactions received may not be continuous with transactions
|
||||||
|
// already included in the current mining block. These transactions will
|
||||||
|
// be automatically eliminated.
|
||||||
if atomic.LoadInt32(&self.mining) == 0 {
|
if atomic.LoadInt32(&self.mining) == 0 {
|
||||||
self.currentMu.Lock()
|
self.currentMu.Lock()
|
||||||
acc, _ := types.Sender(self.current.signer, ev.Tx)
|
txs := make(map[common.Address]types.Transactions)
|
||||||
txs := map[common.Address]types.Transactions{acc: {ev.Tx}}
|
for _, tx := range ev.Txs {
|
||||||
|
acc, _ := types.Sender(self.current.signer, tx)
|
||||||
|
txs[acc] = append(txs[acc], tx)
|
||||||
|
}
|
||||||
txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs)
|
txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs)
|
||||||
|
|
||||||
self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)
|
self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)
|
||||||
self.updateSnapshot()
|
self.updateSnapshot()
|
||||||
self.currentMu.Unlock()
|
self.currentMu.Unlock()
|
||||||
|
|
@ -278,7 +285,7 @@ func (self *worker) update() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// System stopped
|
// System stopped
|
||||||
case <-self.txSub.Err():
|
case <-self.txsSub.Err():
|
||||||
return
|
return
|
||||||
case <-self.chainHeadSub.Err():
|
case <-self.chainHeadSub.Err():
|
||||||
return
|
return
|
||||||
|
|
@ -522,14 +529,16 @@ func (self *worker) updateSnapshot() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) {
|
func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) {
|
||||||
gp := new(core.GasPool).AddGas(env.header.GasLimit)
|
if env.gasPool == nil {
|
||||||
|
env.gasPool = new(core.GasPool).AddGas(env.header.GasLimit)
|
||||||
|
}
|
||||||
|
|
||||||
var coalescedLogs []*types.Log
|
var coalescedLogs []*types.Log
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// If we don't have enough gas for any further transactions then we're done
|
// If we don't have enough gas for any further transactions then we're done
|
||||||
if gp.Gas() < params.TxGas {
|
if env.gasPool.Gas() < params.TxGas {
|
||||||
log.Trace("Not enough gas for further transactions", "gp", gp)
|
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Retrieve the next transaction and abort if all done
|
// Retrieve the next transaction and abort if all done
|
||||||
|
|
@ -553,7 +562,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
||||||
// Start executing the transaction
|
// Start executing the transaction
|
||||||
env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
|
env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
|
||||||
|
|
||||||
err, logs := env.commitTransaction(tx, bc, coinbase, gp)
|
err, logs := env.commitTransaction(tx, bc, coinbase, env.gasPool)
|
||||||
switch err {
|
switch err {
|
||||||
case core.ErrGasLimitReached:
|
case core.ErrGasLimitReached:
|
||||||
// Pop the current out-of-gas transaction without shifting in the next from the account
|
// Pop the current out-of-gas transaction without shifting in the next from the account
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *str
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopRPC terminates an already running websocket RPC API endpoint.
|
// StopWS terminates an already running websocket RPC API endpoint.
|
||||||
func (api *PrivateAdminAPI) StopWS() (bool, error) {
|
func (api *PrivateAdminAPI) StopWS() (bool, error) {
|
||||||
api.node.lock.Lock()
|
api.node.lock.Lock()
|
||||||
defer api.node.lock.Unlock()
|
defer api.node.lock.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ using the same data directory will store this information in different subdirect
|
||||||
the data directory.
|
the data directory.
|
||||||
|
|
||||||
LevelDB databases are also stored within the instance subdirectory. If multiple node
|
LevelDB databases are also stored within the instance subdirectory. If multiple node
|
||||||
instances use the same data directory, openening the databases with identical names will
|
instances use the same data directory, opening the databases with identical names will
|
||||||
create one database for each instance.
|
create one database for each instance.
|
||||||
|
|
||||||
The account key store is shared among all node instances using the same data directory
|
The account key store is shared among all node instances using the same data directory
|
||||||
|
|
@ -84,7 +84,7 @@ directory. Mode instance A opens the database "db", node instance B opens the da
|
||||||
static-nodes.json -- devp2p static node list of instance B
|
static-nodes.json -- devp2p static node list of instance B
|
||||||
db/ -- LevelDB content for "db"
|
db/ -- LevelDB content for "db"
|
||||||
db-2/ -- LevelDB content for "db-2"
|
db-2/ -- LevelDB content for "db-2"
|
||||||
B.ipc -- JSON-RPC UNIX domain socket endpoint of instance A
|
B.ipc -- JSON-RPC UNIX domain socket endpoint of instance B
|
||||||
keystore/ -- account key store, used by both instances
|
keystore/ -- account key store, used by both instances
|
||||||
*/
|
*/
|
||||||
package node
|
package node
|
||||||
|
|
|
||||||
|
|
@ -507,8 +507,8 @@ func TestAPIGather(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Register a batch of services with some configured APIs
|
// Register a batch of services with some configured APIs
|
||||||
calls := make(chan string, 1)
|
calls := make(chan string, 1)
|
||||||
makeAPI := func(result string) *OneMethodApi {
|
makeAPI := func(result string) *OneMethodAPI {
|
||||||
return &OneMethodApi{fun: func() { calls <- result }}
|
return &OneMethodAPI{fun: func() { calls <- result }}
|
||||||
}
|
}
|
||||||
services := map[string]struct {
|
services := map[string]struct {
|
||||||
APIs []rpc.API
|
APIs []rpc.API
|
||||||
|
|
|
||||||
|
|
@ -121,12 +121,12 @@ func InstrumentedServiceMakerC(base ServiceConstructor) ServiceConstructor {
|
||||||
return InstrumentingWrapperMaker(base, reflect.TypeOf(InstrumentedServiceC{}))
|
return InstrumentingWrapperMaker(base, reflect.TypeOf(InstrumentedServiceC{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OneMethodApi is a single-method API handler to be returned by test services.
|
// OneMethodAPI is a single-method API handler to be returned by test services.
|
||||||
type OneMethodApi struct {
|
type OneMethodAPI struct {
|
||||||
fun func()
|
fun func()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *OneMethodApi) TheOneMethod() {
|
func (api *OneMethodAPI) TheOneMethod() {
|
||||||
if api.fun != nil {
|
if api.fun != nil {
|
||||||
api.fun()
|
api.fun()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
8
p2p/discv5/metrics.go
Normal file
8
p2p/discv5/metrics.go
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
package discv5
|
||||||
|
|
||||||
|
import "github.com/ethereum/go-ethereum/metrics"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ingressTrafficMeter = metrics.NewRegisteredMeter("discv5/InboundTraffic", nil)
|
||||||
|
egressTrafficMeter = metrics.NewRegisteredMeter("discv5/OutboundTraffic", nil)
|
||||||
|
)
|
||||||
|
|
@ -334,8 +334,10 @@ func (t *udp) sendPacket(toid NodeID, toaddr *net.UDPAddr, ptype byte, req inter
|
||||||
return hash, err
|
return hash, err
|
||||||
}
|
}
|
||||||
log.Trace(fmt.Sprintf(">>> %v to %x@%v", nodeEvent(ptype), toid[:8], toaddr))
|
log.Trace(fmt.Sprintf(">>> %v to %x@%v", nodeEvent(ptype), toid[:8], toaddr))
|
||||||
if _, err = t.conn.WriteToUDP(packet, toaddr); err != nil {
|
if nbytes, err := t.conn.WriteToUDP(packet, toaddr); err != nil {
|
||||||
log.Trace(fmt.Sprint("UDP send failed:", err))
|
log.Trace(fmt.Sprint("UDP send failed:", err))
|
||||||
|
} else {
|
||||||
|
egressTrafficMeter.Mark(int64(nbytes))
|
||||||
}
|
}
|
||||||
//fmt.Println(err)
|
//fmt.Println(err)
|
||||||
return hash, err
|
return hash, err
|
||||||
|
|
@ -374,6 +376,7 @@ func (t *udp) readLoop() {
|
||||||
buf := make([]byte, 1280)
|
buf := make([]byte, 1280)
|
||||||
for {
|
for {
|
||||||
nbytes, from, err := t.conn.ReadFromUDP(buf)
|
nbytes, from, err := t.conn.ReadFromUDP(buf)
|
||||||
|
ingressTrafficMeter.Mark(int64(nbytes))
|
||||||
if netutil.IsTemporaryError(err) {
|
if netutil.IsTemporaryError(err) {
|
||||||
// Ignore temporary read errors.
|
// Ignore temporary read errors.
|
||||||
log.Debug(fmt.Sprintf("Temporary read error: %v", err))
|
log.Debug(fmt.Sprintf("Temporary read error: %v", err))
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 1 // Major version component of the current release
|
VersionMajor = 1 // Major version component of the current release
|
||||||
VersionMinor = 8 // Minor version component of the current release
|
VersionMinor = 8 // Minor version component of the current release
|
||||||
VersionPatch = 9 // Patch version component of the current release
|
VersionPatch = 10 // Patch version component of the current release
|
||||||
VersionMeta = "unstable" // Version metadata to append to the version string
|
VersionMeta = "stable" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version holds the textual version string.
|
// Version holds the textual version string.
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ func hexToKeybytes(hex []byte) []byte {
|
||||||
if len(hex)&1 != 0 {
|
if len(hex)&1 != 0 {
|
||||||
panic("can't convert hex key of odd length")
|
panic("can't convert hex key of odd length")
|
||||||
}
|
}
|
||||||
key := make([]byte, (len(hex)+1)/2)
|
key := make([]byte, len(hex)/2)
|
||||||
decodeNibbles(hex, key)
|
decodeNibbles(hex, key)
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,12 +196,12 @@ func (h *hasher) store(n node, db *Database, force bool) (node, error) {
|
||||||
if h.onleaf != nil {
|
if h.onleaf != nil {
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *shortNode:
|
case *shortNode:
|
||||||
if child, ok := n.Val.(valueNode); ok {
|
if child, ok := n.Val.(valueNode); ok && child != nil {
|
||||||
h.onleaf(child, hash)
|
h.onleaf(child, hash)
|
||||||
}
|
}
|
||||||
case *fullNode:
|
case *fullNode:
|
||||||
for i := 0; i < 16; i++ {
|
for i := 0; i < 16; i++ {
|
||||||
if child, ok := n.Children[i].(valueNode); ok {
|
if child, ok := n.Children[i].(valueNode); ok && child != nil {
|
||||||
h.onleaf(child, hash)
|
h.onleaf(child, hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Iterator is a key-value trie iterator that traverses a Trie.
|
// Iterator is a key-value trie iterator that traverses a Trie.
|
||||||
|
|
@ -55,31 +56,50 @@ func (it *Iterator) Next() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prove generates the Merkle proof for the leaf node the iterator is currently
|
||||||
|
// positioned on.
|
||||||
|
func (it *Iterator) Prove() [][]byte {
|
||||||
|
return it.nodeIt.LeafProof()
|
||||||
|
}
|
||||||
|
|
||||||
// NodeIterator is an iterator to traverse the trie pre-order.
|
// NodeIterator is an iterator to traverse the trie pre-order.
|
||||||
type NodeIterator interface {
|
type NodeIterator interface {
|
||||||
// Next moves the iterator to the next node. If the parameter is false, any child
|
// Next moves the iterator to the next node. If the parameter is false, any child
|
||||||
// nodes will be skipped.
|
// nodes will be skipped.
|
||||||
Next(bool) bool
|
Next(bool) bool
|
||||||
|
|
||||||
// Error returns the error status of the iterator.
|
// Error returns the error status of the iterator.
|
||||||
Error() error
|
Error() error
|
||||||
|
|
||||||
// Hash returns the hash of the current node.
|
// Hash returns the hash of the current node.
|
||||||
Hash() common.Hash
|
Hash() common.Hash
|
||||||
|
|
||||||
// Parent returns the hash of the parent of the current node. The hash may be the one
|
// Parent returns the hash of the parent of the current node. The hash may be the one
|
||||||
// grandparent if the immediate parent is an internal node with no hash.
|
// grandparent if the immediate parent is an internal node with no hash.
|
||||||
Parent() common.Hash
|
Parent() common.Hash
|
||||||
|
|
||||||
// Path returns the hex-encoded path to the current node.
|
// Path returns the hex-encoded path to the current node.
|
||||||
// Callers must not retain references to the return value after calling Next.
|
// Callers must not retain references to the return value after calling Next.
|
||||||
// For leaf nodes, the last element of the path is the 'terminator symbol' 0x10.
|
// For leaf nodes, the last element of the path is the 'terminator symbol' 0x10.
|
||||||
Path() []byte
|
Path() []byte
|
||||||
|
|
||||||
// Leaf returns true iff the current node is a leaf node.
|
// Leaf returns true iff the current node is a leaf node.
|
||||||
// LeafBlob, LeafKey return the contents and key of the leaf node. These
|
|
||||||
// method panic if the iterator is not positioned at a leaf.
|
|
||||||
// Callers must not retain references to their return value after calling Next
|
|
||||||
Leaf() bool
|
Leaf() bool
|
||||||
LeafBlob() []byte
|
|
||||||
|
// LeafKey returns the key of the leaf. The method panics if the iterator is not
|
||||||
|
// positioned at a leaf. Callers must not retain references to the value after
|
||||||
|
// calling Next.
|
||||||
LeafKey() []byte
|
LeafKey() []byte
|
||||||
|
|
||||||
|
// LeafBlob returns the content of the leaf. The method panics if the iterator
|
||||||
|
// is not positioned at a leaf. Callers must not retain references to the value
|
||||||
|
// after calling Next.
|
||||||
|
LeafBlob() []byte
|
||||||
|
|
||||||
|
// LeafProof returns the Merkle proof of the leaf. The method panics if the
|
||||||
|
// iterator is not positioned at a leaf. Callers must not retain references
|
||||||
|
// to the value after calling Next.
|
||||||
|
LeafProof() [][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodeIteratorState represents the iteration state at one particular node of the
|
// nodeIteratorState represents the iteration state at one particular node of the
|
||||||
|
|
@ -99,8 +119,8 @@ type nodeIterator struct {
|
||||||
err error // Failure set in case of an internal error in the iterator
|
err error // Failure set in case of an internal error in the iterator
|
||||||
}
|
}
|
||||||
|
|
||||||
// iteratorEnd is stored in nodeIterator.err when iteration is done.
|
// errIteratorEnd is stored in nodeIterator.err when iteration is done.
|
||||||
var iteratorEnd = errors.New("end of iteration")
|
var errIteratorEnd = errors.New("end of iteration")
|
||||||
|
|
||||||
// seekError is stored in nodeIterator.err if the initial seek has failed.
|
// seekError is stored in nodeIterator.err if the initial seek has failed.
|
||||||
type seekError struct {
|
type seekError struct {
|
||||||
|
|
@ -139,6 +159,15 @@ func (it *nodeIterator) Leaf() bool {
|
||||||
return hasTerm(it.path)
|
return hasTerm(it.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *nodeIterator) LeafKey() []byte {
|
||||||
|
if len(it.stack) > 0 {
|
||||||
|
if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
||||||
|
return hexToKeybytes(it.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("not at leaf")
|
||||||
|
}
|
||||||
|
|
||||||
func (it *nodeIterator) LeafBlob() []byte {
|
func (it *nodeIterator) LeafBlob() []byte {
|
||||||
if len(it.stack) > 0 {
|
if len(it.stack) > 0 {
|
||||||
if node, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
if node, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
||||||
|
|
@ -148,10 +177,22 @@ func (it *nodeIterator) LeafBlob() []byte {
|
||||||
panic("not at leaf")
|
panic("not at leaf")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *nodeIterator) LeafKey() []byte {
|
func (it *nodeIterator) LeafProof() [][]byte {
|
||||||
if len(it.stack) > 0 {
|
if len(it.stack) > 0 {
|
||||||
if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
||||||
return hexToKeybytes(it.path)
|
hasher := newHasher(0, 0, nil)
|
||||||
|
proofs := make([][]byte, 0, len(it.stack))
|
||||||
|
|
||||||
|
for i, item := range it.stack[:len(it.stack)-1] {
|
||||||
|
// Gather nodes that end up as hash nodes (or the root)
|
||||||
|
node, _, _ := hasher.hashChildren(item.node, nil)
|
||||||
|
hashed, _ := hasher.store(node, nil, false)
|
||||||
|
if _, ok := hashed.(hashNode); ok || i == 0 {
|
||||||
|
enc, _ := rlp.EncodeToBytes(node)
|
||||||
|
proofs = append(proofs, enc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proofs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic("not at leaf")
|
panic("not at leaf")
|
||||||
|
|
@ -162,7 +203,7 @@ func (it *nodeIterator) Path() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *nodeIterator) Error() error {
|
func (it *nodeIterator) Error() error {
|
||||||
if it.err == iteratorEnd {
|
if it.err == errIteratorEnd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if seek, ok := it.err.(seekError); ok {
|
if seek, ok := it.err.(seekError); ok {
|
||||||
|
|
@ -176,7 +217,7 @@ func (it *nodeIterator) Error() error {
|
||||||
// sets the Error field to the encountered failure. If `descend` is false,
|
// sets the Error field to the encountered failure. If `descend` is false,
|
||||||
// skips iterating over any subnodes of the current node.
|
// skips iterating over any subnodes of the current node.
|
||||||
func (it *nodeIterator) Next(descend bool) bool {
|
func (it *nodeIterator) Next(descend bool) bool {
|
||||||
if it.err == iteratorEnd {
|
if it.err == errIteratorEnd {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if seek, ok := it.err.(seekError); ok {
|
if seek, ok := it.err.(seekError); ok {
|
||||||
|
|
@ -201,8 +242,8 @@ func (it *nodeIterator) seek(prefix []byte) error {
|
||||||
// Move forward until we're just before the closest match to key.
|
// Move forward until we're just before the closest match to key.
|
||||||
for {
|
for {
|
||||||
state, parentIndex, path, err := it.peek(bytes.HasPrefix(key, it.path))
|
state, parentIndex, path, err := it.peek(bytes.HasPrefix(key, it.path))
|
||||||
if err == iteratorEnd {
|
if err == errIteratorEnd {
|
||||||
return iteratorEnd
|
return errIteratorEnd
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return seekError{prefix, err}
|
return seekError{prefix, err}
|
||||||
} else if bytes.Compare(path, key) >= 0 {
|
} else if bytes.Compare(path, key) >= 0 {
|
||||||
|
|
@ -246,7 +287,7 @@ func (it *nodeIterator) peek(descend bool) (*nodeIteratorState, *int, []byte, er
|
||||||
// No more child nodes, move back up.
|
// No more child nodes, move back up.
|
||||||
it.pop()
|
it.pop()
|
||||||
}
|
}
|
||||||
return nil, nil, nil, iteratorEnd
|
return nil, nil, nil, errIteratorEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *nodeIteratorState) resolve(tr *Trie, path []byte) error {
|
func (st *nodeIteratorState) resolve(tr *Trie, path []byte) error {
|
||||||
|
|
@ -361,12 +402,16 @@ func (it *differenceIterator) Leaf() bool {
|
||||||
return it.b.Leaf()
|
return it.b.Leaf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *differenceIterator) LeafKey() []byte {
|
||||||
|
return it.b.LeafKey()
|
||||||
|
}
|
||||||
|
|
||||||
func (it *differenceIterator) LeafBlob() []byte {
|
func (it *differenceIterator) LeafBlob() []byte {
|
||||||
return it.b.LeafBlob()
|
return it.b.LeafBlob()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *differenceIterator) LeafKey() []byte {
|
func (it *differenceIterator) LeafProof() [][]byte {
|
||||||
return it.b.LeafKey()
|
return it.b.LeafProof()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *differenceIterator) Path() []byte {
|
func (it *differenceIterator) Path() []byte {
|
||||||
|
|
@ -464,12 +509,16 @@ func (it *unionIterator) Leaf() bool {
|
||||||
return (*it.items)[0].Leaf()
|
return (*it.items)[0].Leaf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *unionIterator) LeafKey() []byte {
|
||||||
|
return (*it.items)[0].LeafKey()
|
||||||
|
}
|
||||||
|
|
||||||
func (it *unionIterator) LeafBlob() []byte {
|
func (it *unionIterator) LeafBlob() []byte {
|
||||||
return (*it.items)[0].LeafBlob()
|
return (*it.items)[0].LeafBlob()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *unionIterator) LeafKey() []byte {
|
func (it *unionIterator) LeafProof() [][]byte {
|
||||||
return (*it.items)[0].LeafKey()
|
return (*it.items)[0].LeafProof()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *unionIterator) Path() []byte {
|
func (it *unionIterator) Path() []byte {
|
||||||
|
|
@ -509,12 +558,10 @@ func (it *unionIterator) Next(descend bool) bool {
|
||||||
heap.Push(it.items, skipped)
|
heap.Push(it.items, skipped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if least.Next(descend) {
|
if least.Next(descend) {
|
||||||
it.count++
|
it.count++
|
||||||
heap.Push(it.items, least)
|
heap.Push(it.items, least)
|
||||||
}
|
}
|
||||||
|
|
||||||
return len(*it.items) > 0
|
return len(*it.items) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,28 +102,28 @@ func (t *SecureTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) err
|
||||||
// VerifyProof checks merkle proofs. The given proof must contain the value for
|
// VerifyProof checks merkle proofs. The given proof must contain the value for
|
||||||
// key in a trie with the given root hash. VerifyProof returns an error if the
|
// key in a trie with the given root hash. VerifyProof returns an error if the
|
||||||
// proof contains invalid trie nodes or the wrong value.
|
// proof contains invalid trie nodes or the wrong value.
|
||||||
func VerifyProof(rootHash common.Hash, key []byte, proofDb DatabaseReader) (value []byte, err error, nodes int) {
|
func VerifyProof(rootHash common.Hash, key []byte, proofDb DatabaseReader) (value []byte, nodes int, err error) {
|
||||||
key = keybytesToHex(key)
|
key = keybytesToHex(key)
|
||||||
wantHash := rootHash
|
wantHash := rootHash
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
buf, _ := proofDb.Get(wantHash[:])
|
buf, _ := proofDb.Get(wantHash[:])
|
||||||
if buf == nil {
|
if buf == nil {
|
||||||
return nil, fmt.Errorf("proof node %d (hash %064x) missing", i, wantHash), i
|
return nil, i, fmt.Errorf("proof node %d (hash %064x) missing", i, wantHash)
|
||||||
}
|
}
|
||||||
n, err := decodeNode(wantHash[:], buf, 0)
|
n, err := decodeNode(wantHash[:], buf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("bad proof node %d: %v", i, err), i
|
return nil, i, fmt.Errorf("bad proof node %d: %v", i, err)
|
||||||
}
|
}
|
||||||
keyrest, cld := get(n, key)
|
keyrest, cld := get(n, key)
|
||||||
switch cld := cld.(type) {
|
switch cld := cld.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
// The trie doesn't contain the key.
|
// The trie doesn't contain the key.
|
||||||
return nil, nil, i
|
return nil, i, nil
|
||||||
case hashNode:
|
case hashNode:
|
||||||
key = keyrest
|
key = keyrest
|
||||||
copy(wantHash[:], cld)
|
copy(wantHash[:], cld)
|
||||||
case valueNode:
|
case valueNode:
|
||||||
return cld, nil, i + 1
|
return cld, i + 1, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,20 +32,46 @@ func init() {
|
||||||
mrand.Seed(time.Now().Unix())
|
mrand.Seed(time.Now().Unix())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeProvers creates Merkle trie provers based on different implementations to
|
||||||
|
// test all variations.
|
||||||
|
func makeProvers(trie *Trie) []func(key []byte) *ethdb.MemDatabase {
|
||||||
|
var provers []func(key []byte) *ethdb.MemDatabase
|
||||||
|
|
||||||
|
// Create a direct trie based Merkle prover
|
||||||
|
provers = append(provers, func(key []byte) *ethdb.MemDatabase {
|
||||||
|
proof := ethdb.NewMemDatabase()
|
||||||
|
trie.Prove(key, 0, proof)
|
||||||
|
return proof
|
||||||
|
})
|
||||||
|
// Create a leaf iterator based Merkle prover
|
||||||
|
provers = append(provers, func(key []byte) *ethdb.MemDatabase {
|
||||||
|
proof := ethdb.NewMemDatabase()
|
||||||
|
if it := NewIterator(trie.NodeIterator(key)); it.Next() && bytes.Equal(key, it.Key) {
|
||||||
|
for _, p := range it.Prove() {
|
||||||
|
proof.Put(crypto.Keccak256(p), p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proof
|
||||||
|
})
|
||||||
|
return provers
|
||||||
|
}
|
||||||
|
|
||||||
func TestProof(t *testing.T) {
|
func TestProof(t *testing.T) {
|
||||||
trie, vals := randomTrie(500)
|
trie, vals := randomTrie(500)
|
||||||
root := trie.Hash()
|
root := trie.Hash()
|
||||||
|
for i, prover := range makeProvers(trie) {
|
||||||
for _, kv := range vals {
|
for _, kv := range vals {
|
||||||
proofs := ethdb.NewMemDatabase()
|
proof := prover(kv.k)
|
||||||
if trie.Prove(kv.k, 0, proofs) != nil {
|
if proof == nil {
|
||||||
t.Fatalf("missing key %x while constructing proof", kv.k)
|
t.Fatalf("prover %d: missing key %x while constructing proof", i, kv.k)
|
||||||
}
|
}
|
||||||
val, err, _ := VerifyProof(root, kv.k, proofs)
|
val, _, err := VerifyProof(root, kv.k, proof)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("VerifyProof error for key %x: %v\nraw proof: %v", kv.k, err, proofs)
|
t.Fatalf("prover %d: failed to verify proof for key %x: %v\nraw proof: %x", i, kv.k, err, proof)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(val, kv.v) {
|
if !bytes.Equal(val, kv.v) {
|
||||||
t.Fatalf("VerifyProof returned wrong value for key %x: got %x, want %x", kv.k, val, kv.v)
|
t.Fatalf("prover %d: verified value mismatch for key %x: have %x, want %x", i, kv.k, val, kv.v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,37 +79,66 @@ func TestProof(t *testing.T) {
|
||||||
func TestOneElementProof(t *testing.T) {
|
func TestOneElementProof(t *testing.T) {
|
||||||
trie := new(Trie)
|
trie := new(Trie)
|
||||||
updateString(trie, "k", "v")
|
updateString(trie, "k", "v")
|
||||||
proofs := ethdb.NewMemDatabase()
|
for i, prover := range makeProvers(trie) {
|
||||||
trie.Prove([]byte("k"), 0, proofs)
|
proof := prover([]byte("k"))
|
||||||
if len(proofs.Keys()) != 1 {
|
if proof == nil {
|
||||||
t.Error("proof should have one element")
|
t.Fatalf("prover %d: nil proof", i)
|
||||||
}
|
}
|
||||||
val, err, _ := VerifyProof(trie.Hash(), []byte("k"), proofs)
|
if proof.Len() != 1 {
|
||||||
|
t.Errorf("prover %d: proof should have one element", i)
|
||||||
|
}
|
||||||
|
val, _, err := VerifyProof(trie.Hash(), []byte("k"), proof)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("VerifyProof error: %v\nproof hashes: %v", err, proofs.Keys())
|
t.Fatalf("prover %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(val, []byte("v")) {
|
if !bytes.Equal(val, []byte("v")) {
|
||||||
t.Fatalf("VerifyProof returned wrong value: got %x, want 'k'", val)
|
t.Fatalf("prover %d: verified value mismatch: have %x, want 'k'", i, val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVerifyBadProof(t *testing.T) {
|
func TestBadProof(t *testing.T) {
|
||||||
trie, vals := randomTrie(800)
|
trie, vals := randomTrie(800)
|
||||||
root := trie.Hash()
|
root := trie.Hash()
|
||||||
|
for i, prover := range makeProvers(trie) {
|
||||||
for _, kv := range vals {
|
for _, kv := range vals {
|
||||||
proofs := ethdb.NewMemDatabase()
|
proof := prover(kv.k)
|
||||||
trie.Prove(kv.k, 0, proofs)
|
if proof == nil {
|
||||||
if len(proofs.Keys()) == 0 {
|
t.Fatalf("prover %d: nil proof", i)
|
||||||
t.Fatal("zero length proof")
|
|
||||||
}
|
}
|
||||||
keys := proofs.Keys()
|
key := proof.Keys()[mrand.Intn(proof.Len())]
|
||||||
key := keys[mrand.Intn(len(keys))]
|
val, _ := proof.Get(key)
|
||||||
node, _ := proofs.Get(key)
|
proof.Delete(key)
|
||||||
proofs.Delete(key)
|
|
||||||
mutateByte(node)
|
mutateByte(val)
|
||||||
proofs.Put(crypto.Keccak256(node), node)
|
proof.Put(crypto.Keccak256(val), val)
|
||||||
if _, err, _ := VerifyProof(root, kv.k, proofs); err == nil {
|
|
||||||
t.Fatalf("expected proof to fail for key %x", kv.k)
|
if _, _, err := VerifyProof(root, kv.k, proof); err == nil {
|
||||||
|
t.Fatalf("prover %d: expected proof to fail for key %x", i, kv.k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that missing keys can also be proven. The test explicitly uses a single
|
||||||
|
// entry trie and checks for missing keys both before and after the single entry.
|
||||||
|
func TestMissingKeyProof(t *testing.T) {
|
||||||
|
trie := new(Trie)
|
||||||
|
updateString(trie, "k", "v")
|
||||||
|
|
||||||
|
for i, key := range []string{"a", "j", "l", "z"} {
|
||||||
|
proof := ethdb.NewMemDatabase()
|
||||||
|
trie.Prove([]byte(key), 0, proof)
|
||||||
|
|
||||||
|
if proof.Len() != 1 {
|
||||||
|
t.Errorf("test %d: proof should have one element", i)
|
||||||
|
}
|
||||||
|
val, _, err := VerifyProof(trie.Hash(), []byte(key), proof)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
|
||||||
|
}
|
||||||
|
if val != nil {
|
||||||
|
t.Fatalf("test %d: verified value mismatch: have %x, want nil", i, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +186,7 @@ func BenchmarkVerifyProof(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
im := i % len(keys)
|
im := i % len(keys)
|
||||||
if _, err, _ := VerifyProof(root, []byte(keys[im]), proofs[im]); err != nil {
|
if _, _, err := VerifyProof(root, []byte(keys[im]), proofs[im]); err != nil {
|
||||||
b.Fatalf("key %x: %v", keys[im], err)
|
b.Fatalf("key %x: %v", keys[im], err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -155,14 +155,19 @@ func (t *SecureTrie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
|
||||||
return t.trie.Commit(onleaf)
|
return t.trie.Commit(onleaf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash returns the root hash of SecureTrie. It does not write to the
|
||||||
|
// database and can be used even if the trie doesn't have one.
|
||||||
func (t *SecureTrie) Hash() common.Hash {
|
func (t *SecureTrie) Hash() common.Hash {
|
||||||
return t.trie.Hash()
|
return t.trie.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Root returns the root hash of SecureTrie.
|
||||||
|
// Deprecated: use Hash instead.
|
||||||
func (t *SecureTrie) Root() []byte {
|
func (t *SecureTrie) Root() []byte {
|
||||||
return t.trie.Root()
|
return t.trie.Root()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of SecureTrie.
|
||||||
func (t *SecureTrie) Copy() *SecureTrie {
|
func (t *SecureTrie) Copy() *SecureTrie {
|
||||||
cpy := *t
|
cpy := *t
|
||||||
return &cpy
|
return &cpy
|
||||||
|
|
|
||||||
28
trie/sync.go
28
trie/sync.go
|
|
@ -68,19 +68,19 @@ func newSyncMemBatch() *syncMemBatch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrieSync is the main state trie synchronisation scheduler, which provides yet
|
// Sync is the main state trie synchronisation scheduler, which provides yet
|
||||||
// unknown trie hashes to retrieve, accepts node data associated with said hashes
|
// unknown trie hashes to retrieve, accepts node data associated with said hashes
|
||||||
// and reconstructs the trie step by step until all is done.
|
// and reconstructs the trie step by step until all is done.
|
||||||
type TrieSync struct {
|
type Sync struct {
|
||||||
database DatabaseReader // Persistent database to check for existing entries
|
database DatabaseReader // Persistent database to check for existing entries
|
||||||
membatch *syncMemBatch // Memory buffer to avoid frequest database writes
|
membatch *syncMemBatch // Memory buffer to avoid frequest database writes
|
||||||
requests map[common.Hash]*request // Pending requests pertaining to a key hash
|
requests map[common.Hash]*request // Pending requests pertaining to a key hash
|
||||||
queue *prque.Prque // Priority queue with the pending requests
|
queue *prque.Prque // Priority queue with the pending requests
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTrieSync creates a new trie data download scheduler.
|
// NewSync creates a new trie data download scheduler.
|
||||||
func NewTrieSync(root common.Hash, database DatabaseReader, callback LeafCallback) *TrieSync {
|
func NewSync(root common.Hash, database DatabaseReader, callback LeafCallback) *Sync {
|
||||||
ts := &TrieSync{
|
ts := &Sync{
|
||||||
database: database,
|
database: database,
|
||||||
membatch: newSyncMemBatch(),
|
membatch: newSyncMemBatch(),
|
||||||
requests: make(map[common.Hash]*request),
|
requests: make(map[common.Hash]*request),
|
||||||
|
|
@ -91,7 +91,7 @@ func NewTrieSync(root common.Hash, database DatabaseReader, callback LeafCallbac
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSubTrie registers a new trie to the sync code, rooted at the designated parent.
|
// AddSubTrie registers a new trie to the sync code, rooted at the designated parent.
|
||||||
func (s *TrieSync) AddSubTrie(root common.Hash, depth int, parent common.Hash, callback LeafCallback) {
|
func (s *Sync) AddSubTrie(root common.Hash, depth int, parent common.Hash, callback LeafCallback) {
|
||||||
// Short circuit if the trie is empty or already known
|
// Short circuit if the trie is empty or already known
|
||||||
if root == emptyRoot {
|
if root == emptyRoot {
|
||||||
return
|
return
|
||||||
|
|
@ -126,7 +126,7 @@ func (s *TrieSync) AddSubTrie(root common.Hash, depth int, parent common.Hash, c
|
||||||
// interpreted as a trie node, but rather accepted and stored into the database
|
// interpreted as a trie node, but rather accepted and stored into the database
|
||||||
// as is. This method's goal is to support misc state metadata retrievals (e.g.
|
// as is. This method's goal is to support misc state metadata retrievals (e.g.
|
||||||
// contract code).
|
// contract code).
|
||||||
func (s *TrieSync) AddRawEntry(hash common.Hash, depth int, parent common.Hash) {
|
func (s *Sync) AddRawEntry(hash common.Hash, depth int, parent common.Hash) {
|
||||||
// Short circuit if the entry is empty or already known
|
// Short circuit if the entry is empty or already known
|
||||||
if hash == emptyState {
|
if hash == emptyState {
|
||||||
return
|
return
|
||||||
|
|
@ -156,7 +156,7 @@ func (s *TrieSync) AddRawEntry(hash common.Hash, depth int, parent common.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Missing retrieves the known missing nodes from the trie for retrieval.
|
// Missing retrieves the known missing nodes from the trie for retrieval.
|
||||||
func (s *TrieSync) Missing(max int) []common.Hash {
|
func (s *Sync) Missing(max int) []common.Hash {
|
||||||
requests := []common.Hash{}
|
requests := []common.Hash{}
|
||||||
for !s.queue.Empty() && (max == 0 || len(requests) < max) {
|
for !s.queue.Empty() && (max == 0 || len(requests) < max) {
|
||||||
requests = append(requests, s.queue.PopItem().(common.Hash))
|
requests = append(requests, s.queue.PopItem().(common.Hash))
|
||||||
|
|
@ -167,7 +167,7 @@ func (s *TrieSync) Missing(max int) []common.Hash {
|
||||||
// Process injects a batch of retrieved trie nodes data, returning if something
|
// Process injects a batch of retrieved trie nodes data, returning if something
|
||||||
// was committed to the database and also the index of an entry if processing of
|
// was committed to the database and also the index of an entry if processing of
|
||||||
// it failed.
|
// it failed.
|
||||||
func (s *TrieSync) Process(results []SyncResult) (bool, int, error) {
|
func (s *Sync) Process(results []SyncResult) (bool, int, error) {
|
||||||
committed := false
|
committed := false
|
||||||
|
|
||||||
for i, item := range results {
|
for i, item := range results {
|
||||||
|
|
@ -213,7 +213,7 @@ func (s *TrieSync) Process(results []SyncResult) (bool, int, error) {
|
||||||
|
|
||||||
// Commit flushes the data stored in the internal membatch out to persistent
|
// Commit flushes the data stored in the internal membatch out to persistent
|
||||||
// storage, returning the number of items written and any occurred error.
|
// storage, returning the number of items written and any occurred error.
|
||||||
func (s *TrieSync) Commit(dbw ethdb.Putter) (int, error) {
|
func (s *Sync) Commit(dbw ethdb.Putter) (int, error) {
|
||||||
// Dump the membatch into a database dbw
|
// Dump the membatch into a database dbw
|
||||||
for i, key := range s.membatch.order {
|
for i, key := range s.membatch.order {
|
||||||
if err := dbw.Put(key[:], s.membatch.batch[key]); err != nil {
|
if err := dbw.Put(key[:], s.membatch.batch[key]); err != nil {
|
||||||
|
|
@ -228,14 +228,14 @@ func (s *TrieSync) Commit(dbw ethdb.Putter) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pending returns the number of state entries currently pending for download.
|
// Pending returns the number of state entries currently pending for download.
|
||||||
func (s *TrieSync) Pending() int {
|
func (s *Sync) Pending() int {
|
||||||
return len(s.requests)
|
return len(s.requests)
|
||||||
}
|
}
|
||||||
|
|
||||||
// schedule inserts a new state retrieval request into the fetch queue. If there
|
// schedule inserts a new state retrieval request into the fetch queue. If there
|
||||||
// is already a pending request for this node, the new request will be discarded
|
// is already a pending request for this node, the new request will be discarded
|
||||||
// and only a parent reference added to the old one.
|
// and only a parent reference added to the old one.
|
||||||
func (s *TrieSync) schedule(req *request) {
|
func (s *Sync) schedule(req *request) {
|
||||||
// If we're already requesting this node, add a new reference and stop
|
// If we're already requesting this node, add a new reference and stop
|
||||||
if old, ok := s.requests[req.hash]; ok {
|
if old, ok := s.requests[req.hash]; ok {
|
||||||
old.parents = append(old.parents, req.parents...)
|
old.parents = append(old.parents, req.parents...)
|
||||||
|
|
@ -248,7 +248,7 @@ func (s *TrieSync) schedule(req *request) {
|
||||||
|
|
||||||
// children retrieves all the missing children of a state trie entry for future
|
// children retrieves all the missing children of a state trie entry for future
|
||||||
// retrieval scheduling.
|
// retrieval scheduling.
|
||||||
func (s *TrieSync) children(req *request, object node) ([]*request, error) {
|
func (s *Sync) children(req *request, object node) ([]*request, error) {
|
||||||
// Gather all the children of the node, irrelevant whether known or not
|
// Gather all the children of the node, irrelevant whether known or not
|
||||||
type child struct {
|
type child struct {
|
||||||
node node
|
node node
|
||||||
|
|
@ -310,7 +310,7 @@ func (s *TrieSync) children(req *request, object node) ([]*request, error) {
|
||||||
// commit finalizes a retrieval request and stores it into the membatch. If any
|
// commit finalizes a retrieval request and stores it into the membatch. If any
|
||||||
// of the referencing parent requests complete due to this commit, they are also
|
// of the referencing parent requests complete due to this commit, they are also
|
||||||
// committed themselves.
|
// committed themselves.
|
||||||
func (s *TrieSync) commit(req *request) (err error) {
|
func (s *Sync) commit(req *request) (err error) {
|
||||||
// Write the node content to the membatch
|
// Write the node content to the membatch
|
||||||
s.membatch.batch[req.hash] = req.data
|
s.membatch.batch[req.hash] = req.data
|
||||||
s.membatch.order = append(s.membatch.order, req.hash)
|
s.membatch.order = append(s.membatch.order, req.hash)
|
||||||
|
|
|
||||||
|
|
@ -87,14 +87,14 @@ func checkTrieConsistency(db *Database, root common.Hash) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that an empty trie is not scheduled for syncing.
|
// Tests that an empty trie is not scheduled for syncing.
|
||||||
func TestEmptyTrieSync(t *testing.T) {
|
func TestEmptySync(t *testing.T) {
|
||||||
dbA := NewDatabase(ethdb.NewMemDatabase())
|
dbA := NewDatabase(ethdb.NewMemDatabase())
|
||||||
dbB := NewDatabase(ethdb.NewMemDatabase())
|
dbB := NewDatabase(ethdb.NewMemDatabase())
|
||||||
emptyA, _ := New(common.Hash{}, dbA)
|
emptyA, _ := New(common.Hash{}, dbA)
|
||||||
emptyB, _ := New(emptyRoot, dbB)
|
emptyB, _ := New(emptyRoot, dbB)
|
||||||
|
|
||||||
for i, trie := range []*Trie{emptyA, emptyB} {
|
for i, trie := range []*Trie{emptyA, emptyB} {
|
||||||
if req := NewTrieSync(trie.Hash(), ethdb.NewMemDatabase(), nil).Missing(1); len(req) != 0 {
|
if req := NewSync(trie.Hash(), ethdb.NewMemDatabase(), nil).Missing(1); len(req) != 0 {
|
||||||
t.Errorf("test %d: content requested for empty trie: %v", i, req)
|
t.Errorf("test %d: content requested for empty trie: %v", i, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,17 +102,17 @@ func TestEmptyTrieSync(t *testing.T) {
|
||||||
|
|
||||||
// Tests that given a root hash, a trie can sync iteratively on a single thread,
|
// Tests that given a root hash, a trie can sync iteratively on a single thread,
|
||||||
// requesting retrieval tasks and returning all of them in one go.
|
// requesting retrieval tasks and returning all of them in one go.
|
||||||
func TestIterativeTrieSyncIndividual(t *testing.T) { testIterativeTrieSync(t, 1) }
|
func TestIterativeSyncIndividual(t *testing.T) { testIterativeSync(t, 1) }
|
||||||
func TestIterativeTrieSyncBatched(t *testing.T) { testIterativeTrieSync(t, 100) }
|
func TestIterativeSyncBatched(t *testing.T) { testIterativeSync(t, 100) }
|
||||||
|
|
||||||
func testIterativeTrieSync(t *testing.T, batch int) {
|
func testIterativeSync(t *testing.T, batch int) {
|
||||||
// Create a random trie to copy
|
// Create a random trie to copy
|
||||||
srcDb, srcTrie, srcData := makeTestTrie()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := ethdb.NewMemDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
triedb := NewDatabase(diskdb)
|
||||||
sched := NewTrieSync(srcTrie.Hash(), diskdb, nil)
|
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||||
|
|
||||||
queue := append([]common.Hash{}, sched.Missing(batch)...)
|
queue := append([]common.Hash{}, sched.Missing(batch)...)
|
||||||
for len(queue) > 0 {
|
for len(queue) > 0 {
|
||||||
|
|
@ -138,14 +138,14 @@ func testIterativeTrieSync(t *testing.T, batch int) {
|
||||||
|
|
||||||
// Tests that the trie scheduler can correctly reconstruct the state even if only
|
// Tests that the trie scheduler can correctly reconstruct the state even if only
|
||||||
// partial results are returned, and the others sent only later.
|
// partial results are returned, and the others sent only later.
|
||||||
func TestIterativeDelayedTrieSync(t *testing.T) {
|
func TestIterativeDelayedSync(t *testing.T) {
|
||||||
// Create a random trie to copy
|
// Create a random trie to copy
|
||||||
srcDb, srcTrie, srcData := makeTestTrie()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := ethdb.NewMemDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
triedb := NewDatabase(diskdb)
|
||||||
sched := NewTrieSync(srcTrie.Hash(), diskdb, nil)
|
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||||
|
|
||||||
queue := append([]common.Hash{}, sched.Missing(10000)...)
|
queue := append([]common.Hash{}, sched.Missing(10000)...)
|
||||||
for len(queue) > 0 {
|
for len(queue) > 0 {
|
||||||
|
|
@ -173,17 +173,17 @@ func TestIterativeDelayedTrieSync(t *testing.T) {
|
||||||
// Tests that given a root hash, a trie can sync iteratively on a single thread,
|
// Tests that given a root hash, a trie can sync iteratively on a single thread,
|
||||||
// requesting retrieval tasks and returning all of them in one go, however in a
|
// requesting retrieval tasks and returning all of them in one go, however in a
|
||||||
// random order.
|
// random order.
|
||||||
func TestIterativeRandomTrieSyncIndividual(t *testing.T) { testIterativeRandomTrieSync(t, 1) }
|
func TestIterativeRandomSyncIndividual(t *testing.T) { testIterativeRandomSync(t, 1) }
|
||||||
func TestIterativeRandomTrieSyncBatched(t *testing.T) { testIterativeRandomTrieSync(t, 100) }
|
func TestIterativeRandomSyncBatched(t *testing.T) { testIterativeRandomSync(t, 100) }
|
||||||
|
|
||||||
func testIterativeRandomTrieSync(t *testing.T, batch int) {
|
func testIterativeRandomSync(t *testing.T, batch int) {
|
||||||
// Create a random trie to copy
|
// Create a random trie to copy
|
||||||
srcDb, srcTrie, srcData := makeTestTrie()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := ethdb.NewMemDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
triedb := NewDatabase(diskdb)
|
||||||
sched := NewTrieSync(srcTrie.Hash(), diskdb, nil)
|
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||||
|
|
||||||
queue := make(map[common.Hash]struct{})
|
queue := make(map[common.Hash]struct{})
|
||||||
for _, hash := range sched.Missing(batch) {
|
for _, hash := range sched.Missing(batch) {
|
||||||
|
|
@ -217,14 +217,14 @@ func testIterativeRandomTrieSync(t *testing.T, batch int) {
|
||||||
|
|
||||||
// Tests that the trie scheduler can correctly reconstruct the state even if only
|
// Tests that the trie scheduler can correctly reconstruct the state even if only
|
||||||
// partial results are returned (Even those randomly), others sent only later.
|
// partial results are returned (Even those randomly), others sent only later.
|
||||||
func TestIterativeRandomDelayedTrieSync(t *testing.T) {
|
func TestIterativeRandomDelayedSync(t *testing.T) {
|
||||||
// Create a random trie to copy
|
// Create a random trie to copy
|
||||||
srcDb, srcTrie, srcData := makeTestTrie()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := ethdb.NewMemDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
triedb := NewDatabase(diskdb)
|
||||||
sched := NewTrieSync(srcTrie.Hash(), diskdb, nil)
|
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||||
|
|
||||||
queue := make(map[common.Hash]struct{})
|
queue := make(map[common.Hash]struct{})
|
||||||
for _, hash := range sched.Missing(10000) {
|
for _, hash := range sched.Missing(10000) {
|
||||||
|
|
@ -264,14 +264,14 @@ func TestIterativeRandomDelayedTrieSync(t *testing.T) {
|
||||||
|
|
||||||
// Tests that a trie sync will not request nodes multiple times, even if they
|
// Tests that a trie sync will not request nodes multiple times, even if they
|
||||||
// have such references.
|
// have such references.
|
||||||
func TestDuplicateAvoidanceTrieSync(t *testing.T) {
|
func TestDuplicateAvoidanceSync(t *testing.T) {
|
||||||
// Create a random trie to copy
|
// Create a random trie to copy
|
||||||
srcDb, srcTrie, srcData := makeTestTrie()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := ethdb.NewMemDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
triedb := NewDatabase(diskdb)
|
||||||
sched := NewTrieSync(srcTrie.Hash(), diskdb, nil)
|
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||||
|
|
||||||
queue := append([]common.Hash{}, sched.Missing(0)...)
|
queue := append([]common.Hash{}, sched.Missing(0)...)
|
||||||
requested := make(map[common.Hash]struct{})
|
requested := make(map[common.Hash]struct{})
|
||||||
|
|
@ -304,14 +304,14 @@ func TestDuplicateAvoidanceTrieSync(t *testing.T) {
|
||||||
|
|
||||||
// Tests that at any point in time during a sync, only complete sub-tries are in
|
// Tests that at any point in time during a sync, only complete sub-tries are in
|
||||||
// the database.
|
// the database.
|
||||||
func TestIncompleteTrieSync(t *testing.T) {
|
func TestIncompleteSync(t *testing.T) {
|
||||||
// Create a random trie to copy
|
// Create a random trie to copy
|
||||||
srcDb, srcTrie, _ := makeTestTrie()
|
srcDb, srcTrie, _ := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := ethdb.NewMemDatabase()
|
diskdb := ethdb.NewMemDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
triedb := NewDatabase(diskdb)
|
||||||
sched := NewTrieSync(srcTrie.Hash(), diskdb, nil)
|
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||||
|
|
||||||
added := []common.Hash{}
|
added := []common.Hash{}
|
||||||
queue := append([]common.Hash{}, sched.Missing(1)...)
|
queue := append([]common.Hash{}, sched.Missing(1)...)
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ func New(root common.Hash, db *Database) (*Trie, error) {
|
||||||
db: db,
|
db: db,
|
||||||
originalRoot: root,
|
originalRoot: root,
|
||||||
}
|
}
|
||||||
if (root != common.Hash{}) && root != emptyRoot {
|
if root != (common.Hash{}) && root != emptyRoot {
|
||||||
rootnode, err := trie.resolveHash(root[:], nil)
|
rootnode, err := trie.resolveHash(root[:], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
73
vendor/github.com/syndtr/goleveldb/leveldb/db.go
generated
vendored
73
vendor/github.com/syndtr/goleveldb/leveldb/db.go
generated
vendored
|
|
@ -35,6 +35,7 @@ type DB struct {
|
||||||
// Stats. Need 64-bit alignment.
|
// Stats. Need 64-bit alignment.
|
||||||
cWriteDelay int64 // The cumulative duration of write delays
|
cWriteDelay int64 // The cumulative duration of write delays
|
||||||
cWriteDelayN int32 // The cumulative number of write delays
|
cWriteDelayN int32 // The cumulative number of write delays
|
||||||
|
inWritePaused int32 // The indicator whether write operation is paused by compaction
|
||||||
aliveSnaps, aliveIters int32
|
aliveSnaps, aliveIters int32
|
||||||
|
|
||||||
// Session.
|
// Session.
|
||||||
|
|
@ -967,7 +968,8 @@ func (db *DB) GetProperty(name string) (value string, err error) {
|
||||||
float64(db.s.stor.writes())/1048576.0)
|
float64(db.s.stor.writes())/1048576.0)
|
||||||
case p == "writedelay":
|
case p == "writedelay":
|
||||||
writeDelayN, writeDelay := atomic.LoadInt32(&db.cWriteDelayN), time.Duration(atomic.LoadInt64(&db.cWriteDelay))
|
writeDelayN, writeDelay := atomic.LoadInt32(&db.cWriteDelayN), time.Duration(atomic.LoadInt64(&db.cWriteDelay))
|
||||||
value = fmt.Sprintf("DelayN:%d Delay:%s", writeDelayN, writeDelay)
|
paused := atomic.LoadInt32(&db.inWritePaused) == 1
|
||||||
|
value = fmt.Sprintf("DelayN:%d Delay:%s Paused:%t", writeDelayN, writeDelay, paused)
|
||||||
case p == "sstables":
|
case p == "sstables":
|
||||||
for level, tables := range v.levels {
|
for level, tables := range v.levels {
|
||||||
value += fmt.Sprintf("--- level %d ---\n", level)
|
value += fmt.Sprintf("--- level %d ---\n", level)
|
||||||
|
|
@ -996,6 +998,75 @@ func (db *DB) GetProperty(name string) (value string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DBStats is database statistics.
|
||||||
|
type DBStats struct {
|
||||||
|
WriteDelayCount int32
|
||||||
|
WriteDelayDuration time.Duration
|
||||||
|
WritePaused bool
|
||||||
|
|
||||||
|
AliveSnapshots int32
|
||||||
|
AliveIterators int32
|
||||||
|
|
||||||
|
IOWrite uint64
|
||||||
|
IORead uint64
|
||||||
|
|
||||||
|
BlockCacheSize int
|
||||||
|
OpenedTablesCount int
|
||||||
|
|
||||||
|
LevelSizes []int64
|
||||||
|
LevelTablesCounts []int
|
||||||
|
LevelRead []int64
|
||||||
|
LevelWrite []int64
|
||||||
|
LevelDurations []time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats populates s with database statistics.
|
||||||
|
func (db *DB) Stats(s *DBStats) error {
|
||||||
|
err := db.ok()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.IORead = db.s.stor.reads()
|
||||||
|
s.IOWrite = db.s.stor.writes()
|
||||||
|
s.WriteDelayCount = atomic.LoadInt32(&db.cWriteDelayN)
|
||||||
|
s.WriteDelayDuration = time.Duration(atomic.LoadInt64(&db.cWriteDelay))
|
||||||
|
s.WritePaused = atomic.LoadInt32(&db.inWritePaused) == 1
|
||||||
|
|
||||||
|
s.OpenedTablesCount = db.s.tops.cache.Size()
|
||||||
|
if db.s.tops.bcache != nil {
|
||||||
|
s.BlockCacheSize = db.s.tops.bcache.Size()
|
||||||
|
} else {
|
||||||
|
s.BlockCacheSize = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
s.AliveIterators = atomic.LoadInt32(&db.aliveIters)
|
||||||
|
s.AliveSnapshots = atomic.LoadInt32(&db.aliveSnaps)
|
||||||
|
|
||||||
|
s.LevelDurations = s.LevelDurations[:0]
|
||||||
|
s.LevelRead = s.LevelRead[:0]
|
||||||
|
s.LevelWrite = s.LevelWrite[:0]
|
||||||
|
s.LevelSizes = s.LevelSizes[:0]
|
||||||
|
s.LevelTablesCounts = s.LevelTablesCounts[:0]
|
||||||
|
|
||||||
|
v := db.s.version()
|
||||||
|
defer v.release()
|
||||||
|
|
||||||
|
for level, tables := range v.levels {
|
||||||
|
duration, read, write := db.compStats.getStat(level)
|
||||||
|
if len(tables) == 0 && duration == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.LevelDurations = append(s.LevelDurations, duration)
|
||||||
|
s.LevelRead = append(s.LevelRead, read)
|
||||||
|
s.LevelWrite = append(s.LevelWrite, write)
|
||||||
|
s.LevelSizes = append(s.LevelSizes, tables.size())
|
||||||
|
s.LevelTablesCounts = append(s.LevelTablesCounts, len(tables))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SizeOf calculates approximate sizes of the given key ranges.
|
// SizeOf calculates approximate sizes of the given key ranges.
|
||||||
// The length of the returned sizes are equal with the length of the given
|
// The length of the returned sizes are equal with the length of the given
|
||||||
// ranges. The returned sizes measure storage space usage, so if the user
|
// ranges. The returned sizes measure storage space usage, so if the user
|
||||||
|
|
|
||||||
4
vendor/github.com/syndtr/goleveldb/leveldb/db_write.go
generated
vendored
4
vendor/github.com/syndtr/goleveldb/leveldb/db_write.go
generated
vendored
|
|
@ -89,7 +89,11 @@ func (db *DB) flush(n int) (mdb *memDB, mdbFree int, err error) {
|
||||||
return false
|
return false
|
||||||
case tLen >= pauseTrigger:
|
case tLen >= pauseTrigger:
|
||||||
delayed = true
|
delayed = true
|
||||||
|
// Set the write paused flag explicitly.
|
||||||
|
atomic.StoreInt32(&db.inWritePaused, 1)
|
||||||
err = db.compTriggerWait(db.tcompCmdC)
|
err = db.compTriggerWait(db.tcompCmdC)
|
||||||
|
// Unset the write paused flag.
|
||||||
|
atomic.StoreInt32(&db.inWritePaused, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
|
|
@ -418,10 +418,10 @@
|
||||||
"revisionTime": "2017-07-05T02:17:15Z"
|
"revisionTime": "2017-07-05T02:17:15Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "k13cCuMJO7+KhR8ZXx5oUqDKGQA=",
|
"checksumSHA1": "TJV50D0q8E3vtc90ibC+qOYdjrw=",
|
||||||
"path": "github.com/syndtr/goleveldb/leveldb",
|
"path": "github.com/syndtr/goleveldb/leveldb",
|
||||||
"revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
|
"revision": "59047f74db0d042c8d8dd8e30bb030bc774a7d7a",
|
||||||
"revisionTime": "2018-05-02T07:23:49Z"
|
"revisionTime": "2018-05-21T04:45:49Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "EKIow7XkgNdWvR/982ffIZxKG8Y=",
|
"checksumSHA1": "EKIow7XkgNdWvR/982ffIZxKG8Y=",
|
||||||
|
|
|
||||||
|
|
@ -159,9 +159,9 @@ func (sc *Client) DeleteSymmetricKey(ctx context.Context, id string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post a message onto the network.
|
// Post a message onto the network.
|
||||||
func (sc *Client) Post(ctx context.Context, message whisper.NewMessage) error {
|
func (sc *Client) Post(ctx context.Context, message whisper.NewMessage) (string, error) {
|
||||||
var ignored bool
|
var hash string
|
||||||
return sc.c.CallContext(ctx, &ignored, "shh_post", message)
|
return hash, sc.c.CallContext(ctx, &hash, "shh_post", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribeMessages subscribes to messages that match the given criteria. This method
|
// SubscribeMessages subscribes to messages that match the given criteria. This method
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue