Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eae63c511c | ||
|
|
ca34e8230e | ||
|
|
342ec83d67 | ||
|
|
38c7eb0f26 | ||
|
|
d51faee240 | ||
|
|
426f62f1a8 | ||
|
|
7677ec1f34 | ||
|
|
d258eee211 | ||
|
|
84f8c0cc1f | ||
|
|
998f6564b2 | ||
|
|
40a2c52397 | ||
|
|
a9c6ef6905 | ||
|
|
ccc0debb63 | ||
|
|
ff9b14617e | ||
|
|
d6ed2f67a8 | ||
|
|
54294b45b1 | ||
|
|
d31802312a | ||
|
|
55b579e02c | ||
|
|
be22ee8dda | ||
|
|
56de337e57 | ||
|
|
c934c06cc1 | ||
|
|
fbf57d53e2 | ||
|
|
6ce21a4744 | ||
|
|
9af364e42b | ||
|
|
09d44247f7 | ||
|
|
0fe47e98c4 | ||
|
|
415969f534 | ||
|
|
d9cee2c172 | ||
|
|
ab6bdbd9b0 | ||
|
|
953b5ac015 | ||
|
|
f2fdb75dd9 | ||
|
|
f9c456e02d | ||
|
|
579bd0f9fb | ||
|
|
49719e21bc | ||
|
|
a2e43d28d0 | ||
|
|
6286c255f1 | ||
|
|
f6bc65fc68 | ||
|
|
ff8a033f18 | ||
|
|
247b5f0369 | ||
|
|
49ec4f0cd1 | ||
|
|
2688dab48c | ||
|
|
595b47e535 | ||
|
|
784aa83942 | ||
|
|
fcc18f4c80 | ||
|
|
53a18d2e27 | ||
|
|
7beccb29be | ||
|
|
5dbd8b42a9 | ||
|
|
4e7dc34ff1 | ||
|
|
4747aad160 | ||
|
|
4ea493e7eb | ||
|
|
c60f6f6214 | ||
|
|
ba975dc093 | ||
|
|
eab6e5a317 | ||
|
|
c4a4613d95 | ||
|
|
fedae95015 | ||
|
|
864e80a48f | ||
|
|
a42be3b78d | ||
|
|
6cf0ab38bd | ||
|
|
5463ed9996 | ||
|
|
d7be5c6619 | ||
|
|
d2fe83dc5c | ||
|
|
16f3c31773 | ||
|
|
5b3af4c3d1 | ||
|
|
60b433ab84 | ||
|
|
fd3da7c69d | ||
|
|
cd9a1d5b37 | ||
|
|
2ad511ce09 | ||
|
|
541f299fbb | ||
|
|
7c02933275 | ||
|
|
f2447bd4c3 | ||
|
|
ea1724de1a | ||
|
|
577d375a0d | ||
|
|
66432f3821 | ||
|
|
5d4d79ae26 | ||
|
|
6a01363d1d | ||
|
|
58c4e033f4 | ||
|
|
5449139ca2 | ||
|
|
579ac6287b | ||
|
|
a7720b5926 | ||
|
|
670bae4cd3 | ||
|
|
4a8d5d2b1e | ||
|
|
d76c5ca532 | ||
|
|
c1ea527573 | ||
|
|
8dfa4f46a9 | ||
|
|
0afd767537 | ||
|
|
448d17b8f7 | ||
|
|
9922943b42 | ||
|
|
a1949d0788 | ||
|
|
9f6af6f812 | ||
|
|
ea171d5bd9 | ||
|
|
1da33028ce | ||
|
|
7a7428a027 | ||
|
|
cfe8f5fd94 | ||
|
|
852aa143ac | ||
|
|
b724d1aada | ||
|
|
86be91b3e2 | ||
|
|
e7067be94f | ||
|
|
9586f2acc7 |
13
.travis.yml
13
.travis.yml
@@ -31,7 +31,6 @@ matrix:
|
||||
script:
|
||||
- unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703
|
||||
- brew update
|
||||
- brew install caskroom/cask/brew-cask
|
||||
- brew cask install osxfuse
|
||||
- go run build/ci.go install
|
||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||
@@ -127,7 +126,7 @@ matrix:
|
||||
|
||||
# This builder does the Android Maven and Azure uploads
|
||||
- os: linux
|
||||
dist: precise # Needed for the android tools
|
||||
dist: trusty
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -147,16 +146,16 @@ matrix:
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
before_install:
|
||||
- curl https://storage.googleapis.com/golang/go1.10.1.linux-amd64.tar.gz | tar -xz
|
||||
- curl https://storage.googleapis.com/golang/go1.10.2.linux-amd64.tar.gz | tar -xz
|
||||
- export PATH=`pwd`/go/bin:$PATH
|
||||
- export GOROOT=`pwd`/go
|
||||
- export GOPATH=$HOME/go
|
||||
script:
|
||||
# Build the Android archive and upload it to Maven Central and Azure
|
||||
- curl https://dl.google.com/android/repository/android-ndk-r15c-linux-x86_64.zip -o android-ndk-r15c.zip
|
||||
- unzip -q android-ndk-r15c.zip && rm android-ndk-r15c.zip
|
||||
- mv android-ndk-r15c $HOME
|
||||
- export ANDROID_NDK=$HOME/android-ndk-r15c
|
||||
- curl https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip -o android-ndk-r16b.zip
|
||||
- unzip -q android-ndk-r16b.zip && rm android-ndk-r16b.zip
|
||||
- mv android-ndk-r16b $HOME
|
||||
- export ANDROID_NDK=$HOME/android-ndk-r16b
|
||||
|
||||
- mkdir -p $GOPATH/src/github.com/ethereum
|
||||
- ln -s `pwd` $GOPATH/src/github.com/ethereum
|
||||
|
||||
@@ -111,9 +111,14 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
|
||||
if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
|
||||
return err
|
||||
}
|
||||
// If the output interface is a struct, make sure names don't collide
|
||||
|
||||
// If the interface is a struct, get of abi->struct_field mapping
|
||||
|
||||
var abi2struct map[string]string
|
||||
if kind == reflect.Struct {
|
||||
if err := requireUniqueStructFieldNames(arguments); err != nil {
|
||||
var err error
|
||||
abi2struct, err = mapAbiToStructFields(arguments, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -123,9 +128,10 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
|
||||
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
err := unpackStruct(value, reflectValue, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
if structField, ok := abi2struct[arg.Name]; ok {
|
||||
if err := set(value.FieldByName(structField), reflectValue, arg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Slice, reflect.Array:
|
||||
if value.Len() < i {
|
||||
@@ -151,17 +157,22 @@ func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interf
|
||||
if len(marshalledValues) != 1 {
|
||||
return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
|
||||
}
|
||||
|
||||
elem := reflect.ValueOf(v).Elem()
|
||||
kind := elem.Kind()
|
||||
reflectValue := reflect.ValueOf(marshalledValues[0])
|
||||
|
||||
var abi2struct map[string]string
|
||||
if kind == reflect.Struct {
|
||||
//make sure names don't collide
|
||||
if err := requireUniqueStructFieldNames(arguments); err != nil {
|
||||
var err error
|
||||
if abi2struct, err = mapAbiToStructFields(arguments, elem); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return unpackStruct(elem, reflectValue, arguments[0])
|
||||
arg := arguments.NonIndexed()[0]
|
||||
if structField, ok := abi2struct[arg.Name]; ok {
|
||||
return set(elem.FieldByName(structField), reflectValue, arg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return set(elem, reflectValue, arguments.NonIndexed()[0])
|
||||
@@ -277,18 +288,3 @@ func capitalise(input string) string {
|
||||
}
|
||||
return strings.ToUpper(input[:1]) + input[1:]
|
||||
}
|
||||
|
||||
//unpackStruct extracts each argument into its corresponding struct field
|
||||
func unpackStruct(value, reflectValue reflect.Value, arg Argument) error {
|
||||
name := capitalise(arg.Name)
|
||||
typ := value.Type()
|
||||
for j := 0; j < typ.NumField(); j++ {
|
||||
// TODO read tags: `abi:"fieldName"`
|
||||
if typ.Field(j).Name == name {
|
||||
if err := set(value.Field(j), reflectValue, arg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@@ -65,7 +66,7 @@ type SimulatedBackend struct {
|
||||
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
||||
// for testing purposes.
|
||||
func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
|
||||
database, _ := ethdb.NewMemDatabase()
|
||||
database := ethdb.NewMemDatabase()
|
||||
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, Alloc: alloc}
|
||||
genesis.MustCommit(database)
|
||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{})
|
||||
@@ -159,7 +160,7 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
|
||||
|
||||
// TransactionReceipt returns the receipt of a transaction.
|
||||
func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||
receipt, _, _, _ := core.GetReceipt(b.database, txHash)
|
||||
receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash)
|
||||
return receipt, nil
|
||||
}
|
||||
|
||||
@@ -430,11 +431,19 @@ func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumb
|
||||
}
|
||||
|
||||
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||
return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
|
||||
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
||||
if number == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return rawdb.ReadReceipts(fb.db, hash, *number), nil
|
||||
}
|
||||
|
||||
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
|
||||
receipts := core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash))
|
||||
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
||||
if number == nil {
|
||||
return nil, nil
|
||||
}
|
||||
receipts := rawdb.ReadReceipts(fb.db, hash, *number)
|
||||
if receipts == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -445,7 +454,7 @@ func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*ty
|
||||
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 {
|
||||
<-quit
|
||||
return nil
|
||||
|
||||
@@ -33,15 +33,15 @@ type Event struct {
|
||||
Inputs Arguments
|
||||
}
|
||||
|
||||
func (event Event) String() string {
|
||||
inputs := make([]string, len(event.Inputs))
|
||||
for i, input := range event.Inputs {
|
||||
func (e Event) String() string {
|
||||
inputs := make([]string, len(e.Inputs))
|
||||
for i, input := range e.Inputs {
|
||||
inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type)
|
||||
if input.Indexed {
|
||||
inputs[i] = fmt.Sprintf("%v indexed %v", input.Name, input.Type)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("event %v(%v)", event.Name, strings.Join(inputs, ", "))
|
||||
return fmt.Sprintf("e %v(%v)", e.Name, strings.Join(inputs, ", "))
|
||||
}
|
||||
|
||||
// Id returns the canonical representation of the event's signature used by the
|
||||
|
||||
@@ -58,12 +58,28 @@ var jsonEventPledge = []byte(`{
|
||||
"type": "event"
|
||||
}`)
|
||||
|
||||
var jsonEventMixedCase = []byte(`{
|
||||
"anonymous": false,
|
||||
"inputs": [{
|
||||
"indexed": false, "name": "value", "type": "uint256"
|
||||
}, {
|
||||
"indexed": false, "name": "_value", "type": "uint256"
|
||||
}, {
|
||||
"indexed": false, "name": "Value", "type": "uint256"
|
||||
}],
|
||||
"name": "MixedCase",
|
||||
"type": "event"
|
||||
}`)
|
||||
|
||||
// 1000000
|
||||
var transferData1 = "00000000000000000000000000000000000000000000000000000000000f4240"
|
||||
|
||||
// "0x00Ce0d46d924CC8437c806721496599FC3FFA268", 2218516807680, "usd"
|
||||
var pledgeData1 = "00000000000000000000000000ce0d46d924cc8437c806721496599fc3ffa2680000000000000000000000000000000000000000000000000000020489e800007573640000000000000000000000000000000000000000000000000000000000"
|
||||
|
||||
// 1000000,2218516807680,1000001
|
||||
var mixedCaseData1 = "00000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000020489e8000000000000000000000000000000000000000000000000000000000000000f4241"
|
||||
|
||||
func TestEventId(t *testing.T) {
|
||||
var table = []struct {
|
||||
definition string
|
||||
@@ -121,6 +137,27 @@ func TestEventTupleUnpack(t *testing.T) {
|
||||
Value *big.Int
|
||||
}
|
||||
|
||||
type EventTransferWithTag struct {
|
||||
// this is valid because `value` is not exportable,
|
||||
// so value is only unmarshalled into `Value1`.
|
||||
value *big.Int
|
||||
Value1 *big.Int `abi:"value"`
|
||||
}
|
||||
|
||||
type BadEventTransferWithSameFieldAndTag struct {
|
||||
Value *big.Int
|
||||
Value1 *big.Int `abi:"value"`
|
||||
}
|
||||
|
||||
type BadEventTransferWithDuplicatedTag struct {
|
||||
Value1 *big.Int `abi:"value"`
|
||||
Value2 *big.Int `abi:"value"`
|
||||
}
|
||||
|
||||
type BadEventTransferWithEmptyTag struct {
|
||||
Value *big.Int `abi:""`
|
||||
}
|
||||
|
||||
type EventPledge struct {
|
||||
Who common.Address
|
||||
Wad *big.Int
|
||||
@@ -133,9 +170,16 @@ func TestEventTupleUnpack(t *testing.T) {
|
||||
Currency [3]byte
|
||||
}
|
||||
|
||||
type EventMixedCase struct {
|
||||
Value1 *big.Int `abi:"value"`
|
||||
Value2 *big.Int `abi:"_value"`
|
||||
Value3 *big.Int `abi:"Value"`
|
||||
}
|
||||
|
||||
bigint := new(big.Int)
|
||||
bigintExpected := big.NewInt(1000000)
|
||||
bigintExpected2 := big.NewInt(2218516807680)
|
||||
bigintExpected3 := big.NewInt(1000001)
|
||||
addr := common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268")
|
||||
var testCases = []struct {
|
||||
data string
|
||||
@@ -158,6 +202,34 @@ func TestEventTupleUnpack(t *testing.T) {
|
||||
jsonEventTransfer,
|
||||
"",
|
||||
"Can unpack ERC20 Transfer event into slice",
|
||||
}, {
|
||||
transferData1,
|
||||
&EventTransferWithTag{},
|
||||
&EventTransferWithTag{Value1: bigintExpected},
|
||||
jsonEventTransfer,
|
||||
"",
|
||||
"Can unpack ERC20 Transfer event into structure with abi: tag",
|
||||
}, {
|
||||
transferData1,
|
||||
&BadEventTransferWithDuplicatedTag{},
|
||||
&BadEventTransferWithDuplicatedTag{},
|
||||
jsonEventTransfer,
|
||||
"struct: abi tag in 'Value2' already mapped",
|
||||
"Can not unpack ERC20 Transfer event with duplicated abi tag",
|
||||
}, {
|
||||
transferData1,
|
||||
&BadEventTransferWithSameFieldAndTag{},
|
||||
&BadEventTransferWithSameFieldAndTag{},
|
||||
jsonEventTransfer,
|
||||
"abi: multiple variables maps to the same abi field 'value'",
|
||||
"Can not unpack ERC20 Transfer event with a field and a tag mapping to the same abi variable",
|
||||
}, {
|
||||
transferData1,
|
||||
&BadEventTransferWithEmptyTag{},
|
||||
&BadEventTransferWithEmptyTag{},
|
||||
jsonEventTransfer,
|
||||
"struct: abi tag in 'Value' is empty",
|
||||
"Can not unpack ERC20 Transfer event with an empty tag",
|
||||
}, {
|
||||
pledgeData1,
|
||||
&EventPledge{},
|
||||
@@ -216,6 +288,13 @@ func TestEventTupleUnpack(t *testing.T) {
|
||||
jsonEventPledge,
|
||||
"abi: cannot unmarshal tuple into map[string]interface {}",
|
||||
"Can not unpack Pledge event into map",
|
||||
}, {
|
||||
mixedCaseData1,
|
||||
&EventMixedCase{},
|
||||
&EventMixedCase{Value1: bigintExpected, Value2: bigintExpected2, Value3: bigintExpected3},
|
||||
jsonEventMixedCase,
|
||||
"",
|
||||
"Can unpack abi variables with mixed case",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@@ -227,7 +306,7 @@ func TestEventTupleUnpack(t *testing.T) {
|
||||
assert.Nil(err, "Should be able to unpack event data.")
|
||||
assert.Equal(tc.expected, tc.dest, tc.name)
|
||||
} else {
|
||||
assert.EqualError(err, tc.error)
|
||||
assert.EqualError(err, tc.error, tc.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package abi
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// indirect recursively dereferences the value until it either gets the value
|
||||
@@ -111,18 +112,101 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
|
||||
return nil
|
||||
}
|
||||
|
||||
// requireUniqueStructFieldNames makes sure field names don't collide
|
||||
func requireUniqueStructFieldNames(args Arguments) error {
|
||||
exists := make(map[string]bool)
|
||||
for _, arg := range args {
|
||||
field := capitalise(arg.Name)
|
||||
if field == "" {
|
||||
return fmt.Errorf("abi: purely underscored output cannot unpack to struct")
|
||||
// mapAbiToStringField maps abi to struct fields.
|
||||
// first round: for each Exportable field that contains a `abi:""` tag
|
||||
// and this field name exists in the arguments, pair them together.
|
||||
// second round: for each argument field that has not been already linked,
|
||||
// find what variable is expected to be mapped into, if it exists and has not been
|
||||
// used, pair them.
|
||||
func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]string, error) {
|
||||
|
||||
typ := value.Type()
|
||||
|
||||
abi2struct := make(map[string]string)
|
||||
struct2abi := make(map[string]string)
|
||||
|
||||
// first round ~~~
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
structFieldName := typ.Field(i).Name
|
||||
|
||||
// skip private struct fields.
|
||||
if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) {
|
||||
continue
|
||||
}
|
||||
if exists[field] {
|
||||
return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field)
|
||||
|
||||
// skip fields that have no abi:"" tag.
|
||||
var ok bool
|
||||
var tagName string
|
||||
if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
|
||||
continue
|
||||
}
|
||||
exists[field] = true
|
||||
|
||||
// check if tag is empty.
|
||||
if tagName == "" {
|
||||
return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName)
|
||||
}
|
||||
|
||||
// check which argument field matches with the abi tag.
|
||||
found := false
|
||||
for _, abiField := range args.NonIndexed() {
|
||||
if abiField.Name == tagName {
|
||||
if abi2struct[abiField.Name] != "" {
|
||||
return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName)
|
||||
}
|
||||
// pair them
|
||||
abi2struct[abiField.Name] = structFieldName
|
||||
struct2abi[structFieldName] = abiField.Name
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
// check if this tag has been mapped.
|
||||
if !found {
|
||||
return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
|
||||
// second round ~~~
|
||||
for _, arg := range args {
|
||||
|
||||
abiFieldName := arg.Name
|
||||
structFieldName := capitalise(abiFieldName)
|
||||
|
||||
if structFieldName == "" {
|
||||
return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
|
||||
}
|
||||
|
||||
// this abi has already been paired, skip it... unless there exists another, yet unassigned
|
||||
// struct field with the same field name. If so, raise an error:
|
||||
// abi: [ { "name": "value" } ]
|
||||
// struct { Value *big.Int , Value1 *big.Int `abi:"value"`}
|
||||
if abi2struct[abiFieldName] != "" {
|
||||
if abi2struct[abiFieldName] != structFieldName &&
|
||||
struct2abi[structFieldName] == "" &&
|
||||
value.FieldByName(structFieldName).IsValid() {
|
||||
return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", abiFieldName)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// return an error if this struct field has already been paired.
|
||||
if struct2abi[structFieldName] != "" {
|
||||
return nil, fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", structFieldName)
|
||||
}
|
||||
|
||||
if value.FieldByName(structFieldName).IsValid() {
|
||||
// pair them
|
||||
abi2struct[abiFieldName] = structFieldName
|
||||
struct2abi[structFieldName] = abiFieldName
|
||||
} else {
|
||||
// not paired, but annotate as used, to detect cases like
|
||||
// abi : [ { "name": "value" }, { "name": "_value" } ]
|
||||
// struct { Value *big.Int }
|
||||
struct2abi[structFieldName] = abiFieldName
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return abi2struct, nil
|
||||
}
|
||||
|
||||
@@ -108,9 +108,8 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
|
||||
func (ks keyStorePassphrase) JoinPath(filename string) string {
|
||||
if filepath.IsAbs(filename) {
|
||||
return filename
|
||||
} else {
|
||||
return filepath.Join(ks.keysDirPath, filename)
|
||||
}
|
||||
return filepath.Join(ks.keysDirPath, filename)
|
||||
}
|
||||
|
||||
// EncryptKey encrypts a key using the specified scrypt parameters into a json
|
||||
|
||||
@@ -56,7 +56,6 @@ func (ks keyStorePlain) StoreKey(filename string, key *Key, auth string) error {
|
||||
func (ks keyStorePlain) JoinPath(filename string) string {
|
||||
if filepath.IsAbs(filename) {
|
||||
return filename
|
||||
} else {
|
||||
return filepath.Join(ks.keysDirPath, filename)
|
||||
}
|
||||
return filepath.Join(ks.keysDirPath, filename)
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ environment:
|
||||
install:
|
||||
- git submodule update --init
|
||||
- rmdir C:\go /s /q
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.1.windows-%GETH_ARCH%.zip
|
||||
- 7z x go1.10.1.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.2.windows-%GETH_ARCH%.zip
|
||||
- 7z x go1.10.2.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||
- go version
|
||||
- gcc --version
|
||||
|
||||
|
||||
178
bmt/bmt.go
178
bmt/bmt.go
@@ -150,29 +150,29 @@ func NewTreePool(hasher BaseHasher, segmentCount, capacity int) *TreePool {
|
||||
}
|
||||
|
||||
// Drain drains the pool until it has no more than n resources
|
||||
func (self *TreePool) Drain(n int) {
|
||||
self.lock.Lock()
|
||||
defer self.lock.Unlock()
|
||||
for len(self.c) > n {
|
||||
<-self.c
|
||||
self.count--
|
||||
func (p *TreePool) Drain(n int) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
for len(p.c) > n {
|
||||
<-p.c
|
||||
p.count--
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve is blocking until it returns an available Tree
|
||||
// it reuses free Trees or creates a new one if size is not reached
|
||||
func (self *TreePool) Reserve() *Tree {
|
||||
self.lock.Lock()
|
||||
defer self.lock.Unlock()
|
||||
func (p *TreePool) Reserve() *Tree {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
var t *Tree
|
||||
if self.count == self.Capacity {
|
||||
return <-self.c
|
||||
if p.count == p.Capacity {
|
||||
return <-p.c
|
||||
}
|
||||
select {
|
||||
case t = <-self.c:
|
||||
case t = <-p.c:
|
||||
default:
|
||||
t = NewTree(self.hasher, self.SegmentSize, self.SegmentCount)
|
||||
self.count++
|
||||
t = NewTree(p.hasher, p.SegmentSize, p.SegmentCount)
|
||||
p.count++
|
||||
}
|
||||
return t
|
||||
}
|
||||
@@ -180,8 +180,8 @@ func (self *TreePool) Reserve() *Tree {
|
||||
// Release gives back a Tree to the pool.
|
||||
// This Tree is guaranteed to be in reusable state
|
||||
// does not need locking
|
||||
func (self *TreePool) Release(t *Tree) {
|
||||
self.c <- t // can never fail but...
|
||||
func (p *TreePool) Release(t *Tree) {
|
||||
p.c <- t // can never fail but...
|
||||
}
|
||||
|
||||
// Tree is a reusable control structure representing a BMT
|
||||
@@ -193,17 +193,17 @@ type Tree struct {
|
||||
}
|
||||
|
||||
// Draw draws the BMT (badly)
|
||||
func (self *Tree) Draw(hash []byte, d int) string {
|
||||
func (t *Tree) Draw(hash []byte, d int) string {
|
||||
var left, right []string
|
||||
var anc []*Node
|
||||
for i, n := range self.leaves {
|
||||
for i, n := range t.leaves {
|
||||
left = append(left, fmt.Sprintf("%v", hashstr(n.left)))
|
||||
if i%2 == 0 {
|
||||
anc = append(anc, n.parent)
|
||||
}
|
||||
right = append(right, fmt.Sprintf("%v", hashstr(n.right)))
|
||||
}
|
||||
anc = self.leaves
|
||||
anc = t.leaves
|
||||
var hashes [][]string
|
||||
for l := 0; len(anc) > 0; l++ {
|
||||
var nodes []*Node
|
||||
@@ -277,42 +277,42 @@ func NewTree(hasher BaseHasher, segmentSize, segmentCount int) *Tree {
|
||||
// methods needed by hash.Hash
|
||||
|
||||
// Size returns the size
|
||||
func (self *Hasher) Size() int {
|
||||
return self.size
|
||||
func (h *Hasher) Size() int {
|
||||
return h.size
|
||||
}
|
||||
|
||||
// BlockSize returns the block size
|
||||
func (self *Hasher) BlockSize() int {
|
||||
return self.blocksize
|
||||
func (h *Hasher) BlockSize() int {
|
||||
return h.blocksize
|
||||
}
|
||||
|
||||
// Sum returns the hash of the buffer
|
||||
// hash.Hash interface Sum method appends the byte slice to the underlying
|
||||
// data before it calculates and returns the hash of the chunk
|
||||
func (self *Hasher) Sum(b []byte) (r []byte) {
|
||||
t := self.bmt
|
||||
i := self.cur
|
||||
func (h *Hasher) Sum(b []byte) (r []byte) {
|
||||
t := h.bmt
|
||||
i := h.cur
|
||||
n := t.leaves[i]
|
||||
j := i
|
||||
// must run strictly before all nodes calculate
|
||||
// datanodes are guaranteed to have a parent
|
||||
if len(self.segment) > self.size && i > 0 && n.parent != nil {
|
||||
if len(h.segment) > h.size && i > 0 && n.parent != nil {
|
||||
n = n.parent
|
||||
} else {
|
||||
i *= 2
|
||||
}
|
||||
d := self.finalise(n, i)
|
||||
self.writeSegment(j, self.segment, d)
|
||||
c := <-self.result
|
||||
self.releaseTree()
|
||||
d := h.finalise(n, i)
|
||||
h.writeSegment(j, h.segment, d)
|
||||
c := <-h.result
|
||||
h.releaseTree()
|
||||
|
||||
// sha3(length + BMT(pure_chunk))
|
||||
if self.blockLength == nil {
|
||||
if h.blockLength == nil {
|
||||
return c
|
||||
}
|
||||
res := self.pool.hasher()
|
||||
res := h.pool.hasher()
|
||||
res.Reset()
|
||||
res.Write(self.blockLength)
|
||||
res.Write(h.blockLength)
|
||||
res.Write(c)
|
||||
return res.Sum(nil)
|
||||
}
|
||||
@@ -321,8 +321,8 @@ func (self *Hasher) Sum(b []byte) (r []byte) {
|
||||
|
||||
// Hash waits for the hasher result and returns it
|
||||
// caller must call this on a BMT Hasher being written to
|
||||
func (self *Hasher) Hash() []byte {
|
||||
return <-self.result
|
||||
func (h *Hasher) Hash() []byte {
|
||||
return <-h.result
|
||||
}
|
||||
|
||||
// Hasher implements the io.Writer interface
|
||||
@@ -330,16 +330,16 @@ func (self *Hasher) Hash() []byte {
|
||||
// Write fills the buffer to hash
|
||||
// with every full segment complete launches a hasher go routine
|
||||
// that shoots up the BMT
|
||||
func (self *Hasher) Write(b []byte) (int, error) {
|
||||
func (h *Hasher) Write(b []byte) (int, error) {
|
||||
l := len(b)
|
||||
if l <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
s := self.segment
|
||||
i := self.cur
|
||||
count := (self.count + 1) / 2
|
||||
need := self.count*self.size - self.cur*2*self.size
|
||||
size := self.size
|
||||
s := h.segment
|
||||
i := h.cur
|
||||
count := (h.count + 1) / 2
|
||||
need := h.count*h.size - h.cur*2*h.size
|
||||
size := h.size
|
||||
if need > size {
|
||||
size *= 2
|
||||
}
|
||||
@@ -356,7 +356,7 @@ func (self *Hasher) Write(b []byte) (int, error) {
|
||||
// read full segments and the last possibly partial segment
|
||||
for need > 0 && i < count-1 {
|
||||
// push all finished chunks we read
|
||||
self.writeSegment(i, s, self.depth)
|
||||
h.writeSegment(i, s, h.depth)
|
||||
need -= size
|
||||
if need < 0 {
|
||||
size += need
|
||||
@@ -365,8 +365,8 @@ func (self *Hasher) Write(b []byte) (int, error) {
|
||||
rest += size
|
||||
i++
|
||||
}
|
||||
self.segment = s
|
||||
self.cur = i
|
||||
h.segment = s
|
||||
h.cur = i
|
||||
// otherwise, we can assume len(s) == 0, so all buffer is read and chunk is not yet full
|
||||
return l, nil
|
||||
}
|
||||
@@ -376,8 +376,8 @@ func (self *Hasher) Write(b []byte) (int, error) {
|
||||
// ReadFrom reads from io.Reader and appends to the data to hash using Write
|
||||
// it reads so that chunk to hash is maximum length or reader reaches EOF
|
||||
// caller must Reset the hasher prior to call
|
||||
func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) {
|
||||
bufsize := self.size*self.count - self.size*self.cur - len(self.segment)
|
||||
func (h *Hasher) ReadFrom(r io.Reader) (m int64, err error) {
|
||||
bufsize := h.size*h.count - h.size*h.cur - len(h.segment)
|
||||
buf := make([]byte, bufsize)
|
||||
var read int
|
||||
for {
|
||||
@@ -385,7 +385,7 @@ func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) {
|
||||
n, err = r.Read(buf)
|
||||
read += n
|
||||
if err == io.EOF || read == len(buf) {
|
||||
hash := self.Sum(buf[:n])
|
||||
hash := h.Sum(buf[:n])
|
||||
if read == len(buf) {
|
||||
err = NewEOC(hash)
|
||||
}
|
||||
@@ -394,7 +394,7 @@ func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) {
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
n, err = self.Write(buf[:n])
|
||||
n, err = h.Write(buf[:n])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
@@ -403,9 +403,9 @@ func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) {
|
||||
}
|
||||
|
||||
// Reset needs to be called before writing to the hasher
|
||||
func (self *Hasher) Reset() {
|
||||
self.getTree()
|
||||
self.blockLength = nil
|
||||
func (h *Hasher) Reset() {
|
||||
h.getTree()
|
||||
h.blockLength = nil
|
||||
}
|
||||
|
||||
// Hasher implements the SwarmHash interface
|
||||
@@ -413,52 +413,52 @@ func (self *Hasher) Reset() {
|
||||
// ResetWithLength needs to be called before writing to the hasher
|
||||
// the argument is supposed to be the byte slice binary representation of
|
||||
// the length of the data subsumed under the hash
|
||||
func (self *Hasher) ResetWithLength(l []byte) {
|
||||
self.Reset()
|
||||
self.blockLength = l
|
||||
func (h *Hasher) ResetWithLength(l []byte) {
|
||||
h.Reset()
|
||||
h.blockLength = l
|
||||
}
|
||||
|
||||
// Release gives back the Tree to the pool whereby it unlocks
|
||||
// it resets tree, segment and index
|
||||
func (self *Hasher) releaseTree() {
|
||||
if self.bmt != nil {
|
||||
n := self.bmt.leaves[self.cur]
|
||||
func (h *Hasher) releaseTree() {
|
||||
if h.bmt != nil {
|
||||
n := h.bmt.leaves[h.cur]
|
||||
for ; n != nil; n = n.parent {
|
||||
n.unbalanced = false
|
||||
if n.parent != nil {
|
||||
n.root = false
|
||||
}
|
||||
}
|
||||
self.pool.Release(self.bmt)
|
||||
self.bmt = nil
|
||||
h.pool.Release(h.bmt)
|
||||
h.bmt = nil
|
||||
|
||||
}
|
||||
self.cur = 0
|
||||
self.segment = nil
|
||||
h.cur = 0
|
||||
h.segment = nil
|
||||
}
|
||||
|
||||
func (self *Hasher) writeSegment(i int, s []byte, d int) {
|
||||
h := self.pool.hasher()
|
||||
n := self.bmt.leaves[i]
|
||||
func (h *Hasher) writeSegment(i int, s []byte, d int) {
|
||||
hash := h.pool.hasher()
|
||||
n := h.bmt.leaves[i]
|
||||
|
||||
if len(s) > self.size && n.parent != nil {
|
||||
if len(s) > h.size && n.parent != nil {
|
||||
go func() {
|
||||
h.Reset()
|
||||
h.Write(s)
|
||||
s = h.Sum(nil)
|
||||
hash.Reset()
|
||||
hash.Write(s)
|
||||
s = hash.Sum(nil)
|
||||
|
||||
if n.root {
|
||||
self.result <- s
|
||||
h.result <- s
|
||||
return
|
||||
}
|
||||
self.run(n.parent, h, d, n.index, s)
|
||||
h.run(n.parent, hash, d, n.index, s)
|
||||
}()
|
||||
return
|
||||
}
|
||||
go self.run(n, h, d, i*2, s)
|
||||
go h.run(n, hash, d, i*2, s)
|
||||
}
|
||||
|
||||
func (self *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) {
|
||||
func (h *Hasher) run(n *Node, hash hash.Hash, d int, i int, s []byte) {
|
||||
isLeft := i%2 == 0
|
||||
for {
|
||||
if isLeft {
|
||||
@@ -470,18 +470,18 @@ func (self *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) {
|
||||
return
|
||||
}
|
||||
if !n.unbalanced || !isLeft || i == 0 && d == 0 {
|
||||
h.Reset()
|
||||
h.Write(n.left)
|
||||
h.Write(n.right)
|
||||
s = h.Sum(nil)
|
||||
hash.Reset()
|
||||
hash.Write(n.left)
|
||||
hash.Write(n.right)
|
||||
s = hash.Sum(nil)
|
||||
|
||||
} else {
|
||||
s = append(n.left, n.right...)
|
||||
}
|
||||
|
||||
self.hash = s
|
||||
h.hash = s
|
||||
if n.root {
|
||||
self.result <- s
|
||||
h.result <- s
|
||||
return
|
||||
}
|
||||
|
||||
@@ -492,20 +492,20 @@ func (self *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) {
|
||||
}
|
||||
|
||||
// getTree obtains a BMT resource by reserving one from the pool
|
||||
func (self *Hasher) getTree() *Tree {
|
||||
if self.bmt != nil {
|
||||
return self.bmt
|
||||
func (h *Hasher) getTree() *Tree {
|
||||
if h.bmt != nil {
|
||||
return h.bmt
|
||||
}
|
||||
t := self.pool.Reserve()
|
||||
self.bmt = t
|
||||
t := h.pool.Reserve()
|
||||
h.bmt = t
|
||||
return t
|
||||
}
|
||||
|
||||
// atomic bool toggle implementing a concurrent reusable 2-state object
|
||||
// atomic addint with %2 implements atomic bool toggle
|
||||
// it returns true if the toggler just put it in the active/waiting state
|
||||
func (self *Node) toggle() bool {
|
||||
return atomic.AddInt32(&self.state, 1)%2 == 1
|
||||
func (n *Node) toggle() bool {
|
||||
return atomic.AddInt32(&n.state, 1)%2 == 1
|
||||
}
|
||||
|
||||
func hashstr(b []byte) string {
|
||||
@@ -525,7 +525,7 @@ func depth(n int) (d int) {
|
||||
|
||||
// finalise is following the zigzags on the tree belonging
|
||||
// to the final datasegment
|
||||
func (self *Hasher) finalise(n *Node, i int) (d int) {
|
||||
func (h *Hasher) finalise(n *Node, i int) (d int) {
|
||||
isLeft := i%2 == 0
|
||||
for {
|
||||
// when the final segment's path is going via left segments
|
||||
@@ -550,8 +550,8 @@ type EOC struct {
|
||||
}
|
||||
|
||||
// Error returns the error string
|
||||
func (self *EOC) Error() string {
|
||||
return fmt.Sprintf("hasher limit reached, chunk hash: %x", self.Hash)
|
||||
func (e *EOC) Error() string {
|
||||
return fmt.Sprintf("hasher limit reached, chunk hash: %x", e.Hash)
|
||||
}
|
||||
|
||||
// NewEOC creates new end of chunk error with the hash
|
||||
|
||||
25
build/ci.go
25
build/ci.go
@@ -731,7 +731,7 @@ func doAndroidArchive(cmdline []string) {
|
||||
// Build the Android archive and Maven resources
|
||||
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind"))
|
||||
build.MustRun(gomobileTool("init", "--ndk", os.Getenv("ANDROID_NDK")))
|
||||
build.MustRun(gomobileTool("bind", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
|
||||
build.MustRun(gomobileTool("bind", "-ldflags", "-s -w", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
|
||||
|
||||
if *local {
|
||||
// If we're building locally, copy bundle to build dir and skip Maven
|
||||
@@ -755,14 +755,18 @@ func doAndroidArchive(cmdline []string) {
|
||||
os.Rename(archive, meta.Package+".aar")
|
||||
if *signer != "" && *deploy != "" {
|
||||
// Import the signing key into the local GPG instance
|
||||
if b64key := os.Getenv(*signer); b64key != "" {
|
||||
key, err := base64.StdEncoding.DecodeString(b64key)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid base64 %s", *signer)
|
||||
}
|
||||
gpg := exec.Command("gpg", "--import")
|
||||
gpg.Stdin = bytes.NewReader(key)
|
||||
build.MustRun(gpg)
|
||||
b64key := os.Getenv(*signer)
|
||||
key, err := base64.StdEncoding.DecodeString(b64key)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid base64 %s", *signer)
|
||||
}
|
||||
gpg := exec.Command("gpg", "--import")
|
||||
gpg.Stdin = bytes.NewReader(key)
|
||||
build.MustRun(gpg)
|
||||
|
||||
keyID, err := build.PGPKeyID(string(key))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Upload the artifacts to Sonatype and/or Maven Central
|
||||
repo := *deploy + "/service/local/staging/deploy/maven2"
|
||||
@@ -771,6 +775,7 @@ func doAndroidArchive(cmdline []string) {
|
||||
}
|
||||
build.MustRunCommand("mvn", "gpg:sign-and-deploy-file", "-e", "-X",
|
||||
"-settings=build/mvn.settings", "-Durl="+repo, "-DrepositoryId=ossrh",
|
||||
"-Dgpg.keyname="+keyID,
|
||||
"-DpomFile="+meta.Package+".pom", "-Dfile="+meta.Package+".aar")
|
||||
}
|
||||
}
|
||||
@@ -852,7 +857,7 @@ func doXCodeFramework(cmdline []string) {
|
||||
// Build the iOS XCode framework
|
||||
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind"))
|
||||
build.MustRun(gomobileTool("init"))
|
||||
bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "-v", "github.com/ethereum/go-ethereum/mobile")
|
||||
bind := gomobileTool("bind", "-ldflags", "-s -w", "--target", "ios", "--tags", "ios", "-v", "github.com/ethereum/go-ethereum/mobile")
|
||||
|
||||
if *local {
|
||||
// If we're building locally, use the build folder and stop afterwards
|
||||
|
||||
@@ -12,6 +12,11 @@ synchronised with the chain or a particular Ethereum node that has no built-in (
|
||||
Clef can run as a daemon on the same machine, or off a usb-stick like [usb armory](https://inversepath.com/usbarmory),
|
||||
or a separate VM in a [QubesOS](https://www.qubes-os.org/) type os setup.
|
||||
|
||||
Check out
|
||||
|
||||
* the [tutorial](tutorial.md) for some concrete examples on how the signer works.
|
||||
* the [setup docs](docs/setup.md) for some information on how to configure it to work on QubesOS or USBArmory.
|
||||
|
||||
|
||||
## Command line flags
|
||||
Clef accepts the following command line options:
|
||||
@@ -49,7 +54,6 @@ Example:
|
||||
signer -keystore /my/keystore -chainid 4
|
||||
```
|
||||
|
||||
Check out the [tutorial](tutorial.md) for some concrete examples on how the signer works.
|
||||
|
||||
## Security model
|
||||
|
||||
@@ -862,3 +866,12 @@ A UI should conform to the following rules.
|
||||
along with the UI.
|
||||
|
||||
|
||||
### UI Implementations
|
||||
|
||||
There are a couple of implementation for a UI. We'll try to keep this list up to date.
|
||||
|
||||
| Name | Repo | UI type| No external resources| Blocky support| Verifies permissions | Hash information | No secondary storage | Statically linked| Can modify parameters|
|
||||
| ---- | ---- | -------| ---- | ---- | ---- |---- | ---- | ---- | ---- |
|
||||
| QtSigner| https://github.com/holiman/qtsigner/| Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: | :+1: (partially)|
|
||||
| GtkSigner| https://github.com/holiman/gtksigner| Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: | :x: |
|
||||
| Frame | https://github.com/floating/frame/commits/go-signer| Electron-based| :x:| :x:| :x:| :x:| ?| :x: | :x: |
|
||||
|
||||
BIN
cmd/clef/docs/qubes/clef_qubes_http.png
Normal file
BIN
cmd/clef/docs/qubes/clef_qubes_http.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
cmd/clef/docs/qubes/clef_qubes_qrexec.png
Normal file
BIN
cmd/clef/docs/qubes/clef_qubes_qrexec.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
cmd/clef/docs/qubes/qrexec-example.png
Normal file
BIN
cmd/clef/docs/qubes/qrexec-example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
23
cmd/clef/docs/qubes/qubes-client.py
Normal file
23
cmd/clef/docs/qubes/qubes-client.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
This implements a dispatcher which listens to localhost:8550, and proxies
|
||||
requests via qrexec to the service qubes.EthSign on a target domain
|
||||
"""
|
||||
|
||||
import http.server
|
||||
import socketserver,subprocess
|
||||
|
||||
PORT=8550
|
||||
TARGET_DOMAIN= 'debian-work'
|
||||
|
||||
class Dispatcher(http.server.BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
post_data = self.rfile.read(int(self.headers['Content-Length']))
|
||||
p = subprocess.Popen(['/usr/bin/qrexec-client-vm',TARGET_DOMAIN,'qubes.Clefsign'],stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
output = p.communicate(post_data)[0]
|
||||
self.wfile.write(output)
|
||||
|
||||
|
||||
with socketserver.TCPServer(("",PORT), Dispatcher) as httpd:
|
||||
print("Serving at port", PORT)
|
||||
httpd.serve_forever()
|
||||
|
||||
16
cmd/clef/docs/qubes/qubes.Clefsign
Normal file
16
cmd/clef/docs/qubes/qubes.Clefsign
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
SIGNER_BIN="/home/user/tools/clef/clef"
|
||||
SIGNER_CMD="/home/user/tools/gtksigner/gtkui.py -s $SIGNER_BIN"
|
||||
|
||||
# Start clef if not already started
|
||||
if [ ! -S /home/user/.clef/clef.ipc ]; then
|
||||
$SIGNER_CMD &
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# Should be started by now
|
||||
if [ -S /home/user/.clef/clef.ipc ]; then
|
||||
# Post incoming request to HTTP channel
|
||||
curl -H "Content-Type: application/json" -X POST -d @- http://localhost:8550 2>/dev/null
|
||||
fi
|
||||
BIN
cmd/clef/docs/qubes/qubes_newaccount-1.png
Normal file
BIN
cmd/clef/docs/qubes/qubes_newaccount-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
cmd/clef/docs/qubes/qubes_newaccount-2.png
Normal file
BIN
cmd/clef/docs/qubes/qubes_newaccount-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
198
cmd/clef/docs/setup.md
Normal file
198
cmd/clef/docs/setup.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# Setting up Clef
|
||||
|
||||
This document describes how Clef can be used in a more secure manner than executing it from your everyday laptop,
|
||||
in order to ensure that the keys remain safe in the event that your computer should get compromised.
|
||||
|
||||
## Qubes OS
|
||||
|
||||
|
||||
### Background
|
||||
|
||||
The Qubes operating system is based around virtual machines (qubes), where a set of virtual machines are configured, typically for
|
||||
different purposes such as:
|
||||
|
||||
- personal
|
||||
- Your personal email, browsing etc
|
||||
- work
|
||||
- Work email etc
|
||||
- vault
|
||||
- a VM without network access, where gpg-keys and/or keepass credentials are stored.
|
||||
|
||||
A couple of dedicated virtual machines handle externalities:
|
||||
|
||||
- sys-net provides networking to all other (network-enabled) machines
|
||||
- sys-firewall handles firewall rules
|
||||
- sys-usb handles USB devices, and can map usb-devices to certain qubes.
|
||||
|
||||
The goal of this document is to describe how we can set up clef to provide secure transaction
|
||||
signing from a `vault` vm, to another networked qube which runs Dapps.
|
||||
|
||||
### Setup
|
||||
|
||||
There are two ways that this can be achieved: integrated via Qubes or integrated via networking.
|
||||
|
||||
|
||||
#### 1. Qubes Integrated
|
||||
|
||||
Qubes provdes a facility for inter-qubes communication via `qrexec`. A qube can request to make a cross-qube RPC request
|
||||
to another qube. The OS then asks the user if the call is permitted.
|
||||
|
||||

|
||||
|
||||
A policy-file can be created to allow such interaction. On the `target` domain, a service is invoked which can read the
|
||||
`stdin` from the `client` qube.
|
||||
|
||||
This is how [Split GPG](https://www.qubes-os.org/doc/split-gpg/) is implemented. We can set up Clef the same way:
|
||||
|
||||
##### Server
|
||||
|
||||

|
||||
|
||||
On the `target` qubes, we need to define the rpc service.
|
||||
|
||||
[qubes.Clefsign](qubes/qubes.Clefsign):
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
SIGNER_BIN="/home/user/tools/clef/clef"
|
||||
SIGNER_CMD="/home/user/tools/gtksigner/gtkui.py -s $SIGNER_BIN"
|
||||
|
||||
# Start clef if not already started
|
||||
if [ ! -S /home/user/.clef/clef.ipc ]; then
|
||||
$SIGNER_CMD &
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# Should be started by now
|
||||
if [ -S /home/user/.clef/clef.ipc ]; then
|
||||
# Post incoming request to HTTP channel
|
||||
curl -H "Content-Type: application/json" -X POST -d @- http://localhost:8550 2>/dev/null
|
||||
fi
|
||||
|
||||
```
|
||||
This RPC service is not complete (see notes about HTTP headers below), but works as a proof-of-concept.
|
||||
It will forward the data received on `stdin` (forwarded by the OS) to Clef's HTTP channel.
|
||||
|
||||
It would have been possible to send data directly to the `/home/user/.clef/.clef.ipc`
|
||||
socket via e.g `nc -U /home/user/.clef/clef.ipc`, but the reason for sending the request
|
||||
data over `HTTP` instead of `IPC` is that we want the ability to forward `HTTP` headers.
|
||||
|
||||
To enable the service:
|
||||
|
||||
``` bash
|
||||
sudo cp qubes.Clefsign /etc/qubes-rpc/
|
||||
sudo chmod +x /etc/qubes-rpc/ qubes.Clefsign
|
||||
```
|
||||
|
||||
This setup uses [gtksigner](https://github.com/holiman/gtksigner), which is a very minimal GTK-based UI that works well
|
||||
with minimal requirements.
|
||||
|
||||
##### Client
|
||||
|
||||
|
||||
On the `client` qube, we need to create a listener which will receive the request from the Dapp, and proxy it.
|
||||
|
||||
|
||||
[qubes-client.py](qubes/client/qubes-client.py):
|
||||
|
||||
```python
|
||||
|
||||
"""
|
||||
This implements a dispatcher which listens to localhost:8550, and proxies
|
||||
requests via qrexec to the service qubes.EthSign on a target domain
|
||||
"""
|
||||
|
||||
import http.server
|
||||
import socketserver,subprocess
|
||||
|
||||
PORT=8550
|
||||
TARGET_DOMAIN= 'debian-work'
|
||||
|
||||
class Dispatcher(http.server.BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
post_data = self.rfile.read(int(self.headers['Content-Length']))
|
||||
p = subprocess.Popen(['/usr/bin/qrexec-client-vm',TARGET_DOMAIN,'qubes.Clefsign'],stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
output = p.communicate(post_data)[0]
|
||||
self.wfile.write(output)
|
||||
|
||||
|
||||
with socketserver.TCPServer(("",PORT), Dispatcher) as httpd:
|
||||
print("Serving at port", PORT)
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
```
|
||||
|
||||
#### Testing
|
||||
|
||||
To test the flow, if we have set up `debian-work` as the `target`, we can do
|
||||
|
||||
```bash
|
||||
$ cat newaccnt.json
|
||||
{ "id": 0, "jsonrpc": "2.0","method": "account_new","params": []}
|
||||
|
||||
$ cat newaccnt.json| qrexec-client-vm debian-work qubes.Clefsign
|
||||
```
|
||||
|
||||
This should pop up first a dialog to allow the IPC call:
|
||||
|
||||

|
||||
|
||||
Followed by a GTK-dialog to approve the operation
|
||||
|
||||

|
||||
|
||||
To test the full flow, we use the client wrapper. Start it on the `client` qube:
|
||||
```
|
||||
[user@work qubes]$ python3 qubes-client.py
|
||||
```
|
||||
|
||||
Make the request over http (`client` qube):
|
||||
```
|
||||
[user@work clef]$ cat newaccnt.json | curl -X POST -d @- http://localhost:8550
|
||||
```
|
||||
And it should show the same popups again.
|
||||
|
||||
##### Pros and cons
|
||||
|
||||
The benefits of this setup are:
|
||||
|
||||
- This is the qubes-os intended model for inter-qube communication,
|
||||
- and thus benefits from qubes-os dialogs and policies for user approval
|
||||
|
||||
However, it comes with a couple of drawbacks:
|
||||
|
||||
- The `qubes-gpg-client` must forward the http request via RPC to the `target` qube. When doing so, the proxy
|
||||
will either drop important headers, or replace them.
|
||||
- The `Host` header is most likely `localhost`
|
||||
- The `Origin` header must be forwarded
|
||||
- Information about the remote ip must be added as a `X-Forwarded-For`. However, Clef cannot always trust an `XFF` header,
|
||||
since malicious clients may lie about `XFF` in order to fool the http server into believing it comes from another address.
|
||||
- Even with a policy in place to allow rpc-calls between `caller` and `target`, there will be several popups:
|
||||
- One qubes-specific where the user specifies the `target` vm
|
||||
- One clef-specific to approve the transaction
|
||||
|
||||
|
||||
#### 2. Network integrated
|
||||
|
||||
The second way to set up Clef on a qubes system is to allow networking, and have Clef listen to a port which is accessible
|
||||
form other qubes.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
## USBArmory
|
||||
|
||||
The [USB armory](https://inversepath.com/usbarmory) is an open source hardware design with an 800 Mhz ARM processor. It is a pocket-size
|
||||
computer. When inserted into a laptop, it identifies itself as a USB network interface, basically adding another network
|
||||
to your computer. Over this new network interface, you can SSH into the device.
|
||||
|
||||
Running Clef off a USB armory means that you can use the armory as a very versatile offline computer, which only
|
||||
ever connects to a local network between your computer and the device itself.
|
||||
|
||||
Needless to say, the while this model should be fairly secure against remote attacks, an attacker with physical access
|
||||
to the USB Armory would trivially be able to extract the contents of the device filesystem.
|
||||
|
||||
@@ -47,11 +47,11 @@ import (
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
// ExternalApiVersion -- see extapi_changelog.md
|
||||
const ExternalApiVersion = "2.0.0"
|
||||
// ExternalAPIVersion -- see extapi_changelog.md
|
||||
const ExternalAPIVersion = "2.0.0"
|
||||
|
||||
// InternalApiVersion -- see intapi_changelog.md
|
||||
const InternalApiVersion = "2.0.0"
|
||||
// InternalAPIVersion -- see intapi_changelog.md
|
||||
const InternalAPIVersion = "2.0.0"
|
||||
|
||||
const legalWarning = `
|
||||
WARNING!
|
||||
@@ -398,10 +398,10 @@ func signer(c *cli.Context) error {
|
||||
}
|
||||
// register signer API with server
|
||||
var (
|
||||
extapiUrl = "n/a"
|
||||
ipcApiUrl = "n/a"
|
||||
extapiURL = "n/a"
|
||||
ipcapiURL = "n/a"
|
||||
)
|
||||
rpcApi := []rpc.API{
|
||||
rpcAPI := []rpc.API{
|
||||
{
|
||||
Namespace: "account",
|
||||
Public: true,
|
||||
@@ -415,12 +415,12 @@ func signer(c *cli.Context) error {
|
||||
|
||||
// start http server
|
||||
httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
|
||||
listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcApi, []string{"account"}, cors, vhosts)
|
||||
listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not start RPC api: %v", err)
|
||||
}
|
||||
extapiUrl = fmt.Sprintf("http://%s", httpEndpoint)
|
||||
log.Info("HTTP endpoint opened", "url", extapiUrl)
|
||||
extapiURL = fmt.Sprintf("http://%s", httpEndpoint)
|
||||
log.Info("HTTP endpoint opened", "url", extapiURL)
|
||||
|
||||
defer func() {
|
||||
listener.Close()
|
||||
@@ -430,19 +430,19 @@ func signer(c *cli.Context) error {
|
||||
}
|
||||
if !c.Bool(utils.IPCDisabledFlag.Name) {
|
||||
if c.IsSet(utils.IPCPathFlag.Name) {
|
||||
ipcApiUrl = c.String(utils.IPCPathFlag.Name)
|
||||
ipcapiURL = c.String(utils.IPCPathFlag.Name)
|
||||
} else {
|
||||
ipcApiUrl = filepath.Join(configDir, "clef.ipc")
|
||||
ipcapiURL = filepath.Join(configDir, "clef.ipc")
|
||||
}
|
||||
|
||||
listener, _, err := rpc.StartIPCEndpoint(ipcApiUrl, rpcApi)
|
||||
listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not start IPC api: %v", err)
|
||||
}
|
||||
log.Info("IPC endpoint opened", "url", ipcApiUrl)
|
||||
log.Info("IPC endpoint opened", "url", ipcapiURL)
|
||||
defer func() {
|
||||
listener.Close()
|
||||
log.Info("IPC endpoint closed", "url", ipcApiUrl)
|
||||
log.Info("IPC endpoint closed", "url", ipcapiURL)
|
||||
}()
|
||||
|
||||
}
|
||||
@@ -453,10 +453,10 @@ func signer(c *cli.Context) error {
|
||||
}
|
||||
ui.OnSignerStartup(core.StartupInfo{
|
||||
Info: map[string]interface{}{
|
||||
"extapi_version": ExternalApiVersion,
|
||||
"intapi_version": InternalApiVersion,
|
||||
"extapi_http": extapiUrl,
|
||||
"extapi_ipc": ipcApiUrl,
|
||||
"extapi_version": ExternalAPIVersion,
|
||||
"intapi_version": InternalAPIVersion,
|
||||
"extapi_http": extapiURL,
|
||||
"extapi_ipc": ipcapiURL,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ type JSONLogger struct {
|
||||
cfg *vm.LogConfig
|
||||
}
|
||||
|
||||
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
|
||||
// into the provided stream.
|
||||
func NewJSONLogger(cfg *vm.LogConfig, writer io.Writer) *JSONLogger {
|
||||
return &JSONLogger{json.NewEncoder(writer), cfg}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
goruntime "runtime"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
goruntime "runtime"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -86,6 +86,7 @@ func runCmd(ctx *cli.Context) error {
|
||||
chainConfig *params.ChainConfig
|
||||
sender = common.BytesToAddress([]byte("sender"))
|
||||
receiver = common.BytesToAddress([]byte("receiver"))
|
||||
blockNumber uint64
|
||||
)
|
||||
if ctx.GlobalBool(MachineFlag.Name) {
|
||||
tracer = NewJSONLogger(logconfig, os.Stdout)
|
||||
@@ -97,13 +98,13 @@ func runCmd(ctx *cli.Context) error {
|
||||
}
|
||||
if ctx.GlobalString(GenesisFlag.Name) != "" {
|
||||
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
genesis := gen.ToBlock(db)
|
||||
statedb, _ = state.New(genesis.Root(), state.NewDatabase(db))
|
||||
chainConfig = gen.Config
|
||||
blockNumber = gen.Number
|
||||
} else {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
}
|
||||
if ctx.GlobalString(SenderFlag.Name) != "" {
|
||||
sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name))
|
||||
@@ -156,11 +157,12 @@ func runCmd(ctx *cli.Context) error {
|
||||
|
||||
initialGas := ctx.GlobalUint64(GasFlag.Name)
|
||||
runtimeConfig := runtime.Config{
|
||||
Origin: sender,
|
||||
State: statedb,
|
||||
GasLimit: initialGas,
|
||||
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
|
||||
Value: utils.GlobalBig(ctx, ValueFlag.Name),
|
||||
Origin: sender,
|
||||
State: statedb,
|
||||
GasLimit: initialGas,
|
||||
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
|
||||
Value: utils.GlobalBig(ctx, ValueFlag.Name),
|
||||
BlockNumber: new(big.Int).SetUint64(blockNumber),
|
||||
EVMConfig: vm.Config{
|
||||
Tracer: tracer,
|
||||
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
|
||||
|
||||
@@ -38,6 +38,8 @@ var stateTestCommand = cli.Command{
|
||||
ArgsUsage: "<file>",
|
||||
}
|
||||
|
||||
// StatetestResult contains the execution status after running a state test, any
|
||||
// error that might have occurred and a dump of the final state if requested.
|
||||
type StatetestResult struct {
|
||||
Name string `json:"name"`
|
||||
Pass bool `json:"pass"`
|
||||
|
||||
@@ -340,7 +340,7 @@ func importWallet(ctx *cli.Context) error {
|
||||
if len(keyfile) == 0 {
|
||||
utils.Fatalf("keyfile must be given as argument")
|
||||
}
|
||||
keyJson, err := ioutil.ReadFile(keyfile)
|
||||
keyJSON, err := ioutil.ReadFile(keyfile)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not read wallet file: %v", err)
|
||||
}
|
||||
@@ -349,7 +349,7 @@ func importWallet(ctx *cli.Context) error {
|
||||
passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
|
||||
|
||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||
acct, err := ks.ImportPreSaleKey(keyJson, passphrase)
|
||||
acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ var bugCommand = cli.Command{
|
||||
Category: "MISCELLANEOUS COMMANDS",
|
||||
}
|
||||
|
||||
const issueUrl = "https://github.com/ethereum/go-ethereum/issues/new"
|
||||
const issueURL = "https://github.com/ethereum/go-ethereum/issues/new"
|
||||
|
||||
// reportBug reports a bug by opening a new URL to the go-ethereum GH issue
|
||||
// tracker and setting default values as the issue body.
|
||||
@@ -58,8 +58,8 @@ func reportBug(ctx *cli.Context) error {
|
||||
fmt.Fprintln(&buff, header)
|
||||
|
||||
// open a new GH issue
|
||||
if !browser.Open(issueUrl + "?body=" + url.QueryEscape(buff.String())) {
|
||||
fmt.Printf("Please file a new issue at %s using this template:\n\n%s", issueUrl, buff.String())
|
||||
if !browser.Open(issueURL + "?body=" + url.QueryEscape(buff.String())) {
|
||||
fmt.Printf("Please file a new issue at %s using this template:\n\n%s", issueURL, buff.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
@@ -131,8 +131,8 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc
|
||||
if genesis != "" {
|
||||
genesisHash = daoGenesisHash
|
||||
}
|
||||
config, err := core.GetChainConfig(db, genesisHash)
|
||||
if err != nil {
|
||||
config := rawdb.ReadChainConfig(db, genesisHash)
|
||||
if config == nil {
|
||||
t.Errorf("test %d: failed to retrieve chain config: %v", test, err)
|
||||
return // we want to return here, the other checks can't make it past this point (nil panic).
|
||||
}
|
||||
|
||||
@@ -223,6 +223,8 @@ func geth(ctx *cli.Context) error {
|
||||
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
||||
// miner.
|
||||
func startNode(ctx *cli.Context, stack *node.Node) {
|
||||
debug.Memsize.Add("node", stack)
|
||||
|
||||
// Start up the node itself
|
||||
utils.StartNode(stack)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
@@ -271,15 +272,13 @@ func ImportPreimages(db *ethdb.LDBDatabase, fn string) error {
|
||||
// Accumulate the preimages and flush when enough ws gathered
|
||||
preimages[crypto.Keccak256Hash(blob)] = common.CopyBytes(blob)
|
||||
if len(preimages) > 1024 {
|
||||
if err := core.WritePreimages(db, 0, preimages); err != nil {
|
||||
return err
|
||||
}
|
||||
rawdb.WritePreimages(db, 0, preimages)
|
||||
preimages = make(map[common.Hash][]byte)
|
||||
}
|
||||
}
|
||||
// Flush the last batch preimage data
|
||||
if len(preimages) > 0 {
|
||||
return core.WritePreimages(db, 0, preimages)
|
||||
rawdb.WritePreimages(db, 0, preimages)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -158,11 +158,11 @@ var (
|
||||
}
|
||||
FastSyncFlag = cli.BoolFlag{
|
||||
Name: "fast",
|
||||
Usage: "Enable fast syncing through state downloads",
|
||||
Usage: "Enable fast syncing through state downloads (replaced by --syncmode)",
|
||||
}
|
||||
LightModeFlag = cli.BoolFlag{
|
||||
Name: "light",
|
||||
Usage: "Enable light client mode",
|
||||
Usage: "Enable light client mode (replaced by --syncmode)",
|
||||
}
|
||||
defaultSyncMode = eth.DefaultConfig.SyncMode
|
||||
SyncModeFlag = TextMarshalerFlag{
|
||||
|
||||
@@ -271,7 +271,9 @@ func initialize() {
|
||||
|
||||
if *mailServerMode {
|
||||
shh.RegisterServer(&mailServer)
|
||||
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
|
||||
if err := mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW); err != nil {
|
||||
utils.Fatalf("Failed to init MailServer: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
server = &p2p.Server{
|
||||
|
||||
@@ -19,15 +19,20 @@ package common
|
||||
|
||||
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 {
|
||||
hex := Bytes2Hex(b)
|
||||
// Prefer output of "0x0" instead of "0x"
|
||||
if len(hex) == 0 {
|
||||
hex = "0"
|
||||
}
|
||||
return "0x" + hex
|
||||
}
|
||||
|
||||
// FromHex returns the bytes represented by the hexadecimal string s.
|
||||
// s may be prefixed with "0x".
|
||||
func FromHex(s string) []byte {
|
||||
if len(s) > 1 {
|
||||
if s[0:2] == "0x" || s[0:2] == "0X" {
|
||||
@@ -40,9 +45,7 @@ func FromHex(s string) []byte {
|
||||
return Hex2Bytes(s)
|
||||
}
|
||||
|
||||
// Copy bytes
|
||||
//
|
||||
// Returns an exact copy of the provided bytes
|
||||
// CopyBytes returns an exact copy of the provided bytes.
|
||||
func CopyBytes(b []byte) (copiedBytes []byte) {
|
||||
if b == nil {
|
||||
return nil
|
||||
@@ -53,14 +56,17 @@ func CopyBytes(b []byte) (copiedBytes []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
// hasHexPrefix validates str begins with '0x' or '0X'.
|
||||
func hasHexPrefix(str string) bool {
|
||||
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 {
|
||||
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 {
|
||||
if len(str)%2 != 0 {
|
||||
return false
|
||||
@@ -73,31 +79,32 @@ func isHex(str string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Bytes2Hex returns the hexadecimal encoding of d.
|
||||
func Bytes2Hex(d []byte) string {
|
||||
return hex.EncodeToString(d)
|
||||
}
|
||||
|
||||
// Hex2Bytes returns the bytes represented by the hexadecimal string str.
|
||||
func Hex2Bytes(str string) []byte {
|
||||
h, _ := hex.DecodeString(str)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// Hex2BytesFixed returns bytes of a specified fixed length flen.
|
||||
func Hex2BytesFixed(str string, flen int) []byte {
|
||||
h, _ := hex.DecodeString(str)
|
||||
if len(h) == flen {
|
||||
return h
|
||||
} else {
|
||||
if len(h) > flen {
|
||||
return h[len(h)-flen:]
|
||||
} else {
|
||||
hh := make([]byte, flen)
|
||||
copy(hh[flen-len(h):flen], h[:])
|
||||
return hh
|
||||
}
|
||||
}
|
||||
if len(h) > flen {
|
||||
return h[len(h)-flen:]
|
||||
}
|
||||
hh := make([]byte, flen)
|
||||
copy(hh[flen-len(h):flen], h[:])
|
||||
return hh
|
||||
}
|
||||
|
||||
// RightPadBytes zero-pads slice to the right up to length l.
|
||||
func RightPadBytes(slice []byte, l int) []byte {
|
||||
if l <= len(slice) {
|
||||
return slice
|
||||
@@ -109,6 +116,7 @@ func RightPadBytes(slice []byte, l int) []byte {
|
||||
return padded
|
||||
}
|
||||
|
||||
// LeftPadBytes zero-pads slice to the left up to length l.
|
||||
func LeftPadBytes(slice []byte, l int) []byte {
|
||||
if l <= len(slice) {
|
||||
return slice
|
||||
|
||||
@@ -78,7 +78,7 @@ func ParseBig256(s string) (*big.Int, bool) {
|
||||
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 {
|
||||
v, ok := ParseBig256(s)
|
||||
if !ok {
|
||||
@@ -186,9 +186,8 @@ func U256(x *big.Int) *big.Int {
|
||||
func S256(x *big.Int) *big.Int {
|
||||
if x.Cmp(tt255) < 0 {
|
||||
return x
|
||||
} else {
|
||||
return new(big.Int).Sub(x, tt256)
|
||||
}
|
||||
return new(big.Int).Sub(x, tt256)
|
||||
}
|
||||
|
||||
// Exp implements exponentiation by squaring.
|
||||
|
||||
@@ -34,13 +34,12 @@ func limitUnsigned256(x *Number) *Number {
|
||||
func limitSigned256(x *Number) *Number {
|
||||
if x.num.Cmp(tt255) < 0 {
|
||||
return x
|
||||
} else {
|
||||
x.num.Sub(x.num, tt256)
|
||||
return x
|
||||
}
|
||||
x.num.Sub(x.num, tt256)
|
||||
return x
|
||||
}
|
||||
|
||||
// Number function
|
||||
// Initialiser is a Number function
|
||||
type Initialiser func(n int64) *Number
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return func(n int64) *Number {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
i.num.Add(x.num, y.num)
|
||||
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 {
|
||||
i.num.Sub(x.num, y.num)
|
||||
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 {
|
||||
i.num.Mul(x.num, y.num)
|
||||
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 {
|
||||
i.num.Div(x.num, y.num)
|
||||
return i.limit(i)
|
||||
}
|
||||
|
||||
// Sets i to x % y
|
||||
// Mod sets i to x % y
|
||||
func (i *Number) Mod(x, y *Number) *Number {
|
||||
i.num.Mod(x.num, y.num)
|
||||
return i.limit(i)
|
||||
}
|
||||
|
||||
// Sets i to x << s
|
||||
// Lsh sets i to x << s
|
||||
func (i *Number) Lsh(x *Number, s uint) *Number {
|
||||
i.num.Lsh(x.num, s)
|
||||
return i.limit(i)
|
||||
}
|
||||
|
||||
// Sets i to x^y
|
||||
// Pow sets i to x^y
|
||||
func (i *Number) Pow(x, y *Number) *Number {
|
||||
i.num.Exp(x.num, y.num, big.NewInt(0))
|
||||
return i.limit(i)
|
||||
@@ -117,13 +116,13 @@ func (i *Number) Pow(x, y *Number) *Number {
|
||||
|
||||
// Setters
|
||||
|
||||
// Set x to i
|
||||
// Set sets x to i
|
||||
func (i *Number) Set(x *Number) *Number {
|
||||
i.num.Set(x.num)
|
||||
return i.limit(i)
|
||||
}
|
||||
|
||||
// Set x bytes to i
|
||||
// SetBytes sets x bytes to i
|
||||
func (i *Number) SetBytes(x []byte) *Number {
|
||||
i.num.SetBytes(x)
|
||||
return i.limit(i)
|
||||
@@ -140,12 +139,12 @@ func (i *Number) Cmp(x *Number) int {
|
||||
|
||||
// Getters
|
||||
|
||||
// Returns the string representation of i
|
||||
// String returns the string representation of i
|
||||
func (i *Number) String() string {
|
||||
return i.num.String()
|
||||
}
|
||||
|
||||
// Returns the byte representation of i
|
||||
// Bytes returns the byte representation of i
|
||||
func (i *Number) Bytes() []byte {
|
||||
return i.num.Bytes()
|
||||
}
|
||||
@@ -160,17 +159,17 @@ func (i *Number) Int64() int64 {
|
||||
return i.num.Int64()
|
||||
}
|
||||
|
||||
// Returns the signed version of i
|
||||
// Int256 returns the signed version of i
|
||||
func (i *Number) Int256() *Number {
|
||||
return Int(0).Set(i)
|
||||
}
|
||||
|
||||
// Returns the unsigned version of i
|
||||
// Uint256 returns the unsigned version of i
|
||||
func (i *Number) Uint256() *Number {
|
||||
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 {
|
||||
for j := 0; j < i.num.BitLen(); j++ {
|
||||
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())
|
||||
}
|
||||
|
||||
// FileExist checks if a file exists at filePath.
|
||||
func FileExist(filePath string) bool {
|
||||
_, err := os.Stat(filePath)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
@@ -39,9 +40,10 @@ func FileExist(filePath string) bool {
|
||||
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) {
|
||||
return filename
|
||||
}
|
||||
return filepath.Join(Datadir, filename)
|
||||
return filepath.Join(datadir, filename)
|
||||
}
|
||||
|
||||
@@ -42,19 +42,30 @@ var (
|
||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||
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 {
|
||||
var h Hash
|
||||
h.SetBytes(b)
|
||||
return h
|
||||
}
|
||||
func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
|
||||
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
||||
|
||||
// Get the string representation of the underlying hash
|
||||
func (h Hash) Str() string { return string(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()) }
|
||||
|
||||
// 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)) }
|
||||
|
||||
// Bytes gets the byte representation of the underlying hash.
|
||||
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) Hex() string { return hexutil.Encode(h[:]) }
|
||||
|
||||
// Hex converts a hash to a hex string.
|
||||
func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
|
||||
|
||||
// TerminalString implements log.TerminalStringer, formatting a string for console
|
||||
// output during logging.
|
||||
@@ -89,7 +100,8 @@ func (h Hash) MarshalText() ([]byte, error) {
|
||||
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) {
|
||||
if len(b) > len(h) {
|
||||
b = b[len(b)-HashLength:]
|
||||
@@ -98,16 +110,6 @@ func (h *Hash) SetBytes(b []byte) {
|
||||
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.
|
||||
func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||
m := rand.Intn(len(h))
|
||||
@@ -117,10 +119,6 @@ func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||
return reflect.ValueOf(h)
|
||||
}
|
||||
|
||||
func EmptyHash(h Hash) bool {
|
||||
return h == Hash{}
|
||||
}
|
||||
|
||||
// UnprefixedHash allows marshaling a Hash without 0x prefix.
|
||||
type UnprefixedHash Hash
|
||||
|
||||
@@ -139,13 +137,21 @@ func (h UnprefixedHash) MarshalText() ([]byte, error) {
|
||||
// Address represents the 20 byte address of an Ethereum account.
|
||||
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 {
|
||||
var a Address
|
||||
a.SetBytes(b)
|
||||
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 HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) }
|
||||
|
||||
// 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)) }
|
||||
|
||||
// IsHexAddress verifies whether a string can represent a valid hex-encoded
|
||||
// Ethereum address or not.
|
||||
@@ -156,11 +162,14 @@ func IsHexAddress(s string) bool {
|
||||
return len(s) == 2*AddressLength && isHex(s)
|
||||
}
|
||||
|
||||
// Get the string representation of the underlying address
|
||||
func (a Address) Str() string { return string(a[:]) }
|
||||
// Bytes gets the string representation of the underlying address.
|
||||
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) Hash() Hash { return BytesToHash(a[:]) }
|
||||
|
||||
// Hash converts an address to a hash by left-padding it with zeros.
|
||||
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
||||
|
||||
// Hex returns an EIP55-compliant hex string representation of the address.
|
||||
func (a Address) Hex() string {
|
||||
@@ -184,7 +193,7 @@ func (a Address) Hex() string {
|
||||
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 {
|
||||
return a.Hex()
|
||||
}
|
||||
@@ -195,7 +204,8 @@ func (a Address) Format(s fmt.State, c rune) {
|
||||
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) {
|
||||
if len(b) > len(a) {
|
||||
b = b[len(b)-AddressLength:]
|
||||
@@ -203,16 +213,6 @@ func (a *Address) SetBytes(b []byte) {
|
||||
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.
|
||||
func (a Address) MarshalText() ([]byte, error) {
|
||||
return hexutil.Bytes(a[:]).MarshalText()
|
||||
@@ -228,7 +228,7 @@ func (a *Address) UnmarshalJSON(input []byte) error {
|
||||
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
|
||||
|
||||
// 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 number%checkpointInterval == 0 {
|
||||
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
|
||||
break
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
@@ -81,7 +82,7 @@ func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic
|
||||
func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") }
|
||||
func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header {
|
||||
if number == 0 {
|
||||
return core.GetHeader(r.db, core.GetCanonicalHash(r.db, 0), 0)
|
||||
return rawdb.ReadHeader(r.db, rawdb.ReadCanonicalHash(r.db, 0), 0)
|
||||
}
|
||||
panic("not supported")
|
||||
}
|
||||
@@ -351,7 +352,7 @@ func TestVoting(t *testing.T) {
|
||||
copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:])
|
||||
}
|
||||
// Create a pristine blockchain with the genesis injected
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
genesis.Commit(db)
|
||||
|
||||
// Assemble a chain of headers from the cast votes
|
||||
|
||||
@@ -156,7 +156,7 @@ type lru struct {
|
||||
futureItem interface{}
|
||||
}
|
||||
|
||||
// newlru create a new least-recently-used cache for ither the verification caches
|
||||
// newlru create a new least-recently-used cache for either the verification caches
|
||||
// or the mining datasets.
|
||||
func newlru(what string, maxItems int, new func(epoch uint64) interface{}) *lru {
|
||||
if maxItems <= 0 {
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -148,7 +149,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||
// Create the database in memory or in a temporary directory.
|
||||
var db ethdb.Database
|
||||
if !disk {
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
} else {
|
||||
dir, err := ioutil.TempDir("", "eth-core-bench")
|
||||
if err != nil {
|
||||
@@ -234,13 +235,15 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
|
||||
ReceiptHash: types.EmptyRootHash,
|
||||
}
|
||||
hash = header.Hash()
|
||||
WriteHeader(db, header)
|
||||
WriteCanonicalHash(db, hash, n)
|
||||
WriteTd(db, hash, n, big.NewInt(int64(n+1)))
|
||||
|
||||
rawdb.WriteHeader(db, header)
|
||||
rawdb.WriteCanonicalHash(db, hash, n)
|
||||
rawdb.WriteTd(db, hash, n, big.NewInt(int64(n+1)))
|
||||
|
||||
if full || n == 0 {
|
||||
block := types.NewBlockWithHeader(header)
|
||||
WriteBody(db, hash, n, block.Body())
|
||||
WriteBlockReceipts(db, hash, n, nil)
|
||||
rawdb.WriteBody(db, hash, n, block.Body())
|
||||
rawdb.WriteReceipts(db, hash, n, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,11 +295,10 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||
header := chain.GetHeaderByNumber(n)
|
||||
if full {
|
||||
hash := header.Hash()
|
||||
GetBody(db, hash, n)
|
||||
GetBlockReceipts(db, hash, n)
|
||||
rawdb.ReadBody(db, hash, n)
|
||||
rawdb.ReadReceipts(db, hash, n)
|
||||
}
|
||||
}
|
||||
|
||||
chain.Stop()
|
||||
db.Close()
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
func TestHeaderVerification(t *testing.T) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
testdb = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
||||
@@ -84,7 +84,7 @@ func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVeri
|
||||
func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
testdb = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
||||
@@ -156,7 +156,7 @@ func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion
|
||||
func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
testdb = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil)
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@@ -202,7 +203,7 @@ func (bc *BlockChain) getProcInterrupt() bool {
|
||||
// assumes that the chain manager mutex is held.
|
||||
func (bc *BlockChain) loadLastState() error {
|
||||
// Restore the last known head block
|
||||
head := GetHeadBlockHash(bc.db)
|
||||
head := rawdb.ReadHeadBlockHash(bc.db)
|
||||
if head == (common.Hash{}) {
|
||||
// Corrupt or empty database, init from scratch
|
||||
log.Warn("Empty database, resetting chain")
|
||||
@@ -228,7 +229,7 @@ func (bc *BlockChain) loadLastState() error {
|
||||
|
||||
// Restore the last known head header
|
||||
currentHeader := currentBlock.Header()
|
||||
if head := GetHeadHeaderHash(bc.db); head != (common.Hash{}) {
|
||||
if head := rawdb.ReadHeadHeaderHash(bc.db); head != (common.Hash{}) {
|
||||
if header := bc.GetHeaderByHash(head); header != nil {
|
||||
currentHeader = header
|
||||
}
|
||||
@@ -237,7 +238,7 @@ func (bc *BlockChain) loadLastState() error {
|
||||
|
||||
// Restore the last known head fast block
|
||||
bc.currentFastBlock.Store(currentBlock)
|
||||
if head := GetHeadFastBlockHash(bc.db); head != (common.Hash{}) {
|
||||
if head := rawdb.ReadHeadFastBlockHash(bc.db); head != (common.Hash{}) {
|
||||
if block := bc.GetBlockByHash(head); block != nil {
|
||||
bc.currentFastBlock.Store(block)
|
||||
}
|
||||
@@ -269,7 +270,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
||||
|
||||
// Rewind the header chain, deleting all block bodies until then
|
||||
delFn := func(hash common.Hash, num uint64) {
|
||||
DeleteBody(bc.db, hash, num)
|
||||
rawdb.DeleteBody(bc.db, hash, num)
|
||||
}
|
||||
bc.hc.SetHead(head, delFn)
|
||||
currentHeader := bc.hc.CurrentHeader()
|
||||
@@ -303,12 +304,10 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
||||
}
|
||||
currentBlock := bc.CurrentBlock()
|
||||
currentFastBlock := bc.CurrentFastBlock()
|
||||
if err := WriteHeadBlockHash(bc.db, currentBlock.Hash()); err != nil {
|
||||
log.Crit("Failed to reset head full block", "err", err)
|
||||
}
|
||||
if err := WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash()); err != nil {
|
||||
log.Crit("Failed to reset head fast block", "err", err)
|
||||
}
|
||||
|
||||
rawdb.WriteHeadBlockHash(bc.db, currentBlock.Hash())
|
||||
rawdb.WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash())
|
||||
|
||||
return bc.loadLastState()
|
||||
}
|
||||
|
||||
@@ -406,9 +405,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
||||
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
|
||||
log.Crit("Failed to write genesis block TD", "err", err)
|
||||
}
|
||||
if err := WriteBlock(bc.db, genesis); err != nil {
|
||||
log.Crit("Failed to write genesis block", "err", err)
|
||||
}
|
||||
rawdb.WriteBlock(bc.db, genesis)
|
||||
|
||||
bc.genesisBlock = genesis
|
||||
bc.insert(bc.genesisBlock)
|
||||
bc.currentBlock.Store(bc.genesisBlock)
|
||||
@@ -474,24 +472,19 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
||||
// Note, this function assumes that the `mu` mutex is held!
|
||||
func (bc *BlockChain) insert(block *types.Block) {
|
||||
// If the block is on a side chain or an unknown one, force other heads onto it too
|
||||
updateHeads := GetCanonicalHash(bc.db, block.NumberU64()) != block.Hash()
|
||||
updateHeads := rawdb.ReadCanonicalHash(bc.db, block.NumberU64()) != block.Hash()
|
||||
|
||||
// Add the block to the canonical chain number scheme and mark as the head
|
||||
if err := WriteCanonicalHash(bc.db, block.Hash(), block.NumberU64()); err != nil {
|
||||
log.Crit("Failed to insert block number", "err", err)
|
||||
}
|
||||
if err := WriteHeadBlockHash(bc.db, block.Hash()); err != nil {
|
||||
log.Crit("Failed to insert head block hash", "err", err)
|
||||
}
|
||||
rawdb.WriteCanonicalHash(bc.db, block.Hash(), block.NumberU64())
|
||||
rawdb.WriteHeadBlockHash(bc.db, block.Hash())
|
||||
|
||||
bc.currentBlock.Store(block)
|
||||
|
||||
// If the block is better than our head or is on a different chain, force update heads
|
||||
if updateHeads {
|
||||
bc.hc.SetCurrentHeader(block.Header())
|
||||
rawdb.WriteHeadFastBlockHash(bc.db, block.Hash())
|
||||
|
||||
if err := WriteHeadFastBlockHash(bc.db, block.Hash()); err != nil {
|
||||
log.Crit("Failed to insert head fast block hash", "err", err)
|
||||
}
|
||||
bc.currentFastBlock.Store(block)
|
||||
}
|
||||
}
|
||||
@@ -509,7 +502,11 @@ func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
|
||||
body := cached.(*types.Body)
|
||||
return body
|
||||
}
|
||||
body := GetBody(bc.db, hash, bc.hc.GetBlockNumber(hash))
|
||||
number := bc.hc.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
body := rawdb.ReadBody(bc.db, hash, *number)
|
||||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -525,7 +522,11 @@ func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
|
||||
if cached, ok := bc.bodyRLPCache.Get(hash); ok {
|
||||
return cached.(rlp.RawValue)
|
||||
}
|
||||
body := GetBodyRLP(bc.db, hash, bc.hc.GetBlockNumber(hash))
|
||||
number := bc.hc.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
body := rawdb.ReadBodyRLP(bc.db, hash, *number)
|
||||
if len(body) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -539,8 +540,7 @@ func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
|
||||
if bc.blockCache.Contains(hash) {
|
||||
return true
|
||||
}
|
||||
ok, _ := bc.db.Has(blockBodyKey(hash, number))
|
||||
return ok
|
||||
return rawdb.HasBody(bc.db, hash, number)
|
||||
}
|
||||
|
||||
// HasState checks if state trie is fully present in the database or not.
|
||||
@@ -567,7 +567,7 @@ func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
|
||||
if block, ok := bc.blockCache.Get(hash); ok {
|
||||
return block.(*types.Block)
|
||||
}
|
||||
block := GetBlock(bc.db, hash, number)
|
||||
block := rawdb.ReadBlock(bc.db, hash, number)
|
||||
if block == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -578,13 +578,17 @@ func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
|
||||
|
||||
// GetBlockByHash retrieves a block from the database by hash, caching it if found.
|
||||
func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
|
||||
return bc.GetBlock(hash, bc.hc.GetBlockNumber(hash))
|
||||
number := bc.hc.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
return bc.GetBlock(hash, *number)
|
||||
}
|
||||
|
||||
// GetBlockByNumber retrieves a block from the database by number, caching it
|
||||
// (associated with its hash) if found.
|
||||
func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
|
||||
hash := GetCanonicalHash(bc.db, number)
|
||||
hash := rawdb.ReadCanonicalHash(bc.db, number)
|
||||
if hash == (common.Hash{}) {
|
||||
return nil
|
||||
}
|
||||
@@ -593,21 +597,28 @@ func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
|
||||
|
||||
// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
|
||||
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||
return GetBlockReceipts(bc.db, hash, GetBlockNumber(bc.db, hash))
|
||||
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
return rawdb.ReadReceipts(bc.db, hash, *number)
|
||||
}
|
||||
|
||||
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
|
||||
// [deprecated by eth/62]
|
||||
func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
|
||||
number := bc.hc.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
block := bc.GetBlock(hash, number)
|
||||
block := bc.GetBlock(hash, *number)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
hash = block.ParentHash()
|
||||
number--
|
||||
*number--
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -712,12 +723,12 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
|
||||
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock.Hash() == hash {
|
||||
newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1)
|
||||
bc.currentFastBlock.Store(newFastBlock)
|
||||
WriteHeadFastBlockHash(bc.db, newFastBlock.Hash())
|
||||
rawdb.WriteHeadFastBlockHash(bc.db, newFastBlock.Hash())
|
||||
}
|
||||
if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash {
|
||||
newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1)
|
||||
bc.currentBlock.Store(newBlock)
|
||||
WriteHeadBlockHash(bc.db, newBlock.Hash())
|
||||
rawdb.WriteHeadBlockHash(bc.db, newBlock.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -802,15 +813,10 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
return i, fmt.Errorf("failed to set receipts data: %v", err)
|
||||
}
|
||||
// Write all the data out into the database
|
||||
if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil {
|
||||
return i, fmt.Errorf("failed to write block body: %v", err)
|
||||
}
|
||||
if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
|
||||
return i, fmt.Errorf("failed to write block receipts: %v", err)
|
||||
}
|
||||
if err := WriteTxLookupEntries(batch, block); err != nil {
|
||||
return i, fmt.Errorf("failed to write lookup metadata: %v", err)
|
||||
}
|
||||
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
|
||||
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)
|
||||
rawdb.WriteTxLookupEntries(batch, block)
|
||||
|
||||
stats.processed++
|
||||
|
||||
if batch.ValueSize() >= ethdb.IdealBatchSize {
|
||||
@@ -834,9 +840,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
|
||||
currentFastBlock := bc.CurrentFastBlock()
|
||||
if bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()).Cmp(td) < 0 {
|
||||
if err := WriteHeadFastBlockHash(bc.db, head.Hash()); err != nil {
|
||||
log.Crit("Failed to update head fast block hash", "err", err)
|
||||
}
|
||||
rawdb.WriteHeadFastBlockHash(bc.db, head.Hash())
|
||||
bc.currentFastBlock.Store(head)
|
||||
}
|
||||
}
|
||||
@@ -864,9 +868,8 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e
|
||||
if err := bc.hc.WriteTd(block.Hash(), block.NumberU64(), td); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := WriteBlock(bc.db, block); err != nil {
|
||||
return err
|
||||
}
|
||||
rawdb.WriteBlock(bc.db, block)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -894,9 +897,8 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||
}
|
||||
// Write other block data using a batch.
|
||||
batch := bc.db.NewBatch()
|
||||
if err := WriteBlock(batch, block); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
rawdb.WriteBlock(batch, block)
|
||||
|
||||
root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
return NonStatTy, err
|
||||
@@ -953,9 +955,8 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)
|
||||
|
||||
// If the total difficulty is higher than our known, add it to the canonical chain
|
||||
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
||||
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
||||
@@ -972,14 +973,10 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||
return NonStatTy, err
|
||||
}
|
||||
}
|
||||
// Write the positional metadata for transaction and receipt lookups
|
||||
if err := WriteTxLookupEntries(batch, block); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
// Write hash preimages
|
||||
if err := WritePreimages(bc.db, block.NumberU64(), state.Preimages()); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
// Write the positional metadata for transaction/receipt lookups and preimages
|
||||
rawdb.WriteTxLookupEntries(batch, block)
|
||||
rawdb.WritePreimages(batch, block.NumberU64(), state.Preimages())
|
||||
|
||||
status = CanonStatTy
|
||||
} else {
|
||||
status = SideStatTy
|
||||
@@ -1256,9 +1253,13 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||
// collectLogs collects the logs that were generated during the
|
||||
// processing of the block that corresponds with the given hash.
|
||||
// These logs are later announced as deleted.
|
||||
collectLogs = func(h common.Hash) {
|
||||
collectLogs = func(hash common.Hash) {
|
||||
// Coalesce logs and set 'Removed'.
|
||||
receipts := GetBlockReceipts(bc.db, h, bc.hc.GetBlockNumber(h))
|
||||
number := bc.hc.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return
|
||||
}
|
||||
receipts := rawdb.ReadReceipts(bc.db, hash, *number)
|
||||
for _, receipt := range receipts {
|
||||
for _, log := range receipt.Logs {
|
||||
del := *log
|
||||
@@ -1327,9 +1328,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||
// insert the block in the canonical way, re-writing history
|
||||
bc.insert(newChain[i])
|
||||
// write lookup entries for hash based transaction/receipt searches
|
||||
if err := WriteTxLookupEntries(bc.db, newChain[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
rawdb.WriteTxLookupEntries(bc.db, newChain[i])
|
||||
addedTxs = append(addedTxs, newChain[i].Transactions()...)
|
||||
}
|
||||
// calculate the difference between deleted and added transactions
|
||||
@@ -1337,7 +1336,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||
// When transactions get deleted from the database that means the
|
||||
// receipts that were created in the fork must also be deleted
|
||||
for _, tx := range diff {
|
||||
DeleteTxLookupEntry(bc.db, tx.Hash())
|
||||
rawdb.DeleteTxLookupEntry(bc.db, tx.Hash())
|
||||
}
|
||||
if len(deletedLogs) > 0 {
|
||||
go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@@ -128,8 +129,8 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
return err
|
||||
}
|
||||
blockchain.mu.Lock()
|
||||
WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
|
||||
WriteBlock(blockchain.db, block)
|
||||
rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
|
||||
rawdb.WriteBlock(blockchain.db, block)
|
||||
statedb.Commit(false)
|
||||
blockchain.mu.Unlock()
|
||||
}
|
||||
@@ -146,8 +147,8 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
|
||||
}
|
||||
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
|
||||
blockchain.mu.Lock()
|
||||
WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
|
||||
WriteHeader(blockchain.db, header)
|
||||
rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
|
||||
rawdb.WriteHeader(blockchain.db, header)
|
||||
blockchain.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
@@ -173,7 +174,7 @@ func TestLastBlock(t *testing.T) {
|
||||
if _, err := blockchain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("Failed to insert block: %v", err)
|
||||
}
|
||||
if blocks[len(blocks)-1].Hash() != GetHeadBlockHash(blockchain.db) {
|
||||
if blocks[len(blocks)-1].Hash() != rawdb.ReadHeadBlockHash(blockchain.db) {
|
||||
t.Fatalf("Write/Get HeadBlockHash failed")
|
||||
}
|
||||
}
|
||||
@@ -568,11 +569,11 @@ func testInsertNonceError(t *testing.T, full bool) {
|
||||
func TestFastVsFullChains(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
gendb, _ = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{
|
||||
gendb = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
}
|
||||
@@ -598,7 +599,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
}
|
||||
})
|
||||
// Import the chain as an archive node for the comparison baseline
|
||||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
archiveDb := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(archiveDb)
|
||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer archive.Stop()
|
||||
@@ -607,7 +608,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
t.Fatalf("failed to process block %d: %v", n, err)
|
||||
}
|
||||
// Fast import the chain as a non-archive node to test
|
||||
fastDb, _ := ethdb.NewMemDatabase()
|
||||
fastDb := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(fastDb)
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer fast.Stop()
|
||||
@@ -639,13 +640,13 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
} else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(ablock.Uncles()) {
|
||||
t.Errorf("block #%d [%x]: uncles mismatch: have %v, want %v", num, hash, fblock.Uncles(), ablock.Uncles())
|
||||
}
|
||||
if freceipts, areceipts := GetBlockReceipts(fastDb, hash, GetBlockNumber(fastDb, hash)), GetBlockReceipts(archiveDb, hash, GetBlockNumber(archiveDb, hash)); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) {
|
||||
if freceipts, areceipts := rawdb.ReadReceipts(fastDb, hash, *rawdb.ReadHeaderNumber(fastDb, hash)), rawdb.ReadReceipts(archiveDb, hash, *rawdb.ReadHeaderNumber(archiveDb, hash)); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) {
|
||||
t.Errorf("block #%d [%x]: receipts mismatch: have %v, want %v", num, hash, freceipts, areceipts)
|
||||
}
|
||||
}
|
||||
// Check that the canonical chains are the same between the databases
|
||||
for i := 0; i < len(blocks)+1; i++ {
|
||||
if fhash, ahash := GetCanonicalHash(fastDb, uint64(i)), GetCanonicalHash(archiveDb, uint64(i)); fhash != ahash {
|
||||
if fhash, ahash := rawdb.ReadCanonicalHash(fastDb, uint64(i)), rawdb.ReadCanonicalHash(archiveDb, uint64(i)); fhash != ahash {
|
||||
t.Errorf("block #%d: canonical hash mismatch: have %v, want %v", i, fhash, ahash)
|
||||
}
|
||||
}
|
||||
@@ -656,12 +657,12 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
gendb, _ = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
gendb = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
)
|
||||
height := uint64(1024)
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), nil)
|
||||
@@ -684,7 +685,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
}
|
||||
}
|
||||
// Import the chain as an archive node and ensure all pointers are updated
|
||||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
archiveDb := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(archiveDb)
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
@@ -698,7 +699,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
assert(t, "archive", archive, height/2, height/2, height/2)
|
||||
|
||||
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||
fastDb, _ := ethdb.NewMemDatabase()
|
||||
fastDb := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(fastDb)
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer fast.Stop()
|
||||
@@ -718,7 +719,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
assert(t, "fast", fast, height/2, height/2, 0)
|
||||
|
||||
// Import the chain as a light node and ensure all pointers are updated
|
||||
lightDb, _ := ethdb.NewMemDatabase()
|
||||
lightDb := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(lightDb)
|
||||
|
||||
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
@@ -741,7 +742,7 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
GasLimit: 3141592,
|
||||
@@ -821,28 +822,28 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
|
||||
// removed tx
|
||||
for i, tx := range (types.Transactions{pastDrop, freshDrop}) {
|
||||
if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
|
||||
if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn != nil {
|
||||
t.Errorf("drop %d: tx %v found while shouldn't have been", i, txn)
|
||||
}
|
||||
if rcpt, _, _, _ := GetReceipt(db, tx.Hash()); rcpt != nil {
|
||||
if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash()); rcpt != nil {
|
||||
t.Errorf("drop %d: receipt %v found while shouldn't have been", i, rcpt)
|
||||
}
|
||||
}
|
||||
// added tx
|
||||
for i, tx := range (types.Transactions{pastAdd, freshAdd, futureAdd}) {
|
||||
if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn == nil {
|
||||
if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn == nil {
|
||||
t.Errorf("add %d: expected tx to be found", i)
|
||||
}
|
||||
if rcpt, _, _, _ := GetReceipt(db, tx.Hash()); rcpt == nil {
|
||||
if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash()); rcpt == nil {
|
||||
t.Errorf("add %d: expected receipt to be found", i)
|
||||
}
|
||||
}
|
||||
// shared tx
|
||||
for i, tx := range (types.Transactions{postponed, swapped}) {
|
||||
if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn == nil {
|
||||
if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn == nil {
|
||||
t.Errorf("share %d: expected tx to be found", i)
|
||||
}
|
||||
if rcpt, _, _, _ := GetReceipt(db, tx.Hash()); rcpt == nil {
|
||||
if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash()); rcpt == nil {
|
||||
t.Errorf("share %d: expected receipt to be found", i)
|
||||
}
|
||||
}
|
||||
@@ -853,7 +854,7 @@ func TestLogReorgs(t *testing.T) {
|
||||
var (
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
// this code generates a log
|
||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||
@@ -897,7 +898,7 @@ func TestLogReorgs(t *testing.T) {
|
||||
|
||||
func TestReorgSideEvent(t *testing.T) {
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
gspec = &Genesis{
|
||||
@@ -997,14 +998,14 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
|
||||
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
|
||||
for {
|
||||
ch := GetCanonicalHash(blockchain.db, block.NumberU64())
|
||||
ch := rawdb.ReadCanonicalHash(blockchain.db, block.NumberU64())
|
||||
if ch == (common.Hash{}) {
|
||||
continue // busy wait for canonical hash to be written
|
||||
}
|
||||
if ch != block.Hash() {
|
||||
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
|
||||
}
|
||||
fb := GetBlock(blockchain.db, ch, block.NumberU64())
|
||||
fb := rawdb.ReadBlock(blockchain.db, ch, block.NumberU64())
|
||||
if fb == nil {
|
||||
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
|
||||
}
|
||||
@@ -1025,7 +1026,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
func TestEIP155Transition(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
@@ -1129,7 +1130,7 @@ func TestEIP155Transition(t *testing.T) {
|
||||
func TestEIP161AccountRemoval(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
@@ -1201,7 +1202,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
@@ -1217,7 +1218,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||
}
|
||||
// Import the canonical and fork chain side by side, verifying the current block
|
||||
// and current header consistency
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
diskdb := ethdb.NewMemDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||
@@ -1246,7 +1247,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
@@ -1261,7 +1262,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||
forks[i] = fork[0]
|
||||
}
|
||||
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
diskdb := ethdb.NewMemDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||
@@ -1292,7 +1293,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
||||
// Generate the original common chain segment and the two competing forks
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
|
||||
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
@@ -1300,7 +1301,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
||||
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
||||
|
||||
// Import the shared chain and the original canonical one
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
diskdb := ethdb.NewMemDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||
@@ -1360,7 +1361,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||
)
|
||||
// Generate the original common chain segment and the two competing forks
|
||||
engine := ethash.NewFaker()
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
genesis := gspec.MustCommit(db)
|
||||
|
||||
blockGenerator := func(i int, block *BlockGen) {
|
||||
@@ -1382,7 +1383,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Import the shared chain and the original canonical one
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
diskdb := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
@@ -206,7 +207,7 @@ func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainE
|
||||
|
||||
// TODO(karalabe): This operation is expensive and might block, causing the event system to
|
||||
// potentially also lock up. We need to do with on a different thread somehow.
|
||||
if h := FindCommonAncestor(c.chainDb, prevHeader, header); h != nil {
|
||||
if h := rawdb.FindCommonAncestor(c.chainDb, prevHeader, header); h != nil {
|
||||
c.newHead(h.Number.Uint64(), true)
|
||||
}
|
||||
}
|
||||
@@ -349,11 +350,11 @@ func (c *ChainIndexer) processSection(section uint64, lastHead common.Hash) (com
|
||||
}
|
||||
|
||||
for number := section * c.sectionSize; number < (section+1)*c.sectionSize; number++ {
|
||||
hash := GetCanonicalHash(c.chainDb, number)
|
||||
hash := rawdb.ReadCanonicalHash(c.chainDb, number)
|
||||
if hash == (common.Hash{}) {
|
||||
return common.Hash{}, fmt.Errorf("canonical block #%d unknown", number)
|
||||
}
|
||||
header := GetHeader(c.chainDb, hash, number)
|
||||
header := rawdb.ReadHeader(c.chainDb, hash, number)
|
||||
if header == nil {
|
||||
return common.Hash{}, fmt.Errorf("block #%d [%x…] not found", number, hash[:4])
|
||||
} else if header.ParentHash != lastHead {
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
@@ -47,7 +48,7 @@ func TestChainIndexerWithChildren(t *testing.T) {
|
||||
// multiple backends. The section size and required confirmation count parameters
|
||||
// are randomized.
|
||||
func testChainIndexer(t *testing.T, count int) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
defer db.Close()
|
||||
|
||||
// Create a chain of indexers and ensure they all report empty
|
||||
@@ -92,10 +93,10 @@ func testChainIndexer(t *testing.T, count int) {
|
||||
inject := func(number uint64) {
|
||||
header := &types.Header{Number: big.NewInt(int64(number)), Extra: big.NewInt(rand.Int63()).Bytes()}
|
||||
if number > 0 {
|
||||
header.ParentHash = GetCanonicalHash(db, number-1)
|
||||
header.ParentHash = rawdb.ReadCanonicalHash(db, number-1)
|
||||
}
|
||||
WriteHeader(db, header)
|
||||
WriteCanonicalHash(db, header.Hash(), number)
|
||||
rawdb.WriteHeader(db, header)
|
||||
rawdb.WriteCanonicalHash(db, header.Hash(), number)
|
||||
}
|
||||
// Start indexer with an already existing chain
|
||||
for i := uint64(0); i <= 100; i++ {
|
||||
|
||||
@@ -256,11 +256,12 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
|
||||
// chain. Depending on the full flag, if creates either a full block chain or a
|
||||
// header only chain.
|
||||
func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *BlockChain, error) {
|
||||
// Initialize a fresh chain with only a genesis block
|
||||
gspec := new(Genesis)
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis := gspec.MustCommit(db)
|
||||
var (
|
||||
db = ethdb.NewMemDatabase()
|
||||
genesis = new(Genesis).MustCommit(db)
|
||||
)
|
||||
|
||||
// Initialize a fresh chain with only a genesis block
|
||||
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{})
|
||||
// Create and inject the requested chain
|
||||
if n == 0 {
|
||||
|
||||
@@ -36,7 +36,7 @@ func ExampleGenerateChain() {
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
)
|
||||
|
||||
// Ensure that key1 has some funds in the genesis block.
|
||||
|
||||
@@ -32,13 +32,13 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
forkBlock := big.NewInt(32)
|
||||
|
||||
// Generate a common prefix for both pro-forkers and non-forkers
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
gspec := new(Genesis)
|
||||
genesis := gspec.MustCommit(db)
|
||||
prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
||||
|
||||
// Create the concurrent, conflicting two nodes
|
||||
proDb, _ := ethdb.NewMemDatabase()
|
||||
proDb := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(proDb)
|
||||
|
||||
proConf := *params.TestChainConfig
|
||||
@@ -48,7 +48,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
defer proBc.Stop()
|
||||
|
||||
conDb, _ := ethdb.NewMemDatabase()
|
||||
conDb := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(conDb)
|
||||
|
||||
conConf := *params.TestChainConfig
|
||||
@@ -67,7 +67,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
// Try to expand both pro-fork and non-fork chains iteratively with other camp's blocks
|
||||
for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ {
|
||||
// Create a pro-fork block, and try to feed into the no-fork chain
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
@@ -92,7 +92,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err)
|
||||
}
|
||||
// Create a no-fork block, and try to feed into the pro-fork chain
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
@@ -118,7 +118,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
@@ -138,7 +138,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err)
|
||||
}
|
||||
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
|
||||
@@ -1,652 +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/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// DatabaseReader wraps the Get method of a backing data store.
|
||||
type DatabaseReader interface {
|
||||
Get(key []byte) (value []byte, err error)
|
||||
}
|
||||
|
||||
// DatabaseDeleter wraps the Delete method of a backing data store.
|
||||
type DatabaseDeleter interface {
|
||||
Delete(key []byte) error
|
||||
}
|
||||
|
||||
var (
|
||||
headHeaderKey = []byte("LastHeader")
|
||||
headBlockKey = []byte("LastBlock")
|
||||
headFastKey = []byte("LastFast")
|
||||
trieSyncKey = []byte("TrieSync")
|
||||
|
||||
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`).
|
||||
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
||||
tdSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + tdSuffix -> td
|
||||
numSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + numSuffix -> hash
|
||||
blockHashPrefix = []byte("H") // blockHashPrefix + hash -> num (uint64 big endian)
|
||||
bodyPrefix = []byte("b") // bodyPrefix + num (uint64 big endian) + hash -> block body
|
||||
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
|
||||
lookupPrefix = []byte("l") // lookupPrefix + hash -> transaction/receipt lookup metadata
|
||||
bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
|
||||
|
||||
preimagePrefix = "secure-key-" // preimagePrefix + hash -> preimage
|
||||
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
||||
|
||||
// Chain index prefixes (use `i` + single byte to avoid mixing data types).
|
||||
BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
|
||||
|
||||
// used by old db, now only used for conversion
|
||||
oldReceiptsPrefix = []byte("receipts-")
|
||||
oldTxMetaSuffix = []byte{0x01}
|
||||
|
||||
ErrChainConfigNotFound = errors.New("ChainConfig not found") // general config not found error
|
||||
|
||||
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
|
||||
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
|
||||
)
|
||||
|
||||
// TxLookupEntry is a positional metadata to help looking up the data content of
|
||||
// a transaction or receipt given only its hash.
|
||||
type TxLookupEntry struct {
|
||||
BlockHash common.Hash
|
||||
BlockIndex uint64
|
||||
Index uint64
|
||||
}
|
||||
|
||||
// encodeBlockNumber encodes a block number as big endian uint64
|
||||
func encodeBlockNumber(number uint64) []byte {
|
||||
enc := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(enc, number)
|
||||
return enc
|
||||
}
|
||||
|
||||
// GetCanonicalHash retrieves a hash assigned to a canonical block number.
|
||||
func GetCanonicalHash(db DatabaseReader, number uint64) common.Hash {
|
||||
data, _ := db.Get(append(append(headerPrefix, encodeBlockNumber(number)...), numSuffix...))
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// missingNumber is returned by GetBlockNumber if no header with the
|
||||
// given block hash has been stored in the database
|
||||
const missingNumber = uint64(0xffffffffffffffff)
|
||||
|
||||
// GetBlockNumber returns the block number assigned to a block hash
|
||||
// if the corresponding header is present in the database
|
||||
func GetBlockNumber(db DatabaseReader, hash common.Hash) uint64 {
|
||||
data, _ := db.Get(append(blockHashPrefix, hash.Bytes()...))
|
||||
if len(data) != 8 {
|
||||
return missingNumber
|
||||
}
|
||||
return binary.BigEndian.Uint64(data)
|
||||
}
|
||||
|
||||
// GetHeadHeaderHash retrieves the hash of the current canonical head block's
|
||||
// header. The difference between this and GetHeadBlockHash is that whereas the
|
||||
// last block hash is only updated upon a full block import, the last header
|
||||
// hash is updated already at header import, allowing head tracking for the
|
||||
// light synchronization mechanism.
|
||||
func GetHeadHeaderHash(db DatabaseReader) common.Hash {
|
||||
data, _ := db.Get(headHeaderKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// GetHeadBlockHash retrieves the hash of the current canonical head block.
|
||||
func GetHeadBlockHash(db DatabaseReader) common.Hash {
|
||||
data, _ := db.Get(headBlockKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// GetHeadFastBlockHash retrieves the hash of the current canonical head block during
|
||||
// fast synchronization. The difference between this and GetHeadBlockHash is that
|
||||
// whereas the last block hash is only updated upon a full block import, the last
|
||||
// fast hash is updated when importing pre-processed blocks.
|
||||
func GetHeadFastBlockHash(db DatabaseReader) common.Hash {
|
||||
data, _ := db.Get(headFastKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// GetTrieSyncProgress retrieves the number of tries nodes fast synced to allow
|
||||
// reportinc correct numbers across restarts.
|
||||
func GetTrieSyncProgress(db DatabaseReader) uint64 {
|
||||
data, _ := db.Get(trieSyncKey)
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
}
|
||||
return new(big.Int).SetBytes(data).Uint64()
|
||||
}
|
||||
|
||||
// GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil
|
||||
// if the header's not found.
|
||||
func GetHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
data, _ := db.Get(headerKey(hash, number))
|
||||
return data
|
||||
}
|
||||
|
||||
// GetHeader retrieves the block header corresponding to the hash, nil if none
|
||||
// found.
|
||||
func GetHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Header {
|
||||
data := GetHeaderRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
header := new(types.Header)
|
||||
if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
|
||||
log.Error("Invalid block header RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
|
||||
func GetBodyRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
data, _ := db.Get(blockBodyKey(hash, number))
|
||||
return data
|
||||
}
|
||||
|
||||
func headerKey(hash common.Hash, number uint64) []byte {
|
||||
return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
}
|
||||
|
||||
func blockBodyKey(hash common.Hash, number uint64) []byte {
|
||||
return append(append(bodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
}
|
||||
|
||||
// GetBody retrieves the block body (transactons, uncles) corresponding to the
|
||||
// hash, nil if none found.
|
||||
func GetBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body {
|
||||
data := GetBodyRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
body := new(types.Body)
|
||||
if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
|
||||
log.Error("Invalid block body RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
// GetTd retrieves a block's total difficulty corresponding to the hash, nil if
|
||||
// none found.
|
||||
func GetTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int {
|
||||
data, _ := db.Get(append(append(append(headerPrefix, encodeBlockNumber(number)...), hash[:]...), tdSuffix...))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
td := new(big.Int)
|
||||
if err := rlp.Decode(bytes.NewReader(data), td); err != nil {
|
||||
log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return td
|
||||
}
|
||||
|
||||
// GetBlock retrieves an entire block corresponding to the hash, assembling it
|
||||
// back from the stored header and body. If either the header or body could not
|
||||
// be retrieved nil is returned.
|
||||
//
|
||||
// Note, due to concurrent download of header and block body the header and thus
|
||||
// canonical hash can be stored in the database but the body data not (yet).
|
||||
func GetBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block {
|
||||
// Retrieve the block header and body contents
|
||||
header := GetHeader(db, hash, number)
|
||||
if header == nil {
|
||||
return nil
|
||||
}
|
||||
body := GetBody(db, hash, number)
|
||||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
// Reassemble the block and return
|
||||
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
|
||||
}
|
||||
|
||||
// GetBlockReceipts retrieves the receipts generated by the transactions included
|
||||
// in a block given by its hash.
|
||||
func GetBlockReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Receipts {
|
||||
data, _ := db.Get(append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash[:]...))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
storageReceipts := []*types.ReceiptForStorage{}
|
||||
if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
|
||||
log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
receipts := make(types.Receipts, len(storageReceipts))
|
||||
for i, receipt := range storageReceipts {
|
||||
receipts[i] = (*types.Receipt)(receipt)
|
||||
}
|
||||
return receipts
|
||||
}
|
||||
|
||||
// GetTxLookupEntry retrieves the positional metadata associated with a transaction
|
||||
// hash to allow retrieving the transaction or receipt by hash.
|
||||
func GetTxLookupEntry(db DatabaseReader, hash common.Hash) (common.Hash, uint64, uint64) {
|
||||
// Load the positional metadata from disk and bail if it fails
|
||||
data, _ := db.Get(append(lookupPrefix, hash.Bytes()...))
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}, 0, 0
|
||||
}
|
||||
// Parse and return the contents of the lookup entry
|
||||
var entry TxLookupEntry
|
||||
if err := rlp.DecodeBytes(data, &entry); err != nil {
|
||||
log.Error("Invalid lookup entry RLP", "hash", hash, "err", err)
|
||||
return common.Hash{}, 0, 0
|
||||
}
|
||||
return entry.BlockHash, entry.BlockIndex, entry.Index
|
||||
}
|
||||
|
||||
// GetTransaction retrieves a specific transaction from the database, along with
|
||||
// its added positional metadata.
|
||||
func GetTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
||||
// Retrieve the lookup metadata and resolve the transaction from the body
|
||||
blockHash, blockNumber, txIndex := GetTxLookupEntry(db, hash)
|
||||
|
||||
if blockHash != (common.Hash{}) {
|
||||
body := GetBody(db, blockHash, blockNumber)
|
||||
if body == nil || len(body.Transactions) <= int(txIndex) {
|
||||
log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash, "index", txIndex)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
return body.Transactions[txIndex], blockHash, blockNumber, txIndex
|
||||
}
|
||||
// Old transaction representation, load the transaction and it's metadata separately
|
||||
data, _ := db.Get(hash.Bytes())
|
||||
if len(data) == 0 {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
var tx types.Transaction
|
||||
if err := rlp.DecodeBytes(data, &tx); err != nil {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
// Retrieve the blockchain positional metadata
|
||||
data, _ = db.Get(append(hash.Bytes(), oldTxMetaSuffix...))
|
||||
if len(data) == 0 {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
var entry TxLookupEntry
|
||||
if err := rlp.DecodeBytes(data, &entry); err != nil {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
return &tx, entry.BlockHash, entry.BlockIndex, entry.Index
|
||||
}
|
||||
|
||||
// GetReceipt retrieves a specific transaction receipt from the database, along with
|
||||
// its added positional metadata.
|
||||
func GetReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
|
||||
// Retrieve the lookup metadata and resolve the receipt from the receipts
|
||||
blockHash, blockNumber, receiptIndex := GetTxLookupEntry(db, hash)
|
||||
|
||||
if blockHash != (common.Hash{}) {
|
||||
receipts := GetBlockReceipts(db, blockHash, blockNumber)
|
||||
if len(receipts) <= int(receiptIndex) {
|
||||
log.Error("Receipt refereced missing", "number", blockNumber, "hash", blockHash, "index", receiptIndex)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
return receipts[receiptIndex], blockHash, blockNumber, receiptIndex
|
||||
}
|
||||
// Old receipt representation, load the receipt and set an unknown metadata
|
||||
data, _ := db.Get(append(oldReceiptsPrefix, hash[:]...))
|
||||
if len(data) == 0 {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
var receipt types.ReceiptForStorage
|
||||
err := rlp.DecodeBytes(data, &receipt)
|
||||
if err != nil {
|
||||
log.Error("Invalid receipt RLP", "hash", hash, "err", err)
|
||||
}
|
||||
return (*types.Receipt)(&receipt), common.Hash{}, 0, 0
|
||||
}
|
||||
|
||||
// GetBloomBits retrieves the compressed bloom bit vector belonging to the given
|
||||
// section and bit index from the.
|
||||
func GetBloomBits(db DatabaseReader, bit uint, section uint64, head common.Hash) ([]byte, error) {
|
||||
key := append(append(bloomBitsPrefix, make([]byte, 10)...), head.Bytes()...)
|
||||
|
||||
binary.BigEndian.PutUint16(key[1:], uint16(bit))
|
||||
binary.BigEndian.PutUint64(key[3:], section)
|
||||
|
||||
return db.Get(key)
|
||||
}
|
||||
|
||||
// WriteCanonicalHash stores the canonical hash for the given block number.
|
||||
func WriteCanonicalHash(db ethdb.Putter, hash common.Hash, number uint64) error {
|
||||
key := append(append(headerPrefix, encodeBlockNumber(number)...), numSuffix...)
|
||||
if err := db.Put(key, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store number to hash mapping", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteHeadHeaderHash stores the head header's hash.
|
||||
func WriteHeadHeaderHash(db ethdb.Putter, hash common.Hash) error {
|
||||
if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last header's hash", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteHeadBlockHash stores the head block's hash.
|
||||
func WriteHeadBlockHash(db ethdb.Putter, hash common.Hash) error {
|
||||
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last block's hash", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteHeadFastBlockHash stores the fast head block's hash.
|
||||
func WriteHeadFastBlockHash(db ethdb.Putter, hash common.Hash) error {
|
||||
if err := db.Put(headFastKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last fast block's hash", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteTrieSyncProgress stores the fast sync trie process counter to support
|
||||
// retrieving it across restarts.
|
||||
func WriteTrieSyncProgress(db ethdb.Putter, count uint64) error {
|
||||
if err := db.Put(trieSyncKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
|
||||
log.Crit("Failed to store fast sync trie progress", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteHeader serializes a block header into the database.
|
||||
func WriteHeader(db ethdb.Putter, header *types.Header) error {
|
||||
data, err := rlp.EncodeToBytes(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hash := header.Hash().Bytes()
|
||||
num := header.Number.Uint64()
|
||||
encNum := encodeBlockNumber(num)
|
||||
key := append(blockHashPrefix, hash...)
|
||||
if err := db.Put(key, encNum); err != nil {
|
||||
log.Crit("Failed to store hash to number mapping", "err", err)
|
||||
}
|
||||
key = append(append(headerPrefix, encNum...), hash...)
|
||||
if err := db.Put(key, data); err != nil {
|
||||
log.Crit("Failed to store header", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteBody serializes the body of a block into the database.
|
||||
func WriteBody(db ethdb.Putter, hash common.Hash, number uint64, body *types.Body) error {
|
||||
data, err := rlp.EncodeToBytes(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return WriteBodyRLP(db, hash, number, data)
|
||||
}
|
||||
|
||||
// WriteBodyRLP writes a serialized body of a block into the database.
|
||||
func WriteBodyRLP(db ethdb.Putter, hash common.Hash, number uint64, rlp rlp.RawValue) error {
|
||||
key := append(append(bodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
if err := db.Put(key, rlp); err != nil {
|
||||
log.Crit("Failed to store block body", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteTd serializes the total difficulty of a block into the database.
|
||||
func WriteTd(db ethdb.Putter, hash common.Hash, number uint64, td *big.Int) error {
|
||||
data, err := rlp.EncodeToBytes(td)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...), tdSuffix...)
|
||||
if err := db.Put(key, data); err != nil {
|
||||
log.Crit("Failed to store block total difficulty", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteBlock serializes a block into the database, header and body separately.
|
||||
func WriteBlock(db ethdb.Putter, block *types.Block) error {
|
||||
// Store the body first to retain database consistency
|
||||
if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
|
||||
return err
|
||||
}
|
||||
// Store the header too, signaling full block ownership
|
||||
if err := WriteHeader(db, block.Header()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteBlockReceipts stores all the transaction receipts belonging to a block
|
||||
// as a single receipt slice. This is used during chain reorganisations for
|
||||
// rescheduling dropped transactions.
|
||||
func WriteBlockReceipts(db ethdb.Putter, hash common.Hash, number uint64, receipts types.Receipts) error {
|
||||
// Convert the receipts into their storage form and serialize them
|
||||
storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
|
||||
for i, receipt := range receipts {
|
||||
storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
|
||||
}
|
||||
bytes, err := rlp.EncodeToBytes(storageReceipts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Store the flattened receipt slice
|
||||
key := append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
if err := db.Put(key, bytes); err != nil {
|
||||
log.Crit("Failed to store block receipts", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteTxLookupEntries stores a positional metadata for every transaction from
|
||||
// a block, enabling hash based transaction and receipt lookups.
|
||||
func WriteTxLookupEntries(db ethdb.Putter, block *types.Block) error {
|
||||
// Iterate over each transaction and encode its metadata
|
||||
for i, tx := range block.Transactions() {
|
||||
entry := TxLookupEntry{
|
||||
BlockHash: block.Hash(),
|
||||
BlockIndex: block.NumberU64(),
|
||||
Index: uint64(i),
|
||||
}
|
||||
data, err := rlp.EncodeToBytes(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Put(append(lookupPrefix, tx.Hash().Bytes()...), data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteBloomBits writes the compressed bloom bits vector belonging to the given
|
||||
// section and bit index.
|
||||
func WriteBloomBits(db ethdb.Putter, bit uint, section uint64, head common.Hash, bits []byte) {
|
||||
key := append(append(bloomBitsPrefix, make([]byte, 10)...), head.Bytes()...)
|
||||
|
||||
binary.BigEndian.PutUint16(key[1:], uint16(bit))
|
||||
binary.BigEndian.PutUint64(key[3:], section)
|
||||
|
||||
if err := db.Put(key, bits); err != nil {
|
||||
log.Crit("Failed to store bloom bits", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCanonicalHash removes the number to hash canonical mapping.
|
||||
func DeleteCanonicalHash(db DatabaseDeleter, number uint64) {
|
||||
db.Delete(append(append(headerPrefix, encodeBlockNumber(number)...), numSuffix...))
|
||||
}
|
||||
|
||||
// DeleteHeader removes all block header data associated with a hash.
|
||||
func DeleteHeader(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
db.Delete(append(blockHashPrefix, hash.Bytes()...))
|
||||
db.Delete(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...))
|
||||
}
|
||||
|
||||
// DeleteBody removes all block body data associated with a hash.
|
||||
func DeleteBody(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
db.Delete(append(append(bodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...))
|
||||
}
|
||||
|
||||
// DeleteTd removes all block total difficulty data associated with a hash.
|
||||
func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
db.Delete(append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...), tdSuffix...))
|
||||
}
|
||||
|
||||
// DeleteBlock removes all block data associated with a hash.
|
||||
func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
DeleteBlockReceipts(db, hash, number)
|
||||
DeleteHeader(db, hash, number)
|
||||
DeleteBody(db, hash, number)
|
||||
DeleteTd(db, hash, number)
|
||||
}
|
||||
|
||||
// DeleteBlockReceipts removes all receipt data associated with a block hash.
|
||||
func DeleteBlockReceipts(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
db.Delete(append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...))
|
||||
}
|
||||
|
||||
// DeleteTxLookupEntry removes all transaction data associated with a hash.
|
||||
func DeleteTxLookupEntry(db DatabaseDeleter, hash common.Hash) {
|
||||
db.Delete(append(lookupPrefix, hash.Bytes()...))
|
||||
}
|
||||
|
||||
// PreimageTable returns a Database instance with the key prefix for preimage entries.
|
||||
func PreimageTable(db ethdb.Database) ethdb.Database {
|
||||
return ethdb.NewTable(db, preimagePrefix)
|
||||
}
|
||||
|
||||
// WritePreimages writes the provided set of preimages to the database. `number` is the
|
||||
// current block number, and is used for debug messages only.
|
||||
func WritePreimages(db ethdb.Database, number uint64, preimages map[common.Hash][]byte) error {
|
||||
table := PreimageTable(db)
|
||||
batch := table.NewBatch()
|
||||
hitCount := 0
|
||||
for hash, preimage := range preimages {
|
||||
if _, err := table.Get(hash.Bytes()); err != nil {
|
||||
batch.Put(hash.Bytes(), preimage)
|
||||
hitCount++
|
||||
}
|
||||
}
|
||||
preimageCounter.Inc(int64(len(preimages)))
|
||||
preimageHitCounter.Inc(int64(hitCount))
|
||||
if hitCount > 0 {
|
||||
if err := batch.Write(); err != nil {
|
||||
return fmt.Errorf("preimage write fail for block %d: %v", number, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBlockChainVersion reads the version number from db.
|
||||
func GetBlockChainVersion(db DatabaseReader) int {
|
||||
var vsn uint
|
||||
enc, _ := db.Get([]byte("BlockchainVersion"))
|
||||
rlp.DecodeBytes(enc, &vsn)
|
||||
return int(vsn)
|
||||
}
|
||||
|
||||
// WriteBlockChainVersion writes vsn as the version number to db.
|
||||
func WriteBlockChainVersion(db ethdb.Putter, vsn int) {
|
||||
enc, _ := rlp.EncodeToBytes(uint(vsn))
|
||||
db.Put([]byte("BlockchainVersion"), enc)
|
||||
}
|
||||
|
||||
// WriteChainConfig writes the chain config settings to the database.
|
||||
func WriteChainConfig(db ethdb.Putter, hash common.Hash, cfg *params.ChainConfig) error {
|
||||
// short circuit and ignore if nil config. GetChainConfig
|
||||
// will return a default.
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
jsonChainConfig, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.Put(append(configPrefix, hash[:]...), jsonChainConfig)
|
||||
}
|
||||
|
||||
// GetChainConfig will fetch the network settings based on the given hash.
|
||||
func GetChainConfig(db DatabaseReader, hash common.Hash) (*params.ChainConfig, error) {
|
||||
jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...))
|
||||
if len(jsonChainConfig) == 0 {
|
||||
return nil, ErrChainConfigNotFound
|
||||
}
|
||||
|
||||
var config params.ChainConfig
|
||||
if err := json.Unmarshal(jsonChainConfig, &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// FindCommonAncestor returns the last common ancestor of two block headers
|
||||
func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header {
|
||||
for bn := b.Number.Uint64(); a.Number.Uint64() > bn; {
|
||||
a = GetHeader(db, a.ParentHash, a.Number.Uint64()-1)
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for an := a.Number.Uint64(); an < b.Number.Uint64(); {
|
||||
b = GetHeader(db, b.ParentHash, b.Number.Uint64()-1)
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for a.Hash() != b.Hash() {
|
||||
a = GetHeader(db, a.ParentHash, a.Number.Uint64()-1)
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
b = GetHeader(db, b.ParentHash, b.Number.Uint64()-1)
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// TxPreEvent is posted when a transaction enters the transaction pool.
|
||||
type TxPreEvent struct{ Tx *types.Transaction }
|
||||
// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
|
||||
type NewTxsEvent struct{ Txs []*types.Transaction }
|
||||
|
||||
// PendingLogsEvent is posted pre mining and notifies of pending logs.
|
||||
type PendingLogsEvent struct {
|
||||
@@ -35,9 +35,6 @@ type PendingStateEvent struct{}
|
||||
// NewMinedBlockEvent is posted when a block has been imported.
|
||||
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
|
||||
type RemovedLogsEvent struct{ Logs []*types.Log }
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
@@ -155,7 +156,7 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
|
||||
}
|
||||
|
||||
// Just commit the new block if there is no stored genesis block.
|
||||
stored := GetCanonicalHash(db, 0)
|
||||
stored := rawdb.ReadCanonicalHash(db, 0)
|
||||
if (stored == common.Hash{}) {
|
||||
if genesis == nil {
|
||||
log.Info("Writing default main-net genesis block")
|
||||
@@ -177,14 +178,11 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
|
||||
|
||||
// Get the existing chain configuration.
|
||||
newcfg := genesis.configOrDefault(stored)
|
||||
storedcfg, err := GetChainConfig(db, stored)
|
||||
if err != nil {
|
||||
if err == ErrChainConfigNotFound {
|
||||
// This case happens if a genesis write was interrupted.
|
||||
log.Warn("Found genesis block without chain config")
|
||||
err = WriteChainConfig(db, stored, newcfg)
|
||||
}
|
||||
return newcfg, stored, err
|
||||
storedcfg := rawdb.ReadChainConfig(db, stored)
|
||||
if storedcfg == nil {
|
||||
log.Warn("Found genesis block without chain config")
|
||||
rawdb.WriteChainConfig(db, stored, newcfg)
|
||||
return newcfg, stored, nil
|
||||
}
|
||||
// Special case: don't change the existing config of a non-mainnet chain if no new
|
||||
// config is supplied. These chains would get AllProtocolChanges (and a compat error)
|
||||
@@ -195,15 +193,16 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
|
||||
|
||||
// Check config compatibility and write the config. Compatibility errors
|
||||
// are returned to the caller unless we're already at block zero.
|
||||
height := GetBlockNumber(db, GetHeadHeaderHash(db))
|
||||
if height == missingNumber {
|
||||
height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db))
|
||||
if height == nil {
|
||||
return newcfg, stored, fmt.Errorf("missing block number for head header hash")
|
||||
}
|
||||
compatErr := storedcfg.CheckCompatible(newcfg, height)
|
||||
if compatErr != nil && height != 0 && compatErr.RewindTo != 0 {
|
||||
compatErr := storedcfg.CheckCompatible(newcfg, *height)
|
||||
if compatErr != nil && *height != 0 && compatErr.RewindTo != 0 {
|
||||
return newcfg, stored, compatErr
|
||||
}
|
||||
return newcfg, stored, WriteChainConfig(db, stored, newcfg)
|
||||
rawdb.WriteChainConfig(db, stored, newcfg)
|
||||
return newcfg, stored, nil
|
||||
}
|
||||
|
||||
func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||
@@ -223,7 +222,7 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||
// to the given database (or discards it if nil).
|
||||
func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||
if db == nil {
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
db = ethdb.NewMemDatabase()
|
||||
}
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
for addr, account := range g.Alloc {
|
||||
@@ -267,29 +266,19 @@ func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
|
||||
if block.Number().Sign() != 0 {
|
||||
return nil, fmt.Errorf("can't commit genesis block with number > 0")
|
||||
}
|
||||
if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteBlock(db, block); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteBlockReceipts(db, block.Hash(), block.NumberU64(), nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteHeadBlockHash(db, block.Hash()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteHeadHeaderHash(db, block.Hash()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty)
|
||||
rawdb.WriteBlock(db, block)
|
||||
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
|
||||
rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
|
||||
rawdb.WriteHeadBlockHash(db, block.Hash())
|
||||
rawdb.WriteHeadHeaderHash(db, block.Hash())
|
||||
|
||||
config := g.Config
|
||||
if config == nil {
|
||||
config = params.AllEthashProtocolChanges
|
||||
}
|
||||
return block, WriteChainConfig(db, block.Hash(), config)
|
||||
rawdb.WriteChainConfig(db, block.Hash(), config)
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// MustCommit writes the genesis block and state to db, panicking on error.
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
@@ -140,7 +141,7 @@ func TestSetupGenesis(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
config, hash, err := test.fn(db)
|
||||
// Check the return values.
|
||||
if !reflect.DeepEqual(err, test.wantErr) {
|
||||
@@ -154,7 +155,7 @@ func TestSetupGenesis(t *testing.T) {
|
||||
t.Errorf("%s: returned hash %s, want %s", test.name, hash.Hex(), test.wantHash.Hex())
|
||||
} else if err == nil {
|
||||
// Check database content.
|
||||
stored := GetBlock(db, test.wantHash, 0)
|
||||
stored := rawdb.ReadBlock(db, test.wantHash, 0)
|
||||
if stored.Hash() != test.wantHash {
|
||||
t.Errorf("%s: block in DB has hash %s, want %s", test.name, stored.Hash(), test.wantHash)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
@@ -97,7 +98,7 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
|
||||
}
|
||||
|
||||
hc.currentHeader.Store(hc.genesisHeader)
|
||||
if head := GetHeadBlockHash(chainDb); head != (common.Hash{}) {
|
||||
if head := rawdb.ReadHeadBlockHash(chainDb); head != (common.Hash{}) {
|
||||
if chead := hc.GetHeaderByHash(head); chead != nil {
|
||||
hc.currentHeader.Store(chead)
|
||||
}
|
||||
@@ -109,13 +110,14 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
|
||||
|
||||
// GetBlockNumber retrieves the block number belonging to the given hash
|
||||
// from the cache or database
|
||||
func (hc *HeaderChain) GetBlockNumber(hash common.Hash) uint64 {
|
||||
func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 {
|
||||
if cached, ok := hc.numberCache.Get(hash); ok {
|
||||
return cached.(uint64)
|
||||
number := cached.(uint64)
|
||||
return &number
|
||||
}
|
||||
number := GetBlockNumber(hc.chainDb, hash)
|
||||
if number != missingNumber {
|
||||
hc.numberCache.Add(hash, number)
|
||||
number := rawdb.ReadHeaderNumber(hc.chainDb, hash)
|
||||
if number != nil {
|
||||
hc.numberCache.Add(hash, *number)
|
||||
}
|
||||
return number
|
||||
}
|
||||
@@ -147,20 +149,19 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||
if err := hc.WriteTd(hash, number, externTd); err != nil {
|
||||
log.Crit("Failed to write header total difficulty", "err", err)
|
||||
}
|
||||
if err := WriteHeader(hc.chainDb, header); err != nil {
|
||||
log.Crit("Failed to write header content", "err", err)
|
||||
}
|
||||
rawdb.WriteHeader(hc.chainDb, header)
|
||||
|
||||
// If the total difficulty is higher than our known, add it to the canonical chain
|
||||
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
||||
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
||||
if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) {
|
||||
// Delete any canonical number assignments above the new head
|
||||
for i := number + 1; ; i++ {
|
||||
hash := GetCanonicalHash(hc.chainDb, i)
|
||||
hash := rawdb.ReadCanonicalHash(hc.chainDb, i)
|
||||
if hash == (common.Hash{}) {
|
||||
break
|
||||
}
|
||||
DeleteCanonicalHash(hc.chainDb, i)
|
||||
rawdb.DeleteCanonicalHash(hc.chainDb, i)
|
||||
}
|
||||
// Overwrite any stale canonical number assignments
|
||||
var (
|
||||
@@ -168,20 +169,17 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||
headNumber = header.Number.Uint64() - 1
|
||||
headHeader = hc.GetHeader(headHash, headNumber)
|
||||
)
|
||||
for GetCanonicalHash(hc.chainDb, headNumber) != headHash {
|
||||
WriteCanonicalHash(hc.chainDb, headHash, headNumber)
|
||||
for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash {
|
||||
rawdb.WriteCanonicalHash(hc.chainDb, headHash, headNumber)
|
||||
|
||||
headHash = headHeader.ParentHash
|
||||
headNumber = headHeader.Number.Uint64() - 1
|
||||
headHeader = hc.GetHeader(headHash, headNumber)
|
||||
}
|
||||
// Extend the canonical chain with the new header
|
||||
if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil {
|
||||
log.Crit("Failed to insert header number", "err", err)
|
||||
}
|
||||
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
|
||||
log.Crit("Failed to insert head header hash", "err", err)
|
||||
}
|
||||
rawdb.WriteCanonicalHash(hc.chainDb, hash, number)
|
||||
rawdb.WriteHeadHeaderHash(hc.chainDb, hash)
|
||||
|
||||
hc.currentHeaderHash = hash
|
||||
hc.currentHeader.Store(types.CopyHeader(header))
|
||||
|
||||
@@ -316,7 +314,7 @@ func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||
if cached, ok := hc.tdCache.Get(hash); ok {
|
||||
return cached.(*big.Int)
|
||||
}
|
||||
td := GetTd(hc.chainDb, hash, number)
|
||||
td := rawdb.ReadTd(hc.chainDb, hash, number)
|
||||
if td == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -328,15 +326,17 @@ func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||
// GetTdByHash retrieves a block's total difficulty in the canonical chain from the
|
||||
// database by hash, caching it if found.
|
||||
func (hc *HeaderChain) GetTdByHash(hash common.Hash) *big.Int {
|
||||
return hc.GetTd(hash, hc.GetBlockNumber(hash))
|
||||
number := hc.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
return hc.GetTd(hash, *number)
|
||||
}
|
||||
|
||||
// WriteTd stores a block's total difficulty into the database, also caching it
|
||||
// along the way.
|
||||
func (hc *HeaderChain) WriteTd(hash common.Hash, number uint64, td *big.Int) error {
|
||||
if err := WriteTd(hc.chainDb, hash, number, td); err != nil {
|
||||
return err
|
||||
}
|
||||
rawdb.WriteTd(hc.chainDb, hash, number, td)
|
||||
hc.tdCache.Add(hash, new(big.Int).Set(td))
|
||||
return nil
|
||||
}
|
||||
@@ -348,7 +348,7 @@ func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header
|
||||
if header, ok := hc.headerCache.Get(hash); ok {
|
||||
return header.(*types.Header)
|
||||
}
|
||||
header := GetHeader(hc.chainDb, hash, number)
|
||||
header := rawdb.ReadHeader(hc.chainDb, hash, number)
|
||||
if header == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -360,7 +360,11 @@ func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header
|
||||
// GetHeaderByHash retrieves a block header from the database by hash, caching it if
|
||||
// found.
|
||||
func (hc *HeaderChain) GetHeaderByHash(hash common.Hash) *types.Header {
|
||||
return hc.GetHeader(hash, hc.GetBlockNumber(hash))
|
||||
number := hc.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
return hc.GetHeader(hash, *number)
|
||||
}
|
||||
|
||||
// HasHeader checks if a block header is present in the database or not.
|
||||
@@ -368,14 +372,13 @@ func (hc *HeaderChain) HasHeader(hash common.Hash, number uint64) bool {
|
||||
if hc.numberCache.Contains(hash) || hc.headerCache.Contains(hash) {
|
||||
return true
|
||||
}
|
||||
ok, _ := hc.chainDb.Has(headerKey(hash, number))
|
||||
return ok
|
||||
return rawdb.HasHeader(hc.chainDb, hash, number)
|
||||
}
|
||||
|
||||
// GetHeaderByNumber retrieves a block header from the database by number,
|
||||
// caching it (associated with its hash) if found.
|
||||
func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||
hash := GetCanonicalHash(hc.chainDb, number)
|
||||
hash := rawdb.ReadCanonicalHash(hc.chainDb, number)
|
||||
if hash == (common.Hash{}) {
|
||||
return nil
|
||||
}
|
||||
@@ -390,9 +393,8 @@ func (hc *HeaderChain) CurrentHeader() *types.Header {
|
||||
|
||||
// SetCurrentHeader sets the current head header of the canonical chain.
|
||||
func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
|
||||
if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil {
|
||||
log.Crit("Failed to insert head header hash", "err", err)
|
||||
}
|
||||
rawdb.WriteHeadHeaderHash(hc.chainDb, head.Hash())
|
||||
|
||||
hc.currentHeader.Store(head)
|
||||
hc.currentHeaderHash = head.Hash()
|
||||
}
|
||||
@@ -416,13 +418,14 @@ func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
|
||||
if delFn != nil {
|
||||
delFn(hash, num)
|
||||
}
|
||||
DeleteHeader(hc.chainDb, hash, num)
|
||||
DeleteTd(hc.chainDb, hash, num)
|
||||
rawdb.DeleteHeader(hc.chainDb, hash, num)
|
||||
rawdb.DeleteTd(hc.chainDb, hash, num)
|
||||
|
||||
hc.currentHeader.Store(hc.GetHeader(hdr.ParentHash, hdr.Number.Uint64()-1))
|
||||
}
|
||||
// Roll back the canonical chain numbering
|
||||
for i := height; i > head; i-- {
|
||||
DeleteCanonicalHash(hc.chainDb, i)
|
||||
rawdb.DeleteCanonicalHash(hc.chainDb, i)
|
||||
}
|
||||
// Clear out any stale content from the caches
|
||||
hc.headerCache.Purge()
|
||||
@@ -434,9 +437,7 @@ func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
|
||||
}
|
||||
hc.currentHeaderHash = hc.CurrentHeader().Hash()
|
||||
|
||||
if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil {
|
||||
log.Crit("Failed to reset head header hash", "err", err)
|
||||
}
|
||||
rawdb.WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash)
|
||||
}
|
||||
|
||||
// SetGenesis sets a new genesis block header for the chain
|
||||
|
||||
@@ -18,7 +18,6 @@ package core
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
@@ -77,18 +76,11 @@ func (tm *TestManager) Db() ethdb.Database {
|
||||
}
|
||||
|
||||
func NewTestManager() *TestManager {
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
fmt.Println("Could not create mem-db, failing")
|
||||
return nil
|
||||
}
|
||||
|
||||
testManager := &TestManager{}
|
||||
testManager.eventMux = new(event.TypeMux)
|
||||
testManager.db = db
|
||||
testManager.db = ethdb.NewMemDatabase()
|
||||
// testManager.txPool = NewTxPool(testManager)
|
||||
// testManager.blockChain = NewBlockChain(testManager)
|
||||
// testManager.stateManager = NewStateManager(testManager)
|
||||
|
||||
return testManager
|
||||
}
|
||||
|
||||
381
core/rawdb/accessors_chain.go
Normal file
381
core/rawdb/accessors_chain.go
Normal file
@@ -0,0 +1,381 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
|
||||
func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash {
|
||||
data, _ := db.Get(append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...))
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// WriteCanonicalHash stores the hash assigned to a canonical block number.
|
||||
func WriteCanonicalHash(db DatabaseWriter, hash common.Hash, number uint64) {
|
||||
key := append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)
|
||||
if err := db.Put(key, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store number to hash mapping", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCanonicalHash removes the number to hash canonical mapping.
|
||||
func DeleteCanonicalHash(db DatabaseDeleter, number uint64) {
|
||||
if err := db.Delete(append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)); err != nil {
|
||||
log.Crit("Failed to delete number to hash mapping", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadHeaderNumber returns the header number assigned to a hash.
|
||||
func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 {
|
||||
data, _ := db.Get(append(headerNumberPrefix, hash.Bytes()...))
|
||||
if len(data) != 8 {
|
||||
return nil
|
||||
}
|
||||
number := binary.BigEndian.Uint64(data)
|
||||
return &number
|
||||
}
|
||||
|
||||
// ReadHeadHeaderHash retrieves the hash of the current canonical head header.
|
||||
func ReadHeadHeaderHash(db DatabaseReader) common.Hash {
|
||||
data, _ := db.Get(headHeaderKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// WriteHeadHeaderHash stores the hash of the current canonical head header.
|
||||
func WriteHeadHeaderHash(db DatabaseWriter, hash common.Hash) {
|
||||
if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last header's hash", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadHeadBlockHash retrieves the hash of the current canonical head block.
|
||||
func ReadHeadBlockHash(db DatabaseReader) common.Hash {
|
||||
data, _ := db.Get(headBlockKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// WriteHeadBlockHash stores the head block's hash.
|
||||
func WriteHeadBlockHash(db DatabaseWriter, hash common.Hash) {
|
||||
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last block's hash", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block.
|
||||
func ReadHeadFastBlockHash(db DatabaseReader) common.Hash {
|
||||
data, _ := db.Get(headFastBlockKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// WriteHeadFastBlockHash stores the hash of the current fast-sync head block.
|
||||
func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) {
|
||||
if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last fast block's hash", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow
|
||||
// reporting correct numbers across restarts.
|
||||
func ReadFastTrieProgress(db DatabaseReader) uint64 {
|
||||
data, _ := db.Get(fastTrieProgressKey)
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
}
|
||||
return new(big.Int).SetBytes(data).Uint64()
|
||||
}
|
||||
|
||||
// WriteFastTrieProgress stores the fast sync trie process counter to support
|
||||
// retrieving it across restarts.
|
||||
func WriteFastTrieProgress(db DatabaseWriter, count uint64) {
|
||||
if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
|
||||
log.Crit("Failed to store fast sync trie progress", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
||||
func ReadHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
data, _ := db.Get(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...))
|
||||
return data
|
||||
}
|
||||
|
||||
// HasHeader verifies the existence of a block header corresponding to the hash.
|
||||
func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool {
|
||||
key := append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...))
|
||||
if has, err := db.Has(key); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadHeader retrieves the block header corresponding to the hash.
|
||||
func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Header {
|
||||
data := ReadHeaderRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
header := new(types.Header)
|
||||
if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
|
||||
log.Error("Invalid block header RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
// WriteHeader stores a block header into the database and also stores the hash-
|
||||
// to-number mapping.
|
||||
func WriteHeader(db DatabaseWriter, header *types.Header) {
|
||||
// Write the hash -> number mapping
|
||||
var (
|
||||
hash = header.Hash().Bytes()
|
||||
number = header.Number.Uint64()
|
||||
encoded = encodeBlockNumber(number)
|
||||
)
|
||||
key := append(headerNumberPrefix, hash...)
|
||||
if err := db.Put(key, encoded); err != nil {
|
||||
log.Crit("Failed to store hash to number mapping", "err", err)
|
||||
}
|
||||
// Write the encoded header
|
||||
data, err := rlp.EncodeToBytes(header)
|
||||
if err != nil {
|
||||
log.Crit("Failed to RLP encode header", "err", err)
|
||||
}
|
||||
key = append(append(headerPrefix, encoded...), hash...)
|
||||
if err := db.Put(key, data); err != nil {
|
||||
log.Crit("Failed to store header", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteHeader removes all block header data associated with a hash.
|
||||
func DeleteHeader(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)); err != nil {
|
||||
log.Crit("Failed to delete header", "err", err)
|
||||
}
|
||||
if err := db.Delete(append(headerNumberPrefix, hash.Bytes()...)); err != nil {
|
||||
log.Crit("Failed to delete hash to number mapping", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
|
||||
func ReadBodyRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
data, _ := db.Get(append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...))
|
||||
return data
|
||||
}
|
||||
|
||||
// WriteBodyRLP stores an RLP encoded block body into the database.
|
||||
func WriteBodyRLP(db DatabaseWriter, hash common.Hash, number uint64, rlp rlp.RawValue) {
|
||||
key := append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
if err := db.Put(key, rlp); err != nil {
|
||||
log.Crit("Failed to store block body", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// HasBody verifies the existence of a block body corresponding to the hash.
|
||||
func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool {
|
||||
key := append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
if has, err := db.Has(key); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadBody retrieves the block body corresponding to the hash.
|
||||
func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body {
|
||||
data := ReadBodyRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
body := new(types.Body)
|
||||
if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
|
||||
log.Error("Invalid block body RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
// WriteBody storea a block body into the database.
|
||||
func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.Body) {
|
||||
data, err := rlp.EncodeToBytes(body)
|
||||
if err != nil {
|
||||
log.Crit("Failed to RLP encode body", "err", err)
|
||||
}
|
||||
WriteBodyRLP(db, hash, number, data)
|
||||
}
|
||||
|
||||
// DeleteBody removes all block body data associated with a hash.
|
||||
func DeleteBody(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)); err != nil {
|
||||
log.Crit("Failed to delete block body", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadTd retrieves a block's total difficulty corresponding to the hash.
|
||||
func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int {
|
||||
data, _ := db.Get(append(append(append(headerPrefix, encodeBlockNumber(number)...), hash[:]...), headerTDSuffix...))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
td := new(big.Int)
|
||||
if err := rlp.Decode(bytes.NewReader(data), td); err != nil {
|
||||
log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return td
|
||||
}
|
||||
|
||||
// WriteTd stores the total difficulty of a block into the database.
|
||||
func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) {
|
||||
data, err := rlp.EncodeToBytes(td)
|
||||
if err != nil {
|
||||
log.Crit("Failed to RLP encode block total difficulty", "err", err)
|
||||
}
|
||||
key := append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...), headerTDSuffix...)
|
||||
if err := db.Put(key, data); err != nil {
|
||||
log.Crit("Failed to store block total difficulty", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteTd removes all block total difficulty data associated with a hash.
|
||||
func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...), headerTDSuffix...)); err != nil {
|
||||
log.Crit("Failed to delete block total difficulty", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadReceipts retrieves all the transaction receipts belonging to a block.
|
||||
func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Receipts {
|
||||
// Retrieve the flattened receipt slice
|
||||
data, _ := db.Get(append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash[:]...))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Convert the revceipts from their storage form to their internal representation
|
||||
storageReceipts := []*types.ReceiptForStorage{}
|
||||
if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
|
||||
log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
receipts := make(types.Receipts, len(storageReceipts))
|
||||
for i, receipt := range storageReceipts {
|
||||
receipts[i] = (*types.Receipt)(receipt)
|
||||
}
|
||||
return receipts
|
||||
}
|
||||
|
||||
// WriteReceipts stores all the transaction receipts belonging to a block.
|
||||
func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts types.Receipts) {
|
||||
// Convert the receipts into their storage form and serialize them
|
||||
storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
|
||||
for i, receipt := range receipts {
|
||||
storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
|
||||
}
|
||||
bytes, err := rlp.EncodeToBytes(storageReceipts)
|
||||
if err != nil {
|
||||
log.Crit("Failed to encode block receipts", "err", err)
|
||||
}
|
||||
// Store the flattened receipt slice
|
||||
key := append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
if err := db.Put(key, bytes); err != nil {
|
||||
log.Crit("Failed to store block receipts", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteReceipts removes all receipt data associated with a block hash.
|
||||
func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)); err != nil {
|
||||
log.Crit("Failed to delete block receipts", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadBlock retrieves an entire block corresponding to the hash, assembling it
|
||||
// back from the stored header and body. If either the header or body could not
|
||||
// be retrieved nil is returned.
|
||||
//
|
||||
// Note, due to concurrent download of header and block body the header and thus
|
||||
// canonical hash can be stored in the database but the body data not (yet).
|
||||
func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block {
|
||||
header := ReadHeader(db, hash, number)
|
||||
if header == nil {
|
||||
return nil
|
||||
}
|
||||
body := ReadBody(db, hash, number)
|
||||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
|
||||
}
|
||||
|
||||
// WriteBlock serializes a block into the database, header and body separately.
|
||||
func WriteBlock(db DatabaseWriter, block *types.Block) {
|
||||
WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
|
||||
WriteHeader(db, block.Header())
|
||||
}
|
||||
|
||||
// DeleteBlock removes all block data associated with a hash.
|
||||
func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
DeleteReceipts(db, hash, number)
|
||||
DeleteHeader(db, hash, number)
|
||||
DeleteBody(db, hash, number)
|
||||
DeleteTd(db, hash, number)
|
||||
}
|
||||
|
||||
// FindCommonAncestor returns the last common ancestor of two block headers
|
||||
func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header {
|
||||
for bn := b.Number.Uint64(); a.Number.Uint64() > bn; {
|
||||
a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for an := a.Number.Uint64(); an < b.Number.Uint64(); {
|
||||
b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1)
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for a.Hash() != b.Hash() {
|
||||
a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1)
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -30,23 +30,21 @@ import (
|
||||
|
||||
// Tests block header storage and retrieval operations.
|
||||
func TestHeaderStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
// Create a test header to move around the database and make sure it's really new
|
||||
header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
|
||||
if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
|
||||
if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
|
||||
t.Fatalf("Non existent header returned: %v", entry)
|
||||
}
|
||||
// Write and verify the header in the database
|
||||
if err := WriteHeader(db, header); err != nil {
|
||||
t.Fatalf("Failed to write header into database: %v", err)
|
||||
}
|
||||
if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry == nil {
|
||||
WriteHeader(db, header)
|
||||
if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry == nil {
|
||||
t.Fatalf("Stored header not found")
|
||||
} else if entry.Hash() != header.Hash() {
|
||||
t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header)
|
||||
}
|
||||
if entry := GetHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
|
||||
if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
|
||||
t.Fatalf("Stored header RLP not found")
|
||||
} else {
|
||||
hasher := sha3.NewKeccak256()
|
||||
@@ -58,14 +56,14 @@ func TestHeaderStorage(t *testing.T) {
|
||||
}
|
||||
// Delete the header and verify the execution
|
||||
DeleteHeader(db, header.Hash(), header.Number.Uint64())
|
||||
if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
|
||||
if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
|
||||
t.Fatalf("Deleted header returned: %v", entry)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests block body storage and retrieval operations.
|
||||
func TestBodyStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
// Create a test body to move around the database and make sure it's really new
|
||||
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
|
||||
@@ -74,19 +72,17 @@ func TestBodyStorage(t *testing.T) {
|
||||
rlp.Encode(hasher, body)
|
||||
hash := common.BytesToHash(hasher.Sum(nil))
|
||||
|
||||
if entry := GetBody(db, hash, 0); entry != nil {
|
||||
if entry := ReadBody(db, hash, 0); entry != nil {
|
||||
t.Fatalf("Non existent body returned: %v", entry)
|
||||
}
|
||||
// Write and verify the body in the database
|
||||
if err := WriteBody(db, hash, 0, body); err != nil {
|
||||
t.Fatalf("Failed to write body into database: %v", err)
|
||||
}
|
||||
if entry := GetBody(db, hash, 0); entry == nil {
|
||||
WriteBody(db, hash, 0, body)
|
||||
if entry := ReadBody(db, hash, 0); entry == nil {
|
||||
t.Fatalf("Stored body not found")
|
||||
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
|
||||
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
|
||||
}
|
||||
if entry := GetBodyRLP(db, hash, 0); entry == nil {
|
||||
if entry := ReadBodyRLP(db, hash, 0); entry == nil {
|
||||
t.Fatalf("Stored body RLP not found")
|
||||
} else {
|
||||
hasher := sha3.NewKeccak256()
|
||||
@@ -98,14 +94,14 @@ func TestBodyStorage(t *testing.T) {
|
||||
}
|
||||
// Delete the body and verify the execution
|
||||
DeleteBody(db, hash, 0)
|
||||
if entry := GetBody(db, hash, 0); entry != nil {
|
||||
if entry := ReadBody(db, hash, 0); entry != nil {
|
||||
t.Fatalf("Deleted body returned: %v", entry)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests block storage and retrieval operations.
|
||||
func TestBlockStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
// Create a test block to move around the database and make sure it's really new
|
||||
block := types.NewBlockWithHeader(&types.Header{
|
||||
@@ -114,50 +110,48 @@ func TestBlockStorage(t *testing.T) {
|
||||
TxHash: types.EmptyRootHash,
|
||||
ReceiptHash: types.EmptyRootHash,
|
||||
})
|
||||
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
t.Fatalf("Non existent block returned: %v", entry)
|
||||
}
|
||||
if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
t.Fatalf("Non existent header returned: %v", entry)
|
||||
}
|
||||
if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
t.Fatalf("Non existent body returned: %v", entry)
|
||||
}
|
||||
// Write and verify the block in the database
|
||||
if err := WriteBlock(db, block); err != nil {
|
||||
t.Fatalf("Failed to write block into database: %v", err)
|
||||
}
|
||||
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil {
|
||||
WriteBlock(db, block)
|
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil {
|
||||
t.Fatalf("Stored block not found")
|
||||
} else if entry.Hash() != block.Hash() {
|
||||
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
|
||||
}
|
||||
if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry == nil {
|
||||
if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry == nil {
|
||||
t.Fatalf("Stored header not found")
|
||||
} else if entry.Hash() != block.Header().Hash() {
|
||||
t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header())
|
||||
}
|
||||
if entry := GetBody(db, block.Hash(), block.NumberU64()); entry == nil {
|
||||
if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil {
|
||||
t.Fatalf("Stored body not found")
|
||||
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
|
||||
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
|
||||
}
|
||||
// Delete the block and verify the execution
|
||||
DeleteBlock(db, block.Hash(), block.NumberU64())
|
||||
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
t.Fatalf("Deleted block returned: %v", entry)
|
||||
}
|
||||
if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
t.Fatalf("Deleted header returned: %v", entry)
|
||||
}
|
||||
if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
t.Fatalf("Deleted body returned: %v", entry)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that partial block contents don't get reassembled into full blocks.
|
||||
func TestPartialBlockStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
block := types.NewBlockWithHeader(&types.Header{
|
||||
Extra: []byte("test block"),
|
||||
UncleHash: types.EmptyUncleHash,
|
||||
@@ -165,31 +159,24 @@ func TestPartialBlockStorage(t *testing.T) {
|
||||
ReceiptHash: types.EmptyRootHash,
|
||||
})
|
||||
// Store a header and check that it's not recognized as a block
|
||||
if err := WriteHeader(db, block.Header()); err != nil {
|
||||
t.Fatalf("Failed to write header into database: %v", err)
|
||||
}
|
||||
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
WriteHeader(db, block.Header())
|
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
t.Fatalf("Non existent block returned: %v", entry)
|
||||
}
|
||||
DeleteHeader(db, block.Hash(), block.NumberU64())
|
||||
|
||||
// Store a body and check that it's not recognized as a block
|
||||
if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
|
||||
t.Fatalf("Failed to write body into database: %v", err)
|
||||
}
|
||||
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
|
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
|
||||
t.Fatalf("Non existent block returned: %v", entry)
|
||||
}
|
||||
DeleteBody(db, block.Hash(), block.NumberU64())
|
||||
|
||||
// Store a header and a body separately and check reassembly
|
||||
if err := WriteHeader(db, block.Header()); err != nil {
|
||||
t.Fatalf("Failed to write header into database: %v", err)
|
||||
}
|
||||
if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
|
||||
t.Fatalf("Failed to write body into database: %v", err)
|
||||
}
|
||||
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil {
|
||||
WriteHeader(db, block.Header())
|
||||
WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
|
||||
|
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil {
|
||||
t.Fatalf("Stored block not found")
|
||||
} else if entry.Hash() != block.Hash() {
|
||||
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
|
||||
@@ -198,142 +185,88 @@ func TestPartialBlockStorage(t *testing.T) {
|
||||
|
||||
// Tests block total difficulty storage and retrieval operations.
|
||||
func TestTdStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
// Create a test TD to move around the database and make sure it's really new
|
||||
hash, td := common.Hash{}, big.NewInt(314)
|
||||
if entry := GetTd(db, hash, 0); entry != nil {
|
||||
if entry := ReadTd(db, hash, 0); entry != nil {
|
||||
t.Fatalf("Non existent TD returned: %v", entry)
|
||||
}
|
||||
// Write and verify the TD in the database
|
||||
if err := WriteTd(db, hash, 0, td); err != nil {
|
||||
t.Fatalf("Failed to write TD into database: %v", err)
|
||||
}
|
||||
if entry := GetTd(db, hash, 0); entry == nil {
|
||||
WriteTd(db, hash, 0, td)
|
||||
if entry := ReadTd(db, hash, 0); entry == nil {
|
||||
t.Fatalf("Stored TD not found")
|
||||
} else if entry.Cmp(td) != 0 {
|
||||
t.Fatalf("Retrieved TD mismatch: have %v, want %v", entry, td)
|
||||
}
|
||||
// Delete the TD and verify the execution
|
||||
DeleteTd(db, hash, 0)
|
||||
if entry := GetTd(db, hash, 0); entry != nil {
|
||||
if entry := ReadTd(db, hash, 0); entry != nil {
|
||||
t.Fatalf("Deleted TD returned: %v", entry)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that canonical numbers can be mapped to hashes and retrieved.
|
||||
func TestCanonicalMappingStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
// Create a test canonical number and assinged hash to move around
|
||||
hash, number := common.Hash{0: 0xff}, uint64(314)
|
||||
if entry := GetCanonicalHash(db, number); entry != (common.Hash{}) {
|
||||
if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) {
|
||||
t.Fatalf("Non existent canonical mapping returned: %v", entry)
|
||||
}
|
||||
// Write and verify the TD in the database
|
||||
if err := WriteCanonicalHash(db, hash, number); err != nil {
|
||||
t.Fatalf("Failed to write canonical mapping into database: %v", err)
|
||||
}
|
||||
if entry := GetCanonicalHash(db, number); entry == (common.Hash{}) {
|
||||
WriteCanonicalHash(db, hash, number)
|
||||
if entry := ReadCanonicalHash(db, number); entry == (common.Hash{}) {
|
||||
t.Fatalf("Stored canonical mapping not found")
|
||||
} else if entry != hash {
|
||||
t.Fatalf("Retrieved canonical mapping mismatch: have %v, want %v", entry, hash)
|
||||
}
|
||||
// Delete the TD and verify the execution
|
||||
DeleteCanonicalHash(db, number)
|
||||
if entry := GetCanonicalHash(db, number); entry != (common.Hash{}) {
|
||||
if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) {
|
||||
t.Fatalf("Deleted canonical mapping returned: %v", entry)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that head headers and head blocks can be assigned, individually.
|
||||
func TestHeadStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
|
||||
blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
|
||||
blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")})
|
||||
|
||||
// Check that no head entries are in a pristine database
|
||||
if entry := GetHeadHeaderHash(db); entry != (common.Hash{}) {
|
||||
if entry := ReadHeadHeaderHash(db); entry != (common.Hash{}) {
|
||||
t.Fatalf("Non head header entry returned: %v", entry)
|
||||
}
|
||||
if entry := GetHeadBlockHash(db); entry != (common.Hash{}) {
|
||||
if entry := ReadHeadBlockHash(db); entry != (common.Hash{}) {
|
||||
t.Fatalf("Non head block entry returned: %v", entry)
|
||||
}
|
||||
if entry := GetHeadFastBlockHash(db); entry != (common.Hash{}) {
|
||||
if entry := ReadHeadFastBlockHash(db); entry != (common.Hash{}) {
|
||||
t.Fatalf("Non fast head block entry returned: %v", entry)
|
||||
}
|
||||
// Assign separate entries for the head header and block
|
||||
if err := WriteHeadHeaderHash(db, blockHead.Hash()); err != nil {
|
||||
t.Fatalf("Failed to write head header hash: %v", err)
|
||||
}
|
||||
if err := WriteHeadBlockHash(db, blockFull.Hash()); err != nil {
|
||||
t.Fatalf("Failed to write head block hash: %v", err)
|
||||
}
|
||||
if err := WriteHeadFastBlockHash(db, blockFast.Hash()); err != nil {
|
||||
t.Fatalf("Failed to write fast head block hash: %v", err)
|
||||
}
|
||||
WriteHeadHeaderHash(db, blockHead.Hash())
|
||||
WriteHeadBlockHash(db, blockFull.Hash())
|
||||
WriteHeadFastBlockHash(db, blockFast.Hash())
|
||||
|
||||
// Check that both heads are present, and different (i.e. two heads maintained)
|
||||
if entry := GetHeadHeaderHash(db); entry != blockHead.Hash() {
|
||||
if entry := ReadHeadHeaderHash(db); entry != blockHead.Hash() {
|
||||
t.Fatalf("Head header hash mismatch: have %v, want %v", entry, blockHead.Hash())
|
||||
}
|
||||
if entry := GetHeadBlockHash(db); entry != blockFull.Hash() {
|
||||
if entry := ReadHeadBlockHash(db); entry != blockFull.Hash() {
|
||||
t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash())
|
||||
}
|
||||
if entry := GetHeadFastBlockHash(db); entry != blockFast.Hash() {
|
||||
if entry := ReadHeadFastBlockHash(db); entry != blockFast.Hash() {
|
||||
t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that positional lookup metadata can be stored and retrieved.
|
||||
func TestLookupStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
|
||||
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
|
||||
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
|
||||
txs := []*types.Transaction{tx1, tx2, tx3}
|
||||
|
||||
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
|
||||
|
||||
// Check that no transactions entries are in a pristine database
|
||||
for i, tx := range txs {
|
||||
if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
|
||||
t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn)
|
||||
}
|
||||
}
|
||||
// Insert all the transactions into the database, and verify contents
|
||||
if err := WriteBlock(db, block); err != nil {
|
||||
t.Fatalf("failed to write block contents: %v", err)
|
||||
}
|
||||
if err := WriteTxLookupEntries(db, block); err != nil {
|
||||
t.Fatalf("failed to write transactions: %v", err)
|
||||
}
|
||||
for i, tx := range txs {
|
||||
if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil {
|
||||
t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
|
||||
} else {
|
||||
if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
|
||||
t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
|
||||
}
|
||||
if tx.Hash() != txn.Hash() {
|
||||
t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Delete the transactions and check purge
|
||||
for i, tx := range txs {
|
||||
DeleteTxLookupEntry(db, tx.Hash())
|
||||
if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
|
||||
t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that receipts associated with a single block can be stored and retrieved.
|
||||
func TestBlockReceiptStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
receipt1 := &types.Receipt{
|
||||
Status: types.ReceiptStatusFailed,
|
||||
@@ -361,14 +294,12 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
|
||||
// Check that no receipt entries are in a pristine database
|
||||
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||
if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 {
|
||||
if rs := ReadReceipts(db, hash, 0); len(rs) != 0 {
|
||||
t.Fatalf("non existent receipts returned: %v", rs)
|
||||
}
|
||||
// Insert the receipt slice into the database and check presence
|
||||
if err := WriteBlockReceipts(db, hash, 0, receipts); err != nil {
|
||||
t.Fatalf("failed to write block receipts: %v", err)
|
||||
}
|
||||
if rs := GetBlockReceipts(db, hash, 0); len(rs) == 0 {
|
||||
WriteReceipts(db, hash, 0, receipts)
|
||||
if rs := ReadReceipts(db, hash, 0); len(rs) == 0 {
|
||||
t.Fatalf("no receipts returned")
|
||||
} else {
|
||||
for i := 0; i < len(receipts); i++ {
|
||||
@@ -381,8 +312,8 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
// Delete the receipt slice and check purge
|
||||
DeleteBlockReceipts(db, hash, 0)
|
||||
if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 {
|
||||
DeleteReceipts(db, hash, 0)
|
||||
if rs := ReadReceipts(db, hash, 0); len(rs) != 0 {
|
||||
t.Fatalf("deleted receipts returned: %v", rs)
|
||||
}
|
||||
}
|
||||
119
core/rawdb/accessors_indexes.go
Normal file
119
core/rawdb/accessors_indexes.go
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// ReadTxLookupEntry retrieves the positional metadata associated with a transaction
|
||||
// hash to allow retrieving the transaction or receipt by hash.
|
||||
func ReadTxLookupEntry(db DatabaseReader, hash common.Hash) (common.Hash, uint64, uint64) {
|
||||
data, _ := db.Get(append(txLookupPrefix, hash.Bytes()...))
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}, 0, 0
|
||||
}
|
||||
var entry TxLookupEntry
|
||||
if err := rlp.DecodeBytes(data, &entry); err != nil {
|
||||
log.Error("Invalid transaction lookup entry RLP", "hash", hash, "err", err)
|
||||
return common.Hash{}, 0, 0
|
||||
}
|
||||
return entry.BlockHash, entry.BlockIndex, entry.Index
|
||||
}
|
||||
|
||||
// WriteTxLookupEntries stores a positional metadata for every transaction from
|
||||
// a block, enabling hash based transaction and receipt lookups.
|
||||
func WriteTxLookupEntries(db DatabaseWriter, block *types.Block) {
|
||||
for i, tx := range block.Transactions() {
|
||||
entry := TxLookupEntry{
|
||||
BlockHash: block.Hash(),
|
||||
BlockIndex: block.NumberU64(),
|
||||
Index: uint64(i),
|
||||
}
|
||||
data, err := rlp.EncodeToBytes(entry)
|
||||
if err != nil {
|
||||
log.Crit("Failed to encode transaction lookup entry", "err", err)
|
||||
}
|
||||
if err := db.Put(append(txLookupPrefix, tx.Hash().Bytes()...), data); err != nil {
|
||||
log.Crit("Failed to store transaction lookup entry", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteTxLookupEntry removes all transaction data associated with a hash.
|
||||
func DeleteTxLookupEntry(db DatabaseDeleter, hash common.Hash) {
|
||||
db.Delete(append(txLookupPrefix, hash.Bytes()...))
|
||||
}
|
||||
|
||||
// ReadTransaction retrieves a specific transaction from the database, along with
|
||||
// its added positional metadata.
|
||||
func ReadTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
||||
blockHash, blockNumber, txIndex := ReadTxLookupEntry(db, hash)
|
||||
if blockHash == (common.Hash{}) {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
body := ReadBody(db, blockHash, blockNumber)
|
||||
if body == nil || len(body.Transactions) <= int(txIndex) {
|
||||
log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash, "index", txIndex)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
return body.Transactions[txIndex], blockHash, blockNumber, txIndex
|
||||
}
|
||||
|
||||
// ReadReceipt retrieves a specific transaction receipt from the database, along with
|
||||
// its added positional metadata.
|
||||
func ReadReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
|
||||
blockHash, blockNumber, receiptIndex := ReadTxLookupEntry(db, hash)
|
||||
if blockHash == (common.Hash{}) {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
receipts := ReadReceipts(db, blockHash, blockNumber)
|
||||
if len(receipts) <= int(receiptIndex) {
|
||||
log.Error("Receipt refereced missing", "number", blockNumber, "hash", blockHash, "index", receiptIndex)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
return receipts[receiptIndex], blockHash, blockNumber, receiptIndex
|
||||
}
|
||||
|
||||
// ReadBloomBits retrieves the compressed bloom bit vector belonging to the given
|
||||
// section and bit index from the.
|
||||
func ReadBloomBits(db DatabaseReader, bit uint, section uint64, head common.Hash) ([]byte, error) {
|
||||
key := append(append(bloomBitsPrefix, make([]byte, 10)...), head.Bytes()...)
|
||||
|
||||
binary.BigEndian.PutUint16(key[1:], uint16(bit))
|
||||
binary.BigEndian.PutUint64(key[3:], section)
|
||||
|
||||
return db.Get(key)
|
||||
}
|
||||
|
||||
// WriteBloomBits stores the compressed bloom bits vector belonging to the given
|
||||
// section and bit index.
|
||||
func WriteBloomBits(db DatabaseWriter, bit uint, section uint64, head common.Hash, bits []byte) {
|
||||
key := append(append(bloomBitsPrefix, make([]byte, 10)...), head.Bytes()...)
|
||||
|
||||
binary.BigEndian.PutUint16(key[1:], uint16(bit))
|
||||
binary.BigEndian.PutUint64(key[3:], section)
|
||||
|
||||
if err := db.Put(key, bits); err != nil {
|
||||
log.Crit("Failed to store bloom bits", "err", err)
|
||||
}
|
||||
}
|
||||
68
core/rawdb/accessors_indexes_test.go
Normal file
68
core/rawdb/accessors_indexes_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
// Tests that positional lookup metadata can be stored and retrieved.
|
||||
func TestLookupStorage(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
|
||||
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
|
||||
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
|
||||
txs := []*types.Transaction{tx1, tx2, tx3}
|
||||
|
||||
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
|
||||
|
||||
// Check that no transactions entries are in a pristine database
|
||||
for i, tx := range txs {
|
||||
if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
|
||||
t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn)
|
||||
}
|
||||
}
|
||||
// Insert all the transactions into the database, and verify contents
|
||||
WriteBlock(db, block)
|
||||
WriteTxLookupEntries(db, block)
|
||||
|
||||
for i, tx := range txs {
|
||||
if txn, hash, number, index := ReadTransaction(db, tx.Hash()); txn == nil {
|
||||
t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
|
||||
} else {
|
||||
if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
|
||||
t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
|
||||
}
|
||||
if tx.Hash() != txn.Hash() {
|
||||
t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Delete the transactions and check purge
|
||||
for i, tx := range txs {
|
||||
DeleteTxLookupEntry(db, tx.Hash())
|
||||
if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
|
||||
t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
90
core/rawdb/accessors_metadata.go
Normal file
90
core/rawdb/accessors_metadata.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// ReadDatabaseVersion retrieves the version number of the database.
|
||||
func ReadDatabaseVersion(db DatabaseReader) int {
|
||||
var version int
|
||||
|
||||
enc, _ := db.Get(databaseVerisionKey)
|
||||
rlp.DecodeBytes(enc, &version)
|
||||
|
||||
return version
|
||||
}
|
||||
|
||||
// WriteDatabaseVersion stores the version number of the database
|
||||
func WriteDatabaseVersion(db DatabaseWriter, version int) {
|
||||
enc, _ := rlp.EncodeToBytes(version)
|
||||
if err := db.Put(databaseVerisionKey, enc); err != nil {
|
||||
log.Crit("Failed to store the database version", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadChainConfig retrieves the consensus settings based on the given genesis hash.
|
||||
func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig {
|
||||
data, _ := db.Get(append(configPrefix, hash[:]...))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
var config params.ChainConfig
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
log.Error("Invalid chain config JSON", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return &config
|
||||
}
|
||||
|
||||
// WriteChainConfig writes the chain config settings to the database.
|
||||
func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConfig) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
data, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
log.Crit("Failed to JSON encode chain config", "err", err)
|
||||
}
|
||||
if err := db.Put(append(configPrefix, hash[:]...), data); err != nil {
|
||||
log.Crit("Failed to store chain config", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadPreimage retrieves a single preimage of the provided hash.
|
||||
func ReadPreimage(db DatabaseReader, hash common.Hash) []byte {
|
||||
data, _ := db.Get(append(preimagePrefix, hash.Bytes()...))
|
||||
return data
|
||||
}
|
||||
|
||||
// WritePreimages writes the provided set of preimages to the database. `number` is the
|
||||
// current block number, and is used for debug messages only.
|
||||
func WritePreimages(db DatabaseWriter, number uint64, preimages map[common.Hash][]byte) {
|
||||
for hash, preimage := range preimages {
|
||||
if err := db.Put(append(preimagePrefix, hash.Bytes()...), preimage); err != nil {
|
||||
log.Crit("Failed to store trie preimage", "err", err)
|
||||
}
|
||||
}
|
||||
preimageCounter.Inc(int64(len(preimages)))
|
||||
preimageHitCounter.Inc(int64(len(preimages)))
|
||||
}
|
||||
33
core/rawdb/interfaces.go
Normal file
33
core/rawdb/interfaces.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rawdb
|
||||
|
||||
// DatabaseReader wraps the Has and Get method of a backing data store.
|
||||
type DatabaseReader interface {
|
||||
Has(key []byte) (bool, error)
|
||||
Get(key []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// DatabaseWriter wraps the Put method of a backing data store.
|
||||
type DatabaseWriter interface {
|
||||
Put(key []byte, value []byte) error
|
||||
}
|
||||
|
||||
// DatabaseDeleter wraps the Delete method of a backing data store.
|
||||
type DatabaseDeleter interface {
|
||||
Delete(key []byte) error
|
||||
}
|
||||
79
core/rawdb/schema.go
Normal file
79
core/rawdb/schema.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package rawdb contains a collection of low level database accessors.
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
)
|
||||
|
||||
// The fields below define the low level database schema prefixing.
|
||||
var (
|
||||
// databaseVerisionKey tracks the current database version.
|
||||
databaseVerisionKey = []byte("DatabaseVersion")
|
||||
|
||||
// headHeaderKey tracks the latest know header's hash.
|
||||
headHeaderKey = []byte("LastHeader")
|
||||
|
||||
// headBlockKey tracks the latest know full block's hash.
|
||||
headBlockKey = []byte("LastBlock")
|
||||
|
||||
// headFastBlockKey tracks the latest known incomplete block's hash duirng fast sync.
|
||||
headFastBlockKey = []byte("LastFast")
|
||||
|
||||
// fastTrieProgressKey tracks the number of trie entries imported during fast sync.
|
||||
fastTrieProgressKey = []byte("TrieSync")
|
||||
|
||||
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
||||
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
||||
headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
|
||||
headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
|
||||
headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
|
||||
|
||||
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
|
||||
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
|
||||
|
||||
txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
|
||||
bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
|
||||
|
||||
preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
|
||||
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
||||
|
||||
// Chain index prefixes (use `i` + single byte to avoid mixing data types).
|
||||
BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
|
||||
|
||||
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
|
||||
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
|
||||
)
|
||||
|
||||
// TxLookupEntry is a positional metadata to help looking up the data content of
|
||||
// a transaction or receipt given only its hash.
|
||||
type TxLookupEntry struct {
|
||||
BlockHash common.Hash
|
||||
BlockIndex uint64
|
||||
Index uint64
|
||||
}
|
||||
|
||||
// encodeBlockNumber encodes a block number as big endian uint64
|
||||
func encodeBlockNumber(number uint64) []byte {
|
||||
enc := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(enc, number)
|
||||
return enc
|
||||
}
|
||||
@@ -26,8 +26,7 @@ import (
|
||||
var addr = common.BytesToAddress([]byte("test"))
|
||||
|
||||
func create() (*ManagedState, *account) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := New(common.Hash{}, NewDatabase(db))
|
||||
statedb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
||||
ms := ManageState(statedb)
|
||||
ms.StateDB.SetNonce(addr, 100)
|
||||
ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr))
|
||||
|
||||
@@ -178,9 +178,7 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
|
||||
}
|
||||
value.SetBytes(content)
|
||||
}
|
||||
if (value != common.Hash{}) {
|
||||
self.cachedStorage[key] = value
|
||||
}
|
||||
self.cachedStorage[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -197,7 +195,6 @@ func (self *stateObject) SetState(db Database, key, value common.Hash) {
|
||||
func (self *stateObject) setState(key, value common.Hash) {
|
||||
self.cachedStorage[key] = value
|
||||
self.dirtyStorage[key] = value
|
||||
|
||||
}
|
||||
|
||||
// updateTrie writes cached storage modifications into the object's storage trie.
|
||||
|
||||
@@ -87,7 +87,7 @@ func (s *StateSuite) TestDump(c *checker.C) {
|
||||
}
|
||||
|
||||
func (s *StateSuite) SetUpTest(c *checker.C) {
|
||||
s.db, _ = ethdb.NewMemDatabase()
|
||||
s.db = ethdb.NewMemDatabase()
|
||||
s.state, _ = New(common.Hash{}, NewDatabase(s.db))
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func (s *StateSuite) TestNull(c *checker.C) {
|
||||
s.state.SetState(address, common.Hash{}, value)
|
||||
s.state.Commit(false)
|
||||
value = s.state.GetState(address, common.Hash{})
|
||||
if !common.EmptyHash(value) {
|
||||
if value != (common.Hash{}) {
|
||||
c.Errorf("expected empty hash. got %x", value)
|
||||
}
|
||||
}
|
||||
@@ -133,8 +133,7 @@ func (s *StateSuite) TestSnapshotEmpty(c *checker.C) {
|
||||
// use testing instead of checker because checker does not support
|
||||
// printing/logging in tests (-check.vv does not work)
|
||||
func TestSnapshot2(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state, _ := New(common.Hash{}, NewDatabase(db))
|
||||
state, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
||||
|
||||
stateobjaddr0 := toAddr([]byte("so0"))
|
||||
stateobjaddr1 := toAddr([]byte("so1"))
|
||||
|
||||
@@ -358,7 +358,7 @@ func (self *StateDB) deleteStateObject(stateObject *stateObject) {
|
||||
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) {
|
||||
// Prefer 'live' objects.
|
||||
if obj := self.stateObjects[addr]; obj != nil {
|
||||
@@ -572,27 +572,6 @@ func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
|
||||
self.txIndex = ti
|
||||
}
|
||||
|
||||
// DeleteSuicides flags the suicided objects for deletion so that it
|
||||
// won't be referenced again when called / queried up on.
|
||||
//
|
||||
// DeleteSuicides should not be used for consensus related updates
|
||||
// under any circumstances.
|
||||
func (s *StateDB) DeleteSuicides() {
|
||||
// Reset refund so that any used-gas calculations can use this method.
|
||||
s.clearJournalAndRefund()
|
||||
|
||||
for addr := range s.stateObjectsDirty {
|
||||
stateObject := s.stateObjects[addr]
|
||||
|
||||
// If the object has been removed by a suicide
|
||||
// flag the object as deleted.
|
||||
if stateObject.suicided {
|
||||
stateObject.deleted = true
|
||||
}
|
||||
delete(s.stateObjectsDirty, addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StateDB) clearJournalAndRefund() {
|
||||
s.journal = newJournal()
|
||||
s.validRevisions = s.validRevisions[:0]
|
||||
|
||||
@@ -39,7 +39,7 @@ import (
|
||||
// actually committing the state.
|
||||
func TestUpdateLeaks(t *testing.T) {
|
||||
// Create an empty state database
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
state, _ := New(common.Hash{}, NewDatabase(db))
|
||||
|
||||
// Update it with some accounts
|
||||
@@ -66,8 +66,8 @@ func TestUpdateLeaks(t *testing.T) {
|
||||
// only the one right before the commit.
|
||||
func TestIntermediateLeaks(t *testing.T) {
|
||||
// Create two state databases, one transitioning to the final state, the other final from the beginning
|
||||
transDb, _ := ethdb.NewMemDatabase()
|
||||
finalDb, _ := ethdb.NewMemDatabase()
|
||||
transDb := ethdb.NewMemDatabase()
|
||||
finalDb := ethdb.NewMemDatabase()
|
||||
transState, _ := New(common.Hash{}, NewDatabase(transDb))
|
||||
finalState, _ := New(common.Hash{}, NewDatabase(finalDb))
|
||||
|
||||
@@ -122,8 +122,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
// https://github.com/ethereum/go-ethereum/pull/15549.
|
||||
func TestCopy(t *testing.T) {
|
||||
// Create a random state test to copy and modify "independently"
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
orig, _ := New(common.Hash{}, NewDatabase(db))
|
||||
orig, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
||||
|
||||
for i := byte(0); i < 255; i++ {
|
||||
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||
@@ -334,8 +333,7 @@ func (test *snapshotTest) String() string {
|
||||
func (test *snapshotTest) run() bool {
|
||||
// Run all actions and create snapshots.
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
state, _ = New(common.Hash{}, NewDatabase(db))
|
||||
state, _ = New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
||||
snapshotRevs = make([]int, len(test.snapshots))
|
||||
sindex = 0
|
||||
)
|
||||
@@ -426,8 +424,7 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
|
||||
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
|
||||
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
|
||||
func TestCopyOfCopy(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
sdb, _ := New(common.Hash{}, NewDatabase(db))
|
||||
sdb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
||||
addr := common.HexToAddress("aaaa")
|
||||
sdb.SetBalance(addr, big.NewInt(42))
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
)
|
||||
|
||||
// NewStateSync create a new state trie download scheduler.
|
||||
func NewStateSync(root common.Hash, database trie.DatabaseReader) *trie.TrieSync {
|
||||
var syncer *trie.TrieSync
|
||||
func NewStateSync(root common.Hash, database trie.DatabaseReader) *trie.Sync {
|
||||
var syncer *trie.Sync
|
||||
callback := func(leaf []byte, parent common.Hash) error {
|
||||
var obj Account
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
syncer = trie.NewTrieSync(root, database, callback)
|
||||
syncer = trie.NewSync(root, database, callback)
|
||||
return syncer
|
||||
}
|
||||
|
||||
@@ -38,8 +38,7 @@ type testAccount struct {
|
||||
// makeTestState create a sample test state to test node-wise reconstruction.
|
||||
func makeTestState() (Database, common.Hash, []*testAccount) {
|
||||
// Create an empty state
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
db := NewDatabase(diskdb)
|
||||
db := NewDatabase(ethdb.NewMemDatabase())
|
||||
state, _ := New(common.Hash{}, db)
|
||||
|
||||
// Fill it with some arbitrary data
|
||||
@@ -125,8 +124,7 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
|
||||
// Tests that an empty state is not scheduled for syncing.
|
||||
func TestEmptyStateSync(t *testing.T) {
|
||||
empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
if req := NewStateSync(empty, db).Missing(1); len(req) != 0 {
|
||||
if req := NewStateSync(empty, ethdb.NewMemDatabase()).Missing(1); len(req) != 0 {
|
||||
t.Errorf("content requested for empty state: %v", req)
|
||||
}
|
||||
}
|
||||
@@ -141,7 +139,7 @@ func testIterativeStateSync(t *testing.T, batch int) {
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb, _ := ethdb.NewMemDatabase()
|
||||
dstDb := ethdb.NewMemDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb)
|
||||
|
||||
queue := append([]common.Hash{}, sched.Missing(batch)...)
|
||||
@@ -173,7 +171,7 @@ func TestIterativeDelayedStateSync(t *testing.T) {
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb, _ := ethdb.NewMemDatabase()
|
||||
dstDb := ethdb.NewMemDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb)
|
||||
|
||||
queue := append([]common.Hash{}, sched.Missing(0)...)
|
||||
@@ -210,7 +208,7 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb, _ := ethdb.NewMemDatabase()
|
||||
dstDb := ethdb.NewMemDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb)
|
||||
|
||||
queue := make(map[common.Hash]struct{})
|
||||
@@ -250,7 +248,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb, _ := ethdb.NewMemDatabase()
|
||||
dstDb := ethdb.NewMemDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb)
|
||||
|
||||
queue := make(map[common.Hash]struct{})
|
||||
@@ -297,7 +295,7 @@ func TestIncompleteStateSync(t *testing.T) {
|
||||
checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb, _ := ethdb.NewMemDatabase()
|
||||
dstDb := ethdb.NewMemDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb)
|
||||
|
||||
added := []common.Hash{}
|
||||
|
||||
@@ -56,7 +56,7 @@ func newTxJournal(path string) *txJournal {
|
||||
|
||||
// load parses a transaction journal dump from disk, loading its contents into
|
||||
// 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
|
||||
if _, err := os.Stat(journal.path); os.IsNotExist(err) {
|
||||
return nil
|
||||
@@ -76,7 +76,21 @@ func (journal *txJournal) load(add func(*types.Transaction) error) error {
|
||||
stream := rlp.NewStream(input, 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 {
|
||||
// Parse the next transaction and terminate on error
|
||||
tx := new(types.Transaction)
|
||||
@@ -84,14 +98,17 @@ func (journal *txJournal) load(add func(*types.Transaction) error) error {
|
||||
if err != io.EOF {
|
||||
failure = err
|
||||
}
|
||||
if batch.Len() > 0 {
|
||||
loadBatch(batch)
|
||||
}
|
||||
break
|
||||
}
|
||||
// Import the transaction and bump the appropriate progress counters
|
||||
// New transaction parsed, queue up for later, import if threnshold is reached
|
||||
total++
|
||||
if err = add(tx); err != nil {
|
||||
log.Debug("Failed to add journaled transaction", "err", err)
|
||||
dropped++
|
||||
continue
|
||||
|
||||
if batch = append(batch, tx); batch.Len() > 1024 {
|
||||
loadBatch(batch)
|
||||
batch = batch[:0]
|
||||
}
|
||||
}
|
||||
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
|
||||
// contents in a price-incrementing way.
|
||||
type txPricedList struct {
|
||||
all *map[common.Hash]*types.Transaction // Pointer to the map of all transactions
|
||||
items *priceHeap // Heap of prices of all the stored transactions
|
||||
stales int // Number of stale price points to (re-heap trigger)
|
||||
all *txLookup // Pointer to the map of all transactions
|
||||
items *priceHeap // Heap of prices of all the stored transactions
|
||||
stales int // Number of stale price points to (re-heap trigger)
|
||||
}
|
||||
|
||||
// newTxPricedList creates a new price-sorted transaction heap.
|
||||
func newTxPricedList(all *map[common.Hash]*types.Transaction) *txPricedList {
|
||||
func newTxPricedList(all *txLookup) *txPricedList {
|
||||
return &txPricedList{
|
||||
all: all,
|
||||
items: new(priceHeap),
|
||||
@@ -425,12 +425,13 @@ func (l *txPricedList) Removed() {
|
||||
return
|
||||
}
|
||||
// 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
|
||||
for _, tx := range *l.all {
|
||||
l.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
|
||||
*l.items = append(*l.items, tx)
|
||||
}
|
||||
return true
|
||||
})
|
||||
heap.Init(l.items)
|
||||
}
|
||||
|
||||
@@ -443,7 +444,7 @@ func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transact
|
||||
for len(*l.items) > 0 {
|
||||
// Discard stale transactions if found during cleanup
|
||||
tx := heap.Pop(l.items).(*types.Transaction)
|
||||
if _, ok := (*l.all)[tx.Hash()]; !ok {
|
||||
if l.all.Get(tx.Hash()) == nil {
|
||||
l.stales--
|
||||
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
|
||||
for len(*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--
|
||||
heap.Pop(l.items)
|
||||
continue
|
||||
@@ -500,7 +501,7 @@ func (l *txPricedList) Discard(count int, local *accountSet) types.Transactions
|
||||
for len(*l.items) > 0 && count > 0 {
|
||||
// Discard stale transactions if found during cleanup
|
||||
tx := heap.Pop(l.items).(*types.Transaction)
|
||||
if _, ok := (*l.all)[tx.Hash()]; !ok {
|
||||
if l.all.Get(tx.Hash()) == nil {
|
||||
l.stales--
|
||||
continue
|
||||
}
|
||||
|
||||
162
core/tx_pool.go
162
core/tx_pool.go
@@ -200,11 +200,11 @@ type TxPool struct {
|
||||
locals *accountSet // Set of local transaction to exempt from eviction rules
|
||||
journal *txJournal // Journal of local transaction to back up to disk
|
||||
|
||||
pending map[common.Address]*txList // All currently processable transactions
|
||||
queue map[common.Address]*txList // Queued but non-processable transactions
|
||||
beats map[common.Address]time.Time // Last heartbeat from each known account
|
||||
all map[common.Hash]*types.Transaction // All transactions to allow lookups
|
||||
priced *txPricedList // All transactions sorted by price
|
||||
pending map[common.Address]*txList // All currently processable transactions
|
||||
queue map[common.Address]*txList // Queued but non-processable transactions
|
||||
beats map[common.Address]time.Time // Last heartbeat from each known account
|
||||
all *txLookup // All transactions to allow lookups
|
||||
priced *txPricedList // All transactions sorted by price
|
||||
|
||||
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),
|
||||
queue: make(map[common.Address]*txList),
|
||||
beats: make(map[common.Address]time.Time),
|
||||
all: make(map[common.Hash]*types.Transaction),
|
||||
all: newTxLookup(),
|
||||
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
||||
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
|
||||
}
|
||||
pool.locals = newAccountSet(pool.signer)
|
||||
pool.priced = newTxPricedList(&pool.all)
|
||||
pool.priced = newTxPricedList(pool.all)
|
||||
pool.reset(nil, chain.CurrentBlock().Header())
|
||||
|
||||
// If local transactions and journaling is enabled, load from disk
|
||||
if !config.NoLocals && 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)
|
||||
}
|
||||
if err := pool.journal.rotate(pool.local()); err != nil {
|
||||
@@ -444,9 +444,9 @@ func (pool *TxPool) Stop() {
|
||||
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.
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
// If the transaction is already known, discard it
|
||||
hash := tx.Hash()
|
||||
if pool.all[hash] != nil {
|
||||
if pool.all.Get(hash) != nil {
|
||||
log.Trace("Discarding already known transaction", "hash", hash)
|
||||
return false, fmt.Errorf("known transaction: %x", hash)
|
||||
}
|
||||
@@ -616,15 +616,15 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
// 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 pool.priced.Underpriced(tx, pool.locals) {
|
||||
if !local && pool.priced.Underpriced(tx, pool.locals) {
|
||||
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
|
||||
underpricedTxCounter.Inc(1)
|
||||
return false, ErrUnderpriced
|
||||
}
|
||||
// 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 {
|
||||
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
|
||||
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
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
pool.all.Remove(old.Hash())
|
||||
pool.priced.Removed()
|
||||
pendingReplaceCounter.Inc(1)
|
||||
}
|
||||
pool.all[tx.Hash()] = tx
|
||||
pool.all.Add(tx)
|
||||
pool.priced.Put(tx)
|
||||
pool.journalTx(from, tx)
|
||||
|
||||
log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -689,12 +689,12 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er
|
||||
}
|
||||
// Discard any previous transaction and mark this
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
pool.all.Remove(old.Hash())
|
||||
pool.priced.Removed()
|
||||
queuedReplaceCounter.Inc(1)
|
||||
}
|
||||
if pool.all[hash] == nil {
|
||||
pool.all[hash] = tx
|
||||
if pool.all.Get(hash) == nil {
|
||||
pool.all.Add(tx)
|
||||
pool.priced.Put(tx)
|
||||
}
|
||||
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!
|
||||
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
|
||||
if pool.pending[addr] == nil {
|
||||
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)
|
||||
if !inserted {
|
||||
// An older transaction was better, discard this
|
||||
delete(pool.all, hash)
|
||||
pool.all.Remove(hash)
|
||||
pool.priced.Removed()
|
||||
|
||||
pendingDiscardCounter.Inc(1)
|
||||
return
|
||||
return false
|
||||
}
|
||||
// Otherwise discard any previous transaction and mark this
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
pool.all.Remove(old.Hash())
|
||||
pool.priced.Removed()
|
||||
|
||||
pendingReplaceCounter.Inc(1)
|
||||
}
|
||||
// Failsafe to work around direct pending inserts (tests)
|
||||
if pool.all[hash] == nil {
|
||||
pool.all[hash] = tx
|
||||
if pool.all.Get(hash) == nil {
|
||||
pool.all.Add(tx)
|
||||
pool.priced.Put(tx)
|
||||
}
|
||||
// Set the potentially new pending nonce and notify any subsystems of the new tx
|
||||
pool.beats[addr] = time.Now()
|
||||
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
|
||||
@@ -839,7 +840,7 @@ func (pool *TxPool) Status(hashes []common.Hash) []TxStatus {
|
||||
|
||||
status := make([]TxStatus, len(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
|
||||
if pool.pending[from] != nil && pool.pending[from].txs.items[tx.Nonce()] != nil {
|
||||
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
|
||||
// and nil otherwise.
|
||||
func (pool *TxPool) Get(hash common.Hash) *types.Transaction {
|
||||
pool.mu.RLock()
|
||||
defer pool.mu.RUnlock()
|
||||
|
||||
return pool.all[hash]
|
||||
return pool.all.Get(hash)
|
||||
}
|
||||
|
||||
// removeTx removes a single transaction from the queue, moving all subsequent
|
||||
// transactions back to the future queue.
|
||||
func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
|
||||
// Fetch the transaction we wish to delete
|
||||
tx, ok := pool.all[hash]
|
||||
if !ok {
|
||||
tx := pool.all.Get(hash)
|
||||
if tx == nil {
|
||||
return
|
||||
}
|
||||
addr, _ := types.Sender(pool.signer, tx) // already validated during insertion
|
||||
|
||||
// Remove it from the list of known transactions
|
||||
delete(pool.all, hash)
|
||||
pool.all.Remove(hash)
|
||||
if outofbound {
|
||||
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
|
||||
// invalidated transactions (low nonce, low balance) are deleted.
|
||||
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
|
||||
if accounts == nil {
|
||||
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)) {
|
||||
hash := tx.Hash()
|
||||
log.Trace("Removed old queued transaction", "hash", hash)
|
||||
delete(pool.all, hash)
|
||||
pool.all.Remove(hash)
|
||||
pool.priced.Removed()
|
||||
}
|
||||
// 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 {
|
||||
hash := tx.Hash()
|
||||
log.Trace("Removed unpayable queued transaction", "hash", hash)
|
||||
delete(pool.all, hash)
|
||||
pool.all.Remove(hash)
|
||||
pool.priced.Removed()
|
||||
queuedNofundsCounter.Inc(1)
|
||||
}
|
||||
// Gather all executable transactions and promote them
|
||||
for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) {
|
||||
hash := tx.Hash()
|
||||
log.Trace("Promoting queued transaction", "hash", hash)
|
||||
pool.promoteTx(addr, hash, tx)
|
||||
if pool.promoteTx(addr, hash, tx) {
|
||||
log.Trace("Promoting queued transaction", "hash", hash)
|
||||
promoted = append(promoted, tx)
|
||||
}
|
||||
}
|
||||
// Drop all transactions over the allowed limit
|
||||
if !pool.locals.contains(addr) {
|
||||
for _, tx := range list.Cap(int(pool.config.AccountQueue)) {
|
||||
hash := tx.Hash()
|
||||
delete(pool.all, hash)
|
||||
pool.all.Remove(hash)
|
||||
pool.priced.Removed()
|
||||
queuedRateLimitCounter.Inc(1)
|
||||
log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
|
||||
@@ -957,6 +960,10 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
|
||||
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
|
||||
pending := uint64(0)
|
||||
for _, list := range pool.pending {
|
||||
@@ -991,7 +998,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
|
||||
for _, tx := range list.Cap(list.Len() - 1) {
|
||||
// Drop the transaction from the global pools too
|
||||
hash := tx.Hash()
|
||||
delete(pool.all, hash)
|
||||
pool.all.Remove(hash)
|
||||
pool.priced.Removed()
|
||||
|
||||
// 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) {
|
||||
// Drop the transaction from the global pools too
|
||||
hash := tx.Hash()
|
||||
delete(pool.all, hash)
|
||||
pool.all.Remove(hash)
|
||||
pool.priced.Removed()
|
||||
|
||||
// Update the account nonce to the dropped transaction
|
||||
@@ -1082,7 +1089,7 @@ func (pool *TxPool) demoteUnexecutables() {
|
||||
for _, tx := range list.Forward(nonce) {
|
||||
hash := tx.Hash()
|
||||
log.Trace("Removed old pending transaction", "hash", hash)
|
||||
delete(pool.all, hash)
|
||||
pool.all.Remove(hash)
|
||||
pool.priced.Removed()
|
||||
}
|
||||
// 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 {
|
||||
hash := tx.Hash()
|
||||
log.Trace("Removed unpayable pending transaction", "hash", hash)
|
||||
delete(pool.all, hash)
|
||||
pool.all.Remove(hash)
|
||||
pool.priced.Removed()
|
||||
pendingNofundsCounter.Inc(1)
|
||||
}
|
||||
@@ -1162,3 +1169,68 @@ func (as *accountSet) containsTx(tx *types.Transaction) bool {
|
||||
func (as *accountSet) add(addr common.Address) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -78,8 +78,7 @@ func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ec
|
||||
}
|
||||
|
||||
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(diskdb))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
key, _ := crypto.GenerateKey()
|
||||
@@ -95,7 +94,7 @@ func validateTxPoolInternals(pool *TxPool) error {
|
||||
|
||||
// Ensure the total transaction set is consistent with pending + queued
|
||||
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)
|
||||
}
|
||||
if priced := pool.priced.items.Len() - pool.priced.stales; priced != pending+queued {
|
||||
@@ -119,21 +118,27 @@ func validateTxPoolInternals(pool *TxPool) error {
|
||||
|
||||
// validateEvents checks that the correct number of transaction addition events
|
||||
// were fired on the pool's event feed.
|
||||
func validateEvents(events chan TxPreEvent, count int) error {
|
||||
for i := 0; i < count; i++ {
|
||||
func validateEvents(events chan NewTxsEvent, count int) error {
|
||||
var received []*types.Transaction
|
||||
|
||||
for len(received) < count {
|
||||
select {
|
||||
case <-events:
|
||||
case ev := <-events:
|
||||
received = append(received, ev.Txs...)
|
||||
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 {
|
||||
case tx := <-events:
|
||||
return fmt.Errorf("more than %d events fired: %v", count, tx.Tx)
|
||||
case ev := <-events:
|
||||
return fmt.Errorf("more than %d events fired: %v", count, ev.Txs)
|
||||
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
// 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.
|
||||
}
|
||||
return nil
|
||||
@@ -158,8 +163,7 @@ func (c *testChain) State() (*state.StateDB, error) {
|
||||
// a state change between those fetches.
|
||||
stdb := c.statedb
|
||||
if *c.trigger {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(db))
|
||||
c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
// simulate that the new head block included tx0 and tx1
|
||||
c.statedb.SetNonce(c.address, 2)
|
||||
c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether))
|
||||
@@ -175,10 +179,9 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.GenerateKey()
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
trigger = false
|
||||
)
|
||||
|
||||
@@ -332,8 +335,7 @@ func TestTransactionChainFork(t *testing.T) {
|
||||
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
resetState := func() {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
||||
|
||||
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
@@ -362,8 +364,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
|
||||
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
resetState := func() {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
||||
|
||||
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
@@ -400,8 +401,8 @@ func TestTransactionDoubleNonce(t *testing.T) {
|
||||
t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash())
|
||||
}
|
||||
// Ensure the total transaction count is correct
|
||||
if len(pool.all) != 1 {
|
||||
t.Error("expected 1 total transactions, got", len(pool.all))
|
||||
if pool.all.Count() != 1 {
|
||||
t.Error("expected 1 total transactions, got", pool.all.Count())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,8 +424,8 @@ func TestTransactionMissingNonce(t *testing.T) {
|
||||
if pool.queue[addr].Len() != 1 {
|
||||
t.Error("expected 1 queued transaction, got", pool.queue[addr].Len())
|
||||
}
|
||||
if len(pool.all) != 1 {
|
||||
t.Error("expected 1 total transactions, got", len(pool.all))
|
||||
if pool.all.Count() != 1 {
|
||||
t.Error("expected 1 total transactions, got", pool.all.Count())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,8 +488,8 @@ func TestTransactionDropping(t *testing.T) {
|
||||
if pool.queue[account].Len() != 3 {
|
||||
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
|
||||
}
|
||||
if len(pool.all) != 6 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 6)
|
||||
if pool.all.Count() != 6 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 6)
|
||||
}
|
||||
pool.lockedReset(nil, nil)
|
||||
if pool.pending[account].Len() != 3 {
|
||||
@@ -497,8 +498,8 @@ func TestTransactionDropping(t *testing.T) {
|
||||
if pool.queue[account].Len() != 3 {
|
||||
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
|
||||
}
|
||||
if len(pool.all) != 6 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 6)
|
||||
if pool.all.Count() != 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
|
||||
pool.currentState.AddBalance(account, big.NewInt(-650))
|
||||
@@ -522,8 +523,8 @@ func TestTransactionDropping(t *testing.T) {
|
||||
if _, ok := pool.queue[account].txs.items[tx12.Nonce()]; ok {
|
||||
t.Errorf("out-of-fund queued transaction present: %v", tx11)
|
||||
}
|
||||
if len(pool.all) != 4 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
|
||||
if pool.all.Count() != 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
|
||||
pool.chain.(*testBlockChain).gasLimit = 100
|
||||
@@ -541,8 +542,8 @@ func TestTransactionDropping(t *testing.T) {
|
||||
if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok {
|
||||
t.Errorf("over-gased queued transaction present: %v", tx11)
|
||||
}
|
||||
if len(pool.all) != 2 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 2)
|
||||
if pool.all.Count() != 2 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,8 +554,7 @@ func TestTransactionPostponing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the postponing with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
@@ -596,8 +596,8 @@ func TestTransactionPostponing(t *testing.T) {
|
||||
if len(pool.queue) != 0 {
|
||||
t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
|
||||
}
|
||||
if len(pool.all) != len(txs) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs))
|
||||
if pool.all.Count() != len(txs) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs))
|
||||
}
|
||||
pool.lockedReset(nil, nil)
|
||||
if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) {
|
||||
@@ -606,8 +606,8 @@ func TestTransactionPostponing(t *testing.T) {
|
||||
if len(pool.queue) != 0 {
|
||||
t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
|
||||
}
|
||||
if len(pool.all) != len(txs) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs))
|
||||
if pool.all.Count() != 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
|
||||
for _, addr := range accs {
|
||||
@@ -656,8 +656,8 @@ func TestTransactionPostponing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(pool.all) != len(txs)/2 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs)/2)
|
||||
if pool.all.Count() != len(txs)/2 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs)/2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -675,7 +675,7 @@ func TestTransactionGapFilling(t *testing.T) {
|
||||
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
// 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)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
@@ -748,8 +748,8 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(pool.all) != int(testTxPoolConfig.AccountQueue) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), testTxPoolConfig.AccountQueue)
|
||||
if pool.all.Count() != int(testTxPoolConfig.AccountQueue) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), testTxPoolConfig.AccountQueue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,8 +769,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -858,8 +857,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
|
||||
evictionInterval = time.Second
|
||||
|
||||
// Create the pool to test the non-expiration enforcement
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -928,7 +926,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
|
||||
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
// 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)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
@@ -944,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)
|
||||
}
|
||||
}
|
||||
if len(pool.all) != int(testTxPoolConfig.AccountQueue+5) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), testTxPoolConfig.AccountQueue+5)
|
||||
if pool.all.Count() != int(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 {
|
||||
t.Fatalf("event firing failed: %v", err)
|
||||
@@ -995,8 +993,8 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
|
||||
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))
|
||||
}
|
||||
if len(pool1.all) != len(pool2.all) {
|
||||
t.Errorf("total transaction count mismatch: one-by-one algo %d, batch algo %d", 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", pool1.all.Count(), pool2.all.Count())
|
||||
}
|
||||
if err := validateTxPoolInternals(pool1); err != nil {
|
||||
t.Errorf("pool 1 internal state corrupted: %v", err)
|
||||
@@ -1013,8 +1011,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1060,8 +1057,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1095,8 +1091,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1144,15 +1139,14 @@ func TestTransactionPoolRepricing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the pricing enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// 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)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
@@ -1266,8 +1260,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the pricing enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
@@ -1329,8 +1322,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the pricing enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1341,12 +1333,12 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||
defer pool.Stop()
|
||||
|
||||
// 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)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
keys := make([]*ecdsa.PrivateKey, 3)
|
||||
keys := make([]*ecdsa.PrivateKey, 4)
|
||||
for i := 0; i < len(keys); i++ {
|
||||
keys[i], _ = crypto.GenerateKey()
|
||||
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
|
||||
@@ -1406,18 +1398,22 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
// Ensure that adding local transactions can push out even higher priced ones
|
||||
tx := pricedTransaction(1, 100000, big.NewInt(0), keys[2])
|
||||
if err := pool.AddLocal(tx); err != nil {
|
||||
t.Fatalf("failed to add underpriced local transaction: %v", err)
|
||||
ltx = pricedTransaction(1, 100000, big.NewInt(0), keys[2])
|
||||
if err := pool.AddLocal(ltx); err != nil {
|
||||
t.Fatalf("failed to append underpriced local transaction: %v", err)
|
||||
}
|
||||
ltx = pricedTransaction(0, 100000, big.NewInt(0), keys[3])
|
||||
if err := pool.AddLocal(ltx); err != nil {
|
||||
t.Fatalf("failed to add new underpriced local transaction: %v", err)
|
||||
}
|
||||
pending, queued = pool.Stats()
|
||||
if pending != 2 {
|
||||
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
|
||||
if pending != 3 {
|
||||
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
|
||||
}
|
||||
if queued != 2 {
|
||||
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
|
||||
if queued != 1 {
|
||||
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
|
||||
}
|
||||
if err := validateEvents(events, 1); err != nil {
|
||||
if err := validateEvents(events, 2); err != nil {
|
||||
t.Fatalf("local event firing failed: %v", err)
|
||||
}
|
||||
if err := validateTxPoolInternals(pool); err != nil {
|
||||
@@ -1432,8 +1428,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the pricing enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1444,7 +1439,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
|
||||
defer pool.Stop()
|
||||
|
||||
// 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)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
@@ -1499,15 +1494,14 @@ func TestTransactionReplacement(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the pricing enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// 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)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
@@ -1594,8 +1588,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
||||
os.Remove(journal)
|
||||
|
||||
// Create the original pool to inject transaction into the journal
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1693,8 +1686,7 @@ func TestTransactionStatusCheck(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the status retrievals with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
|
||||
@@ -12,10 +12,11 @@ import (
|
||||
|
||||
var _ = (*receiptMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
type Receipt struct {
|
||||
PostState hexutil.Bytes `json:"root"`
|
||||
Status hexutil.Uint `json:"status"`
|
||||
Status hexutil.Uint64 `json:"status"`
|
||||
CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Logs []*Log `json:"logs" gencodec:"required"`
|
||||
@@ -25,7 +26,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
var enc Receipt
|
||||
enc.PostState = r.PostState
|
||||
enc.Status = hexutil.Uint(r.Status)
|
||||
enc.Status = hexutil.Uint64(r.Status)
|
||||
enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed)
|
||||
enc.Bloom = r.Bloom
|
||||
enc.Logs = r.Logs
|
||||
@@ -35,10 +36,11 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
type Receipt struct {
|
||||
PostState *hexutil.Bytes `json:"root"`
|
||||
Status *hexutil.Uint `json:"status"`
|
||||
Status *hexutil.Uint64 `json:"status"`
|
||||
CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Logs []*Log `json:"logs" gencodec:"required"`
|
||||
@@ -54,7 +56,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
r.PostState = *dec.PostState
|
||||
}
|
||||
if dec.Status != nil {
|
||||
r.Status = uint(*dec.Status)
|
||||
r.Status = uint64(*dec.Status)
|
||||
}
|
||||
if dec.CumulativeGasUsed == nil {
|
||||
return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
|
||||
|
||||
@@ -36,17 +36,17 @@ var (
|
||||
|
||||
const (
|
||||
// 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 = uint(1)
|
||||
ReceiptStatusSuccessful = uint64(1)
|
||||
)
|
||||
|
||||
// Receipt represents the results of a transaction.
|
||||
type Receipt struct {
|
||||
// Consensus fields
|
||||
PostState []byte `json:"root"`
|
||||
Status uint `json:"status"`
|
||||
Status uint64 `json:"status"`
|
||||
CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Logs []*Log `json:"logs" gencodec:"required"`
|
||||
@@ -59,7 +59,7 @@ type Receipt struct {
|
||||
|
||||
type receiptMarshaling struct {
|
||||
PostState hexutil.Bytes
|
||||
Status hexutil.Uint
|
||||
Status hexutil.Uint64
|
||||
CumulativeGasUsed hexutil.Uint64
|
||||
GasUsed hexutil.Uint64
|
||||
}
|
||||
|
||||
@@ -339,11 +339,14 @@ type TransactionsByPriceAndNonce struct {
|
||||
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
|
||||
// Initialize a price based heap with the head transactions
|
||||
heads := make(TxByPrice, 0, len(txs))
|
||||
for _, accTxs := range txs {
|
||||
for from, accTxs := range txs {
|
||||
heads = append(heads, accTxs[0])
|
||||
// Ensure the sender address is from the signer
|
||||
acc, _ := Sender(signer, accTxs[0])
|
||||
txs[acc] = accTxs[1:]
|
||||
if from != acc {
|
||||
delete(txs, from)
|
||||
}
|
||||
}
|
||||
heap.Init(&heads)
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
// Find the previous and next nonce of this account
|
||||
prev, next := i-1, i+1
|
||||
for j := i - 1; j >= 0; j-- {
|
||||
if fromj, _ := Sender(signer, txs[j]); fromi == fromj {
|
||||
prev = j
|
||||
break
|
||||
}
|
||||
}
|
||||
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())
|
||||
|
||||
// If the next tx has different from account, the price must be lower than the current one
|
||||
if i+1 < len(txs) {
|
||||
next := txs[i+1]
|
||||
fromNext, _ := Sender(signer, next)
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,15 +139,15 @@ func (c *Contract) Value() *big.Int {
|
||||
}
|
||||
|
||||
// SetCode sets the code to the contract
|
||||
func (self *Contract) SetCode(hash common.Hash, code []byte) {
|
||||
self.Code = code
|
||||
self.CodeHash = hash
|
||||
func (c *Contract) SetCode(hash common.Hash, code []byte) {
|
||||
c.Code = code
|
||||
c.CodeHash = hash
|
||||
}
|
||||
|
||||
// SetCallCode sets the code of the contract and address of the backing data
|
||||
// object
|
||||
func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
|
||||
self.Code = code
|
||||
self.CodeHash = hash
|
||||
self.CodeAddr = addr
|
||||
func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
|
||||
c.Code = code
|
||||
c.CodeHash = hash
|
||||
c.CodeAddr = addr
|
||||
}
|
||||
|
||||
@@ -160,6 +160,11 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||
precompiles = PrecompiledContractsByzantium
|
||||
}
|
||||
if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
|
||||
// Calling a non existing account, don't do antything, but ping the tracer
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
|
||||
}
|
||||
return nil, gas, nil
|
||||
}
|
||||
evm.StateDB.CreateAccount(addr)
|
||||
|
||||
@@ -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)
|
||||
// 2. From a non-zero value address to a zero-value address (DELETE)
|
||||
// 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
|
||||
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)
|
||||
|
||||
return params.SstoreClearGas, nil
|
||||
} else {
|
||||
// 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 {
|
||||
return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.dup(evm.interpreter.intPool, int(size))
|
||||
|
||||
@@ -33,7 +33,7 @@ type (
|
||||
var errGasUintOverflow = errors.New("gas uint64 overflow")
|
||||
|
||||
type operation struct {
|
||||
// op is the operation function
|
||||
// execute is the operation function
|
||||
execute executionFunc
|
||||
// gasCost is the gas function and returns the gas required for execution
|
||||
gasCost gasFunc
|
||||
|
||||
@@ -31,9 +31,9 @@ import (
|
||||
|
||||
type Storage map[common.Hash]common.Hash
|
||||
|
||||
func (self Storage) Copy() Storage {
|
||||
func (s Storage) Copy() Storage {
|
||||
cpy := make(Storage)
|
||||
for key, value := range self {
|
||||
for key, value := range s {
|
||||
cpy[key] = value
|
||||
}
|
||||
|
||||
|
||||
@@ -51,14 +51,14 @@ func (m *Memory) Resize(size uint64) {
|
||||
}
|
||||
|
||||
// Get returns offset + size as a new slice
|
||||
func (self *Memory) Get(offset, size int64) (cpy []byte) {
|
||||
func (m *Memory) Get(offset, size int64) (cpy []byte) {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(self.store) > int(offset) {
|
||||
if len(m.store) > int(offset) {
|
||||
cpy = make([]byte, size)
|
||||
copy(cpy, self.store[offset:offset+size])
|
||||
copy(cpy, m.store[offset:offset+size])
|
||||
|
||||
return
|
||||
}
|
||||
@@ -67,13 +67,13 @@ func (self *Memory) Get(offset, size int64) (cpy []byte) {
|
||||
}
|
||||
|
||||
// GetPtr returns the offset + size
|
||||
func (self *Memory) GetPtr(offset, size int64) []byte {
|
||||
func (m *Memory) GetPtr(offset, size int64) []byte {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(self.store) > int(offset) {
|
||||
return self.store[offset : offset+size]
|
||||
if len(m.store) > int(offset) {
|
||||
return m.store[offset : offset+size]
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -375,10 +375,10 @@ var opCodeToString = map[OpCode]string{
|
||||
SWAP: "SWAP",
|
||||
}
|
||||
|
||||
func (o OpCode) String() string {
|
||||
str := opCodeToString[o]
|
||||
func (op OpCode) String() string {
|
||||
str := opCodeToString[op]
|
||||
if len(str) == 0 {
|
||||
return fmt.Sprintf("Missing opcode 0x%x", int(o))
|
||||
return fmt.Sprintf("Missing opcode 0x%x", int(op))
|
||||
}
|
||||
|
||||
return str
|
||||
|
||||
@@ -99,8 +99,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||
setDefaults(cfg)
|
||||
|
||||
if cfg.State == nil {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(db))
|
||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
}
|
||||
var (
|
||||
address = common.BytesToAddress([]byte("contract"))
|
||||
@@ -130,8 +129,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
||||
setDefaults(cfg)
|
||||
|
||||
if cfg.State == nil {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(db))
|
||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
}
|
||||
var (
|
||||
vmenv = NewEnv(cfg)
|
||||
|
||||
@@ -94,8 +94,7 @@ func TestExecute(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCall(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
state, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
address := common.HexToAddress("0x0a")
|
||||
state.SetCode(address, []byte{
|
||||
byte(vm.PUSH1), 10,
|
||||
|
||||
@@ -35,8 +35,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
secp256k1_N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
|
||||
secp256k1_halfN = new(big.Int).Div(secp256k1_N, big.NewInt(2))
|
||||
secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
|
||||
secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
|
||||
)
|
||||
|
||||
// Keccak256 calculates and returns the Keccak256 hash of the input data.
|
||||
@@ -68,7 +68,7 @@ func Keccak512(data ...[]byte) []byte {
|
||||
return d.Sum(nil)
|
||||
}
|
||||
|
||||
// Creates an ethereum address given the bytes and the nonce
|
||||
// CreateAddress creates an ethereum address given the bytes and the nonce
|
||||
func CreateAddress(b common.Address, nonce uint64) common.Address {
|
||||
data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
|
||||
return common.BytesToAddress(Keccak256(data)[12:])
|
||||
@@ -99,7 +99,7 @@ func toECDSA(d []byte, strict bool) (*ecdsa.PrivateKey, error) {
|
||||
priv.D = new(big.Int).SetBytes(d)
|
||||
|
||||
// The priv.D must < N
|
||||
if priv.D.Cmp(secp256k1_N) >= 0 {
|
||||
if priv.D.Cmp(secp256k1N) >= 0 {
|
||||
return nil, fmt.Errorf("invalid private key, >=N")
|
||||
}
|
||||
// The priv.D must not be zero or negative.
|
||||
@@ -184,11 +184,11 @@ func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool {
|
||||
}
|
||||
// reject upper range of s values (ECDSA malleability)
|
||||
// see discussion in secp256k1/libsecp256k1/include/secp256k1.h
|
||||
if homestead && s.Cmp(secp256k1_halfN) > 0 {
|
||||
if homestead && s.Cmp(secp256k1halfN) > 0 {
|
||||
return false
|
||||
}
|
||||
// Frontier: allow s to be in full N range
|
||||
return r.Cmp(secp256k1_N) < 0 && s.Cmp(secp256k1_N) < 0 && (v == 0 || v == 1)
|
||||
return r.Cmp(secp256k1N) < 0 && s.Cmp(secp256k1N) < 0 && (v == 0 || v == 1)
|
||||
}
|
||||
|
||||
func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
|
||||
|
||||
@@ -154,7 +154,7 @@ func TestValidateSignatureValues(t *testing.T) {
|
||||
minusOne := big.NewInt(-1)
|
||||
one := common.Big1
|
||||
zero := common.Big0
|
||||
secp256k1nMinus1 := new(big.Int).Sub(secp256k1_N, common.Big1)
|
||||
secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, common.Big1)
|
||||
|
||||
// correct v,r,s
|
||||
check(true, 0, one, one)
|
||||
@@ -181,9 +181,9 @@ func TestValidateSignatureValues(t *testing.T) {
|
||||
// correct sig with max r,s
|
||||
check(true, 0, secp256k1nMinus1, secp256k1nMinus1)
|
||||
// correct v, combinations of incorrect r,s at upper limit
|
||||
check(false, 0, secp256k1_N, secp256k1nMinus1)
|
||||
check(false, 0, secp256k1nMinus1, secp256k1_N)
|
||||
check(false, 0, secp256k1_N, secp256k1_N)
|
||||
check(false, 0, secp256k1N, secp256k1nMinus1)
|
||||
check(false, 0, secp256k1nMinus1, secp256k1N)
|
||||
check(false, 0, secp256k1N, secp256k1N)
|
||||
|
||||
// current callers ensures r,s cannot be negative, but let's test for that too
|
||||
// as crypto package could be used stand-alone
|
||||
|
||||
@@ -77,7 +77,7 @@ func (BitCurve *BitCurve) Params() *elliptic.CurveParams {
|
||||
}
|
||||
}
|
||||
|
||||
// IsOnBitCurve returns true if the given (x,y) lies on the BitCurve.
|
||||
// IsOnCurve returns true if the given (x,y) lies on the BitCurve.
|
||||
func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
|
||||
// y² = x³ + b
|
||||
y2 := new(big.Int).Mul(y, y) //y²
|
||||
|
||||
@@ -49,7 +49,7 @@ func randSig() []byte {
|
||||
// tests for malleability
|
||||
// highest bit of signature ECDSA s value must be 0, in the 33th byte
|
||||
func compactSigCheck(t *testing.T, sig []byte) {
|
||||
var b int = int(sig[32])
|
||||
var b = int(sig[32])
|
||||
if b < 0 {
|
||||
t.Errorf("highest bit is negative: %d", b)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ func VerifySignature(pubkey, hash, signature []byte) bool {
|
||||
return false
|
||||
}
|
||||
// Reject malleable signatures. libsecp256k1 does this check but btcec doesn't.
|
||||
if sig.S.Cmp(secp256k1_halfN) > 0 {
|
||||
if sig.S.Cmp(secp256k1halfN) > 0 {
|
||||
return false
|
||||
}
|
||||
return sig.Verify(hash, key)
|
||||
|
||||
@@ -19,6 +19,7 @@ package eth
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
@@ -28,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
@@ -343,8 +345,10 @@ func NewPrivateDebugAPI(config *params.ChainConfig, eth *Ethereum) *PrivateDebug
|
||||
|
||||
// Preimage is a debug API function that returns the preimage for a sha3 hash, if known.
|
||||
func (api *PrivateDebugAPI) Preimage(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||
db := core.PreimageTable(api.eth.ChainDb())
|
||||
return db.Get(hash.Bytes())
|
||||
if preimage := rawdb.ReadPreimage(api.eth.ChainDb(), hash); preimage != nil {
|
||||
return preimage, nil
|
||||
}
|
||||
return nil, errors.New("unknown preimage")
|
||||
}
|
||||
|
||||
// GetBadBLocks returns a list of the last 'bad blocks' that the client has seen on the network
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@@ -36,26 +37,26 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// EthApiBackend implements ethapi.Backend for full nodes
|
||||
type EthApiBackend struct {
|
||||
// EthAPIBackend implements ethapi.Backend for full nodes
|
||||
type EthAPIBackend struct {
|
||||
eth *Ethereum
|
||||
gpo *gasprice.Oracle
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) ChainConfig() *params.ChainConfig {
|
||||
func (b *EthAPIBackend) ChainConfig() *params.ChainConfig {
|
||||
return b.eth.chainConfig
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) CurrentBlock() *types.Block {
|
||||
func (b *EthAPIBackend) CurrentBlock() *types.Block {
|
||||
return b.eth.blockchain.CurrentBlock()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SetHead(number uint64) {
|
||||
func (b *EthAPIBackend) SetHead(number uint64) {
|
||||
b.eth.protocolManager.downloader.Cancel()
|
||||
b.eth.blockchain.SetHead(number)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
|
||||
func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
|
||||
// Pending block is only known by the miner
|
||||
if blockNr == rpc.PendingBlockNumber {
|
||||
block := b.eth.miner.PendingBlock()
|
||||
@@ -68,7 +69,7 @@ func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNum
|
||||
return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr)), nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) {
|
||||
func (b *EthAPIBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) {
|
||||
// Pending block is only known by the miner
|
||||
if blockNr == rpc.PendingBlockNumber {
|
||||
block := b.eth.miner.PendingBlock()
|
||||
@@ -81,7 +82,7 @@ func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumb
|
||||
return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
||||
func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
||||
// Pending state is only known by the miner
|
||||
if blockNr == rpc.PendingBlockNumber {
|
||||
block, state := b.eth.miner.Pending()
|
||||
@@ -96,16 +97,23 @@ func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.
|
||||
return stateDb, header, err
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) {
|
||||
return b.eth.blockchain.GetBlockByHash(blockHash), nil
|
||||
func (b *EthAPIBackend) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||
return b.eth.blockchain.GetBlockByHash(hash), nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
|
||||
return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil
|
||||
func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||
if number := rawdb.ReadHeaderNumber(b.eth.chainDb, hash); number != nil {
|
||||
return rawdb.ReadReceipts(b.eth.chainDb, hash, *number), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) {
|
||||
receipts := core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash))
|
||||
func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
|
||||
number := rawdb.ReadHeaderNumber(b.eth.chainDb, hash)
|
||||
if number == nil {
|
||||
return nil, nil
|
||||
}
|
||||
receipts := rawdb.ReadReceipts(b.eth.chainDb, hash, *number)
|
||||
if receipts == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -116,11 +124,11 @@ func (b *EthApiBackend) GetLogs(ctx context.Context, blockHash common.Hash) ([][
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
|
||||
func (b *EthAPIBackend) GetTd(blockHash common.Hash) *big.Int {
|
||||
return b.eth.blockchain.GetTdByHash(blockHash)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
|
||||
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
|
||||
state.SetBalance(msg.From(), math.MaxBig256)
|
||||
vmError := func() error { return nil }
|
||||
|
||||
@@ -128,31 +136,31 @@ func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state *sta
|
||||
return vm.NewEVM(context, state, b.eth.chainConfig, vmCfg), vmError, nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||
func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||
return b.eth.BlockChain().SubscribeRemovedLogsEvent(ch)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
||||
func (b *EthAPIBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
||||
return b.eth.BlockChain().SubscribeChainEvent(ch)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
|
||||
func (b *EthAPIBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
|
||||
return b.eth.BlockChain().SubscribeChainHeadEvent(ch)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
|
||||
func (b *EthAPIBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
|
||||
return b.eth.BlockChain().SubscribeChainSideEvent(ch)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||
func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||
return b.eth.BlockChain().SubscribeLogsEvent(ch)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
return b.eth.txPool.AddLocal(signedTx)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetPoolTransactions() (types.Transactions, error) {
|
||||
func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
|
||||
pending, err := b.eth.txPool.Pending()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -164,56 +172,56 @@ func (b *EthApiBackend) GetPoolTransactions() (types.Transactions, error) {
|
||||
return txs, nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetPoolTransaction(hash common.Hash) *types.Transaction {
|
||||
func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction {
|
||||
return b.eth.txPool.Get(hash)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||
return b.eth.txPool.State().GetNonce(addr), nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) Stats() (pending int, queued int) {
|
||||
func (b *EthAPIBackend) Stats() (pending int, queued int) {
|
||||
return b.eth.txPool.Stats()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
||||
func (b *EthAPIBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
||||
return b.eth.TxPool().Content()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
||||
return b.eth.TxPool().SubscribeTxPreEvent(ch)
|
||||
func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||
return b.eth.TxPool().SubscribeNewTxsEvent(ch)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) Downloader() *downloader.Downloader {
|
||||
func (b *EthAPIBackend) Downloader() *downloader.Downloader {
|
||||
return b.eth.Downloader()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) ProtocolVersion() int {
|
||||
func (b *EthAPIBackend) ProtocolVersion() int {
|
||||
return b.eth.EthVersion()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
|
||||
func (b *EthAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
|
||||
return b.gpo.SuggestPrice(ctx)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) ChainDb() ethdb.Database {
|
||||
func (b *EthAPIBackend) ChainDb() ethdb.Database {
|
||||
return b.eth.ChainDb()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) EventMux() *event.TypeMux {
|
||||
func (b *EthAPIBackend) EventMux() *event.TypeMux {
|
||||
return b.eth.EventMux()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) AccountManager() *accounts.Manager {
|
||||
func (b *EthAPIBackend) AccountManager() *accounts.Manager {
|
||||
return b.eth.AccountManager()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) BloomStatus() (uint64, uint64) {
|
||||
func (b *EthAPIBackend) BloomStatus() (uint64, uint64) {
|
||||
sections, _, _ := b.eth.bloomIndexer.Sections()
|
||||
return params.BloomBitsBlocks, sections
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {
|
||||
func (b *EthAPIBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {
|
||||
for i := 0; i < bloomFilterThreads; i++ {
|
||||
go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests)
|
||||
}
|
||||
|
||||
@@ -31,8 +31,7 @@ var dumper = spew.ConfigState{Indent: " "}
|
||||
func TestStorageRangeAt(t *testing.T) {
|
||||
// Create a state where account 0x010000... has a few storage entries.
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
state, _ = state.New(common.Hash{}, state.NewDatabase(db))
|
||||
state, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
addr = common.Address{0x01}
|
||||
keys = []common.Hash{ // hashes of Keys of storage
|
||||
common.HexToHash("340dd630ad21bf010b4e676dbfa9ba9a02175262d1fa356232cfde6cb5b47ef2"),
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@@ -201,7 +202,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
|
||||
break
|
||||
}
|
||||
task.statedb.DeleteSuicides()
|
||||
task.statedb.Finalise(true)
|
||||
task.results[i] = &txTraceResult{Result: res}
|
||||
}
|
||||
// Stream the result back to the user or abort on teardown
|
||||
@@ -533,7 +534,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
|
||||
// and returns them as a JSON object.
|
||||
func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
||||
// Retrieve the transaction and assemble its EVM context
|
||||
tx, blockHash, _, index := core.GetTransaction(api.eth.ChainDb(), hash)
|
||||
tx, blockHash, _, index := rawdb.ReadTransaction(api.eth.ChainDb(), hash)
|
||||
if tx == nil {
|
||||
return nil, fmt.Errorf("transaction %x not found", hash)
|
||||
}
|
||||
@@ -640,7 +641,8 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
|
||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
|
||||
}
|
||||
statedb.DeleteSuicides()
|
||||
// Ensure any modifications are committed to the state
|
||||
statedb.Finalise(true)
|
||||
}
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
@@ -63,8 +64,7 @@ type Ethereum struct {
|
||||
chainConfig *params.ChainConfig
|
||||
|
||||
// Channel for shutting down the service
|
||||
shutdownChan chan bool // Channel for shutting down the Ethereum
|
||||
stopDbUpgrade func() error // stop chain db sequential key upgrade
|
||||
shutdownChan chan bool // Channel for shutting down the Ethereum
|
||||
|
||||
// Handlers
|
||||
txPool *core.TxPool
|
||||
@@ -82,7 +82,7 @@ type Ethereum struct {
|
||||
bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
|
||||
bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports
|
||||
|
||||
ApiBackend *EthApiBackend
|
||||
APIBackend *EthAPIBackend
|
||||
|
||||
miner *miner.Miner
|
||||
gasPrice *big.Int
|
||||
@@ -112,7 +112,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stopDbUpgrade := upgradeDeduplicateData(chainDb)
|
||||
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
|
||||
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
|
||||
return nil, genesisErr
|
||||
@@ -127,7 +126,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
accountManager: ctx.AccountManager,
|
||||
engine: CreateConsensusEngine(ctx, &config.Ethash, chainConfig, chainDb),
|
||||
shutdownChan: make(chan bool),
|
||||
stopDbUpgrade: stopDbUpgrade,
|
||||
networkId: config.NetworkId,
|
||||
gasPrice: config.GasPrice,
|
||||
etherbase: config.Etherbase,
|
||||
@@ -138,11 +136,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
log.Info("Initialising Ethereum protocol", "versions", ProtocolVersions, "network", config.NetworkId)
|
||||
|
||||
if !config.SkipBcVersionCheck {
|
||||
bcVersion := core.GetBlockChainVersion(chainDb)
|
||||
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
|
||||
if bcVersion != core.BlockChainVersion && bcVersion != 0 {
|
||||
return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, core.BlockChainVersion)
|
||||
}
|
||||
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
|
||||
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
|
||||
}
|
||||
var (
|
||||
vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
|
||||
@@ -156,7 +154,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
||||
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
|
||||
eth.blockchain.SetHead(compat.RewindTo)
|
||||
core.WriteChainConfig(chainDb, genesisHash, chainConfig)
|
||||
rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
|
||||
}
|
||||
eth.bloomIndexer.Start(eth.blockchain)
|
||||
|
||||
@@ -171,12 +169,12 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
|
||||
eth.miner.SetExtra(makeExtraData(config.ExtraData))
|
||||
|
||||
eth.ApiBackend = &EthApiBackend{eth, nil}
|
||||
eth.APIBackend = &EthAPIBackend{eth, nil}
|
||||
gpoParams := config.GPO
|
||||
if gpoParams.Default == nil {
|
||||
gpoParams.Default = config.GasPrice
|
||||
}
|
||||
eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams)
|
||||
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
|
||||
|
||||
return eth, nil
|
||||
}
|
||||
@@ -217,14 +215,14 @@ func CreateConsensusEngine(ctx *node.ServiceContext, config *ethash.Config, chai
|
||||
return clique.New(chainConfig.Clique, db)
|
||||
}
|
||||
// Otherwise assume proof-of-work
|
||||
switch {
|
||||
case config.PowMode == ethash.ModeFake:
|
||||
switch config.PowMode {
|
||||
case ethash.ModeFake:
|
||||
log.Warn("Ethash used in fake mode")
|
||||
return ethash.NewFaker()
|
||||
case config.PowMode == ethash.ModeTest:
|
||||
case ethash.ModeTest:
|
||||
log.Warn("Ethash used in test mode")
|
||||
return ethash.NewTester()
|
||||
case config.PowMode == ethash.ModeShared:
|
||||
case ethash.ModeShared:
|
||||
log.Warn("Ethash used in shared mode")
|
||||
return ethash.NewShared()
|
||||
default:
|
||||
@@ -241,10 +239,10 @@ 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.
|
||||
func (s *Ethereum) APIs() []rpc.API {
|
||||
apis := ethapi.GetAPIs(s.ApiBackend)
|
||||
apis := ethapi.GetAPIs(s.APIBackend)
|
||||
|
||||
// Append any APIs exposed explicitly by the consensus engine
|
||||
apis = append(apis, s.engine.APIs(s.BlockChain())...)
|
||||
@@ -274,7 +272,7 @@ func (s *Ethereum) APIs() []rpc.API {
|
||||
}, {
|
||||
Namespace: "eth",
|
||||
Version: "1.0",
|
||||
Service: filters.NewPublicFilterAPI(s.ApiBackend, false),
|
||||
Service: filters.NewPublicFilterAPI(s.APIBackend, false),
|
||||
Public: true,
|
||||
}, {
|
||||
Namespace: "admin",
|
||||
@@ -325,13 +323,13 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
|
||||
return common.Address{}, fmt.Errorf("etherbase must be explicitly specified")
|
||||
}
|
||||
|
||||
// set in js console via admin interface or wrapper from cli flags
|
||||
func (self *Ethereum) SetEtherbase(etherbase common.Address) {
|
||||
self.lock.Lock()
|
||||
self.etherbase = etherbase
|
||||
self.lock.Unlock()
|
||||
// SetEtherbase sets the mining reward address.
|
||||
func (s *Ethereum) SetEtherbase(etherbase common.Address) {
|
||||
s.lock.Lock()
|
||||
s.etherbase = etherbase
|
||||
s.lock.Unlock()
|
||||
|
||||
self.miner.SetEtherbase(etherbase)
|
||||
s.miner.SetEtherbase(etherbase)
|
||||
}
|
||||
|
||||
func (s *Ethereum) StartMining(local bool) error {
|
||||
@@ -411,9 +409,6 @@ func (s *Ethereum) Start(srvr *p2p.Server) error {
|
||||
// Stop implements node.Service, terminating all internal goroutines used by the
|
||||
// Ethereum protocol.
|
||||
func (s *Ethereum) Stop() error {
|
||||
if s.stopDbUpgrade != nil {
|
||||
s.stopDbUpgrade()
|
||||
}
|
||||
s.bloomIndexer.Close()
|
||||
s.blockchain.Stop()
|
||||
s.protocolManager.Stop()
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/bitutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
@@ -60,8 +61,8 @@ func (eth *Ethereum) startBloomHandlers() {
|
||||
task := <-request
|
||||
task.Bitsets = make([][]byte, len(task.Sections))
|
||||
for i, section := range task.Sections {
|
||||
head := core.GetCanonicalHash(eth.chainDb, (section+1)*params.BloomBitsBlocks-1)
|
||||
if compVector, err := core.GetBloomBits(eth.chainDb, task.Bit, section, head); err == nil {
|
||||
head := rawdb.ReadCanonicalHash(eth.chainDb, (section+1)*params.BloomBitsBlocks-1)
|
||||
if compVector, err := rawdb.ReadBloomBits(eth.chainDb, task.Bit, section, head); err == nil {
|
||||
if blob, err := bitutil.DecompressBytes(compVector, int(params.BloomBitsBlocks)/8); err == nil {
|
||||
task.Bitsets[i] = blob
|
||||
} else {
|
||||
@@ -107,7 +108,7 @@ func NewBloomIndexer(db ethdb.Database, size uint64) *core.ChainIndexer {
|
||||
db: db,
|
||||
size: size,
|
||||
}
|
||||
table := ethdb.NewTable(db, string(core.BloomBitsIndexPrefix))
|
||||
table := ethdb.NewTable(db, string(rawdb.BloomBitsIndexPrefix))
|
||||
|
||||
return core.NewChainIndexer(db, table, backend, size, bloomConfirms, bloomThrottling, "bloombits")
|
||||
}
|
||||
@@ -137,7 +138,7 @@ func (b *BloomIndexer) Commit() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
core.WriteBloomBits(batch, uint(i), b.section, b.head, bitutil.CompressBytes(bits))
|
||||
rawdb.WriteBloomBits(batch, uint(i), b.section, b.head, bitutil.CompressBytes(bits))
|
||||
}
|
||||
return batch.Write()
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package eth implements the Ethereum protocol.
|
||||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
var deduplicateData = []byte("dbUpgrade_20170714deduplicateData")
|
||||
|
||||
// upgradeDeduplicateData checks the chain database version and
|
||||
// starts a background process to make upgrades if necessary.
|
||||
// Returns a stop function that blocks until the process has
|
||||
// been safely stopped.
|
||||
func upgradeDeduplicateData(db ethdb.Database) func() error {
|
||||
// If the database is already converted or empty, bail out
|
||||
data, _ := db.Get(deduplicateData)
|
||||
if len(data) > 0 && data[0] == 42 {
|
||||
return nil
|
||||
}
|
||||
if data, _ := db.Get([]byte("LastHeader")); len(data) == 0 {
|
||||
db.Put(deduplicateData, []byte{42})
|
||||
return nil
|
||||
}
|
||||
// Start the deduplication upgrade on a new goroutine
|
||||
log.Warn("Upgrading database to use lookup entries")
|
||||
stop := make(chan chan error)
|
||||
|
||||
go func() {
|
||||
// Create an iterator to read the entire database and covert old lookup entires
|
||||
it := db.(*ethdb.LDBDatabase).NewIterator()
|
||||
defer func() {
|
||||
if it != nil {
|
||||
it.Release()
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
converted uint64
|
||||
failed error
|
||||
)
|
||||
for failed == nil && it.Next() {
|
||||
// Skip any entries that don't look like old transaction meta entries (<hash>0x01)
|
||||
key := it.Key()
|
||||
if len(key) != common.HashLength+1 || key[common.HashLength] != 0x01 {
|
||||
continue
|
||||
}
|
||||
// Skip any entries that don't contain metadata (name clash between <hash>0x01 and <some-prefix><hash>)
|
||||
var meta struct {
|
||||
BlockHash common.Hash
|
||||
BlockIndex uint64
|
||||
Index uint64
|
||||
}
|
||||
if err := rlp.DecodeBytes(it.Value(), &meta); err != nil {
|
||||
continue
|
||||
}
|
||||
// Skip any already upgraded entries (clash due to <hash> ending with 0x01 (old suffix))
|
||||
hash := key[:common.HashLength]
|
||||
|
||||
if hash[0] == byte('l') {
|
||||
// Potential clash, the "old" `hash` must point to a live transaction.
|
||||
if tx, _, _, _ := core.GetTransaction(db, common.BytesToHash(hash)); tx == nil || !bytes.Equal(tx.Hash().Bytes(), hash) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Convert the old metadata to a new lookup entry, delete duplicate data
|
||||
if failed = db.Put(append([]byte("l"), hash...), it.Value()); failed == nil { // Write the new lookup entry
|
||||
if failed = db.Delete(hash); failed == nil { // Delete the duplicate transaction data
|
||||
if failed = db.Delete(append([]byte("receipts-"), hash...)); failed == nil { // Delete the duplicate receipt data
|
||||
if failed = db.Delete(key); failed != nil { // Delete the old transaction metadata
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Bump the conversion counter, and recreate the iterator occasionally to
|
||||
// avoid too high memory consumption.
|
||||
converted++
|
||||
if converted%100000 == 0 {
|
||||
it.Release()
|
||||
it = db.(*ethdb.LDBDatabase).NewIterator()
|
||||
it.Seek(key)
|
||||
|
||||
log.Info("Deduplicating database entries", "deduped", converted)
|
||||
}
|
||||
// Check for termination, or continue after a bit of a timeout
|
||||
select {
|
||||
case errc := <-stop:
|
||||
errc <- nil
|
||||
return
|
||||
case <-time.After(time.Microsecond * 100):
|
||||
}
|
||||
}
|
||||
// Upgrade finished, mark a such and terminate
|
||||
if failed == nil {
|
||||
log.Info("Database deduplication successful", "deduped", converted)
|
||||
db.Put(deduplicateData, []byte{42})
|
||||
} else {
|
||||
log.Error("Database deduplication failed", "deduped", converted, "err", failed)
|
||||
}
|
||||
it.Release()
|
||||
it = nil
|
||||
|
||||
errc := <-stop
|
||||
errc <- failed
|
||||
}()
|
||||
// Assembly the cancellation callback
|
||||
return func() error {
|
||||
errc := make(chan error)
|
||||
stop <- errc
|
||||
return <-errc
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user