mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
Merge branch 'master' of github.com:ethereum/go-ethereum into swarm-termbin
This commit is contained in:
commit
4cfa5127ae
277 changed files with 16108 additions and 5399 deletions
33
.travis.yml
33
.travis.yml
|
|
@ -5,29 +5,38 @@ matrix:
|
|||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
go: 1.5.4
|
||||
env:
|
||||
- GO15VENDOREXPERIMENT=1
|
||||
- os: linux
|
||||
dist: trusty
|
||||
go: 1.6.2
|
||||
- os: linux
|
||||
dist: trusty
|
||||
sudo: required
|
||||
go: 1.7.5
|
||||
script:
|
||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse
|
||||
- sudo modprobe fuse
|
||||
- sudo chmod 666 /dev/fuse
|
||||
- sudo chown root:$USER /etc/fuse.conf
|
||||
- go run build/ci.go install
|
||||
- go run build/ci.go test -coverage
|
||||
|
||||
# These are the latest Go versions, only run go vet and misspell on these
|
||||
# These are the latest Go versions.
|
||||
- os: linux
|
||||
dist: trusty
|
||||
sudo: required
|
||||
go: 1.8
|
||||
script:
|
||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse
|
||||
- sudo modprobe fuse
|
||||
- sudo chmod 666 /dev/fuse
|
||||
- sudo chown root:$USER /etc/fuse.conf
|
||||
- go run build/ci.go install
|
||||
- go run build/ci.go test -coverage -vet -misspell
|
||||
- go run build/ci.go test -coverage -misspell
|
||||
|
||||
- os: osx
|
||||
go: 1.8
|
||||
sudo: required
|
||||
script:
|
||||
- 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 -vet -misspell
|
||||
- go run build/ci.go test -coverage -misspell
|
||||
|
||||
# This builder does the Ubuntu PPA and Linux Azure uploads
|
||||
- os: linux
|
||||
|
|
@ -158,7 +167,7 @@ matrix:
|
|||
|
||||
# Build the iOS framework and upload it to CocoaPods and Azure
|
||||
- gem uninstall cocoapods -a
|
||||
- gem install cocoapods --pre
|
||||
- gem install cocoapods
|
||||
|
||||
- mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak
|
||||
- sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb
|
||||
|
|
|
|||
9
Makefile
9
Makefile
|
|
@ -40,6 +40,15 @@ test: all
|
|||
clean:
|
||||
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
||||
|
||||
# The devtools target installs tools required for 'go generate'.
|
||||
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
||||
|
||||
devtools:
|
||||
go get -u golang.org/x/tools/cmd/stringer
|
||||
go get -u github.com/jteeuwen/go-bindata/go-bindata
|
||||
go get -u github.com/fjl/gencodec
|
||||
go install ./cmd/abigen
|
||||
|
||||
# Cross Compilation Targets (xgo)
|
||||
|
||||
geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ For prerequisites and detailed build instructions please read the
|
|||
[Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum)
|
||||
on the wiki.
|
||||
|
||||
Building geth requires both a Go and a C compiler.
|
||||
Building geth requires both a Go (version 1.7 or later) and a C compiler.
|
||||
You can install them using your favourite package manager.
|
||||
Once the dependencies are installed, run
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@
|
|||
package bind
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package backends
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -26,6 +27,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"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"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -33,12 +35,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier)
|
||||
var chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), EIP150Block: new(big.Int), EIP158Block: new(big.Int)}
|
||||
|
||||
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
|
||||
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
||||
|
||||
|
|
@ -59,11 +57,12 @@ type SimulatedBackend struct {
|
|||
|
||||
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
||||
// for testing purposes.
|
||||
func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend {
|
||||
func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
|
||||
database, _ := ethdb.NewMemDatabase()
|
||||
core.WriteGenesisBlockForTesting(database, accounts...)
|
||||
blockchain, _ := core.NewBlockChain(database, chainConfig, new(core.FakePow), new(event.TypeMux), vm.Config{})
|
||||
backend := &SimulatedBackend{database: database, blockchain: blockchain}
|
||||
genesis := core.Genesis{Config: params.AllProtocolChanges, Alloc: alloc}
|
||||
genesis.MustCommit(database)
|
||||
blockchain, _ := core.NewBlockChain(database, genesis.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
backend := &SimulatedBackend{database: database, blockchain: blockchain, config: genesis.Config}
|
||||
backend.rollback()
|
||||
return backend
|
||||
}
|
||||
|
|
@ -89,7 +88,7 @@ func (b *SimulatedBackend) Rollback() {
|
|||
}
|
||||
|
||||
func (b *SimulatedBackend) rollback() {
|
||||
blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
|
||||
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
|
||||
b.pendingBlock = blocks[0]
|
||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
|
||||
}
|
||||
|
|
@ -252,7 +251,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
|||
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain)
|
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
vmenv := vm.NewEVM(evmContext, statedb, chainConfig, vm.Config{})
|
||||
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
|
||||
gaspool := new(core.GasPool).AddGas(math.MaxBig256)
|
||||
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
||||
return ret, gasUsed, err
|
||||
|
|
@ -273,7 +272,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
|||
panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
|
||||
}
|
||||
|
||||
blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||
for _, tx := range b.pendingBlock.Transactions() {
|
||||
block.AddTx(tx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package bind
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -26,7 +27,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// SignerFn is a signer function callback when a contract requires a method to
|
||||
|
|
@ -35,7 +35,8 @@ type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Tra
|
|||
|
||||
// CallOpts is the collection of options to fine tune a contract call request.
|
||||
type CallOpts struct {
|
||||
Pending bool // Whether to operate on the pending state or the last known one
|
||||
Pending bool // Whether to operate on the pending state or the last known one
|
||||
From common.Address // Optional the sender address, otherwise the first account is used
|
||||
|
||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||
}
|
||||
|
|
@ -108,7 +109,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
|
|||
return err
|
||||
}
|
||||
var (
|
||||
msg = ethereum.CallMsg{To: &c.address, Data: input}
|
||||
msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
|
||||
ctx = ensureContext(opts.Context)
|
||||
code []byte
|
||||
output []byte
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth := bind.NewKeyedTransactor(key)
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||
|
||||
// Deploy an interaction tester contract and call a transaction on it
|
||||
_, _, interactor, err := DeployInteractor(auth, sim, "Deploy string")
|
||||
|
|
@ -210,7 +210,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth := bind.NewKeyedTransactor(key)
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||
|
||||
// Deploy a tuple tester contract and execute a structured call on it
|
||||
_, _, getter, err := DeployGetter(auth, sim)
|
||||
|
|
@ -242,7 +242,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth := bind.NewKeyedTransactor(key)
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||
|
||||
// Deploy a tuple tester contract and execute a structured call on it
|
||||
_, _, tupler, err := DeployTupler(auth, sim)
|
||||
|
|
@ -284,7 +284,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth := bind.NewKeyedTransactor(key)
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||
|
||||
// Deploy a slice tester contract and execute a n array call on it
|
||||
_, _, slicer, err := DeploySlicer(auth, sim)
|
||||
|
|
@ -318,7 +318,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth := bind.NewKeyedTransactor(key)
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||
|
||||
// Deploy a default method invoker contract and execute its default method
|
||||
_, _, defaulter, err := DeployDefaulter(auth, sim)
|
||||
|
|
@ -351,7 +351,7 @@ var bindTests = []struct {
|
|||
`[{"constant":true,"inputs":[],"name":"String","outputs":[{"name":"","type":"string"}],"type":"function"}]`,
|
||||
`
|
||||
// Create a simulator and wrap a non-deployed contract
|
||||
sim := backends.NewSimulatedBackend()
|
||||
sim := backends.NewSimulatedBackend(nil)
|
||||
|
||||
nonexistent, err := NewNonExistent(common.Address{}, sim)
|
||||
if err != nil {
|
||||
|
|
@ -387,7 +387,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth := bind.NewKeyedTransactor(key)
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||
|
||||
// Deploy a funky gas pattern contract
|
||||
_, _, limiter, err := DeployFunkyGasPattern(auth, sim)
|
||||
|
|
@ -408,6 +408,45 @@ var bindTests = []struct {
|
|||
}
|
||||
`,
|
||||
},
|
||||
// Test that constant functions can be called from an (optional) specified address
|
||||
{
|
||||
`CallFrom`,
|
||||
`
|
||||
contract CallFrom {
|
||||
function callFrom() constant returns(address) {
|
||||
return msg.sender;
|
||||
}
|
||||
}
|
||||
`, `6060604052346000575b6086806100176000396000f300606060405263ffffffff60e060020a60003504166349f8e98281146022575b6000565b34600057602c6055565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b335b905600a165627a7a72305820aef6b7685c0fa24ba6027e4870404a57df701473fe4107741805c19f5138417c0029`,
|
||||
`[{"constant":true,"inputs":[],"name":"callFrom","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}]`,
|
||||
`
|
||||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth := bind.NewKeyedTransactor(key)
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||
|
||||
// Deploy a sender tester contract and execute a structured call on it
|
||||
_, _, callfrom, err := DeployCallFrom(auth, sim)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to deploy sender contract: %v", err)
|
||||
}
|
||||
sim.Commit()
|
||||
|
||||
if res, err := callfrom.CallFrom(nil); err != nil {
|
||||
t.Errorf("Failed to call constant function: %v", err)
|
||||
} else if res != (common.Address{}) {
|
||||
t.Errorf("Invalid address returned, want: %x, got: %x", (common.Address{}), res)
|
||||
}
|
||||
|
||||
for _, addr := range []common.Address{common.Address{}, common.Address{1}, common.Address{2}} {
|
||||
if res, err := callfrom.CallFrom(&bind.CallOpts{From: addr}); err != nil {
|
||||
t.Fatalf("Failed to call constant function: %v", err)
|
||||
} else if res != addr {
|
||||
t.Fatalf("Invalid address returned, want: %x, got: %x", addr, res)
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
// Tests that packages generated by the binder can be successfully compiled and
|
||||
|
|
@ -419,7 +458,7 @@ func TestBindings(t *testing.T) {
|
|||
t.Skip("go sdk not found for testing")
|
||||
}
|
||||
// Skip the test if the go-ethereum sources are symlinked (https://github.com/golang/go/issues/14845)
|
||||
linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend())\n}")
|
||||
linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend(nil))\n}")
|
||||
linkTestDeps, err := imports.Process("", []byte(linkTestCode), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed check for goimports symlink bug: %v", err)
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@
|
|||
package bind
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// WaitMined waits for tx to be mined on the blockchain.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package bind_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -27,7 +28,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
|
|
@ -53,9 +53,8 @@ var waitDeployedTests = map[string]struct {
|
|||
|
||||
func TestWaitDeployed(t *testing.T) {
|
||||
for name, test := range waitDeployedTests {
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAccount{
|
||||
Address: crypto.PubkeyToAddress(testKey.PublicKey),
|
||||
Balance: big.NewInt(10000000000),
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{
|
||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)},
|
||||
})
|
||||
|
||||
// Create the transaction.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ package usbwallet
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -55,7 +56,12 @@ type LedgerHub struct {
|
|||
updating bool // Whether the event notification loop is running
|
||||
|
||||
quit chan chan error
|
||||
lock sync.RWMutex
|
||||
|
||||
stateLock sync.RWMutex // Protects the internals of the hub from racey access
|
||||
|
||||
// TODO(karalabe): remove if hotplug lands on Windows
|
||||
commsPend int // Number of operations blocking enumeration
|
||||
commsLock sync.Mutex // Lock protecting the pending counter and enumeration
|
||||
}
|
||||
|
||||
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
|
||||
|
|
@ -76,8 +82,8 @@ func (hub *LedgerHub) Wallets() []accounts.Wallet {
|
|||
// Make sure the list of wallets is up to date
|
||||
hub.refreshWallets()
|
||||
|
||||
hub.lock.RLock()
|
||||
defer hub.lock.RUnlock()
|
||||
hub.stateLock.RLock()
|
||||
defer hub.stateLock.RUnlock()
|
||||
|
||||
cpy := make([]accounts.Wallet, len(hub.wallets))
|
||||
copy(cpy, hub.wallets)
|
||||
|
|
@ -88,15 +94,29 @@ func (hub *LedgerHub) Wallets() []accounts.Wallet {
|
|||
// list of wallets based on the found devices.
|
||||
func (hub *LedgerHub) refreshWallets() {
|
||||
// Don't scan the USB like crazy it the user fetches wallets in a loop
|
||||
hub.lock.RLock()
|
||||
hub.stateLock.RLock()
|
||||
elapsed := time.Since(hub.refreshed)
|
||||
hub.lock.RUnlock()
|
||||
hub.stateLock.RUnlock()
|
||||
|
||||
if elapsed < ledgerRefreshThrottling {
|
||||
return
|
||||
}
|
||||
// Retrieve the current list of Ledger devices
|
||||
var ledgers []hid.DeviceInfo
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
// hidapi on Linux opens the device during enumeration to retrieve some infos,
|
||||
// breaking the Ledger protocol if that is waiting for user confirmation. This
|
||||
// is a bug acknowledged at Ledger, but it won't be fixed on old devices so we
|
||||
// need to prevent concurrent comms ourselves. The more elegant solution would
|
||||
// be to ditch enumeration in favor of hutplug events, but that don't work yet
|
||||
// on Windows so if we need to hack it anyway, this is more elegant for now.
|
||||
hub.commsLock.Lock()
|
||||
if hub.commsPend > 0 { // A confirmation is pending, don't refresh
|
||||
hub.commsLock.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, info := range hid.Enumerate(0, 0) { // Can't enumerate directly, one valid ID is the 0 wildcard
|
||||
for _, id := range ledgerDeviceIDs {
|
||||
if info.VendorID == id.Vendor && info.ProductID == id.Product {
|
||||
|
|
@ -105,8 +125,12 @@ func (hub *LedgerHub) refreshWallets() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if runtime.GOOS == "linux" {
|
||||
// See rationale before the enumeration why this is needed and only on Linux.
|
||||
hub.commsLock.Unlock()
|
||||
}
|
||||
// Transform the current list of wallets into the new one
|
||||
hub.lock.Lock()
|
||||
hub.stateLock.Lock()
|
||||
|
||||
wallets := make([]accounts.Wallet, 0, len(ledgers))
|
||||
events := []accounts.WalletEvent{}
|
||||
|
|
@ -121,7 +145,7 @@ func (hub *LedgerHub) refreshWallets() {
|
|||
}
|
||||
// If there are no more wallets or the device is before the next, wrap new wallet
|
||||
if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 {
|
||||
wallet := &ledgerWallet{url: &url, info: ledger, log: log.New("url", url)}
|
||||
wallet := &ledgerWallet{hub: hub, url: &url, info: ledger, log: log.New("url", url)}
|
||||
|
||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true})
|
||||
wallets = append(wallets, wallet)
|
||||
|
|
@ -140,7 +164,7 @@ func (hub *LedgerHub) refreshWallets() {
|
|||
}
|
||||
hub.refreshed = time.Now()
|
||||
hub.wallets = wallets
|
||||
hub.lock.Unlock()
|
||||
hub.stateLock.Unlock()
|
||||
|
||||
// Fire all wallet events and return
|
||||
for _, event := range events {
|
||||
|
|
@ -152,8 +176,8 @@ func (hub *LedgerHub) refreshWallets() {
|
|||
// receive notifications on the addition or removal of Ledger wallets.
|
||||
func (hub *LedgerHub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
|
||||
// We need the mutex to reliably start/stop the update loop
|
||||
hub.lock.Lock()
|
||||
defer hub.lock.Unlock()
|
||||
hub.stateLock.Lock()
|
||||
defer hub.stateLock.Unlock()
|
||||
|
||||
// Subscribe the caller and track the subscriber count
|
||||
sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink))
|
||||
|
|
@ -182,12 +206,12 @@ func (hub *LedgerHub) updater() {
|
|||
hub.refreshWallets()
|
||||
|
||||
// If all our subscribers left, stop the updater
|
||||
hub.lock.Lock()
|
||||
hub.stateLock.Lock()
|
||||
if hub.updateScope.Count() == 0 {
|
||||
hub.updating = false
|
||||
hub.lock.Unlock()
|
||||
hub.stateLock.Unlock()
|
||||
return
|
||||
}
|
||||
hub.lock.Unlock()
|
||||
hub.stateLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
package usbwallet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
|
|
@ -38,7 +39,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/karalabe/hid"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Maximum time between wallet health checks to detect USB unplugs.
|
||||
|
|
@ -83,6 +83,7 @@ var errInvalidVersionReply = errors.New("invalid version reply")
|
|||
|
||||
// ledgerWallet represents a live USB Ledger hardware wallet.
|
||||
type ledgerWallet struct {
|
||||
hub *LedgerHub // USB hub the device originates from (TODO(karalabe): remove if hotplug lands on Windows)
|
||||
url *accounts.URL // Textual URL uniquely identifying this wallet
|
||||
|
||||
info hid.DeviceInfo // Known USB device infos about the wallet
|
||||
|
|
@ -576,6 +577,17 @@ func (w *ledgerWallet) SignTx(account accounts.Account, tx *types.Transaction, c
|
|||
<-w.commsLock
|
||||
defer func() { w.commsLock <- struct{}{} }()
|
||||
|
||||
// Ensure the device isn't screwed with while user confirmation is pending
|
||||
// TODO(karalabe): remove if hotplug lands on Windows
|
||||
w.hub.commsLock.Lock()
|
||||
w.hub.commsPend++
|
||||
w.hub.commsLock.Unlock()
|
||||
|
||||
defer func() {
|
||||
w.hub.commsLock.Lock()
|
||||
w.hub.commsPend--
|
||||
w.hub.commsLock.Unlock()
|
||||
}()
|
||||
return w.ledgerSign(path, account.Address, tx, chainID)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,4 +36,4 @@ after_build:
|
|||
|
||||
test_script:
|
||||
- set CGO_ENABLED=1
|
||||
- go run build\ci.go test -vet -coverage
|
||||
- go run build\ci.go test -coverage
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -1,18 +1,3 @@
|
|||
# Vendored Dependencies
|
||||
|
||||
Dependencies are almost all vendored in at the standard Go `/vendor` path. This allows
|
||||
people to build go-ethereum using the standard toolchain without any particular package
|
||||
manager. It also plays nicely with `go get`, not requiring external code to be relied on.
|
||||
|
||||
The one single dependent package missing from `vendor` is `golang.org/x/net/context`. As
|
||||
this is a package exposed via public library APIs, it must not be vendored as dependent
|
||||
code woulnd't be able to instantiate.
|
||||
|
||||
To allow reproducible builds of go-ethereum nonetheless that don't need network access
|
||||
during build time to fetch `golang.org/x/net/context`, a version was copied into our repo
|
||||
at the very specific `/build/_vendor` path, which is added automatically by all CI build
|
||||
scripts and the makefile too.
|
||||
|
||||
# Debian Packaging
|
||||
|
||||
Tagged releases and develop branch commits are available as installable Debian packages
|
||||
|
|
|
|||
60
build/ci.go
60
build/ci.go
|
|
@ -24,7 +24,7 @@ Usage: go run ci.go <command> <command flags/arguments>
|
|||
Available commands are:
|
||||
|
||||
install [ -arch architecture ] [ packages... ] -- builds packages and executables
|
||||
test [ -coverage ] [ -vet ] [ -misspell ] [ packages... ] -- runs the tests
|
||||
test [ -coverage ] [ -misspell ] [ packages... ] -- runs the tests
|
||||
archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -upload dest ] -- archives build artefacts
|
||||
importkeys -- imports signing keys from env
|
||||
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
|
||||
|
|
@ -83,6 +83,10 @@ var (
|
|||
Name: "geth",
|
||||
Description: "Ethereum CLI client.",
|
||||
},
|
||||
{
|
||||
Name: "bootnode",
|
||||
Description: "Ethereum bootnode.",
|
||||
},
|
||||
{
|
||||
Name: "rlpdump",
|
||||
Description: "Developer utility tool that prints RLP structures.",
|
||||
|
|
@ -158,9 +162,9 @@ func doInstall(cmdline []string) {
|
|||
|
||||
// Check Go version. People regularly open issues about compilation
|
||||
// failure with outdated Go. This should save them the trouble.
|
||||
if runtime.Version() < "go1.4" && !strings.HasPrefix(runtime.Version(), "devel") {
|
||||
if runtime.Version() < "go1.7" && !strings.HasPrefix(runtime.Version(), "devel") {
|
||||
log.Println("You have Go version", runtime.Version())
|
||||
log.Println("go-ethereum requires at least Go version 1.4 and cannot")
|
||||
log.Println("go-ethereum requires at least Go version 1.7 and cannot")
|
||||
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
@ -169,6 +173,8 @@ func doInstall(cmdline []string) {
|
|||
if flag.NArg() > 0 {
|
||||
packages = flag.Args()
|
||||
}
|
||||
packages = build.ExpandPackagesNoVendor(packages)
|
||||
|
||||
if *arch == "" || *arch == runtime.GOARCH {
|
||||
goinstall := goTool("install", buildFlags(env)...)
|
||||
goinstall.Args = append(goinstall.Args, "-v")
|
||||
|
|
@ -211,20 +217,16 @@ func doInstall(cmdline []string) {
|
|||
}
|
||||
|
||||
func buildFlags(env build.Environment) (flags []string) {
|
||||
if os.Getenv("GO_OPENCL") != "" {
|
||||
flags = append(flags, "-tags", "opencl")
|
||||
var ld []string
|
||||
if env.Commit != "" {
|
||||
ld = append(ld, "-X", "main.gitCommit="+env.Commit)
|
||||
}
|
||||
if runtime.GOOS == "darwin" {
|
||||
ld = append(ld, "-s")
|
||||
}
|
||||
|
||||
// Since Go 1.5, the separator char for link time assignments
|
||||
// is '=' and using ' ' prints a warning. However, Go < 1.5 does
|
||||
// not support using '='.
|
||||
sep := " "
|
||||
if runtime.Version() > "go1.5" || strings.Contains(runtime.Version(), "devel") {
|
||||
sep = "="
|
||||
}
|
||||
// Set gitCommit constant via link-time assignment.
|
||||
if env.Commit != "" {
|
||||
flags = append(flags, "-ldflags", "-X main.gitCommit"+sep+env.Commit)
|
||||
if len(ld) > 0 {
|
||||
flags = append(flags, "-ldflags", strings.Join(ld, " "))
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
|
@ -245,10 +247,7 @@ func goToolArch(arch string, subcmd string, args ...string) *exec.Cmd {
|
|||
cmd.Args = append(cmd.Args, []string{"-ldflags", "-extldflags -Wl,--allow-multiple-definition"}...)
|
||||
}
|
||||
}
|
||||
cmd.Env = []string{
|
||||
"GO15VENDOREXPERIMENT=1",
|
||||
"GOPATH=" + build.GOPATH(),
|
||||
}
|
||||
cmd.Env = []string{"GOPATH=" + build.GOPATH()}
|
||||
if arch == "" || arch == runtime.GOARCH {
|
||||
cmd.Env = append(cmd.Env, "GOBIN="+GOBIN)
|
||||
} else {
|
||||
|
|
@ -270,38 +269,25 @@ func goToolArch(arch string, subcmd string, args ...string) *exec.Cmd {
|
|||
|
||||
func doTest(cmdline []string) {
|
||||
var (
|
||||
vet = flag.Bool("vet", false, "Whether to run go vet")
|
||||
misspell = flag.Bool("misspell", false, "Whether to run the spell checker")
|
||||
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
env := build.Env()
|
||||
|
||||
packages := []string{"./..."}
|
||||
if len(flag.CommandLine.Args()) > 0 {
|
||||
packages = flag.CommandLine.Args()
|
||||
}
|
||||
if len(packages) == 1 && packages[0] == "./..." {
|
||||
// Resolve ./... manually since go vet will fail on vendored stuff
|
||||
out, err := goTool("list", "./...").CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("package listing failed: %v\n%s", err, string(out))
|
||||
}
|
||||
packages = []string{}
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
if !strings.Contains(line, "vendor") {
|
||||
packages = append(packages, strings.TrimSpace(line))
|
||||
}
|
||||
}
|
||||
}
|
||||
packages = build.ExpandPackagesNoVendor(packages)
|
||||
|
||||
// Run analysis tools before the tests.
|
||||
if *vet {
|
||||
build.MustRun(goTool("vet", packages...))
|
||||
}
|
||||
build.MustRun(goTool("vet", packages...))
|
||||
if *misspell {
|
||||
spellcheck(packages)
|
||||
}
|
||||
// Run the actual tests.
|
||||
gotest := goTool("test")
|
||||
gotest := goTool("test", buildFlags(env)...)
|
||||
// Test a single package at a time. CI builders are slow
|
||||
// and some tests run into timeouts under load.
|
||||
gotest.Args = append(gotest.Args, "-p", "1")
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ fi
|
|||
|
||||
# Set up the environment to use the workspace.
|
||||
GOPATH="$workspace"
|
||||
GO15VENDOREXPERIMENT=1
|
||||
export GOPATH GO15VENDOREXPERIMENT
|
||||
export GOPATH
|
||||
|
||||
# Run the command inside the workspace.
|
||||
cd "$ethdir/go-ethereum"
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/asm"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var disasmCommand = cli.Command{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
|
@ -54,10 +55,13 @@ participating.
|
|||
Action: importChain,
|
||||
Name: "import",
|
||||
Usage: "Import a blockchain file",
|
||||
ArgsUsage: "<filename>",
|
||||
ArgsUsage: "<filename> (<filename 2> ... <filename N>) ",
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
TODO: Please write this
|
||||
The import command imports blocks from an RLP-encoded form. The form can be one file
|
||||
with several RLP-encoded blocks, or several files can be used.
|
||||
If only one file is used, import error will result in failure. If several files are used,
|
||||
processing will proceed even if an individual RLP-file import failure occurs.
|
||||
`,
|
||||
}
|
||||
exportCommand = cli.Command{
|
||||
|
|
@ -107,22 +111,27 @@ func initGenesis(ctx *cli.Context) error {
|
|||
stack := makeFullNode(ctx)
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack)
|
||||
|
||||
genesisFile, err := os.Open(genesisPath)
|
||||
file, err := os.Open(genesisPath)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to read genesis file: %v", err)
|
||||
}
|
||||
defer genesisFile.Close()
|
||||
defer file.Close()
|
||||
|
||||
block, err := core.WriteGenesisBlock(chaindb, genesisFile)
|
||||
genesis := new(core.Genesis)
|
||||
if err := json.NewDecoder(file).Decode(genesis); err != nil {
|
||||
utils.Fatalf("invalid genesis file: %v", err)
|
||||
}
|
||||
|
||||
_, hash, err := core.SetupGenesisBlock(chaindb, genesis)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to write genesis block: %v", err)
|
||||
}
|
||||
log.Info("Successfully wrote genesis state", "hash", block.Hash())
|
||||
log.Info("Successfully wrote genesis state", "hash", hash)
|
||||
return nil
|
||||
}
|
||||
|
||||
func importChain(ctx *cli.Context) error {
|
||||
if len(ctx.Args()) != 1 {
|
||||
if len(ctx.Args()) < 1 {
|
||||
utils.Fatalf("This command requires an argument.")
|
||||
}
|
||||
stack := makeFullNode(ctx)
|
||||
|
|
@ -146,9 +155,19 @@ func importChain(ctx *cli.Context) error {
|
|||
}()
|
||||
// Import the chain
|
||||
start := time.Now()
|
||||
if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
|
||||
utils.Fatalf("Import error: %v", err)
|
||||
|
||||
if len(ctx.Args()) == 1 {
|
||||
if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
|
||||
utils.Fatalf("Import error: %v", err)
|
||||
}
|
||||
} else {
|
||||
for _, arg := range ctx.Args() {
|
||||
if err := utils.ImportChain(chain, arg); err != nil {
|
||||
log.Error("Import error", "file", arg, "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Import done in %v.\n\n", time.Since(start))
|
||||
|
||||
// Output pre-compaction stats mostly to see the import trashing
|
||||
|
|
@ -171,6 +190,10 @@ func importChain(ctx *cli.Context) error {
|
|||
fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000)
|
||||
fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs))
|
||||
|
||||
if ctx.GlobalIsSet(utils.NoCompactionFlag.Name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compact the entire database to more accurately measure disk io and print the stats
|
||||
start = time.Now()
|
||||
fmt.Println("Compacting entire database...")
|
||||
|
|
|
|||
|
|
@ -91,6 +91,12 @@ func init() {
|
|||
utils.BootnodesFlag,
|
||||
utils.DataDirFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.EthashCacheDirFlag,
|
||||
utils.EthashCachesInMemoryFlag,
|
||||
utils.EthashCachesOnDiskFlag,
|
||||
utils.EthashDatasetDirFlag,
|
||||
utils.EthashDatasetsInMemoryFlag,
|
||||
utils.EthashDatasetsOnDiskFlag,
|
||||
utils.FastSyncFlag,
|
||||
utils.LightModeFlag,
|
||||
utils.LightServFlag,
|
||||
|
|
@ -106,7 +112,6 @@ func init() {
|
|||
utils.GasPriceFlag,
|
||||
utils.MinerThreadsFlag,
|
||||
utils.MiningEnabledFlag,
|
||||
utils.AutoDAGFlag,
|
||||
utils.TargetGasLimitFlag,
|
||||
utils.NATFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
|
|
@ -140,6 +145,7 @@ func init() {
|
|||
utils.EthStatsURLFlag,
|
||||
utils.MetricsEnabledFlag,
|
||||
utils.FakePoWFlag,
|
||||
utils.NoCompactionFlag,
|
||||
utils.SolcPathFlag,
|
||||
utils.GpoMinGasPriceFlag,
|
||||
utils.GpoMaxGasPriceFlag,
|
||||
|
|
@ -297,7 +303,15 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
|||
if err := stack.Service(ðereum); err != nil {
|
||||
utils.Fatalf("ethereum service not running: %v", err)
|
||||
}
|
||||
if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name)); err != nil {
|
||||
if threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name); threads > 0 {
|
||||
type threaded interface {
|
||||
SetThreads(threads int)
|
||||
}
|
||||
if th, ok := ethereum.Engine().(threaded); ok {
|
||||
th.SetThreads(threads)
|
||||
}
|
||||
}
|
||||
if err := ethereum.StartMining(); err != nil {
|
||||
utils.Fatalf("Failed to start mining: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
|
@ -87,7 +87,7 @@ func makedag(ctx *cli.Context) error {
|
|||
utils.Fatalf("Can't find dir")
|
||||
}
|
||||
fmt.Println("making DAG, this could take awhile...")
|
||||
ethash.MakeDAG(blockNum, dir)
|
||||
ethash.MakeDataset(blockNum, dir)
|
||||
}
|
||||
default:
|
||||
wrongArgs()
|
||||
|
|
|
|||
|
|
@ -77,6 +77,17 @@ var AppHelpFlagGroups = []flagGroup{
|
|||
utils.LightKDFFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ETHASH",
|
||||
Flags: []cli.Flag{
|
||||
utils.EthashCacheDirFlag,
|
||||
utils.EthashCachesInMemoryFlag,
|
||||
utils.EthashCachesOnDiskFlag,
|
||||
utils.EthashDatasetDirFlag,
|
||||
utils.EthashDatasetsInMemoryFlag,
|
||||
utils.EthashDatasetsOnDiskFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "PERFORMANCE TUNING",
|
||||
Flags: []cli.Flag{
|
||||
|
|
@ -131,7 +142,6 @@ var AppHelpFlagGroups = []flagGroup{
|
|||
Flags: []cli.Flag{
|
||||
utils.MiningEnabledFlag,
|
||||
utils.MinerThreadsFlag,
|
||||
utils.AutoDAGFlag,
|
||||
utils.EtherbaseFlag,
|
||||
utils.TargetGasLimitFlag,
|
||||
utils.GasPriceFlag,
|
||||
|
|
|
|||
58
cmd/swarm/list.go
Normal file
58
cmd/swarm/list.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
swarm "github.com/ethereum/go-ethereum/swarm/api/client"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
func list(ctx *cli.Context) {
|
||||
args := ctx.Args()
|
||||
|
||||
if len(args) < 1 {
|
||||
utils.Fatalf("Please supply a manifest reference as the first argument")
|
||||
} else if len(args) > 2 {
|
||||
utils.Fatalf("Too many arguments - usage 'swarm ls manifest [prefix]'")
|
||||
}
|
||||
manifest := args[0]
|
||||
|
||||
var prefix string
|
||||
if len(args) == 2 {
|
||||
prefix = args[1]
|
||||
}
|
||||
|
||||
bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||
client := swarm.NewClient(bzzapi)
|
||||
entries, err := client.ManifestFileList(manifest, prefix)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to generate file and directory list: %s", err)
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 1, 2, 2, ' ', 0)
|
||||
defer w.Flush()
|
||||
fmt.Fprintln(w, "HASH\tCONTENT TYPE\tPATH")
|
||||
for _, entry := range entries {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", entry.Hash, entry.ContentType, entry.Path)
|
||||
}
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ func init() {
|
|||
// Override flag defaults so bzzd can run alongside geth.
|
||||
utils.ListenPortFlag.Value = 30399
|
||||
utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"}
|
||||
utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, web3"
|
||||
utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, swarmfs, web3"
|
||||
|
||||
// Set up the cli app.
|
||||
app.Action = bzzd
|
||||
|
|
@ -153,6 +153,15 @@ The output of this command is supposed to be machine-readable.
|
|||
ArgsUsage: " <file>",
|
||||
Description: `
|
||||
"upload a file or directory to swarm using the HTTP API and prints the root hash",
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: list,
|
||||
Name: "ls",
|
||||
Usage: "list files and directories contained in a manifest",
|
||||
ArgsUsage: " <manifest> [<prefix>]",
|
||||
Description: `
|
||||
Lists files and directories contained in a manifest.
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
swarm "github.com/ethereum/go-ethereum/swarm/api/client"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
|
|
@ -41,7 +42,7 @@ func add(ctx *cli.Context) {
|
|||
|
||||
ctype string
|
||||
wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
|
||||
mroot manifest
|
||||
mroot swarm.Manifest
|
||||
)
|
||||
|
||||
if len(args) > 3 {
|
||||
|
|
@ -75,7 +76,7 @@ func update(ctx *cli.Context) {
|
|||
|
||||
ctype string
|
||||
wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
|
||||
mroot manifest
|
||||
mroot swarm.Manifest
|
||||
)
|
||||
if len(args) > 3 {
|
||||
ctype = args[3]
|
||||
|
|
@ -105,7 +106,7 @@ func remove(ctx *cli.Context) {
|
|||
path = args[1]
|
||||
|
||||
wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
|
||||
mroot manifest
|
||||
mroot swarm.Manifest
|
||||
)
|
||||
|
||||
newManifest := removeEntryFromManifest(ctx, mhash, path)
|
||||
|
|
@ -123,21 +124,21 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin
|
|||
|
||||
var (
|
||||
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||
client = &client{api: bzzapi}
|
||||
longestPathEntry = manifestEntry{
|
||||
client = swarm.NewClient(bzzapi)
|
||||
longestPathEntry = swarm.ManifestEntry{
|
||||
Path: "",
|
||||
Hash: "",
|
||||
ContentType: "",
|
||||
}
|
||||
)
|
||||
|
||||
mroot, err := client.downloadManifest(mhash)
|
||||
mroot, err := client.DownloadManifest(mhash)
|
||||
if err != nil {
|
||||
utils.Fatalf("Manifest download failed: %v", err)
|
||||
}
|
||||
|
||||
//TODO: check if the "hash" to add is valid and present in swarm
|
||||
_, err = client.downloadManifest(hash)
|
||||
_, err = client.DownloadManifest(hash)
|
||||
if err != nil {
|
||||
utils.Fatalf("Hash to add is not present: %v", err)
|
||||
}
|
||||
|
|
@ -162,7 +163,7 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin
|
|||
newHash := addEntryToManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype)
|
||||
|
||||
// Replace the hash for parent Manifests
|
||||
newMRoot := manifest{}
|
||||
newMRoot := swarm.Manifest{}
|
||||
for _, entry := range mroot.Entries {
|
||||
if longestPathEntry.Path == entry.Path {
|
||||
entry.Hash = newHash
|
||||
|
|
@ -172,7 +173,7 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin
|
|||
mroot = newMRoot
|
||||
} else {
|
||||
// Add the entry in the leaf Manifest
|
||||
newEntry := manifestEntry{
|
||||
newEntry := swarm.ManifestEntry{
|
||||
Path: path,
|
||||
Hash: hash,
|
||||
ContentType: ctype,
|
||||
|
|
@ -180,7 +181,7 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin
|
|||
mroot.Entries = append(mroot.Entries, newEntry)
|
||||
}
|
||||
|
||||
newManifestHash, err := client.uploadManifest(mroot)
|
||||
newManifestHash, err := client.UploadManifest(mroot)
|
||||
if err != nil {
|
||||
utils.Fatalf("Manifest upload failed: %v", err)
|
||||
}
|
||||
|
|
@ -192,20 +193,20 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st
|
|||
|
||||
var (
|
||||
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||
client = &client{api: bzzapi}
|
||||
newEntry = manifestEntry{
|
||||
client = swarm.NewClient(bzzapi)
|
||||
newEntry = swarm.ManifestEntry{
|
||||
Path: "",
|
||||
Hash: "",
|
||||
ContentType: "",
|
||||
}
|
||||
longestPathEntry = manifestEntry{
|
||||
longestPathEntry = swarm.ManifestEntry{
|
||||
Path: "",
|
||||
Hash: "",
|
||||
ContentType: "",
|
||||
}
|
||||
)
|
||||
|
||||
mroot, err := client.downloadManifest(mhash)
|
||||
mroot, err := client.DownloadManifest(mhash)
|
||||
if err != nil {
|
||||
utils.Fatalf("Manifest download failed: %v", err)
|
||||
}
|
||||
|
|
@ -236,7 +237,7 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st
|
|||
newHash := updateEntryInManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype)
|
||||
|
||||
// Replace the hash for parent Manifests
|
||||
newMRoot := manifest{}
|
||||
newMRoot := swarm.Manifest{}
|
||||
for _, entry := range mroot.Entries {
|
||||
if longestPathEntry.Path == entry.Path {
|
||||
entry.Hash = newHash
|
||||
|
|
@ -249,10 +250,10 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st
|
|||
|
||||
if newEntry.Path != "" {
|
||||
// Replace the hash for leaf Manifest
|
||||
newMRoot := manifest{}
|
||||
newMRoot := swarm.Manifest{}
|
||||
for _, entry := range mroot.Entries {
|
||||
if newEntry.Path == entry.Path {
|
||||
myEntry := manifestEntry{
|
||||
myEntry := swarm.ManifestEntry{
|
||||
Path: entry.Path,
|
||||
Hash: hash,
|
||||
ContentType: ctype,
|
||||
|
|
@ -265,7 +266,7 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st
|
|||
mroot = newMRoot
|
||||
}
|
||||
|
||||
newManifestHash, err := client.uploadManifest(mroot)
|
||||
newManifestHash, err := client.UploadManifest(mroot)
|
||||
if err != nil {
|
||||
utils.Fatalf("Manifest upload failed: %v", err)
|
||||
}
|
||||
|
|
@ -276,20 +277,20 @@ func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
|
|||
|
||||
var (
|
||||
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||
client = &client{api: bzzapi}
|
||||
entryToRemove = manifestEntry{
|
||||
client = swarm.NewClient(bzzapi)
|
||||
entryToRemove = swarm.ManifestEntry{
|
||||
Path: "",
|
||||
Hash: "",
|
||||
ContentType: "",
|
||||
}
|
||||
longestPathEntry = manifestEntry{
|
||||
longestPathEntry = swarm.ManifestEntry{
|
||||
Path: "",
|
||||
Hash: "",
|
||||
ContentType: "",
|
||||
}
|
||||
)
|
||||
|
||||
mroot, err := client.downloadManifest(mhash)
|
||||
mroot, err := client.DownloadManifest(mhash)
|
||||
if err != nil {
|
||||
utils.Fatalf("Manifest download failed: %v", err)
|
||||
}
|
||||
|
|
@ -318,7 +319,7 @@ func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
|
|||
newHash := removeEntryFromManifest(ctx, longestPathEntry.Hash, newPath)
|
||||
|
||||
// Replace the hash for parent Manifests
|
||||
newMRoot := manifest{}
|
||||
newMRoot := swarm.Manifest{}
|
||||
for _, entry := range mroot.Entries {
|
||||
if longestPathEntry.Path == entry.Path {
|
||||
entry.Hash = newHash
|
||||
|
|
@ -330,7 +331,7 @@ func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
|
|||
|
||||
if entryToRemove.Path != "" {
|
||||
// remove the entry in this Manifest
|
||||
newMRoot := manifest{}
|
||||
newMRoot := swarm.Manifest{}
|
||||
for _, entry := range mroot.Entries {
|
||||
if entryToRemove.Path != entry.Path {
|
||||
newMRoot.Entries = append(newMRoot.Entries, entry)
|
||||
|
|
@ -339,7 +340,7 @@ func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
|
|||
mroot = newMRoot
|
||||
}
|
||||
|
||||
newManifestHash, err := client.uploadManifest(mroot)
|
||||
newManifestHash, err := client.UploadManifest(mroot)
|
||||
if err != nil {
|
||||
utils.Fatalf("Manifest upload failed: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,21 +18,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
swarm "github.com/ethereum/go-ethereum/swarm/api/client"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
|
|
@ -48,8 +44,8 @@ func upload(ctx *cli.Context) {
|
|||
mimeType = ctx.GlobalString(SwarmUploadMimeType.Name)
|
||||
)
|
||||
|
||||
var client = &client{api: bzzapi}
|
||||
var entry manifestEntry
|
||||
var client = swarm.NewClient(bzzapi)
|
||||
var entry swarm.ManifestEntry
|
||||
var file string
|
||||
|
||||
if len(args) != 1 {
|
||||
|
|
@ -84,25 +80,25 @@ func upload(ctx *cli.Context) {
|
|||
if !wantManifest {
|
||||
utils.Fatalf("Manifest is required for directory uploads")
|
||||
}
|
||||
mhash, err := client.uploadDirectory(file, defaultPath)
|
||||
mhash, err := client.UploadDirectory(file, defaultPath)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to upload directory: %v", err)
|
||||
}
|
||||
fmt.Println(mhash)
|
||||
return
|
||||
}
|
||||
entry, err = client.uploadFile(file, fi, mimeType)
|
||||
entry, err = client.UploadFile(file, fi, mimeType)
|
||||
if err != nil {
|
||||
utils.Fatalf("Upload failed: %v", err)
|
||||
}
|
||||
mroot := manifest{[]manifestEntry{entry}}
|
||||
mroot := swarm.Manifest{Entries: []swarm.ManifestEntry{entry}}
|
||||
if !wantManifest {
|
||||
// Print the manifest. This is the only output to stdout.
|
||||
mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
|
||||
fmt.Println(string(mrootJSON))
|
||||
return
|
||||
}
|
||||
hash, err := client.uploadManifest(mroot)
|
||||
hash, err := client.UploadManifest(mroot)
|
||||
if err != nil {
|
||||
utils.Fatalf("Manifest upload failed: %v", err)
|
||||
}
|
||||
|
|
@ -132,173 +128,3 @@ func homeDir() string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// client wraps interaction with the swarm HTTP gateway.
|
||||
type client struct {
|
||||
api string
|
||||
}
|
||||
|
||||
// manifest is the JSON representation of a swarm manifest.
|
||||
type manifestEntry struct {
|
||||
Hash string `json:"hash,omitempty"`
|
||||
ContentType string `json:"contentType,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
// manifest is the JSON representation of a swarm manifest.
|
||||
type manifest struct {
|
||||
Entries []manifestEntry `json:"entries,omitempty"`
|
||||
}
|
||||
|
||||
func (c *client) uploadDirectory(dir string, defaultPath string) (string, error) {
|
||||
mhash, err := c.postRaw("application/json", 2, ioutil.NopCloser(bytes.NewReader([]byte("{}"))))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to upload empty manifest")
|
||||
}
|
||||
if len(defaultPath) > 0 {
|
||||
fi, err := os.Stat(defaultPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
mhash, err = c.uploadToManifest(mhash, "", defaultPath, fi)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
prefix := filepath.ToSlash(filepath.Clean(dir)) + "/"
|
||||
err = filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || fi.IsDir() {
|
||||
return err
|
||||
}
|
||||
if !strings.HasPrefix(path, dir) {
|
||||
return fmt.Errorf("path %s outside directory %s", path, dir)
|
||||
}
|
||||
uripath := strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix)
|
||||
mhash, err = c.uploadToManifest(mhash, uripath, path, fi)
|
||||
return err
|
||||
})
|
||||
return mhash, err
|
||||
}
|
||||
|
||||
func (c *client) uploadFile(file string, fi os.FileInfo, mimetype_ string) (manifestEntry, error) {
|
||||
var mimetype string
|
||||
hash, err := c.uploadFileContent(file, fi)
|
||||
if mimetype_ != "" {
|
||||
mimetype = mimetype_
|
||||
log.Info("Mime type set by override", "mime", mimetype)
|
||||
} else {
|
||||
ext := filepath.Ext(file)
|
||||
log.Info("Ext" , "ext", ext, "file", file)
|
||||
if ext != "" {
|
||||
mimetype = mime.TypeByExtension(filepath.Ext(fi.Name()))
|
||||
log.Info("Mime type set by fileextension", "mime", mimetype, "ext", filepath.Ext(file))
|
||||
} else {
|
||||
f, err := os.Open(file)
|
||||
if err == nil {
|
||||
first512 := make([]byte, 512)
|
||||
fread, _ := f.ReadAt(first512, 0)
|
||||
if fread > 0 {
|
||||
mimetype = http.DetectContentType(first512[:fread])
|
||||
log.Info("Mime type set by autodetection", "mime", mimetype)
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m := manifestEntry{
|
||||
Hash: hash,
|
||||
ContentType: mimetype,
|
||||
}
|
||||
return m, err
|
||||
}
|
||||
|
||||
func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) {
|
||||
fd, err := os.Open(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer fd.Close()
|
||||
log.Info("Uploading swarm content", "file", file, "bytes", fi.Size())
|
||||
return c.postRaw("application/octet-stream", fi.Size(), fd)
|
||||
}
|
||||
|
||||
func (c *client) uploadManifest(m manifest) (string, error) {
|
||||
jsm, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Info("Uploading swarm manifest")
|
||||
return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm)))
|
||||
}
|
||||
|
||||
func (c *client) uploadToManifest(mhash string, path string, fpath string, fi os.FileInfo) (string, error) {
|
||||
fd, err := os.Open(fpath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer fd.Close()
|
||||
log.Info("Uploading swarm content and path", "file", fpath, "bytes", fi.Size(), "path", path)
|
||||
req, err := http.NewRequest("PUT", c.api+"/bzz:/"+mhash+"/"+path, fd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("content-type", mime.TypeByExtension(filepath.Ext(fi.Name())))
|
||||
req.ContentLength = fi.Size()
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 400 {
|
||||
return "", fmt.Errorf("bad status: %s", resp.Status)
|
||||
}
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
return string(content), err
|
||||
}
|
||||
|
||||
func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) {
|
||||
req, err := http.NewRequest("POST", c.api+"/bzzr:/", body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("content-type", mimetype)
|
||||
req.ContentLength = size
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 400 {
|
||||
return "", fmt.Errorf("bad status: %s", resp.Status)
|
||||
}
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
return string(content), err
|
||||
}
|
||||
|
||||
func (c *client) downloadManifest(mhash string) (manifest, error) {
|
||||
|
||||
mroot := manifest{}
|
||||
req, err := http.NewRequest("GET", c.api+"/bzzr:/"+mhash, nil)
|
||||
if err != nil {
|
||||
return mroot, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return mroot, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return mroot, fmt.Errorf("bad status: %s", resp.Status)
|
||||
|
||||
}
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
|
||||
err = json.Unmarshal(content, &mroot)
|
||||
if err != nil {
|
||||
return mroot, fmt.Errorf("Manifest %v is malformed: %v", mhash, err)
|
||||
}
|
||||
return mroot, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,15 +23,16 @@ import (
|
|||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
|
@ -49,7 +50,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
|
@ -114,6 +114,34 @@ var (
|
|||
Name: "keystore",
|
||||
Usage: "Directory for the keystore (default = inside the datadir)",
|
||||
}
|
||||
EthashCacheDirFlag = DirectoryFlag{
|
||||
Name: "ethash.cachedir",
|
||||
Usage: "Directory to store the ethash verification caches (default = inside the datadir)",
|
||||
}
|
||||
EthashCachesInMemoryFlag = cli.IntFlag{
|
||||
Name: "ethash.cachesinmem",
|
||||
Usage: "Number of recent ethash caches to keep in memory (16MB each)",
|
||||
Value: 2,
|
||||
}
|
||||
EthashCachesOnDiskFlag = cli.IntFlag{
|
||||
Name: "ethash.cachesondisk",
|
||||
Usage: "Number of recent ethash caches to keep on disk (16MB each)",
|
||||
Value: 3,
|
||||
}
|
||||
EthashDatasetDirFlag = DirectoryFlag{
|
||||
Name: "ethash.dagdir",
|
||||
Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
|
||||
}
|
||||
EthashDatasetsInMemoryFlag = cli.IntFlag{
|
||||
Name: "ethash.dagsinmem",
|
||||
Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
|
||||
Value: 1,
|
||||
}
|
||||
EthashDatasetsOnDiskFlag = cli.IntFlag{
|
||||
Name: "ethash.dagsondisk",
|
||||
Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
|
||||
Value: 2,
|
||||
}
|
||||
NetworkIdFlag = cli.IntFlag{
|
||||
Name: "networkid",
|
||||
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)",
|
||||
|
|
@ -121,7 +149,7 @@ var (
|
|||
}
|
||||
TestNetFlag = cli.BoolFlag{
|
||||
Name: "testnet",
|
||||
Usage: "Ropsten network: pre-configured test network",
|
||||
Usage: "Ropsten network: pre-configured proof-of-work test network",
|
||||
}
|
||||
DevModeFlag = cli.BoolFlag{
|
||||
Name: "dev",
|
||||
|
|
@ -184,10 +212,6 @@ var (
|
|||
Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine",
|
||||
Value: params.GenesisGasLimit.Uint64(),
|
||||
}
|
||||
AutoDAGFlag = cli.BoolFlag{
|
||||
Name: "autodag",
|
||||
Usage: "Enable automatic DAG pregeneration",
|
||||
}
|
||||
EtherbaseFlag = cli.StringFlag{
|
||||
Name: "etherbase",
|
||||
Usage: "Public address for block mining rewards (default = first account created)",
|
||||
|
|
@ -244,7 +268,10 @@ var (
|
|||
Name: "fakepow",
|
||||
Usage: "Disables proof-of-work verification",
|
||||
}
|
||||
|
||||
NoCompactionFlag = cli.BoolFlag{
|
||||
Name: "nocompaction",
|
||||
Usage: "Disables db compaction after import",
|
||||
}
|
||||
// RPC settings
|
||||
RPCEnabledFlag = cli.BoolFlag{
|
||||
Name: "rpc",
|
||||
|
|
@ -429,6 +456,36 @@ func MakeDataDir(ctx *cli.Context) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// MakeEthashCacheDir returns the directory to use for storing the ethash cache
|
||||
// dumps.
|
||||
func MakeEthashCacheDir(ctx *cli.Context) string {
|
||||
if ctx.GlobalIsSet(EthashCacheDirFlag.Name) && ctx.GlobalString(EthashCacheDirFlag.Name) == "" {
|
||||
return ""
|
||||
}
|
||||
if !ctx.GlobalIsSet(EthashCacheDirFlag.Name) {
|
||||
return "ethash"
|
||||
}
|
||||
return ctx.GlobalString(EthashCacheDirFlag.Name)
|
||||
}
|
||||
|
||||
// MakeEthashDatasetDir returns the directory to use for storing the full ethash
|
||||
// dataset dumps.
|
||||
func MakeEthashDatasetDir(ctx *cli.Context) string {
|
||||
if !ctx.GlobalIsSet(EthashDatasetDirFlag.Name) {
|
||||
home := os.Getenv("HOME")
|
||||
if home == "" {
|
||||
if user, err := user.Current(); err == nil {
|
||||
home = user.HomeDir
|
||||
}
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
return filepath.Join(home, "AppData", "Ethash")
|
||||
}
|
||||
return filepath.Join(home, ".ethash")
|
||||
}
|
||||
return ctx.GlobalString(EthashDatasetDirFlag.Name)
|
||||
}
|
||||
|
||||
// MakeIPCPath creates an IPC path configuration from the set command line flags,
|
||||
// returning an empty string if IPC was explicitly disabled, or the set path.
|
||||
func MakeIPCPath(ctx *cli.Context) string {
|
||||
|
|
@ -729,7 +786,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
|||
|
||||
ethConf := ð.Config{
|
||||
Etherbase: MakeEtherbase(ks, ctx),
|
||||
ChainConfig: MakeChainConfig(ctx, stack),
|
||||
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
|
||||
LightMode: ctx.GlobalBool(LightModeFlag.Name),
|
||||
LightServ: ctx.GlobalInt(LightServFlag.Name),
|
||||
|
|
@ -749,7 +805,12 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
|||
GpobaseStepUp: ctx.GlobalInt(GpobaseStepUpFlag.Name),
|
||||
GpobaseCorrectionFactor: ctx.GlobalInt(GpobaseCorrectionFactorFlag.Name),
|
||||
SolcPath: ctx.GlobalString(SolcPathFlag.Name),
|
||||
AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name),
|
||||
EthashCacheDir: MakeEthashCacheDir(ctx),
|
||||
EthashCachesInMem: ctx.GlobalInt(EthashCachesInMemoryFlag.Name),
|
||||
EthashCachesOnDisk: ctx.GlobalInt(EthashCachesOnDiskFlag.Name),
|
||||
EthashDatasetDir: MakeEthashDatasetDir(ctx),
|
||||
EthashDatasetsInMem: ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name),
|
||||
EthashDatasetsOnDisk: ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name),
|
||||
EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name),
|
||||
}
|
||||
|
||||
|
|
@ -760,7 +821,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
|||
ethConf.NetworkId = 3
|
||||
}
|
||||
ethConf.Genesis = core.DefaultTestnetGenesisBlock()
|
||||
|
||||
case ctx.GlobalBool(DevModeFlag.Name):
|
||||
ethConf.Genesis = core.DevGenesisBlock()
|
||||
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
|
||||
|
|
@ -822,67 +882,6 @@ func SetupNetwork(ctx *cli.Context) {
|
|||
params.TargetGasLimit = new(big.Int).SetUint64(ctx.GlobalUint64(TargetGasLimitFlag.Name))
|
||||
}
|
||||
|
||||
// MakeChainConfig reads the chain configuration from the database in ctx.Datadir.
|
||||
func MakeChainConfig(ctx *cli.Context, stack *node.Node) *params.ChainConfig {
|
||||
db := MakeChainDatabase(ctx, stack)
|
||||
defer db.Close()
|
||||
|
||||
return MakeChainConfigFromDb(ctx, db)
|
||||
}
|
||||
|
||||
// MakeChainConfigFromDb reads the chain configuration from the given database.
|
||||
func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainConfig {
|
||||
// If the chain is already initialized, use any existing chain configs
|
||||
config := new(params.ChainConfig)
|
||||
|
||||
genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0), 0)
|
||||
if genesis != nil {
|
||||
storedConfig, err := core.GetChainConfig(db, genesis.Hash())
|
||||
switch err {
|
||||
case nil:
|
||||
config = storedConfig
|
||||
case core.ChainConfigNotFoundErr:
|
||||
// No configs found, use empty, will populate below
|
||||
default:
|
||||
Fatalf("Could not make chain configuration: %v", err)
|
||||
}
|
||||
}
|
||||
// set chain id in case it's zero.
|
||||
if config.ChainId == nil {
|
||||
config.ChainId = new(big.Int)
|
||||
}
|
||||
// Check whether we are allowed to set default config params or not:
|
||||
// - If no genesis is set, we're running either mainnet or testnet (private nets use `geth init`)
|
||||
// - If a genesis is already set, ensure we have a configuration for it (mainnet or testnet)
|
||||
defaults := genesis == nil ||
|
||||
(genesis.Hash() == params.MainNetGenesisHash && !ctx.GlobalBool(TestNetFlag.Name)) ||
|
||||
(genesis.Hash() == params.TestNetGenesisHash && ctx.GlobalBool(TestNetFlag.Name))
|
||||
|
||||
if defaults {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config = params.TestnetChainConfig
|
||||
} else if ctx.GlobalBool(DevModeFlag.Name) {
|
||||
config = params.AllProtocolChanges
|
||||
} else {
|
||||
// Homestead fork
|
||||
config.HomesteadBlock = params.MainNetHomesteadBlock
|
||||
// DAO fork
|
||||
config.DAOForkBlock = params.MainNetDAOForkBlock
|
||||
config.DAOForkSupport = true
|
||||
|
||||
// DoS reprice fork
|
||||
config.EIP150Block = params.MainNetHomesteadGasRepriceBlock
|
||||
config.EIP150Hash = params.MainNetHomesteadGasRepriceHash
|
||||
|
||||
// DoS state cleanup fork
|
||||
config.EIP155Block = params.MainNetSpuriousDragon
|
||||
config.EIP158Block = params.MainNetSpuriousDragon
|
||||
config.ChainId = params.MainNetChainID
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func ChainDbName(ctx *cli.Context) string {
|
||||
if ctx.GlobalBool(LightModeFlag.Name) {
|
||||
return "lightchaindata"
|
||||
|
|
@ -906,26 +905,34 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
|||
return chainDb
|
||||
}
|
||||
|
||||
func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
||||
var genesis *core.Genesis
|
||||
switch {
|
||||
case ctx.GlobalBool(TestNetFlag.Name):
|
||||
genesis = core.DefaultTestnetGenesisBlock()
|
||||
case ctx.GlobalBool(DevModeFlag.Name):
|
||||
genesis = core.DevGenesisBlock()
|
||||
}
|
||||
return genesis
|
||||
}
|
||||
|
||||
// MakeChain creates a chain manager from set command line flags.
|
||||
func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) {
|
||||
var err error
|
||||
chainDb = MakeChainDatabase(ctx, stack)
|
||||
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
_, err := core.WriteTestNetGenesisBlock(chainDb)
|
||||
if err != nil {
|
||||
Fatalf("Failed to write testnet genesis: %v", err)
|
||||
}
|
||||
}
|
||||
chainConfig := MakeChainConfigFromDb(ctx, chainDb)
|
||||
|
||||
pow := pow.PoW(core.FakePow{})
|
||||
engine := ethash.NewFaker()
|
||||
if !ctx.GlobalBool(FakePoWFlag.Name) {
|
||||
pow = ethash.New()
|
||||
engine = ethash.New("", 1, 0, "", 1, 0)
|
||||
}
|
||||
chain, err = core.NewBlockChain(chainDb, chainConfig, pow, new(event.TypeMux), vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)})
|
||||
config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx))
|
||||
if err != nil {
|
||||
Fatalf("Could not start chainmanager: %v", err)
|
||||
Fatalf("%v", err)
|
||||
}
|
||||
vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}
|
||||
chain, err = core.NewBlockChain(chainDb, config, engine, new(event.TypeMux), vmcfg)
|
||||
if err != nil {
|
||||
Fatalf("Can't create BlockChain: %v", err)
|
||||
}
|
||||
return chain, chainDb
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ func (b *Bytes) UnmarshalJSON(input []byte) error {
|
|||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (b *Bytes) UnmarshalText(input []byte) error {
|
||||
raw, err := checkText(input)
|
||||
raw, err := checkText(input, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -73,7 +73,28 @@ func (b Bytes) String() string {
|
|||
// determines the required input length. This function is commonly used to implement the
|
||||
// UnmarshalText method for fixed-size types.
|
||||
func UnmarshalFixedText(typname string, input, out []byte) error {
|
||||
raw, err := checkText(input)
|
||||
raw, err := checkText(input, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw)/2 != len(out) {
|
||||
return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
|
||||
}
|
||||
// Pre-verify syntax before modifying out.
|
||||
for _, b := range raw {
|
||||
if decodeNibble(b) == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
}
|
||||
hex.Decode(out, raw)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalFixedUnprefixedText decodes the input as a string with optional 0x prefix. The
|
||||
// length of out determines the required input length. This function is commonly used to
|
||||
// implement the UnmarshalText method for fixed-size types.
|
||||
func UnmarshalFixedUnprefixedText(typname string, input, out []byte) error {
|
||||
raw, err := checkText(input, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -243,14 +264,15 @@ func bytesHave0xPrefix(input []byte) bool {
|
|||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||
}
|
||||
|
||||
func checkText(input []byte) ([]byte, error) {
|
||||
func checkText(input []byte, wantPrefix bool) ([]byte, error) {
|
||||
if len(input) == 0 {
|
||||
return nil, nil // empty strings are allowed
|
||||
}
|
||||
if !bytesHave0xPrefix(input) {
|
||||
if bytesHave0xPrefix(input) {
|
||||
input = input[2:]
|
||||
} else if wantPrefix {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
input = input[2:]
|
||||
if len(input)%2 != 0 {
|
||||
return nil, ErrOddLength
|
||||
}
|
||||
|
|
|
|||
|
|
@ -337,3 +337,38 @@ func TestUnmarshalUint(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalFixedUnprefixedText(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want []byte
|
||||
wantErr error
|
||||
}{
|
||||
{input: "0x2", wantErr: ErrOddLength},
|
||||
{input: "2", wantErr: ErrOddLength},
|
||||
{input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")},
|
||||
{input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")},
|
||||
// check that output is not modified for partially correct input
|
||||
{input: "444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}},
|
||||
{input: "0x444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}},
|
||||
// valid inputs
|
||||
{input: "44444444", want: []byte{0x44, 0x44, 0x44, 0x44}},
|
||||
{input: "0x44444444", want: []byte{0x44, 0x44, 0x44, 0x44}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
out := make([]byte, 4)
|
||||
err := UnmarshalFixedUnprefixedText("x", []byte(test.input), out)
|
||||
switch {
|
||||
case err == nil && test.wantErr != nil:
|
||||
t.Errorf("%q: got no error, expected %q", test.input, test.wantErr)
|
||||
case err != nil && test.wantErr == nil:
|
||||
t.Errorf("%q: unexpected error %q", test.input, err)
|
||||
case err != nil && err.Error() != test.wantErr.Error():
|
||||
t.Errorf("%q: error mismatch: got %q, want %q", test.input, err, test.wantErr)
|
||||
}
|
||||
if test.want != nil && !bytes.Equal(out, test.want) {
|
||||
t.Errorf("%q: output mismatch: got %x, want %x", test.input, out, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package math
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
|
|
@ -35,6 +36,24 @@ const (
|
|||
wordBytes = wordBits / 8
|
||||
)
|
||||
|
||||
// HexOrDecimal256 marshals big.Int as hex or decimal.
|
||||
type HexOrDecimal256 big.Int
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (i *HexOrDecimal256) UnmarshalText(input []byte) error {
|
||||
bigint, ok := ParseBig256(string(input))
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid hex or decimal integer %q", input)
|
||||
}
|
||||
*i = HexOrDecimal256(*bigint)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (i *HexOrDecimal256) MarshalText() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf("%#x", (*big.Int)(i))), nil
|
||||
}
|
||||
|
||||
// ParseBig256 parses s as a 256 bit integer in decimal or hexadecimal syntax.
|
||||
// Leading zeros are accepted. The empty string parses as zero.
|
||||
func ParseBig256(s string) (*big.Int, bool) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestParseBig256(t *testing.T) {
|
||||
func TestHexOrDecimal256(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
num *big.Int
|
||||
|
|
@ -47,13 +47,14 @@ func TestParseBig256(t *testing.T) {
|
|||
{"115792089237316195423570985008687907853269984665640564039457584007913129639936", nil, false},
|
||||
}
|
||||
for _, test := range tests {
|
||||
num, ok := ParseBig256(test.input)
|
||||
if ok != test.ok {
|
||||
t.Errorf("ParseBig(%q) -> ok = %t, want %t", test.input, ok, test.ok)
|
||||
var num HexOrDecimal256
|
||||
err := num.UnmarshalText([]byte(test.input))
|
||||
if (err == nil) != test.ok {
|
||||
t.Errorf("ParseBig(%q) -> (err == nil) == %t, want %t", test.input, err == nil, test.ok)
|
||||
continue
|
||||
}
|
||||
if num != nil && test.num != nil && num.Cmp(test.num) != 0 {
|
||||
t.Errorf("ParseBig(%q) -> %d, want %d", test.input, num, test.num)
|
||||
if test.num != nil && (*big.Int)(&num).Cmp(test.num) != 0 {
|
||||
t.Errorf("ParseBig(%q) -> %d, want %d", test.input, (*big.Int)(&num), test.num)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@
|
|||
|
||||
package math
|
||||
|
||||
import "strconv"
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
// Integer limit values.
|
||||
|
|
@ -34,6 +37,24 @@ const (
|
|||
MaxUint64 = 1<<64 - 1
|
||||
)
|
||||
|
||||
// HexOrDecimal64 marshals uint64 as hex or decimal.
|
||||
type HexOrDecimal64 uint64
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (i *HexOrDecimal64) UnmarshalText(input []byte) error {
|
||||
int, ok := ParseUint64(string(input))
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid hex or decimal integer %q", input)
|
||||
}
|
||||
*i = HexOrDecimal64(int)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (i HexOrDecimal64) MarshalText() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf("%#x", uint64(i))), nil
|
||||
}
|
||||
|
||||
// ParseUint64 parses s as an integer in decimal or hexadecimal syntax.
|
||||
// Leading zeros are accepted. The empty string parses as zero.
|
||||
func ParseUint64(s string) (uint64, bool) {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func TestOverflow(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParseUint64(t *testing.T) {
|
||||
func TestHexOrDecimal64(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
num uint64
|
||||
|
|
@ -88,12 +88,13 @@ func TestParseUint64(t *testing.T) {
|
|||
{"18446744073709551617", 0, false},
|
||||
}
|
||||
for _, test := range tests {
|
||||
num, ok := ParseUint64(test.input)
|
||||
if ok != test.ok {
|
||||
t.Errorf("ParseUint64(%q) -> ok = %t, want %t", test.input, ok, test.ok)
|
||||
var num HexOrDecimal64
|
||||
err := num.UnmarshalText([]byte(test.input))
|
||||
if (err == nil) != test.ok {
|
||||
t.Errorf("ParseUint64(%q) -> (err == nil) = %t, want %t", test.input, err == nil, test.ok)
|
||||
continue
|
||||
}
|
||||
if ok && num != test.num {
|
||||
if err == nil && uint64(num) != test.num {
|
||||
t.Errorf("ParseUint64(%q) -> %d, want %d", test.input, num, test.num)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
|
|
@ -30,13 +31,8 @@ const (
|
|||
AddressLength = 20
|
||||
)
|
||||
|
||||
type (
|
||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||
Hash [HashLength]byte
|
||||
|
||||
// Address represents the 20 byte address of an Ethereum account.
|
||||
Address [AddressLength]byte
|
||||
)
|
||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||
type Hash [HashLength]byte
|
||||
|
||||
func BytesToHash(b []byte) Hash {
|
||||
var h Hash
|
||||
|
|
@ -113,7 +109,24 @@ func EmptyHash(h Hash) bool {
|
|||
return h == Hash{}
|
||||
}
|
||||
|
||||
// UnprefixedHash allows marshaling a Hash without 0x prefix.
|
||||
type UnprefixedHash Hash
|
||||
|
||||
// UnmarshalText decodes the hash from hex. The 0x prefix is optional.
|
||||
func (h *UnprefixedHash) UnmarshalText(input []byte) error {
|
||||
return hexutil.UnmarshalFixedUnprefixedText("UnprefixedHash", input, h[:])
|
||||
}
|
||||
|
||||
// MarshalText encodes the hash as hex.
|
||||
func (h UnprefixedHash) MarshalText() ([]byte, error) {
|
||||
return []byte(hex.EncodeToString(h[:])), nil
|
||||
}
|
||||
|
||||
/////////// Address
|
||||
|
||||
// Address represents the 20 byte address of an Ethereum account.
|
||||
type Address [AddressLength]byte
|
||||
|
||||
func BytesToAddress(b []byte) Address {
|
||||
var a Address
|
||||
a.SetBytes(b)
|
||||
|
|
@ -181,12 +194,15 @@ func (a *Address) UnmarshalText(input []byte) error {
|
|||
return hexutil.UnmarshalFixedText("Address", input, a[:])
|
||||
}
|
||||
|
||||
// PP Pretty Prints a byte slice in the following format:
|
||||
// hex(value[:4])...(hex[len(value)-4:])
|
||||
func PP(value []byte) string {
|
||||
if len(value) <= 8 {
|
||||
return Bytes2Hex(value)
|
||||
}
|
||||
// UnprefixedHash allows marshaling an Address without 0x prefix.
|
||||
type UnprefixedAddress Address
|
||||
|
||||
return fmt.Sprintf("%x...%x", value[:4], value[len(value)-4])
|
||||
// UnmarshalText decodes the address from hex. The 0x prefix is optional.
|
||||
func (a *UnprefixedAddress) UnmarshalText(input []byte) error {
|
||||
return hexutil.UnmarshalFixedUnprefixedText("UnprefixedAddress", input, a[:])
|
||||
}
|
||||
|
||||
// MarshalText encodes the address as hex.
|
||||
func (a UnprefixedAddress) MarshalText() ([]byte, error) {
|
||||
return []byte(hex.EncodeToString(a[:])), nil
|
||||
}
|
||||
|
|
|
|||
94
consensus/consensus.go
Normal file
94
consensus/consensus.go
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2017 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 consensus implements different Ethereum consensus engines.
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// ChainReader defines a small collection of methods needed to access the local
|
||||
// blockchain during header and/or uncle verification.
|
||||
type ChainReader interface {
|
||||
// Config retrieves the blockchain's chain configuration.
|
||||
Config() *params.ChainConfig
|
||||
|
||||
// CurrentHeader retrieves the current header from the local chain.
|
||||
CurrentHeader() *types.Header
|
||||
|
||||
// GetHeader retrieves a block header from the database by hash and number.
|
||||
GetHeader(hash common.Hash, number uint64) *types.Header
|
||||
|
||||
// GetHeaderByNumber retrieves a block header from the database by number.
|
||||
GetHeaderByNumber(number uint64) *types.Header
|
||||
|
||||
// GetBlock retrieves a block from the database by hash and number.
|
||||
GetBlock(hash common.Hash, number uint64) *types.Block
|
||||
}
|
||||
|
||||
// Engine is an algorithm agnostic consensus engine.
|
||||
type Engine interface {
|
||||
// VerifyHeader checks whether a header conforms to the consensus rules of a
|
||||
// given engine. Verifying the seal may be done optionally here, or explicitly
|
||||
// via the VerifySeal method.
|
||||
VerifyHeader(chain ChainReader, header *types.Header, seal bool) error
|
||||
|
||||
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
|
||||
// concurrently. The method returns a quit channel to abort the operations and
|
||||
// a results channel to retrieve the async verifications (the order is that of
|
||||
// the input slice).
|
||||
VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
|
||||
|
||||
// VerifyUncles verifies that the given block's uncles conform to the consensus
|
||||
// rules of a given engine.
|
||||
VerifyUncles(chain ChainReader, block *types.Block) error
|
||||
|
||||
// VerifySeal checks whether the crypto seal on a header is valid according to
|
||||
// the consensus rules of the given engine.
|
||||
VerifySeal(chain ChainReader, header *types.Header) error
|
||||
|
||||
// Prepare initializes the consensus fields of a block header according to the
|
||||
// rules of a particular engine. The changes are executed inline.
|
||||
Prepare(chain ChainReader, header *types.Header) error
|
||||
|
||||
// Finalize runs any post-transaction state modifications (e.g. block rewards)
|
||||
// and assembles the final block.
|
||||
//
|
||||
// Note, the block header and state database might be updated to reflect any
|
||||
// consensus rules that happen at finalization (e.g. block rewards).
|
||||
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
|
||||
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
|
||||
|
||||
// Seal generates a new block for the given input block with the local miner's
|
||||
// seal place on top.
|
||||
Seal(chain ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error)
|
||||
|
||||
// APIs returns the RPC APIs this consensus engine provides.
|
||||
APIs(chain ChainReader) []rpc.API
|
||||
}
|
||||
|
||||
// PoW is a consensus engine based on proof-of-work.
|
||||
type PoW interface {
|
||||
Engine
|
||||
|
||||
// Hashrate returns the current mining hashrate of a PoW consensus engine.
|
||||
Hashrate() float64
|
||||
}
|
||||
1103
consensus/ethash/algorithm.go
Normal file
1103
consensus/ethash/algorithm.go
Normal file
File diff suppressed because it is too large
Load diff
47
consensus/ethash/algorithm_go1.7.go
Normal file
47
consensus/ethash/algorithm_go1.7.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2017 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 !go1.8
|
||||
|
||||
package ethash
|
||||
|
||||
// cacheSize calculates and returns the size of the ethash verification cache that
|
||||
// belongs to a certain block number. The cache size grows linearly, however, we
|
||||
// always take the highest prime below the linearly growing threshold in order to
|
||||
// reduce the risk of accidental regularities leading to cyclic behavior.
|
||||
func cacheSize(block uint64) uint64 {
|
||||
// If we have a pre-generated value, use that
|
||||
epoch := int(block / epochLength)
|
||||
if epoch < len(cacheSizes) {
|
||||
return cacheSizes[epoch]
|
||||
}
|
||||
// We don't have a way to verify primes fast before Go 1.8
|
||||
panic("fast prime testing unsupported in Go < 1.8")
|
||||
}
|
||||
|
||||
// datasetSize calculates and returns the size of the ethash mining dataset that
|
||||
// belongs to a certain block number. The dataset size grows linearly, however, we
|
||||
// always take the highest prime below the linearly growing threshold in order to
|
||||
// reduce the risk of accidental regularities leading to cyclic behavior.
|
||||
func datasetSize(block uint64) uint64 {
|
||||
// If we have a pre-generated value, use that
|
||||
epoch := int(block / epochLength)
|
||||
if epoch < len(datasetSizes) {
|
||||
return datasetSizes[epoch]
|
||||
}
|
||||
// We don't have a way to verify primes fast before Go 1.8
|
||||
panic("fast prime testing unsupported in Go < 1.8")
|
||||
}
|
||||
57
consensus/ethash/algorithm_go1.8.go
Normal file
57
consensus/ethash/algorithm_go1.8.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2017 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 go1.8
|
||||
|
||||
package ethash
|
||||
|
||||
import "math/big"
|
||||
|
||||
// cacheSize calculates and returns the size of the ethash verification cache that
|
||||
// belongs to a certain block number. The cache size grows linearly, however, we
|
||||
// always take the highest prime below the linearly growing threshold in order to
|
||||
// reduce the risk of accidental regularities leading to cyclic behavior.
|
||||
func cacheSize(block uint64) uint64 {
|
||||
// If we have a pre-generated value, use that
|
||||
epoch := int(block / epochLength)
|
||||
if epoch < len(cacheSizes) {
|
||||
return cacheSizes[epoch]
|
||||
}
|
||||
// No known cache size, calculate manually (sanity branch only)
|
||||
size := uint64(cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes)
|
||||
for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
|
||||
size -= 2 * hashBytes
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// datasetSize calculates and returns the size of the ethash mining dataset that
|
||||
// belongs to a certain block number. The dataset size grows linearly, however, we
|
||||
// always take the highest prime below the linearly growing threshold in order to
|
||||
// reduce the risk of accidental regularities leading to cyclic behavior.
|
||||
func datasetSize(block uint64) uint64 {
|
||||
// If we have a pre-generated value, use that
|
||||
epoch := int(block / epochLength)
|
||||
if epoch < len(datasetSizes) {
|
||||
return datasetSizes[epoch]
|
||||
}
|
||||
// No known dataset size, calculate manually (sanity branch only)
|
||||
size := uint64(datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes)
|
||||
for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
|
||||
size -= 2 * mixBytes
|
||||
}
|
||||
return size
|
||||
}
|
||||
46
consensus/ethash/algorithm_go1.8_test.go
Normal file
46
consensus/ethash/algorithm_go1.8_test.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2017 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 go1.8
|
||||
|
||||
package ethash
|
||||
|
||||
import "testing"
|
||||
|
||||
// Tests whether the dataset size calculator work correctly by cross checking the
|
||||
// hard coded lookup table with the value generated by it.
|
||||
func TestSizeCalculations(t *testing.T) {
|
||||
var tests []uint64
|
||||
|
||||
// Verify all the cache sizes from the lookup table
|
||||
defer func(sizes []uint64) { cacheSizes = sizes }(cacheSizes)
|
||||
tests, cacheSizes = cacheSizes, []uint64{}
|
||||
|
||||
for i, test := range tests {
|
||||
if size := cacheSize(uint64(i*epochLength) + 1); size != test {
|
||||
t.Errorf("cache %d: cache size mismatch: have %d, want %d", i, size, test)
|
||||
}
|
||||
}
|
||||
// Verify all the dataset sizes from the lookup table
|
||||
defer func(sizes []uint64) { datasetSizes = sizes }(datasetSizes)
|
||||
tests, datasetSizes = datasetSizes, []uint64{}
|
||||
|
||||
for i, test := range tests {
|
||||
if size := datasetSize(uint64(i*epochLength) + 1); size != test {
|
||||
t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", i, size, test)
|
||||
}
|
||||
}
|
||||
}
|
||||
763
consensus/ethash/algorithm_test.go
Normal file
763
consensus/ethash/algorithm_test.go
Normal file
|
|
@ -0,0 +1,763 @@
|
|||
// Copyright 2017 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 ethash
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// Tests that verification caches can be correctly generated.
|
||||
func TestCacheGeneration(t *testing.T) {
|
||||
tests := []struct {
|
||||
size uint64
|
||||
epoch uint64
|
||||
cache []byte
|
||||
}{
|
||||
{
|
||||
size: 1024,
|
||||
epoch: 0,
|
||||
cache: hexutil.MustDecode("0x" +
|
||||
"7ce2991c951f7bf4c4c1bb119887ee07871eb5339d7b97b8588e85c742de90e5bafd5bbe6ce93a134fb6be9ad3e30db99d9528a2ea7846833f52e9ca119b6b54" +
|
||||
"8979480c46e19972bd0738779c932c1b43e665a2fd3122fc3ddb2691f353ceb0ed3e38b8f51fd55b6940290743563c9f8fa8822e611924657501a12aafab8a8d" +
|
||||
"88fb5fbae3a99d14792406672e783a06940a42799b1c38bc28715db6d37cb11f9f6b24e386dc52dd8c286bd8c36fa813dffe4448a9f56ebcbeea866b42f68d22" +
|
||||
"6c32aae4d695a23cab28fd74af53b0c2efcc180ceaaccc0b2e280103d097a03c1d1b0f0f26ce5f32a90238f9bc49f645db001ef9cd3d13d44743f841fad11a37" +
|
||||
"fa290c62c16042f703578921f30b9951465aae2af4a5dad43a7341d7b4a62750954965a47a1c3af638dc3495c4d62a9bab843168c9fc0114e79cffd1b2827b01" +
|
||||
"75d30ba054658f214e946cf24c43b40d3383fbb0493408e5c5392434ca21bbcf43200dfb876c713d201813934fa485f48767c5915745cf0986b1dc0f33e57748" +
|
||||
"bf483ee2aff4248dfe461ec0504a13628401020fc22638584a8f2f5206a13b2f233898c78359b21c8226024d0a7a93df5eb6c282bdbf005a4aab497e096f2847" +
|
||||
"76c71cee57932a8fb89f6d6b8743b60a4ea374899a94a2e0f218d5c55818cefb1790c8529a76dba31ebb0f4592d709b49587d2317970d39c086f18dd244291d9" +
|
||||
"eedb16705e53e3350591bd4ff4566a3595ac0f0ce24b5e112a3d033bc51b6fea0a92296dea7f5e20bf6ee6bc347d868fda193c395b9bb147e55e5a9f67cfe741" +
|
||||
"7eea7d699b155bd13804204df7ea91fa9249e4474dddf35188f77019c67d201e4c10d7079c5ad492a71afff9a23ca7e900ba7d1bdeaf3270514d8eb35eab8a0a" +
|
||||
"718bb7273aeb37768fa589ed8ab01fbf4027f4ebdbbae128d21e485f061c20183a9bc2e31edbda0727442e9d58eb0fe198440fe199e02e77c0f7b99973f1f74c" +
|
||||
"c9089a51ab96c94a84d66e6aa48b2d0a4543adb5a789039a2aa7b335ca85c91026c7d3c894da53ae364188c3fd92f78e01d080399884a47385aa792e38150cda" +
|
||||
"a8620b2ebeca41fbc773bb837b5e724d6eb2de570d99858df0d7d97067fb8103b21757873b735097b35d3bea8fd1c359a9e8a63c1540c76c9784cf8d975e995c" +
|
||||
"778401b94a2e66e6993ad67ad3ecdc2acb17779f1ea8606827ec92b11c728f8c3b6d3f04a3e6ed05ff81dd76d5dc5695a50377bc135aaf1671cf68b750315493" +
|
||||
"6c64510164d53312bf3c41740c7a237b05faf4a191bd8a95dafa068dbcf370255c725900ce5c934f36feadcfe55b687c440574c1f06f39d207a8553d39156a24" +
|
||||
"845f64fd8324bb85312979dead74f764c9677aab89801ad4f927f1c00f12e28f22422bb44200d1969d9ab377dd6b099dc6dbc3222e9321b2c1e84f8e2f07731c"),
|
||||
},
|
||||
{
|
||||
size: 1024,
|
||||
epoch: 1,
|
||||
cache: hexutil.MustDecode("0x" +
|
||||
"1f56855d59cc5a085720899b4377a0198f1abe948d85fe5820dc0e346b7c0931b9cde8e541d751de3b2b3275d0aabfae316209d5879297d8bd99f8a033c9d4df" +
|
||||
"35add1029f4e6404a022d504fb8023e42989aba985a65933b0109c7218854356f9284983c9e7de97de591828ae348b63d1fc78d8db58157344d4e06530ffd422" +
|
||||
"5c7f6080d451ff94961ec2dd9e28e6d81b49102451676dbdcb6ef1094c1e8b29e7e808d47b2ba5aeb52dabf00d5f0ee08c116289cbf56d8132e5ca557c3d6220" +
|
||||
"5ba3a48539acabfd4ca3c89e3aaa668e24ffeaeb9eb0136a9fc5a8a676b6d5ad76175eeda0a1fa44b5ff5591079e4b7f581569b6c82416adcb82d7e92980df67" +
|
||||
"2248c4024013e7be52cf91a82491627d9e6d80eda2770ab82badc5e120cd33a4c84495f718b57396a8f397e797087fad81fa50f0e2f5da71e40816a85de35a96" +
|
||||
"3cd351364905c45b3116ff25851d43a2ca1d2aa5cdb408440dabef8c57778fc18608bf431d0c7ffd37649a21a7bb9d90def39c821669dbaf165c0262434dfb08" +
|
||||
"5d057a12de4a7a59fd2dfc931c29c20371abf748b69b618a9bd485b3fb3166cad4d3d27edf0197aabeceb28b96670bdf020f26d1bb9b564aaf82d866bdffd6d4" +
|
||||
"1aea89e20b15a5d1264ab01d1556bfc2a266081609d60928216bd9646038f07de9fedcc9f2b86ab1b07d7bd88ba1df08b3d89b2ac789001b48a723f217debcb7" +
|
||||
"090303a3ef50c1d5d99a75c640ec2b401ab149e06511753d8c49cafdde2929ae61e09cc0f0319d262869d21ead9e0cf5ff2de3dbedfb994f32432d2e4aa44c82" +
|
||||
"7c42781d1477fe03ea0772998e776d63363c6c3edd2d52c89b4d2c9d89cdd90fa33b2b41c8e3f78ef06fe90bcf5cc5756d33a032f16b744141aaa8852bb4cb3a" +
|
||||
"40792b93489c6d6e56c235ec4aa36c263e9b766a4daaff34b2ea709f9f811aef498a65bfbc1deffd36fcc4d1a123345fac7bf57a1fb50394843cd28976a6c7ff" +
|
||||
"fe70f7b8d8f384aa06e2c9964c92a8788cef397fffdd35181b42a35d5d98cd7244bbd09e802888d7efc0311ae58e0961e3656205df4bdc553f317df4b6ede4ca" +
|
||||
"846294a32aec830ab1aa5aac4e78b821c35c70fd752fec353e373bf9be656e775a0111bcbeffdfebd3bd5251d27b9f6971aa561a2bd27a99d61b2ce3965c3726" +
|
||||
"1e114353e6a31b09340f4078b8a8c6ce6ff4213067a8f21020f78aff4f8b472b701ef730aacb8ce7806ea31b14abe8f8efdd6357ca299d339abc4e43ba324ad1" +
|
||||
"efe6eb1a5a6e137daa6ec9f6be30931ca368a944cfcf2a0a29f9a9664188f0466e6f078c347f9fe26a9a89d2029462b19245f24ace47aecace6ef85a4e96b31b" +
|
||||
"5f470eb0165c6375eb8f245d50a25d521d1e569e3b2dccce626752bb26eae624a24511e831a81fab6898a791579f462574ca4851e6588116493dbccc3072e0c5"),
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
cache := make([]uint32, tt.size/4)
|
||||
generateCache(cache, tt.epoch, seedHash(tt.epoch*epochLength+1))
|
||||
|
||||
want := make([]uint32, tt.size/4)
|
||||
prepare(want, tt.cache)
|
||||
|
||||
if !reflect.DeepEqual(cache, want) {
|
||||
t.Errorf("cache %d: content mismatch: have %x, want %x", i, cache, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatasetGeneration(t *testing.T) {
|
||||
tests := []struct {
|
||||
epoch uint64
|
||||
cacheSize uint64
|
||||
datasetSize uint64
|
||||
dataset []byte
|
||||
}{
|
||||
{
|
||||
epoch: 0,
|
||||
cacheSize: 1024,
|
||||
datasetSize: 32 * 1024,
|
||||
dataset: hexutil.MustDecode("0x" +
|
||||
"4bc09fbd530a041dd2ec296110a29e8f130f179c59d223f51ecce3126e8b0c74326abc2f32ccd9d7f976bd0944e3ccf8479db39343cbbffa467046ca97e2da63" +
|
||||
"da5f9d9688c7c33ab7b8aace570e422fa48b24659b72fc534669209d66389ca15b099c5604601e7581488e3bd6925cec0f12d465f8004d4fa84793f8e1e46a1b" +
|
||||
"31b7298991c6142f4f0b6e6b296728ae5fa63ccb667b61fbb1b078003d18d97b906af157debed5e6c55d5a61cae90c85f9e97d565314a2f9fd9e0c08430547d0" +
|
||||
"7cfcee3271f921b95c32a11596219abaa30abc62c2c72c6725078c436c677320594df6bcb92134c1b114fffec982a1f68f13a9f812f074b9fb9c78f2cd4c1c90" +
|
||||
"7ebf1e447f7a422b06303921e3d54f430584d849eaa4b7d652e92a5d659bdfc462adcdd7991e8c66a19da4ddb5390463d073941491859397f135ebbbdbdf5801" +
|
||||
"cafb873c383893390141ae385515504d74a33608273310c312ba468046d2e20c271a38cc0e3920b39705050e752f34f244fc23ddd17ff18677756a87671d4145" +
|
||||
"3aebf97e4890da1d645f41eb20da92a8537c787ce419580073c46aa3bb154952993142ec5b4fb6e8f108fd15fc618cd5c27b45a37ee6dcd52a4ce656c0f58604" +
|
||||
"717ec55f5e592355f1f20e8316f8fd77243734a8b0f50ad93c1d95b5b0482afb22cd0667d935bd6053d7198b54974e10d100df7ca3ec2e0bb5ccce5807b266e0" +
|
||||
"8429d5fec2ae6ae1cc7c5efc27f19c89d4b4a6c5c0b9397886dac635ba37446ff528b582457a4fe7f803f1a47903574f8982d4a679b627396a4e97aaa12fa179" +
|
||||
"0d31ba52e9010bc3c26ace81f702f86649fe9eeda9ec03b74a8a5cf540d82e22af33ab893564397dfc4edd8b1677350df5b82ab61d24db95f58fd2d78afb49c7" +
|
||||
"2d2b1fefa8ff6606b8623829cc752ea37d663b945f3f1d48ad07b1416af252f81b55acd8f164da4faa9d9453721b3b795041ce7df7c77edc13865dbe04fee331" +
|
||||
"47daebe18c183c4a6594a6df3a4d2dc5e3811d805102c9c49286e3d12b38927fa49a7b0cdcb1d799f57118953e31c560aae213a1799d59a78ae68f0590347061" +
|
||||
"fc2668caf08f860452f6b7d3ebc1efecc2e1227d33296b1f1850360dee7236e85274eaede4d18a58b4261ce1f6a7d283dcf64e6d021813f82a566354445327e5" +
|
||||
"6217279b2393fe5aa0f9eb149d4866e1105106bcc221810ceaf053f2ec733d8a22f409c1baf955e50184005c5d55de907de97f5f713b62ae10937e1a7af6267b" +
|
||||
"d2a239e8589017197c343b81540bc26bc52bffd5336fb1da1202a511c7175014d2f500b9d9ce78e4b9f2b158d0fb27af352b6f78c129cad642fe909612c9d658" +
|
||||
"17a8d7f9195ee97201675a918e3cf520fdc19f92b7e6a3db806d4f3799361334082cc58a22ddb4e4f5760bd1667c177b26be325166c6bbed669a158fc87acd43" +
|
||||
"a2462e12578d72db6606f9e24ae659ff411ac9b31d696b8354fd08a591622967a14f8468eaaae3907b7818154ba2d6e4581589354d178bb6ae1c03651c44bbf0" +
|
||||
"e7fa52cb0da09508b5a444aed05a54f416841247a4fe36bd5529029e3adf78b105e22468ed775f4d0954504dd55f2c9b9e6b3a086370b2c0b6fec7efd6914e07" +
|
||||
"26627edb7a04869a874e31f448271077a7de3031cf81bdbc39848efee6075e0d65fa3a32640e9f0395cf7ec12139992aff0a54e0a7dfe5048b3cc03246b56f7d" +
|
||||
"3093538a7b87538d8792a665bc589373621b2f3cf47d2c1f8f580fe34d79c6b2a66323ce89808ce0e5cf77700f5a4446c4be01a310e8f7c7ebefe756b0044886" +
|
||||
"a0477c88ee8ea8c71503748a4cf9eb40ad5c1c8accf7c63c0f43a94ed2b8a5999df3ab9b11b80de73310e036ca88668e640015fcf9cd18eed05517d54896f43e" +
|
||||
"25e7931b44872c4e4183500e0e8c5103292bca1c0d6b0b00c9acce25d31204bb3e4f255c03a0a0916664e9c831b28b364078109a74411a11afb1e610c7d1c9d4" +
|
||||
"ba5e10d0ee0da409654d9e7308395e17caeb9caebccb0192679866e6f2ecb5f10044333bb70d61712adb6d74cdec6918ed9a71d9925da576a1e6f4e906a5cd5f" +
|
||||
"0e94a25e48a4141e4e2770144b63e2449b0f84c82879f34d78440cc430196ba85a213fdac1bcf279a46d7592fa29a876bb7a2efb7081365522a3f06fdceaedd3" +
|
||||
"cc0335cef9ea570733fe8799bb1b918aa7732b4d175929d80c7844a78e19f2dc6a6febf648f49b40320b0f7d784e7f84e45408d70b046bd01cbd8fdaf606fcd3" +
|
||||
"02f4e5a48ab8d13e93a246adfcc94f3109e02a7a969986e75b6ced6bf2d11a55ab77488e131b65a06398fa8e384dc90d875584c9b17cdcf2da5dd72a461cd07c" +
|
||||
"4a955c5fe48509b3284476c42247e086de7d63839b7358cf4ebd9edf9ac8b6fd0c096166405de19c51e8785009d30feb67cdb8ff9ba55459dfdffba8c022e26c" +
|
||||
"0ebd399e4b76ccb4d5491a862c2c4d8cdf1461a96c9b98150e170efacec980edc00a2c7f6d7c6bea3075627e1eb386a7f1ede1059da81a4ac5cf35aa173c88c5" +
|
||||
"1818dc0fbc688b68b82ddc225b6c87588e0c680e303e737c82a13e34be58df8b0cb336aeacc698c79e7682ebb69e6cd6bdc5d11790c96afcfa9290f39515142f" +
|
||||
"5f90b938216a1d14bc049ce3f0ac135722208b989d2557d3520c2186479f179e50fe5b125b8d6638a65047729c6249b9b2c6381c9103c97d1b389cc9cdb31c21" +
|
||||
"8a2eecbf4b9ad1dcfa57446cde88f96563a544c49d6f5303a84a1b7cf074fca78e67e72c9ffa0c542fb646418c6434b16b771088140725cf2dc723c1a975c4ca" +
|
||||
"8a80e633721274907353f51e95952c2b403b45750b42ad10961f60473eb54616f61f7b038c5b7eca475d6a2b844994a9eeddce4f7bb49782e50ef78bc13b85d1" +
|
||||
"9e956f47c60823f3d1981413cb78d309f63a844694861b11b5238961c71f61d82daef6795734f0961e92b9167c57f48e91693e9656fcc6e88f9ce2d373da26bf" +
|
||||
"45b3dff50211fec72387005a7e04828e4ae7ddd10fc2332acf5f1b0f67adcd863752573c2d24488857bfc58c41af45be7641f5cfff611f184612fc0d695866f4" +
|
||||
"2b396b1d9881f442c4a995f4b500f02d4ab4b53ad6e01776ab0e244583f01301203a1515f3dbb73906014e36c7143bf882b005f0228ca0562623893c8a24b7c6" +
|
||||
"4c2c561912010c121b5c3a1e35e75c0b094731e9c0d6acf5a2b1e5b179355525a175640579705f898feb98bffa25633bc126613fa27d2ceb214812902ada23f4" +
|
||||
"367a78655d0d2276095c9e83dfa79153730103963499c367c5621fecfd0888253df82b3d5716703ef92594cf269310b9e6c892c488edb3bba1d0b216e92f622a" +
|
||||
"7f8f7f00d2926d81a4c7ca6cef40d240576a8d5541ccf561c8e0e699925d20347ba7493ed6e182cfe3b633e70b3ce3a0d90813574f6fe329c495d3cd46fd5d7e" +
|
||||
"bdde58d7eafcb134a9a5d3e5d66136e8c9b5d9ecac195dcc44158941c9fe2d87db52a7ddcedc9f82ec160901cc36a9c877af80ceae0563dfa75cabde5d7a7c94" +
|
||||
"9f24bc190f7c2045368356474ff6eee284e7125d1c5f9a036fbde24cecfd3a30481ce077f20cbcb31924368296abf66ce4834102cf7cb949d1b4c6faa6d006ef" +
|
||||
"21379cead5d5a39324d41555c46e0b42a1e871143e47f8e6b3d794e75d7a43c282732766d856e04e666ea346657b157404b0fc8534a2dee8243d40a5e37609e6" +
|
||||
"18bc1d52b91a7623aaf8214a97e4c8c5d860b31c3792b129354a121a7a7e42b50dfbe3ab6590769401eb280545547a43c3a1455355d5d5fdedccb472abfe75b8" +
|
||||
"f5e7d62b0b31553d8d55de0c3c71e6f5a2abba6fe81e9a42ec1968f235bc4296c1ac5df7430917453384450ab56dafa7c7af764cefa3b0bc861c52ae27692365" +
|
||||
"9d7d9ed7609958884147acca867909a75bb6a2c364debefaf98c7ff70c7f4acb5cdb81100fd79a48c5139f8bbdc6553b509f1eb0f5d5d31886a602cd669b3f9f" +
|
||||
"59195a1fa2bcff1170003ba1b2e5e9ad7f2bfcd0573d0f2be9d8fc1773c3a63a2b9292cdbf9b4515c0b1d51772e5ee95303ff493d85314c989e269df4ec3a916" +
|
||||
"40988a11c6a4ad96f7d0541a150edf444c2b1672aa6d37564453b835c2d39864c05c4366492fc9164bf73795410e7aae8206430403357fec6389142b4976b218" +
|
||||
"d70622b4098e322f73020a0d045f07668d1e512c6eeed6e2befbfc3a6ac64054396df96fd41f7aeefa0ab1f66bb52ee1a1df066f365fc43ff0800b0398b621f9" +
|
||||
"a415895268505a81517c44a56dc94e76580fd107dba034bab9f4f4b8a9f881ff34c60c406c47b6d4a998894401006aa88f328393f9cd55a2b4d24db5abbcb05e" +
|
||||
"20d392f3feab3ca12dac475eb3690f2bf9c699d7d90900d9a840068c8cdda2ca7a27bebd685a26eb01a768259a65ab4d7efc1811c87a5a1f4e5038f6b3dc74a6" +
|
||||
"b46d9ac58d31bfc22dac23645aeef819329c9419326f22e1c24c53457baf62ae9b92ab5f999d4ef0ccfb5a21b7598340eb2d399ec81588b6a674c5a1e45aa238" +
|
||||
"c55cae8e1af0f5d64ea378b8afeab263a3a2e5c71cdda4cdb824ae55df2b0260aadf386275ef57781d46f6da3d0b300ea68c14a620c25b5df738c54aef04d63b" +
|
||||
"7dee06cd225e9ed00e78abcddca5a133d8b5e0d9b287e6436014c5da729442239bddb7ecd3fe34e6f6e530134a03ef45de4ae4fe3bf507f16cdfb9bab1fc90e8" +
|
||||
"dc565e4a7ead95352c5a894661e5d82c6d0fc47843d5cab12c4013db76c90734cbff34c73d0d873ac9b27b417665f4948469865f33179624860604a9aba2ceb1" +
|
||||
"68e74b6af3d1ad0bfcac4180ea844339a034b6b2c3e2f61f0c7afbaa76c1ebe93727df1d3db27d59a5cf51b2baaf637b6eb8a20302ef9af0b25dbe3a5e74331c" +
|
||||
"6b0c8a0cf2a2ad72d2e19797983e09468ea95270dc229f2fa084dd2aa96e722016504f6d82508572d9c30711c3ef41ae3ae2f36cc6f5dddbcb0b40d9499b24c5" +
|
||||
"4cd36d2927a6b9d57e335e4fca7f0f16887711a8c1ffa0b48bda46c506ca444b7c23e2c8dd086c2a87283d5fc0d58e9a384106837318dc84ffe65b52d4cb9141" +
|
||||
"2672adfe139c3327350fe3cf355a08c0ca43598a253833e114243c5253077d65643323f5d69b3c7902d91bab7a0928754e7d80afab8d48539fcbe0d9ab83b4db" +
|
||||
"43a6594c4071df2ef35acd1f53006a570f09104f1776b26a303e2aec93a00d2fd8c952d1ca0e54504cd9b469be7c1e71557ec31467ecc773ee817b17c4418712" +
|
||||
"163ae86646b20b80c85860e828c48e88f1309c9ff018e6a95f4c1178de6a4f9f5860039511845da7d8727b5d824ba2502d0a3d76ce74372db77c2050c728dd65" +
|
||||
"b3a15da4f1e1e41c3c2acfebc5618e5e923d503c43a3421d2628ac037c5ce13c74c4ee14d47af02323872f6bf2e8bf09d017ea6e8ec4d3f9fc4fb203ac4e1663" +
|
||||
"756b11629224c676713a42b1f43dfd6362876be1c4865928688765589e26c8dd8bc04ca18d76ced7f786cdb0fa5028ae53991d5b7b45f93bbd50aeb97300f04e" +
|
||||
"69c6736f270907f6a7ad76dde0a365183a961bc8385511e0f22ce0cb8f3c42c5d3928621841e30285fb625294865409267dbb0cf91730ba2fb1088fb79789a54" +
|
||||
"a856311bdca5b0ac0e95fbc79b11c561dc03ea82db182808031e86ec327097143ee761bb62dae8a9f4101fabcac1fc87b3c2080820582dc8a7a8287364550013" +
|
||||
"08053c781b3eb279c89e817fe97103b6930fef2dbf7728def389403a4283f63ec04ae953784b749f0ea6f08749781cd17fadfd15bb197afd2f4e0a8aade2b1ad" +
|
||||
"5100cbdce49ed59658993c00e06bf57c0026b97beadc30cd25f586ff03ab40fcd731535c9a1ccb2c99dc7f8815feab767e1237cb069981f28d8fe26bdec24218" +
|
||||
"488e6086c0ab0efc5d4211fa0726b3a11387df9bb62b863a7b154ca390a268f5e49f50dec45d24bece2a06575cc07a24bfff017d7445024739efb050ace5f345" +
|
||||
"98dacda843d4ef5bfb2c931dc16ee3dd8b61a6f01d9a7de8bbb6d89ca8695f8ef8bd1cc6e0455848fac7691e6789218790270aef40fba114557fd88ff74fe8fc" +
|
||||
"476d9b9665d7e45582540710ce92c8dcad1ad8c05642a23a0d58c02db37ae1a0e70fbc5f71b1300fe398c74cbad37fd57379f58dd3e2d3de6860a17acf3c9321" +
|
||||
"02eb4f9d596497bd849c5bfaf59a83113ef389b6896aa4d4665504a22486299993a9987b2bbdb47d59b3f6ce5d2c9f9ba33b5f0760388ca7f8d8af07c1cd28f5" +
|
||||
"67a417a59ebde4bb9867d4e7b7b79dd8665602c029e9a16a7718efde3d034f13f7f0b9af1702c335893526cb87afc2100e874b25c37fd666bf34bf6a653c7cf5" +
|
||||
"44e1fe0286a6723c7d33461dea380b392dad68f79a78fe1b785d7833ca0d1cd68cff472991a625e3099f3ad2cdc99bd37eae35353cecf424098389dbaf1885fd" +
|
||||
"7db54909a92ee879609eb2e9ef4de1f4338f0df53dbde486ede944ae69869fac701d4f1f48c83757b470ea28c9de2ae5f1ef5d1c91118d16ca0d80b1baf3d314" +
|
||||
"056949df27a09eff70c9ac50b54feff67a165ce5e22ba2222defedc7c39e02356c3553e97524c1506441527da4f5de121142ccd494f83114b3ca2dc37e15c752" +
|
||||
"e2faed7d50254124d68f67e26f4f50c9f0edf6e58b916ca830c4e33801dc11039b18292b87b08f4f2edbaaacddcdab78ff3a0004f86034080f2ca4394b14aed4" +
|
||||
"31e38e3605e6b257bd772954d2f4b846a17df7ed6e5dafa33964d9e56a07a19898fb4dfe8b2ddbd11fa0013e6ebc0e429a5166a43d1ec45557cd1fc1bddbec4b" +
|
||||
"2e9ca26395394c96395ff8f557bd0f7f805c09f0c18534585b7c7fc1d07f145372983ad77fa804fbb7765934e72beae0929a87cc6bf7f6c242ff5db2d4d5541c" +
|
||||
"8c366d22e24e1da5379836fc0eb484683285f99f178b98ca170464bdff60ee04584c12c65408102ac6dc7d10bf58a7d770bf1b3c636a48f934f6f4bbdbcc75d3" +
|
||||
"fc551de3ebaf77006707f6120b3804f2bef9b4bd59f5996610c09ba3953994d1b78a9f3bc3bafeb52266f10755ea842e5b4370c937c09afd34a092ff9b98b4d3" +
|
||||
"518bc2480d4b132455b7f03774ad76b83b254742117921c31cebeab5f39c145f7f373a5603d17dd95217ba1af37a0aa95b2992efcd02d0bb4ad08ebafb31440f" +
|
||||
"1ccfce45882b547ee4bf6ec7ecae11ed79fc63b03636c8a14ec4e0f6877eb658d839be2eac0f10a8948e74203f46078ce66aad2764ff05590e2ac7a8dd8b3036" +
|
||||
"901fcb7ff3369ee989a28e34b9b62e1e607d14da3049ded1a4ee50257195eaaef995bed79ec85111abb522aba1fb306869a1ab381e82943f35345bb5502bb90a" +
|
||||
"e2a0af77526a84754ee4d600ba7f8ac98705ee687bab949a081849889d7b83a21a3dd34af84dc2b9458230ef0ff44c6398d3c6e48e5c09c399ac4d4c7b285549" +
|
||||
"e0bcab7fd96de42f072f1cb633e3e250745321049d0d7ecdef4636e70e94c8414e76ecaedd6ee0792e97de11e7dc1e1e1801ad68f9147278e268d7ad76c5bbb7" +
|
||||
"98386fdc13ca8c77569d96e0debba8ea3b751352136c8f1c8d611a69f1baa9aa4b9d0a476ebd5dd21339ef7f97f09aa86b69a7b114cebe17a6b0e58bf52803d6" +
|
||||
"fd47d9eac3a988b51e9bca95c546d49367a3126bf8ee44fbd0e77611473a1d3d2de0ce4ea54f9bb7f9dd0d0c065f613a623fad43a445eba294fd00037492914f" +
|
||||
"b74d10d0b97a0cf9bd3151c3cade89521f36b6fe1aca7f352e79a77d063da5337a7c88d90e9e566bcd97732baa4459305967c2f65adf1a4a4c7991cbc99df3b7" +
|
||||
"14335a107a97a4ab104bc94fecd1d003fe6d2f22e717853c449881c4ccaa7e7a1e44961a14a47a0d0aa1b1493dd02760ff4d31fbddf5941f93c8e5925d1886e2" +
|
||||
"8761baef8610fa6be016c8f4fe65bb0f335152d5e94893e274f2ab90118e4c07957d252963755b4b638ffc0a734fbe6e32c2e304b10a46a4eed330d101c4f0ae" +
|
||||
"011e7f94b89bc0eb9d358a6548b3f0c47ccc3c2d986d381437c49041629c6cbf61bdf0825efe17e4abef128003681450ceeff0e28842895d8e338c247abf81cb" +
|
||||
"7260fd45042c1f6c630a4b195579721392e577fbfdb9f5b003a8b9a6bc15ae754f6255131a0be600c7b07e2cee1ffe32aad4687f9a429998ed9059a99fd879ea" +
|
||||
"c4dcb55f4551bbb70c187cf1b162e2ca4a929edd6ec9260877df652622ae073fc63c0d8522d3882ba888ac50a67a68fb6530193f93165093a1d8132e87d8887e" +
|
||||
"ff2fdab0fbae6ab9506dae61fada4023133d166bcf1956aedc3237c77d1c81dcc84ae957d89367b0fc950c78e58f2cb9c4fd93e16b94421fdecd46c3ff55592e" +
|
||||
"4374a7f7d8ede9923115770cb416071e8f102d4ad78b891464ffd14f589c238c8e13a4e2a81744d179e7d3ae36cffee75ceb99633face85d077d0c15b3970930" +
|
||||
"075dc08b420e0a545200895207c5a746a18ce9ab64a50d3dcf44da857fb65e4efc29b2b4d532dc6a03b699dcfd77030a4945e6431273e25f06ad8f913c2a9eb7" +
|
||||
"59d8d3049868d337e451726d95c4cf8baf381096fc9b62679175dc8f14e52f8b99f212cab6544414c62f17c8323256cce95356dcd351e34c7a1576b17c1406d7" +
|
||||
"5b8bcca8099a1993df1541ded61b876ae83396b191b719c4b1cbe50d73fc13da352d827ba09aa7fdfef3e4e0273c31ef4fd38b93cf64199c3969a7c09dd5e0f3" +
|
||||
"ff93a5a7db9c2c1ec25e3060bcb5481c6802e1eca78f31862842ea08e8f92b8e52856c4c9fedd0bf20e386cfdf926425f7756ff41fd3567c5bf334e96e3f492f" +
|
||||
"74bd0519d8d98efa0b427ba681b8b1be8fab041ff084dc5f8c4d5d48f481115d7e407ad8a6034f481c2be86f8451980c3aa83a3fff245d90d13801a54527e97b" +
|
||||
"e392b25867882d43e3819f4a8aa380db63954ec23d2f0c11a7aa5bc7a3aedc43ecd3b024280ed8843399e28deb954bfc11a3197fb14a9c9a895859e390e9586e" +
|
||||
"2ad21da39bb9ba79a62222d228a0fc96a24e801f00afc3f98d2168a8a253f24deffe461f6313de9b433e1d2e307239c0e3fd5d9fe4c8352c2c6797b1737e93fc" +
|
||||
"14d411bc69bbc9d78cf91734052b8aa1dab348e4c243b8e6d623865c135f807de8d5fd88f3921327affa37066dd538351bc4ec52eece88856de0a424a87d062a" +
|
||||
"f68cf24db37dbaa8e8e96a812fbf32ccafdf1b9d27f11bea23df02143bd09061a881c010819a315a5b6ee44b3c60979b3f7b41f488b2429d49377d6542fb0e22" +
|
||||
"d09a0ef5b81aa7c8134c0aecdc7a4f9228559d0bb826d30fd77fe0f834212647ce61e22fef0a1c10eb4177de81c31c12054a15f81b605619f3045646110673d0" +
|
||||
"b2d79d80577fa43284266fd2ed54f9a3b9df3509f79559c5bc51a58521bfeb2f95d8851527b7ea47b92a694f6ea2b67dc2d4f506d11d2db32c2929cdf5c8816b" +
|
||||
"7f0c310cceb7ede08d5965ed2c7be6c0a317251c7d31cc4a15f6d7976a8a1e6a2f386fe0071d43a50bd0ce5e864a8e449fe9600c6e4a84866879c490de9f9d46" +
|
||||
"3f22708abf34d3e180dbb6005484a6afad373838cdac335f05c034e3090b2fbaaa53fa2db1f96bbe141d570f17363ff98672500e16994b79be74634755b09e66" +
|
||||
"f1b37e338c946bf85e06c97e31dbddf257d58fd10468278648d86f38710c2ca0b6ea7cac4ea0e2c49b96bf1998bde1b3d38aa853736308e12b4a0d467fdb8a73" +
|
||||
"0d810ce45518614bd5845f58a9835a5cfbe745f45ef59ce9a677d10d8c9f6294f1a0565301efb3c6610afda35167150bd326c77057e530c213da63af3e6a600a" +
|
||||
"d87b16ec5cbf76a13764f71b3e7e0c867086ebd9fad02e1d747030064e071a13da4758cd0fa20872b3dc350f4cbfcde1b68a97aca41e32207b40beddec412c0e" +
|
||||
"c75d87c6671ed94bda5170aa2866509161c28d550190675f60139a7b460469f3d4829b3c65f5d185936582629160522fcfdcc53fd0dcc8fc46de11d52bfcc5e6" +
|
||||
"3407ecbbb682cc1693d6543756fa4e068e92ae1a94924a1ff6891361e5f262b7d3c3a3bc2866f54e6d03ebd5479afa3f424077d51668cc60e23b35fb0222ae22" +
|
||||
"5223ba8a8c416b68c8853022d150c951f06f8f85c2078d3035b8ac3ae984ffcfb024431acaae8bfbeb981870f9ad6bbb88d7d5ff34ba21a44cbffd0aeaa435ba" +
|
||||
"7d40d22602e807ac9a69db514ab13248133142cf03fac999a2b185f34d83fdb495ef042d4a5e92f2624193c88858d91c0812b18fd67046cf50635e6ab1ea9ade" +
|
||||
"7b1fe783dc5147f14f9194cfa92c03a0456f4171f9e5c156fee1c607a1e9e06535f2dac49b92ddf5fdacbf88a062bd7ca5439bae645100121e598deee6043baa" +
|
||||
"85cc0d727f08d37a766a55a9ca21ffb6594fb73f9aad15be4a64bafddb6c85d00f7bb5705d9e56b410dd80df8b087b6d67c7ca84eff2ad699f901415fab21343" +
|
||||
"6351a9bdf83b440e29f3950c7e4c49963ab109686d78fce629e9207db2e17eb5f02f01db6441002d72c06c6c6bbcdc0a7443589ba29909a5a78864ad51e1dfda" +
|
||||
"14782d869e4989ac3c5ef0aa1eabe540e9e7cd4e8eabe25b07f300a134a92718186f085d5c10a711ed0e574bf7550f6bccfc3c094d6e59619bde9fd892af8ef2" +
|
||||
"50e1cc3afdcd9c84ccb97344542843028b00064b0c3d18ac0f0703fe6f9683d40813abbb883e164c5797bc1555338566cf8cdd358e9fcb0e93f08f7ae06a5121" +
|
||||
"c67a231106ad8fd42f0798d7185c2de78b8b76c10e82272a405212ce3b904f90236eeea02054953b967cb614e8f8ac49b977152a52df981c86fa4a92f7f70eb6" +
|
||||
"cd4eb65986564039b0d77f8bafedb4fcbf9c34b8fe9c5fa87b0785c118a8624498fb0184a0dbbfb16777579e1964330c12e494449f6aa5cf69ec4a32054be553" +
|
||||
"6027e0d27c7044abd4c0b8e43db703209037efcfd08944647a90a1ab0c71011753354990cac5a472fae44dc370aac8131ebdf31456a8484e7fdefd268cbf5cb5" +
|
||||
"85ac615039d3655b1348fc0b3b078ac41cbcaf6aaedcc1153bb8d55c307f45405ad6a959abb37bf8891c8dec79a9d7ccd9b791cb60361d4a28f33ec0dfd13fa8" +
|
||||
"e0b9b29e14bf36f5047e51a39c2efcefcc156bd08e46c5c1000a3cdc2bb20713e19d6f492c40e51eb93628cf85d07041ae5353e7decc824cbb1db8ab3a7a7fca" +
|
||||
"ff04c2af423bcfb1864ddc864624b827ddcff2a2f8fdb7a3d86d76e72b4f850ec1262d8fc89e7b12e4cc618afe6a2bdf205075c2008f93b7281d80180199409c" +
|
||||
"de850d1f14ca0ff960f69772385cf0f0a0f47cafd5489ea4fd8b68ec7aa539b942379139756c95bb90818842cd43511edbb7577ae469f46728b13a61e6eede06" +
|
||||
"3a4cdfab5ed590feb807d55d76e518d1d74bfa6704f7c8ccc672824b4d5ef5fa5b3ab8fdf2b6c1753404ba35b76aaa931a4e0e5ca7e440524166b23e9a8be9e8" +
|
||||
"635381f6c9086802d428fece81395dada6b3b866e905ec00ccc4fb9b8415dd15e443f84b7220e3b28700ce3d88f9c6df2afea39e0ead537a50ee11f0c247ee86" +
|
||||
"d4b3074e8761de4de611c409c6d4c369c2c11742a7763f6550edfaae49afeec33353a14d2ae60687dbeefd2fe29689da6ae79d7b06042dfd25a68bde9182fba4" +
|
||||
"1ac53706a8b96535057fc2f99ac84a9cfc6549920c3e2cab44e48a08e77207b6a95b2f6179d6dfd6c2d9e3c91106a7a687e40bb2a1c5ccf566c0e31a0fdbd0a4" +
|
||||
"f270f9812208f939efd9698a8b28ce9c5633f18ace7ab0a7550d9e7e26cf62eef49200331e19a64bed648b5d18ceb389bafbcb3f280ba78e4cf03b053f2a5f08" +
|
||||
"3c852452837138004073cf6726143179386279f1a8f15d44876c19bf6c2e2992ce6056191bb1a386f0e1f6f249495cff126991c6560e3f613e56525c0c49b5cc" +
|
||||
"2ea4e736d83480f2b45d7dc840b849887f54a2aa072e72e3fd0db34e5cddb02221fdf2a40fb6ec271ba3a09de8dc73c24328c5d9a33ae0adc9874902f25d5bef" +
|
||||
"4d85914557e2983c93fba16cdd4bd929e878b5d51b142b6e9aa0ce84871b7b03ee6cc13251e17547c2d20a7d4e948760e207e29de58a7ccb71b87f99d79837db" +
|
||||
"d0f293ad3d33ffe91435598e8a4584b7b7ef5b1a895a2827b4976f81d335e4aa6feda3539690899619a4cb34fdcbbecf1b8b38cec2ec7c07ce84ec3044f49656" +
|
||||
"28fdba8971585afb509526640d36425777b6ddf5b2a49d795fdcf71e57fd35f29fff37890541b6e152f14fb6ea4c70a1b9f159d02ed895a68dcc276f5d5ae83e" +
|
||||
"47c021392ee22a398c8c73b3446d61562b3ec596036959aa645a65e5d24f733e142ec0e184b72a2adcbe3913932b2c9503c856a7e989d24f306e01e99268188d" +
|
||||
"f858694e297803effeb8e28bf8fb63ed6787acc2c61f509e19099607512d40928a08e649474a43728b63523175fad12ad088aade0c1e20815c7c12773bc959e8" +
|
||||
"640ee23eef2b1653ae8918615b45158a01be5a5f39a75a7c6cd8f1f6b463516539771ad251d5c2d40c5049877765512c44e58bd3b9ac3a0ac281771097880fe2" +
|
||||
"c9516dcd6f1373e1e8a52fc485d104004dcc839fe3d120f1432b213388dd37980ce8238c87a70d5abe95d78d696d2436eb23a8f620ce74335d5e47f6524b11c3" +
|
||||
"e22288644b539e3ab664dd5fd6bafb02897aab35adaef204f82d9318b22f45b787f5bacd74b01d23537973060868a47f2e3a45c1d8805a1d657f2332af8170e2" +
|
||||
"9435d7540e70e92a8c8794bf22d3e11d54ff2d48cbc7a1ac3cecfc48f80fe521f6852f97aafa0605f3e7084b15e61a74869512c9c2d84180686ea07b562cf35b" +
|
||||
"5a0ca529481ddbdba9c60729f821dc7a5a8b5c7eaef1ea7927d455a702aab538e7441933c4fff2d27de5659d6fa41f0ee72bb13a829839267f3a7b51a81a85b0" +
|
||||
"d737194d94e1bf8173248cb057cee19eb5e2cdda38c529298f3c4d3b95400198063c5b27e9262f9c66425c65568a09035bed9cd55c1f2ec4becb6b9c59445398" +
|
||||
"ad5b7c85142e713b6dd32493dcb817c8bcdbd728e325c25c5a14d764b63f960d1e48a0bc7f4d2bf51060f83b1d1f2591c6a9b79182e686b887a2c1461442e2f9" +
|
||||
"16e8582e298f87ca95a8052df33af20ebded7bb1c528920233d1aca3b3789494d97084890fa3db0ea7eb561b0087c4a90000db41ea072613f91ebba82790f33c" +
|
||||
"fd52cdd92d2ef1246911ef1dd82ad083881b72a08a40ee55884380dd136a7c0724cded69c6abf1f156b14ecd7284abcbf66522264145ee78ab0ef0d2a74eb390" +
|
||||
"10946d5efefb7175164e96621d3f158de8b57956b8b1155c35b32007e47d915cb61dabd556a370537737574741fcf9a8a23f7155bf1f0e3d3c0d2088d1191d9c" +
|
||||
"9c974139303f3dda55a70ab4810fddca3561114969d370f4e6bad60a53815eab1c4613854d04ba8b049dd7ab1a935c728299d1502ff9aa3fbb356f87f2a52b6e" +
|
||||
"947dc79b5fd211ed31dee722d3fd857f43aad973fbfacb7cbfe1b2553bdc76142ccae5b4021a4647b8d8087925dd3191a57198792b6f918de87a92705ce57905" +
|
||||
"f2dcfb67a20f8c77e700933432d60a4536d0959415f15f3eb8a788f1b19c497d3b68194e27ee736231835469d8bf0ce1717ecf533ab77dd97b35881d8eda959f" +
|
||||
"54a7935b1bc11d7f2e472757734afaf0463da3fad9804eb948e8d6444e8394b33f1c187618c7c02371ee6d378ebb7a20b6049a5504daa71999d15944ee82650a" +
|
||||
"2388f374f3ec3afd4ca58ef3f2588997d194a2741252cf6562e00cd6b5c5fe4066454d2b3150317694052b4dafb40c2f04c850e4062cd8f0af2da75280046850" +
|
||||
"77990788b27fa457ae9d0b622d18fc070f1d2661ecab529b5cb82f30a29610dc6a9e93ca9a2617ab0109957a45c1204e5eedb8860c6f4d57122060f39a4194fc" +
|
||||
"a285f1e9e7a75cc3511b8cb4865719c2260a630845051876e7795bba59573b6ce5faf7e5708eda7be25dd49c8cace4c04c541074d703e6601e043f6c63a0a371" +
|
||||
"1a381f0ff83d136f4aa29de266169ce5b3105cbfeffba370fa306a93830e3c0519a495b8b9f4b72078e2c45421b4b0667f903676a1339c70ddd1a90dbd21853b" +
|
||||
"2826ac3fa5add5073c634d4c5e87db0efe18638ee93c460257e52aacb8600ff36739818056110b2e974a1959e3784903aa97b0fcd9264f7d8f6bb5d8b7d9f03c" +
|
||||
"4b643955bf7966250936d4e7d651712db5e695a6a36b5e6f56c651ff737042b5bb73638e21ca6ce9a3e63fbb1906675d97001d7ee240d277d62df18acb169677" +
|
||||
"963d231c5276bdf5767ec35fbedb062e61c23d759aefd287b2dd62a0d6f0518d90b3c1756fde50afd33cab395ddf3cd538b9ad8862a199141331c63110c9ddaf" +
|
||||
"fa3d6c63a1fb1b45529eace826cc29a1df5df327bb782e573c41864c18e6d31401d19719326e5c35bb50de7fdc67177a6a6015b4264fecba2360ab72ae8b060a" +
|
||||
"6c66c5a05782a15fe3c1833b47e3495d29f2cfa579fcb08f02fd064e9ef2ef5564ac6a43cfbcae7d79e9f87ebc2176611823c6624db11892f8c47f8c96a49539" +
|
||||
"1c18f821ecdefb343eae3fd98dae1ef96fa3527788543c0d06d9793579cc62d91dc4d25312901c6368ba81c8536c6287230e8f97d25f6c77366609580cf26a27" +
|
||||
"88502a9aada84a794d3674ae11cd1742cf245e9d9502dbb5b340c2a6c79e3607f6b47666e1ea991ccfbdf6cc41ede46d043bc4d3e5e6882414dc65d62f9f47b9" +
|
||||
"fb7b828a89afd6361ae458c2cdc82f459c54977072702ee5a4c22955b8019d8b8d91f558897c4b661f8e5412ccdc10c40521303c0ffd353a0c04cebca5622a71" +
|
||||
"192b144d0f9c5c0706a130df887526b7b6e0f358ad9f7d0fd4d87c5fdb29a7453388c0d009da0d4c47a5d6cf8363892ac42b6ce3388771f698802b4dbfd66aa3" +
|
||||
"5fa6a6f8b42dd8446324501c807b6e72cdd35cfe08956a52f86bb4709fe2980f62152dba3571f18fcc4c1cf7a25384c4b5174e93e5afc9b9f12db2bd505ddade" +
|
||||
"d670d0d71b9548f9a07ef98521961cd96e8f363cf3222336bc4baa284b5305aab47dace615c1b3f3fb1ee23ad9ca3f58b086d9169ee5b2d3c2831e1db4f905da" +
|
||||
"11e1fe79e3d48c01bd9879ed68391e4d24d6db8d6774cb8747e7ea368aba3bbf355386408af4a59b23fce74a5e673a1044db66ed8529a65462269480736cdaa5" +
|
||||
"0784fbd77e1c41197335b4c517af8a67eef5b7165c5fd6022cceed0396089c3985c36595497db0a0fcae478e4e4d68c57b93f466aae86dd4244633beaa8116a0" +
|
||||
"de25d2a54353b7ee85fee58ad4780a2957d69816585a64f65e75f332614aa6786d1a1432f6acde385d3d6e870bc968c60c81401726a958f0caae336c83a9523a" +
|
||||
"c174faed43ec67473dcd151506e334a6aaf1731dd3aaa831f934be83beaefafa11810e7eb140f4fe80cfba574e6106c1bfe9f0b20173a4ec2663ce0580df6daa" +
|
||||
"7966a3a8906677ab680025782c61b95cec6a73b5deb16599e6521f9c6c4cae0d9286566388d5181d6ba11c51a25c62b510d9b1793f3ce9f73ff0c9226c8aae69" +
|
||||
"5d014287df074a244014720ee38e3968557db00aa63dab71854b8573c42c65116e3d88bf040d53ef3165a5827c717179e2939e310be5eaf6fb75447ba98ce925" +
|
||||
"98e83a32a90eea848500a30eaaaceb307d37b1201b83a744468a1a52632ce5525c1fce5f702421e42e7cc4c61caed539dc09001cd31a8a2b48a783c36c56a3a2" +
|
||||
"d50de42c63981c86642cc92bcceeec8a66b4afad3c1be1df4bcb8beedd442c281080c94692bf453196ed1a66a074d56a8e7f60238ce18358373efc173e70c691" +
|
||||
"f832e1139bc04e6258d77cf7529af7ce5eca28ca5cda818625c0bb5beca96d99fc9b6689a7771434aa96e23c55a41cff7b7b718df58260b3bc91762034debf49" +
|
||||
"7d8ca8d5764c52bc9665bf86db5407ee1b786d90f8d7772597eceb98f0121e3996e771d951568a162f6b71042998db8208ece5b8b0c68107b8e2079765b0d8c3" +
|
||||
"2747597072756208b0d84415a5334a88d916bda390e26ccf3046b860e7ccbe22c48cd3d3f51bb65a98ace74d52613f782db726babd02780b8d620655bf9d551c" +
|
||||
"ae9ef3056e3d24f5e7c3105c4857492fedd244ac2b8c30a874c1446630b042d819bc6b6d2d96829de903db22af706e93c5ae876d72c633600222443d1765bc62" +
|
||||
"a8a20c458ae55bce8cbbef753cccc5e7d929408d6a3709467373651f0163128aba4142ecc56ef11ff1fabf5eaf6e955b4252d1350e9002300a1236ab2fa0ed34" +
|
||||
"c9cc7dc1d4f09bd31296cec1493e725b57cc496fdac4e8d26197376bda7f74c0965c4352bc9d5c731df04f9908899cce6ec3afe15210d115992b2d95308dd032" +
|
||||
"13c557ac527424c7db02475a2fc78b88d022d212c3d02d5ee490e2436e6e572e8a1330465b9052f8a3de01aab76662d18fa3d076fb77103fe432d549bc861fcf" +
|
||||
"f63f3401cda31673ee48826b68b387802fea4471deb1fc928586f1b1614c16311c9820b563ab0112c28af5c1af5121818540c4b7d7f549b33906c1b86c6674ad" +
|
||||
"799acee7342e4a79d9295493b2430fd08f373338795764621bca444868f3f42b0e40abd4b8e148cad2861fb4980b83bb58d40eeecd8d8cb1ef1ece17b0ab72e5" +
|
||||
"06c6e650a3a43081f545acbac51ed7e121df51edb75120cce30ef7dbf41fad331120e537fb35be45d93de4fac0cadc7e5f644e2b767a285facd5f12845559785" +
|
||||
"57f4afc276e21d77f6162062430dc8918435f035f435ea419ae9f1ddb6afd46b243f8bd6a3a33e7970e7e76fab9ba6afa72a4806189462f9d0f231a23e3ee1cc" +
|
||||
"51cd10cb9043a27deecaca866751f971254fbe3084c243ef5f516bb652988b770896ae5abfa12db2eb2abe404cf694e9f60d47e734e260ae668b750e11b26001" +
|
||||
"0d2bee5ca555a44523742fb069e484f7a69c12d4bad026c03ed7af10ebc9cf2f54d143fbe4de83448df80668217a11f5a1187f35ff306e6c685cfc2417c14aa7" +
|
||||
"aeba1fb7dab05c913fbcbb8e677dd0f89324048862220ab6f5340c38b70804f625f5a526d6675a49fdc22ea6ceed477097fc723a7b6eaffd65c48dbee13df566" +
|
||||
"f8f3449d91abb367cf37a8460fc8072c4ac75f88be8b9c840ef438cbf12a2e7d55799f641316e3381f72265425f3e90fbeaa9919533d8f9262da27f1f933d4f9" +
|
||||
"a83e07aeb968016fed89e7b16babf0b6af3800a27c9c3d330b6bf8be447d31bedcc526b1bb53ecb10c3ea098bfa7d014d93274bec70b6e82bd5c443e860835f0" +
|
||||
"ae82b7be7c78cd996e0990e3cac8c1c431481c8159ae1dbc40c03f4ac543e5758f347e12715822d86c881030de83a76ba1c49e4d4836bab7b5287122ccf523d2" +
|
||||
"33935d802d2bca303cf57b36a5ff17e7c611f1cf99699881ae464da2911d77580587a7228db8325f204adb14413a13fe318e995d60e35c88bb47b99ba9ee8daa" +
|
||||
"3e40ce5818876a3911107a159125dcf768ba04074e5771334e0de430c439070422508577e474e9532f7dfbc489d0c87d37103920415b6c116a422ac15e0736a8" +
|
||||
"1e1e317adc87005f868815950882fc7497794c5eaf76f9def434d198304ff495bd2f9f4026aea330450741fb969700b953ab265aabf1fe146d861ba2aedc53d4" +
|
||||
"f929abec2dee710aed8fa605fbb9bba914eaff01fdc113836d34d855383e4a311b4ec6ef6e80dfe32bc8035d84ddc4e2c305c112b93560112c1f3dff800d6043" +
|
||||
"7eab01991f924075b4dea4db01c377ee1ca374d383ff1fbb463bf7078f6cc7509a0ecf536871abe7c95bf89f29c71f72f1a2002854113cb0d6d2192c00123010" +
|
||||
"8dc9477808a218f84afb81f0274718c024393d5be66edaac7406e520b0c8e2c02ab98ee7b290db261f2122ea68bd79f2cc6dc64936af5064cce2b4d1b7078703" +
|
||||
"951b6b81b9b60b99da4c2d12bbb50351a5b7713541db0958740910ff69e748c71bc7470a3c05489febefd384e06d267371935f652736bbcaacb20c34bd50144c" +
|
||||
"71923b5a521ac4b1ba694d024ba51b4bef3ffcff74d5dc63810b2c0f529073e13ec3232d8647ad124b21ff73402d371c0db39d46cf4d2d4cf7ad43fd8dd365f6" +
|
||||
"9b6b7bcdf664df0e62ba58f3ca0c62ad6fdcc9b091fb4926cb47b5ff8de7d3b12bd8709a46e5c3d5f0d22934c7a0574ee70b87af97d0fa46f7d9673915fed1d5" +
|
||||
"a6c57197524ec9978d1bdf65633721ea2ccc25626dcb5e7f5e090b00e413c10a6d20b45fb8e98c22928de6dda184e856c86792c7cd09d38e4333a76882d363f1" +
|
||||
"7f4d773ba104b2d04fd81027da087258fb175bfa8005c035a4719bac5b9630ae57889fb3b52a0fd47ec4060137b0f95fa5d5684172d07ca91e91eaf20dbfdea8" +
|
||||
"a3e23937f33d8774f30c7e8e5d4b2d5371e5ea5e8d290970904c4c1ff33baf675ed79599653808f652ec4fd0088877f7dd7973023ccc8377d1ada2b80c07d077" +
|
||||
"d7208686354f511925a3514c9e93c13525353b3d9528ab678e3e783c290ead88c2c3d6230bd4cb3bf79fce6dc3e95bfebda41e5d994e61ab083d73408ff6b627" +
|
||||
"6996a263d2920170fff6869c2311441837a2fc190bee104328591b402defa38b421b972b01d020bd20b1b6a6ae884b23eb829fdf032a81d4f199a87ef125d4cc" +
|
||||
"8662e24deb93700980e6ebc6882bcbaaa0283492e81f81e76bbe2ce18df4fb665436310658918ee217b5da262f1a1adbd59eb3c555cfebb12280058c75b5b33f" +
|
||||
"8aa8c2d7cebf12ce46c5f49ecec5a865a9f0b65476793884f0021f8731b1bd288f55dfa1665776b2aee1007bcaa6d92a76a2ba9925bcfa68db7cc727b2a07ebc" +
|
||||
"e24c0314c96ee4d6164c699e585461388dd73476a1e0519d92f51b64eb2842a7b17bb55d512d52da802df63206ee926f6a6a8c32de7b30e7cd3f23e37e0fd82a" +
|
||||
"556323736ecd9de77494a2f8702463f40fb837c2a99270b9050b0cbbc2c305a32380ff5fa94bf9c101c667f36293c12ff9aaf6e0a810b75230caf915135cbe6e" +
|
||||
"63ffb2a0e8632d32f72a65aa965fc556e10ddf6d5e40be919066eebda09d581a32156e1675300f52c8b355e88696fc2a67dd8e350a6e902e082af28a9809ba11" +
|
||||
"ae0a5fd9c6627fb808d757147e5d59cffd9c45874478ab226e72909ccba6592a54391d072c7eb0221f1ff7be9924b9d037e4f8c31e94fdc814a8c4cc7ad4c9f6" +
|
||||
"eacd5af66dd76bb6222b2fd3ea50a828fc3a91ef8b084214bfdcca56348517be18ca472166dd7f18c8e444e3641486e7dada626ced8710fc73a2b09b6e9395b0" +
|
||||
"31ee2c48c9183851357d230204c911b345457de602824273193b795fc21e90a0c1cdaaba36787424b23ce73e2116947f143f9641d39a4c07c2e40e02f3bd7c68" +
|
||||
"6899fd57e3eb23c6f5615c9dbc279fca0d4218bc79d928e70018533a85b4646bdc78015149b4d41d77ec7b46900e7fd5250116ce978f825569bd887bf3fd0365" +
|
||||
"e1259a7514116fdcdd6da3ffdf432bbe8e59b9bca9222c5dca1eaa61caf29b8461ddced6f312838fe490f742db696fadddd19bab8de6bedaade878be07aca4ac" +
|
||||
"76d69b81a6890e66dccd702720c3bd5601c6abdab95fbe4ccde6e35385b75e1977d5085ace928adfa382ea2890889017b9c4c81d9ba4629771f84cced6280db7" +
|
||||
"a6cd83ff9375ffb0a75a6bebba9a209f048788ba39127c1036e4bd0aad9be40754fd75295611e455909a818a3541af32eae98df7222353a4405da0e7be9f1cf1" +
|
||||
"bcb823fdea7976a810e8a3c7bf93fd947f961a344a93aa1ba99bf2df48ec82769d8c08e7b14191050d5706a9467c9122f34e27f060dd4d6e936c414c4e551b9e" +
|
||||
"5d6b5b58347ed0012a8a323f41b43bf5e960b2806de59da85b998affdb490fbc965d569114223db3ca65df69a617f6808bea23017327ddaf32990070aaf5f444" +
|
||||
"a9db44a57b5c92bc27bc71c5f8a2b6929edfed8e182bf5942564ef045c75448450eb1a4e4e09a1875e8a4a74f229879ccb7a2f2cd0359abd91a782c2ec1f68bb" +
|
||||
"40ce0a63bcc014b198adc222fc957eec0483f5b93f0db91b7ab3b3e3c59841dae057eec97abb55fc42b2de124946e66ed2a7fe8cd047cb79051b55f82594ab45" +
|
||||
"711c92364f932a5fd274fe184c85583ac7cfaf258c57e296f9c18fd181308565315e27272cbad3b21cb4490ca0e5f675365caac42f299e22d8a74ca51a9d0883" +
|
||||
"bb376804e234502db66067e7a434d38c3dc075346e888e4558b1745d00458df99db02f0e4c37702fb0989387f74d002a924790a6b7351ee0f41684bef079be26" +
|
||||
"ee9d70b560c006cff4b08b9578afb5019c21ab9418ae4ecaa7a1cfed2d880a06a03c2c7711b601a2cb3d9193e1577b4f1d0e614c0be1f69205fa6524fee80bf1" +
|
||||
"e1f1906b50e75fea2d19b8a83071a460145e1730581e5e9538888d2e797ee3cbd3b31399ecb4d6244ee44362493802b142ea397c2e7a3c1bc86f0ea0546a38ce" +
|
||||
"574e1df0c27ad8a28dca70f659ae6a1369d8b3aee7d0dd24ea370cc2bc1b1a4dc9f63911b63e60fe4ed8552bbca10e01c82d11b0ddf748d234b4aa3b31683c09" +
|
||||
"86358fad680dd2178902beadc4646b3eceff572631ff9e6b64d8a622ad9f0308cc46b7d422ce792fe5573e9b9480e1ae9fedf31edaaac3b08c5a2c6c27d6b033" +
|
||||
"6b92a3da7b838bb0a2916ebb6ee72bf33a7fa70630491f49c67031ce4b9dec2315088d0a5cbf7473fd121e0ef5f4e92d43114014c9f8c6e671086a446eb1f66f" +
|
||||
"70f0cb0c668998ed96ee0ad2687946681fe40dc46cbd170e0cabb6f6216be61221f171fb2f4273f58c10d5c4eccafd1df62fdc8ac2c5c8f6d5eb637b71fa89e3" +
|
||||
"f8347343f89667a4450c5c6e3791034d2dc3a593185b55bb95d8f8f2984ef981e4b692c1383ace4cb2c4adb80d5d582857b5d0e3ccb12845a59587b47232ad20" +
|
||||
"926efa78e05a57b136e284401c516296b6b194d541ec165d11ef94f166cb52f45145d745ff3deaf643b5c45573ed0e69a22f0e0c9c5367f6d1398105516729b6" +
|
||||
"3f2edf1b01ad9633edf80efbba6555d4253fd99b45a36f16ba98ea0bb0d80533aed806544a084a398a692f698c78b9bcfc9b4d3328dd869dbf7085893b8dafe1" +
|
||||
"59e0517c2f6a3ddfd4a8c670072b30c96b90f81fcc08523e4fd75919752bfa52a1db7c374debbd83ca8e311b98b0d8275bedad215847fa8984cb50e108f69550" +
|
||||
"f6517d719dbb5dade1d3c283357e14b6d9e85d61e33813546517e1262a7cbac814d79cf6b7e21b0fbbee9b6314f02b2d4e6995d2231670884c78cfd86a2acbcf" +
|
||||
"0a178ba64de2f13f022e22b9b968ceefaff374aff02b703811f3dc541a69a21d6e1c5d1aca48889b125ff1274e65413f61e42bb0194b60b65a3454c696033cc8" +
|
||||
"e3cc3613a52850296a0154bde0e2a81b7a6489bfce505dbe1bc44e0e1052f678297bb19cbdf7970bfa5268af8a54eee004063f9894118ddce7fae8bbba53a428" +
|
||||
"678cec8a2bf6cca2b1a5f4a2e95562437e4eae41167f39d2a150f7c46c1eb6da35587f7234d870b16ed91c7db548ddc99967381b4bb4f3a2b0a5ebcbc7ab1b06" +
|
||||
"7d5418768eaf7d526ca116e239ceb3ab393c45f3b32b713c11fa8e5ae8d7611e6008fa08d1305d5655315a72c85a04dc853da3e8ea9d46674194e15226f126c1" +
|
||||
"a233c26dd7d3cc04ae572320d0c351911b6fcdbc0b8450523e96022f4b964d4e479b6cb1c40a6d27699b57ed2952ef7fb3172c69ba7beb8c8633a01070ac4344" +
|
||||
"d4c401acf8ca7fcafeaa59e1d4c2ff251bb67dbe10a862103df1b416fd2097fe412b3da9d4095b48ea094fc3bbf2ca41e4452af3a179580e3bc11a7d97ba050c" +
|
||||
"ae1d6b8075da267b3ae2231a1fcfce0c976402f34963c007d4f85d9ca95646990d1bb09691ceea3b34211dc58409e052d0acf8c2296a7e8fb52d7c673506d89b" +
|
||||
"847c369daec7909da8657e8976f59f2ef4c8a049b46fdf30d6d223ea4175e4d60e469bcea0eb3bdcaa4d6024f2b43cf6de9bb40efa9172381291079dd82ac5b2" +
|
||||
"39f2051a7f1aabcb8d50333e8c160de19ce1c76ced8056a0724ac630dd45ec4e315437391158a633c179a3d1f364b475454fd29c1e539077b9d5f7227786a5d9" +
|
||||
"d8ec78e5615c25e517e9fcaf07611b85dff2c131a1b11a901a431a601854e5cb627cf7b8b0c5e66ad6cf60b7ffd6c6441f9ecd58f414013279e9de533d8d797b" +
|
||||
"936cfdbfcc78342b7ab586457541df5f3b7d1873612df200896e2929f44c6fe10d24f7e6dbe52b6c42c0a40c947c1cbda2a41437079eebcdc29716d80957c159" +
|
||||
"627e7366cc16df92cdedfa9f52edc848335f1c7152652fe24661a469fd503393229063c7ab20d8d895139a2f580dceac9f6dd4c4ac652b1d60c2b8a1b0b2923a" +
|
||||
"86c31742807549e6d523b3c88d31e8534b9e05a6c63f6c8fb8a1eb4dad733d92e7071e410f0087ca3074f4a2df511ae89cefe9ed09a8df603d61f23754e43cc2" +
|
||||
"e42bcdcbe58b0587aba9a62f32c7507116fdc8a9db3d65d6c0097c8f473eb7f3bcd11ab81d5b636b0812b7982201a63d0b8d40f2c38f65ffd953668eaa5751b3" +
|
||||
"dab7f038aa7adbcd1f1102267c9d55d43649f9b4f65f1851546c5a9ef2c7ef56e84b16f12641e9d5ddaa78ec778b5f113b2e06bad5821e1a5203b006a774e36f" +
|
||||
"56c9336d92c8cd8bddcf014b6d58c394e2a93554af6361fc1bbd13c359fed98bb5adfa4dd1266e2744e126e1bc029ab28fd68b648a2ab26ac23252171b298641" +
|
||||
"2621f2a8697a00ab3fdc1b3b04921390ee16d213601ab249a51830661051d34eb777f690fc2d8dfb8e0898567e388830bac8b0bc896f43003feadf34256a927e" +
|
||||
"b4d9293e32ca135351a19d1246cda30551c87de1e148ff5ea576b67e19e1a0389b88a5548b3b1a8cbee19eecc7de5c2333264c711d50d688a1c57eebc28dd6f3" +
|
||||
"3dc0e4cb857973c3d0f28683a6f3c09db9f54b8fabeca9e4f9b86d794ca55d6611858f0d48736adc10dd6763ed7199bad81369ab1f3de30f521d43382bcccb7b" +
|
||||
"be0178f716d5c3cb87488cebd7d9e2bbe671dfcf2512f1b815075777ea92a867f35e09ff0110e61db24423d0598eb6fd078dde0dc2b5d7f5e0bb6fee207da109" +
|
||||
"2e656b5c982866d5fe01e6db79809646559a6f2b9088e977789aac74435dc625b54296b25788bfbbda9bbb25247d428f5141b03172fa11f12339b91ca96c92e7" +
|
||||
"ea5a128c8046087dc7a7eba63e3bdb200565d8a103e7b3c292b088eb06aa27b43688c8516bbffcf123499574f00908ff43d66b79106cebaf16725f1dee600a29" +
|
||||
"7b3a3da878940867f9549e65c73ea798ca923b012fb8a7ef3e2ded1d2c4e85635219f627dc4feb90f884ae6436e7b44f9159f9889d8e194828e079cd2ee60a7a" +
|
||||
"6fbb6b8fc1f7355d7322709fabddd76e4283ddda3018b7882ad79b32bac133da415453eecd5bb1f0deb4f3b987a71a2f2e60194cde63a42b91b39bfe51b4aa8c" +
|
||||
"20952b601df11d170c65a7fe935915890849a367936e97bd242edf305eaf2f4f4fb9e5ee1464c51a899ba5cc69cf56731502c1b75d0d565b1dce15440b0de0f5" +
|
||||
"58bd4f810bf058af99c158a2be0dd02a01bb5317f55675f4d42c6766fc61271954b6988c33a84518bcedbac8de305946d060d19c4691c026953ebd680a4c9012" +
|
||||
"0e8bd54675d6c33cc86e65f5cd3c34cb1e6fd47784a64f39e95a1945b5c21df2b3288f963863b33366908b05c2bd499dd25c1b8e97329d7e435899afeaed174d" +
|
||||
"2a2471b6e8d6ad7a0b1b6a8b19fbd976362283e5abffcbd2cd310245092749b23e0d114e727622953487f373c833281a74a1b97742ca99e49cac14d9102e3680" +
|
||||
"404509889ace009c47d075ba9891e7f67b89aca3e213150f3c715cbab1869135601612d7dffda3cc104b6508f56eb8b7e7f379b21e1ce290ce5fb96f53e3a7eb" +
|
||||
"c7f7bddcbdfc266f23b775602d8d12527d30446cb4144df7fe3c2756e232a8ffca625d7b6ea2c8c0a92e6425ba67ab75160623c39f01fd96856b582e257a6930" +
|
||||
"224c6da90a6eac4249214c3b85aef52835d904a8a5e224d59eae0c80a33b3141ffb31a7d8e62833fa4c850fa6be135558fff5434777df45feed00316c475759f" +
|
||||
"ac6e014e9d3cf23e7322281ed75623ed69a81d6f05ee7de193f6b44ede4a94ced27aef5ab9056144593a836da80f5297875e7bd84d8ca6df95de8650b00b3528" +
|
||||
"123132f26aabf755d00450648e44f3beafa4dc746775958c6dd88bee825c29112a3af582bb2ebe628d70364fe9ad01b8a9961d5b71018690440151486114af1a" +
|
||||
"d85679bcd3eca510c6d6887e70e0d04b04fc2db5ab1eb21fff925b66f08f4fcbf31be3d743154056ba137727b63576e72f1756029c86bbcf9452fc6cfd89f3b5" +
|
||||
"9f243d84c410253ba7c9284191a0ed87b2513901a93606f1aeb736c90dfe40c0a343d45e9a992ea894b22ee5d49e0f7d55d9bddaa6c74bde8ca5839db67b77a9" +
|
||||
"ef740f9a47241f05e5dc1b9c95c459cc9db560b1db090daa3f4c6de46f695a158baaf357a1fc63ebc0d9db8144137ec4bd69c5af89cdf9cfa66e06bff6339d62" +
|
||||
"2c372fbe5a855d14fa7ff3726512f966e4da0556b29ca6d7517803f897d0e1911f9b46a291002a8320091aa7016cb7ac993e35c8b0f5aed3c94ff0b5dadd8b77" +
|
||||
"056d06d1bed59aaf7bca8516c3bba6b33e12df2e5ca4aa40664b3bf48c4dc2c57cfd74c765fe9f794f55b5df6ac6dd2b3592bbc71354c8dd9ae41b0a05e1c7c0" +
|
||||
"d3bd1a0ac6b671c48c01d4a0fec7a01ad11040f213461759f9e029c835ca1d22f9a661b69d72bc46e34b1be7ed85a21830fb87baa74d7ab145ac1647f5f2df68" +
|
||||
"671100d4d9e41082d3c81f3b5a6e603bb33fd56c1dbcbdce5e213c651da45d9d1dd7532d9a955202338387af6315137dc458fed62920a0e721aa7ff1660981c0" +
|
||||
"e4c3de0a4863f6f660a7c1b9745ea26036a25cfa37e1337ded405ebb0401d7041a7938800a97a032fcababcc06391a77a580b1a61de014db9d7e280ffa6b2381" +
|
||||
"ab6969ae5cfcca00a47ac2fa05be02aae7beb806d2afcc11dc0642d2a12ecde2d2926efd9fe790e1bee19f9114d22ca42f438ef656a1311e4931ab7fac93ed17" +
|
||||
"3f68ea0abed18cc2c8905bb2d599780690eabe4996e38872a3190fee361df9fecd5906f664106de4835f8fbb657366327871a2d38cbb671df04e0d14fe97e260" +
|
||||
"c42eb07bd1d70514913c7a64a51e405cc92e06845e5a78981fef9822fc79e9937ce0513138f6bbf247f5c457da708cf84e30d083b4ba48d2d43d70e7c31e9482" +
|
||||
"4472617910a3de4369217b4daf892c2c3250d1de0457e88b3bcb5c4568f9b26aa675c551a9a730fe9ea8145ce7f8e23ec825be9be3b9edd588c391295fe31ac5" +
|
||||
"bfc97d2e438ca9bf6551728b3be6d6c6ac064baca763e0eaa24f754f4bbc84a4377de45fb6a8f37150865df18749df1af4ea911b62f616dd4dd4b25b27c7b6fd" +
|
||||
"99d8c00ce8a53fde3ced091891e8daf43cade10086be046ee5607003de24101db49b1a4fb0ac270d05bab12583e263e903e94dab8bba7c785e40499ab01ff92b" +
|
||||
"b82c2e5342dce84881adedf77cab593f541e4c963f4f9ffc80a16bd4eb7f20ef4bf3f57abc7cbd86332d8be80f0794fc82767d13c71d8ee20468ee35c13308b0" +
|
||||
"dc29ebe8c6a81e02ee9a21807ff57e4d932edcaf59ae9e76f7cdad46b32f94a74982f0887d7083c90ff54058e873b10cec67fba1b717deba5356e170dec1a40d" +
|
||||
"36c57674ad8d43c5c98022b553fe060251b994271585f702de3e71fb1c8e36293dd44a4b99a1baf33f6205e9fbc9acdfe8cfdf007224f93a7104e7803454fdc0" +
|
||||
"9fc5a20be59f600ee734847257a5ad62c599a7fa836d1174a6291e61c1be4b310bd4d7b7cb9be976dbdfdd2b99340a9863c8c0e5009165d7097317e6c3a29cfc" +
|
||||
"dc84b19bc68f38694998f626567b80ce6699124b12bae4bb9e661c2484f5109517318341287e142a849d61d0d7b11d4996547e7325f28842dcaed26367f7a888" +
|
||||
"e58c24c857da2f48a9fb91c78cf351a23e82ae443223580a9fe15a6a778f6c13be66888219e3e15971170712b6c356520cc15e4e75167993b66e6f125799cd40" +
|
||||
"86c72588a85f68361f1c2f09e87f9a4de95ef9a3b92c3313664a706cb72916b96a9cb50771f6917ddcf696ce8d7f2525745fb6edc30bf3fdaad66ca5b013300a" +
|
||||
"7ec7cd274327b1b9cd931c068d8fa9fd6336d59f6ac79b84a24b34c47e408b3bcb8ead49428c123922e54bfcaec7e39c4d6ef79e5645a35f715d151e679ef5c6" +
|
||||
"6f86cd013fdaab978ee4e52eea5e2753e693271344a1f215e1c690de06f29c856c469ccb484d445bacb16694f4def1537cdb32260705e8a50fd65e98a24967a2" +
|
||||
"456af6cf90643638999389a35de6e192068fd2e2ec29aced58611560c792ea5c7fa37583ebd5452a8d94cbf1898937dd8aa6656047e6e03f84dfd0bded514a6c" +
|
||||
"b47ca71c2cf1e76f606c04374663712fd96925eecf0ac1c38392390c8cb095f39e1814252ded78b55ebeb9915dc5e2ec14fe99e3a075bd389ac601681f154286" +
|
||||
"885289e568a8646d94abc806b4637492e3a407cde582d42764eef0d56ab14b00e9aa1f64d8fdd533d4314145c8255c44d0c746af6da844d285eb044d57e8cafb" +
|
||||
"ab6c3b962e0177f11a839f4a5c0d2c2e8d5f76375ac115e0a89f460ea1be238f974a68e0693d15790117106c1a65ab5f7aa08e738aa888d5b56be39d2078837d" +
|
||||
"fb2357d86f5be85a9af41aed611b231495564493e46acc90c6a3e67d5b055320290aef508aa6a1896f19cb5633edc0fec023216726e50960a44d81e0614ce748" +
|
||||
"6ccfdaf620eaac0517e8cdeb1095d55f3a60d61dd27d967eb26128b84c9ea8418779e074cee8961c5dac811ce5ee8134d3910a47de7a1344293f5c64ae8f1b38" +
|
||||
"9d6c457dc74e7005c339394f5f24630f5e40cf270640d1e4c27cb6a74fb440f3203026acfcd31f39cd4844ede7e785290878fac8770f930e96c3edf61748dc6f" +
|
||||
"b7476832cf77ddfbe8eb8e12fd002038630301439ef8a7659bb10593a92cb84018e1ec78856f403e1eb9d6343aa0bbd77a63d776f1d12838f27f3cf6296ab0b3" +
|
||||
"b4436f0ec545a5a1e92a5351fb273b3ed56a40e5a5d25e0057f4077bfeba2e2d8cb17a553b157609b20bfb5cd2699af9936f50d823bb59a950a24b8fd15ed705" +
|
||||
"b1628663f0eb5b5c2b18f000ab039bc425ebafb2010e1a2264c38fa2bbd0f77e38eac8acd670565490fd60cac7fd28d988c8dc0658505dd98425f22c94647d44" +
|
||||
"5d0236b97ea58b3c71feee90be0055ce1fabda5ebfced9d9bf5efaeac8408c4b6bcdb39851cfe038d88ada5211de2f0f69e9e3c62453106c366cf0c40971c0e8" +
|
||||
"e8f2a790aa66999a0cb4cdb57a8c2d812e9e4a66df2f001a57e291864339257ec26c9bc2dc6cb2eb5c3301c167e1ed0387f9ce9f76c6759ebe5c68e8be378c42" +
|
||||
"e0350b344acbc8b40c95cee9e82bb43cef5e91a32a6be8a727d5fbe089321ede3abee4da6b9f41775d7e9abc36f6a5d26ab88ba32978b5ea0ad63f0ce8a772be" +
|
||||
"5aa51143bcd00d78bdcbd69beb652139ad658dc7ad242b2057eacee092aab4940d6ff993a8c7d8fbe93c08c93c45d5f3a01058f3c75c94be9da1a19a97754734" +
|
||||
"b713e1ad6b7cd472619ec1abd4cf42f50b0648661c2b8dbe8976037c094c7176090ba94618e1918db44f5d2c367a0c7f911132d9a8b2398b9417542c7ad99b53" +
|
||||
"a7ca48253bab8382a1a24d35b9b9818bda513f4b52fc576a71fa63e72aa8042ee1fc806c6fd3fc16e07ed2caf9f82bd3bb6b393b2708c051c24c2e05aaf72531" +
|
||||
"d865888db06f719314d6094b2c4f0718c151c88958d2d6c8a6f49464f81cc46709dde026f4e05325ea4ca2dddf9a79bf98bff3aa5eb412434f0b7457b4ed47ab" +
|
||||
"85a212e0c7720c78c961d56141bff0f964622d4d3984c1017de6f5846c72fae0c771a819ba6c111bd739fcf16f4b85f8101e7c3f0daefe753ec130a6f34c7697" +
|
||||
"4dc531f83715ecae28bf2e55111778ae42aef17fa95340584cfae3d4599af9dbd10211baf3aafa8ac8a07edf8243daffd6a6486b1e3be4b60711194261e2b646" +
|
||||
"e2667554cc0bb2fc07054b653231cede43154c9002890ca20b0ac81c4788847c6ecf7c174e528f36f8cfc53f3366fa9ce07b1843939cf6d318ed11f7ba6eb791" +
|
||||
"ce25e75cbe37d2ee3d45bea487d969de041011959c0fed4e6c86802a7485fad70059ece14a29b03d4df41677acf71419ee63f1101060ca5e4ca0ab2edd71fe77" +
|
||||
"46c6bd9f36bdbbf0a9956eaaf974f7bef982cd34881abd686fe77b536c85d042d77dadd00c5cb0130737e5318a025e6ae6af96ca28cbd41094d86a85765ff891" +
|
||||
"af825793910c406470cc61be5d9282911d2faf82abfb309598fce0101ca64abe3920701a958c20ac35927733466a23de809afa2bdf331f68c3ab0cfa08b0c549" +
|
||||
"a20e9b50dbe85d22d215d0e5fef854ba271a4c0f95e6abca19018bdd4a042721887418136b4a60cf291bf06ec47a5a4d2f9b29f988733c6bf6f65da5a95f8939" +
|
||||
"fe0f2bab0bdce98569a81f861014e532f6a995542db02b6bdf3169191d300fb0429c1cae1d2dd4d29e0b61751576e04b558d38d3afcce8326c2871e969c1492a" +
|
||||
"8391c0becec29edcf7f038a8093471763db9f13b97114acf7a979f5ba3bf6f990317890ee0705850fb97bfacf306a0ad621b2c3b633af01fc5aa059c0e22ed17" +
|
||||
"23584dde6cf140bd1d0087ca9090ca9f07d3b93c60938af8df976555455bafbd8cc986ba32fc3f15b5962dbb2d37b6ae55a7de0c0c6f2366be0278e26bf9a725" +
|
||||
"f61f2bcb545d66f79261783f7f03395f2a5d27e56af62a01ffcf778c3c686e244bd9b7e5029d1d40dd2250705c6825bf78e83730212640cb5ba54191b61fce33" +
|
||||
"ce6df7721b15662162b631d99e6431efd24ec35639c2b97f10374fa5b9e2ca4231f523195206fb9695ec7721c98d74f29533cf714866adae8edbe8ed2d0969c4" +
|
||||
"9ed36200c4b8b75131e6d1efa913106bb0759aa8255bd6a9dc2b00407358f4523486575b111676730094f46d0a7b95427df74f053c6611b4c465efa5310f760c" +
|
||||
"5ff081e841e5f90c2de35855d45a7f35ce73d7c7f9f61fbfa953398e042c3946aaa4b7a2094d95410b8a5ef76c8b57d49f77311192b3f4578f37bda1a426de7c" +
|
||||
"7cc54b5400bd16bb30cd8d1b7b42ff31c5e3759e3c9a7668174c02bc5a08f1bcc7e3ba145fa5f5c41e48877b41b0ef8fffd0f75c6547047c2e7b7c7e1aef2cda" +
|
||||
"c4a778adbf71257618b4eb3c6dbd8211f829c1d6373415b969cc48f33d586d2678e7c1b441364a9fe2bb426a33b2a132741fac547766d196df3505fdb17977de" +
|
||||
"7853cdcd8d9932eb9452620aa4921b4416f65055d77573b132a40795bf142815b655e670bf2c4464adb5d826a1744c8049d7a6cfbc8a4634e66eb32f0cb6fa17" +
|
||||
"ffa8925131c3a253101733406a2a3a0dc61ec3ca1448623b6295791d4e2d65d303f78038e15d0ef75d823759bcb4b277b51410c37d5efbbb2e3a9e0cd78a8475" +
|
||||
"05d44bb1fed7f72b1bf1a96ad0148e816d34c66b1b5faf172b8141ba007bf2e5dbbfee4b09ef66656ea3cde54f086040d14116aa7f3584ab6773f6091a2fbcee" +
|
||||
"f59d6ea115f88ef9fcb358c87c35caf7c1a6022e141a3c688beef17da5a619e733d854218b30d5edc39b933b19dedd6750acabc52234934b08f930b608a18008" +
|
||||
"838cb0fb73d4c78af0c468d9fa4fc5852135ae91ae00a99a6c603371d09b031ee37f87586cdc83897d8fd8ee2e07b9d0478a812d3f7eddca08860386e3ad9521" +
|
||||
"98d5fc04fd0aba4b3da6ab8bdd9eca8e0399a2012d6158ed75ced5f432a223449b4e3db3fd4b19c494a69e9f2670833f8a88f7b2873319e9495f03fc69b6d098" +
|
||||
"6006e3ffd8cdb9c1b98f72345848deea1b98ff6ef766f4398e642e5f2b217c1a87a608c1dc701bbb79d75a4433ca1d600061836888a220ee262124d145d371f6" +
|
||||
"576f04cf71701133787a97aaa615ca98138c2be1046604d885e2f274b0de8743af50ad5dfc4c3a09164448e102be577eecf77ffaec1724f91f00f908ff6af41e" +
|
||||
"57056dfa8f5dbcade85a66c10e524bae55922b4084407fb36ca8d6b7322f76a8139be9455a34440c719d0db8a36385efa48841170c8d35046407b586f5bbd169" +
|
||||
"7cbaf6819b663fb17d0f0ae89691a099a8ccf47fa61fb6dbb22b3298e5cf2465e4b93c49da70fa76924fdf29389194cc5c61cb4b3084d0851bc3018270d1a24c" +
|
||||
"b4b04e8af927d9fec9ea1c9ce18d4dbe61f7aac0ffd4e7c2e9729b49ed9874b883ec644864c6d9ad0422c4d89f87df1dfb2c96314b6a3e19afd21783f365445f" +
|
||||
"bef10562a26b48df42dc344ddd63fcc03220dbde98f1109cade221027c40f0f996f4beb29513c3979ba374c4c6a2c2dc6276ca6be66eecf1dcb245d6efe78aea" +
|
||||
"e49ece37f87894bef3c0cb1b993d974685564e2476c12c8d8f63a1aaf142fe34a6840be340b64f96d441f4537dff434ddce630101ed9f78e807881f6b7590697" +
|
||||
"bc97e60accd7a135d8915781f4fc22e437145154dad0a39e5e306c117b11deb10462ba74d58e81de7674ef0bcb20b38511991447f63ad906b11abd4ba88df3c9" +
|
||||
"e6931f87fece49f48543fed0439c88ad78f82aabb32dea03d030bdd76efef6b737daac2de2db1cce10e2ec74565b0a609606cbb6aa259ba88715229b8176c874" +
|
||||
"db3fc4f6db9f167e7b2d55b33a261f9eecb69a0d36ecf9ec4f8f9cee5b74bcdc5d77b02ada89f56259edeec0d9ea866ccf454b9abd29d5d21041179912a5c302" +
|
||||
"1862d850c3ff483e09479957df5bde03a29504b4a43e1fd40af2b8a2653a37cae89c8d917aecdec3959fecd32b7fd313a61e134abc15ad008aa993aba9629a5f" +
|
||||
"0af0ec713f742bee096e171729e70530b60f910ef83746a61580f0cc6d67723792c0e0e94775d5b1edf37864a50678d197bb34a97e84d7f764c0bfe05f4b2d0c" +
|
||||
"dc431d1f4410500dbe2758eb05bb6b19b154707c255a97cedc6aec1841f1817f6bcba0b9a9c1d3aebf747bec4423c71309fb8b4ada90dd9f7adbcffebbc905de" +
|
||||
"74ce531403df33457c4d0b970fea5df4f85732e3c33c5b8242b041141a8c51a62f0bb14dbe07b14d3f5ce646d76e87b258e9b62128f9c0c0a8014f2c5b3d3dbd" +
|
||||
"a3a77be6222419cd3fbbd3b842c46c099f142bcd36442961e8205ec5d7fd159befdbff12693953307026f1e06fd57b6447dd3cb52df466f0352cc46f27d1fc56" +
|
||||
"56e06f68ca2847d291421bc9e0af6bbcfb7b3ce07600827809506ba3f96f40ca22766f8cba32d4461488f6596082a52c11e9ac908922075a7b443c41e55b719d" +
|
||||
"9cac9fb587cf02432e1accf3cb7a16de0d5bc3a1c0aeff5a1795680b4551316e3d7b5a9bc63a09c6f75b0f00eb69fb6ef5130c1ec40c7a7d5d6ccce364b74f63" +
|
||||
"a836a4a711027e592d6a70e10e573cc6d730a0def4a7a2d4dfcf3b0aba37fa2060ae6935710191c023a0b8e123a67ee811ed43b5127a1c4cf82d52ad6c40fd66" +
|
||||
"1160f77dc320bbed349c8b6d08b2a7a6234a8dc88e4744b51d2d7c56e02f1c3bea9e6c2c3d5522ca02ec7e0b8160555eaf28797ed30b5c931a73562791f5f0a1" +
|
||||
"b7ce83824bae17de449cff41312bd441f34df62904f4a0265d6fb9b8a352895ac6f0025d6b2074570970b4e679c559d03ef40794708eae36567008d9c33f7fc3" +
|
||||
"5f8df7e901c27f408cc7fcc52631f1178695ea660d07df541e5a32721d145a32e8d32f06301b5073149e8798371fdb1a2daa5e1d02c24da07682a2cbacf5af55" +
|
||||
"64810e479e5966dc6bfe14b4472c42cb154e19f7b8659d42de5ac926224cc6b0d8f3fa797058fd6e21ea85146838c4612760d84e24341825b6931a6417327394" +
|
||||
"0154125254d4e11ac80e475a178605e851e1be39695cdc0781da241f232cedb32a04b1cc7352882fb635162ec3f5fc5004cfa7d03780753c14173ae7b12a71cd" +
|
||||
"b40d4835023a00a4803bdfb6916956ade9f687af567e6f29981120d306084ad061ca1585f0e9497fdb27f9d54cbac8fecff176145114ebfc17e3f346b246ab91" +
|
||||
"094dac0e684a708b45dcea16378fc29683dd033310068339b13d995dce77a50f9ee9cb4cf564566b05ce352a21159ad21e720e05ce6069a5ef4e9fe8ffd28516" +
|
||||
"8356a0b80c4d1da547776f486a117f6f7ff6557edae7d68834cd71973517cfe4af045ad0fecdead68edc8017000958b69410247a51bd9bd3152dd57389f25223" +
|
||||
"d5e88c0d343ab3aeb89b763eeb7ee48b3966fee147a4614e436c9a1a398487c80a001700666251b3dd6a2e5dc96814d21e6498e75811ba4c51160cbecb7d5510" +
|
||||
"62697171a25a6abbc41fd806c3dfc83daaa10d7ce47f5a29ef0d85dda5a61429c637520e6a4048624cbb25f53977254cf803848ad81f25eda07690fe7a0466e4" +
|
||||
"d18a2fd145dda1c94a994bc4ba5ce1aa1b50c38151febee757afceeaa91c7b35e57b90ff7b62efa929dcb962d32dde5a0bc3159524728621a3d7487eb7c3edd8" +
|
||||
"6df3f8a18e590039bfc84a22b23b11468c90dcdc8506187233d8a6b3dc9785ddf6f341709fefccde91a7a0925a8446b1896aabd6a6826ef88b756a9711cb3b78" +
|
||||
"1ab1f4df4d0515070e41fd5b0c5483270307e60eaaaf0b3a48f6bb96eef6141075445285675bb12f2ce38b42c91c1e063400d7bba9b322a0783e7d2f5d3f8874" +
|
||||
"52ceb65bdedaa032336d969d2e0e3007d2ac07bcf054b9d0330f2e26c486c054bfa709fdabe283ed9a4ae67cee24f40f2a5c4e70160e6ceead208ca400959922" +
|
||||
"70bc35be104c9ad94cdbe288b1c599db1758331340c9e927bc9d688e4186d5badd463bd3ba116bdc22a39c604778cd95503ce4ce642041e89bcdeb86fb19ab25" +
|
||||
"e1f94ed2a2f857b228ed4a582ad411d7273a0d5189bf7a2b87a135753e03383033b989ea532041ab9ed397ecb3ce61e01923b3729068f6828ffd12e2ab1d28db" +
|
||||
"6ed7423d458decb00476657a0580b4af3aa5615bf07df55beaa2bec71447aeb39791477dd09349bf573e29e9c4fd454b4bbf1e19591bf38dc47c83bf39babdc8" +
|
||||
"737d69ce4b586cd10ed406426b88e686c11072f04c680e8b6275166e2dbe91f701642b1b4ed92d23d6fe14f39ff7f5a09401f3a398eb4bb742f6cf10aa35e767" +
|
||||
"7e6e92aec791e94f8122e8c9cc9d0bc979e3eac6562ab614ff20330b00d9cdbd08e9deaeaf5cd67b49164f550c5f5c2d7523fe5ad71a2bd03fe2a97329980cb3" +
|
||||
"049ecf6d677d815e56f7cc27407cf73528549ea98265ef90277c14763d5acb3572f5a482432cd8288972af580fdd3418889bfa6a373c4813c4c45e933ea4ec72" +
|
||||
"cdd068089c2a30897dd57031445834de9f23faf506ad930d843b1cabad2c0aa8965d1b5e57032c969f9f55fe2a3049f4e63d5b5c6f5f760da5ba44e3bb9307e9" +
|
||||
"ea39973d05a74a49e15bb71eaecf62373153ca316fdd40b1c64ce2896c95a7b5df970c2bec85edbd5ed84fa7949c08d5ec4d987052fffe357d444e2408a22295" +
|
||||
"6ac1fb272f5023740b381e00dae9f09751a33bc6ad673c4221ce3f932164deb99f1da3eb3581caf475e385bcc56d47a7a1615a9543403750f0121d5482c4ea5c" +
|
||||
"94fa3428178f6a4deea08d754ba2abb3d1aa48c3e06f06ffcdf4571579d398cd991e60599e9633fae6a0c07e10e538aebb7d33aa127c830f14b083728f6ad7eb" +
|
||||
"c9a60a0ba222f47780eaa82a21393a49defee97aa8c3aa2fa53a2af86059a7587074128c2fee7060f398ae70b156d53aba0bf1af4bff10a966ce7e6382cec4b6" +
|
||||
"054a8f6b9ef0e8729ee182f86c862f9b7d5ea36ef7e15bed10ed41b25738c380e58cf42795e3202749074fe5cb6e8fcb49a116f54d84734a834609a3443b8b42" +
|
||||
"97c05ced428f5756ba59bfc1535bc7e16d370d81b72b1f3f78ba75c820b22e485dc042e4f38e93cc2918a491750c92998f03aee571cbe9abce4d00fdf9801f9e" +
|
||||
"8e0fa276822e1e5349945f1d337e656b431c48c1a2e9d4142ea14e9427881bf201ad8cd8effaa6fe6a7e07c8112299db1b327a0cc34c9fbd35596f4ee25caeee" +
|
||||
"221afad93ce7df64aa6ac766cf4fe1660446dcbafdfb86b4e0fea78c29c3e84ce42da4a503178bd250a6dbc4fc65e397062229001da05d5be118dea7ca5ce67f" +
|
||||
"b4ee07a8b01e408aebef2c913434921df686a242b7d015a559f9efdc54ad62d7f31ceb72463041843d7fb60f948fed03ff143fe24ab81bd4ef6bdabb856ef1b7" +
|
||||
"174cc987436322271bf48423114e05758a08cdbf300931fc7e950830b7ee920f7033541f1db9b0d2b91cad80d06c049b05fd0a76d6dc211bef2a08d53b1d16d4" +
|
||||
"2232fb263941daac4e004542517807341bde98e9990a97739ef86d66c7a51324f1f6911cce4c3db37ebacb6e58eb02d8f7d6ea31338b56a99649c4e730a01bca" +
|
||||
"deb6fc87cabe00addf1bf76b83927de26bc2bb3f0cd5945d863b0c31cfe8fd4b60462000a911755cbecdb6a98139041d52df498aa99aa3876836ce5b5bb426e7" +
|
||||
"c22b5977902e0b3425fdbdb8f44e8758b207b469c3e5363f552c89fbf778e95e8b7ff6566ab591fb68a8bde38d8169c708a321b669c08d9ecf1a06c5321bb1cc" +
|
||||
"9c8a585b6381645edfbd1ea4a2ad7e7eb8be6c431958add393c0a257aeb283644c6fe97580aef613f1b9d83e5b009f7a4d059025c11e0a0a67801be511dc097e" +
|
||||
"4e7c065684effcafab83e0e716e2d0862e83b295f82089ed3ba4f6897c8d8eb2b358231f95eef840e1fe22e9065de2b3dfb3633e2968135756cd9c109e8acbb3" +
|
||||
"172bbb6680c2e4fd69e179916a7849315c9f4dc86991d75cc6358617454694b3fcf2471ec7fca6ea2d99f704b9aa37a25a3b3183c5e32e3711346ba2336d6001" +
|
||||
"489afb9cbd8822dbe4f0323ebf7cfa9367b6548213d473c0f07b1bb6d16e1c66fd2bfa1ca623e03149fc81eb6f71c12e7b4b76ca588548bb4872469687f334f9" +
|
||||
"7e114a16a0a58ec70ed74ef69dd96666a85aa52d1ca812235796d90b9af4296247f4c1ab632effeaaef6acbb637f1aa9379195b3b668ca541bc6eb595bbc430b" +
|
||||
"28adc5d1a26fd4cc2239516ac9ca9c0c028110926a2f88881a5886554c31539f4c8260e16364f4ac27710d2becdadf573f4a2b7b55d76ef059432c91c6f5beb1" +
|
||||
"56686a620bdb4aea50df564cc0c5ccd8a93c454e06b8969a0f59d63ae5a29105149c08a5de65e87b0dc445dc5d86db8788ba77b83e22125c69621140d7f17906" +
|
||||
"4ec0157a877cc51ce3c0d565bdf6c884f69b0ca631d6863770f6db30eff847e33c8b30d5714668a38a09f454ee44ff2b7f97207f10efcba74325378f6f272ef9" +
|
||||
"9f09c501c12bd0a4155f559a604204b36751ce8d4c0af35a8b445a9290c305d5d3ea21f944e31df9a711ee90bd16a37850e2a87c3bd3fdecdc6e2f261a5d6d0d" +
|
||||
"580990fcab9228cbb39f8c79608d821ce27c10b0ee0b5a96474759f67970cbe03cec9fe594765bc935abccf867b9717a4087465c8604eae89497c8ffae7e46f7" +
|
||||
"ade2848916b54aa796809cb98a4364b7b44c17944dbc408909a92d4cbb24a514b72fe8de7d1cbba0a101973fa9b29d97dcf1f4ed8a05d5e0cb38849dc6e2d041" +
|
||||
"16892ce649e0a553a727bfbb1d5794a059d6a411e43876e561d83bd22c054680cc8fa928f5f4be2d849f02ddf9c6d11ba35810b81553e1938ab013663f6ef35b" +
|
||||
"08f06260932d7acf99f57967eec57a61f03d880c3225e53102a672f5842da21aaaee02444d372ab8ed7096235a4926e3288912d9c736c2c4dc49918abdfdd6d6" +
|
||||
"d0df5be0133abd61b02a6f008909c5350f9077598ab2e612603431bddd3826e314feb280585b37eb89e597f7f0bdb738a9a93d9af224659d50c8f7479b240487" +
|
||||
"76c2a960eb18923fa2d3b31b3d20ef538759cf22f5b415d19bdae689f2bab651d79ff99f77a721bc1b2233da12c12be0c9881ad82fc97a6343b3af8207dda8b6" +
|
||||
"5c600644d741b8a16750964e341e060260c4de26f991f3a1f6a606d1153565f1c9cfee58eef327edc0e9cfaa206ce930b191f521be2344949bc75d583a413a96" +
|
||||
"ee4edac424cbf9bdad2883c96a1306b96ee059d8044e3b7af4e7138697f142774ed6409a86a3c6c456600d4e405e6117ec759f4b22d7e5a185b0f9c67ad987bc" +
|
||||
"58d2e8c929d4a487e5b77201d7c1416878e8d63258b2f58727cb631494cf1d68b99c28493b99b0409ccc1f9c218a2b95c45ff36563f0045ae5c3098f641ea6a9" +
|
||||
"b48a3e1489831b2d176a1e0cb2afe6bd8cc5e797de01391e47e798c1aa945d33d5e7dd607aa73c9efe93f0646adcd7e211303ac7deea4d02c80370e8e867e2ad" +
|
||||
"9942bfd5a66143560a1f59e5be1f3aeecd7eab689a4a481aec78045ae0604f69d9eea550152f6e2bc692529357b509d60e5a497bd94e63dc698cdfa2a3a55976" +
|
||||
"0b2d072a2fe9c1fd41f31518aae0edaab532591476a9c5a61cd76937575cef71ff5dd66e158e7820b4b6bff4067cc26ee9aa66f41b80f078645b920512b5efd8" +
|
||||
"88b3644601a72e3f665b9c8f0ee246593667379b8fa043718f2d75c21d2a11640c328971c32d5743c11ada6c95cfabf1c6b66e0b09342afc899e1f272ec48a7f" +
|
||||
"ba5a51943763bf969cbac879363e14dad1952517d8f4b463511adccf25e655bced7cd9666d01dd4f2a0a21729ac4f44970c9c478a995d1c3b358a244110f1db9" +
|
||||
"fe6335685701e0c2660ae69d33a93a75e44f5374b979a5af140200db43ff612be2728548192ebfd0a3860a9e135b910fe3fb249926d334167622bf4123bdf0d5" +
|
||||
"38e9ff2a3bb67a44f8407328e3c94b47d92e0401aa1db85459967699804df245a7808f972683afded9cad8fbce15c1be38fd10c62c7abc302eb0537d5cc573ec" +
|
||||
"245513a87c1a8d386f7ef0c4a91ec3c602b14a14ae395da13284df3391b929c7379e181c5d3d4597e3c955ef6e3dc2fc55890df04785bdd4e3fa35ac775f44ef" +
|
||||
"9d7813cc036d6bcc316e869eeddf7b30e4b837e9285eb20397b4d7e0d12080c502c750268bcd6ffc323cb094afbe8304ae840d37be833878697f2cf931faf06d" +
|
||||
"28dc6c7e1b5df69327127b47eddd0237f1bb5942ee5903d8cbfe1b11484199e90fe7c8e7f2f725deb2293630bd8c8a377d539736e2ccc2b90c08b97abd8c5ce4" +
|
||||
"ea91a6219ab06c61c31eb48a35587b3c1719f387bd8c2063c5a79d041ca8a9ffac2e3c728f74efdb74ee0730f84cb3a8aefff7c8a1b570127cfc93eb6d3327f5" +
|
||||
"ba7f886dee8be0548f710d6bfb18cbe5910bf61aed2c95028006f419241d968933aa00bb0760a41d2693465827a00837a84cadaf8a8e804d175adc5915c6cb6e" +
|
||||
"fefb2cf70db063f2f3812da17586436c176aa0a815dfc7983eb88bfb1b6d1db7ab119cd3058c0db4d1910034f70f6eedfee8b742ea45af9780f415be2f851061" +
|
||||
"313a218ad48e992b75afaa07c33ca47ee0155fe72e13d7e5736e512c5e5a45d351f7c7902d8b0fa31b34569a9aea31b018d63d572a9898c389d07caa427f114d" +
|
||||
"251263d56cf5d6663159c2b32683b266fb909ba9d4caadaeda6700c03b25307cdea597a3287fd76082dbf33f073482872fdb494b892112c594d7f265d2799b5e" +
|
||||
"5ec46a30fbf1557fa344a664a7af457a4e8ce2c014a270215d3f95d47a42d8f86a61d6d6b363d04a99a0d8f06c5b15cd803d951aea0ca185a807ca4c677db789" +
|
||||
"fca64f0c5ba95b8c64f930eda658f9f773a9e1c8669589a7d98ade8dbc2c2c4cbbaf6ea2bbc6e762d4098f4db0d3f055958ae9da15ae57ee0b60fb9513dacf5a" +
|
||||
"d65e34613570186acecf9e165bfa470aabcd35f22620497fbcabf220c53cff84eec12cf9965297b364f0e9122895c175d213fc2a9c9cbf27ebe1cf96fdacaf1c" +
|
||||
"1c79ede66cfaa5057d33e09b31b43869812e5a0ce730663c18c4333141ae9565e437d99ade6b2cbe005214e8b3392c55bf4d7b38ef16e7f84b4ba3c85e1dfd1a" +
|
||||
"ca8da1a5c75fd190e7752926533327880aed1461c7e9de2443ba0a2d094f4a14d5fffd3b102ea78acd34d162e82ab78fbb82bfbc8a9708ab532aa861643c39cd" +
|
||||
"2bc89f2be53c583f9930fb2da14f1c5d5f218384b1740a76bd8b7ddd2c9888c8d7e7e78cc7a3304fa41995c7c1c3316894296caeeb9711f0e6bf16abc380bd41" +
|
||||
"10448be3cb03cc3246ee7b9559c858307001033c84ecf89690526544c05c146f206d4a21e710597bb57759d232154a1f9d88eb3f3440374bad1e901da7a154b8" +
|
||||
"39a6d1b1b6b2ab0be872ff036a9f9f769a169fbf91bada732d8f28c453b9be49011b211155fa5c588b43018775f99e3b92b322a4c41282326b79fd26541ccafc" +
|
||||
"c0e2f09797e3217fb0e5785b72e654dbcde8ba14b2d56faa2218748c6789c158bb635d43c9a64690b004ab70f457e9fd959b2d90875966968c7ac44b103283e7" +
|
||||
"50b60deeb1f89444aee25fbdb7fa3a96d70c3dce38246f111e466cdfa3b807c54ed584f5b1a64456e923dcf37f45b36cea3d602ba3a55a4fd883ebb6dc198650" +
|
||||
"b522461614656897b9b7d408d48b12e594af06c91f715b32a4ed65a379f0ab461acb9b8b20d1f1b12e9f7fea422c0c7d545eff4152e06002cbd120fd74b483d3" +
|
||||
"a0ee30cfd851c98e9aa8fb19b60528de4a75b412bed656933ae8ab600aeaef5befdcca4d35fa472ed38ffb91a9017c19c5d500426f262ba379034c45cf5d1627" +
|
||||
"48da223207721b4bc4504b79309f3d622c53dfe3c83ff8866dd7614a2e90a85c077b2e18bf1cb6008f0d785d6a0ffd5f15a83a343036f3fdd25314bfe47b5a12" +
|
||||
"58a7c89475f39a58a671d0a17f6fd100a8928181b94d8d53149316d5addf14bd398b538e2593273f02cf296fd73ff92d02230de939dae94e03d44ce93dd4dfa1" +
|
||||
"b9219fd369c854ec409d7bf94b316e5e9c16e1ba525a783e24bd3fc0ecc949be245c402efae8ea77aaca74c78703506cfd5a5a614793e04c76652b4f344f79fd" +
|
||||
"f2da1e34f650fc1094116ead723813d204ffe375d20707fd94d90f21c009194201c88d22afaee83a8a6be7518dc915331b863664e033d397c64e1516c0fd9324" +
|
||||
"11614a1bdf2feb86e0d0ae21e784a55086c596c7eedd44d3afd7295455450f507f1c1a33c9ba94d50931ec054d8740510ea23990c266f30678a74fdd485b482b" +
|
||||
"cbfb4070e3f10b66c65a4210794a3137adabe887ffb9bcf2a30c625138f840b2666610e76e5a0abc183088a94930c025836653eddbc440621bbf94761c74e108" +
|
||||
"3672c6a914a753fd452e8e7a02c54b21d7bab4b705b4509b9b5b27e2e5144289eece950c3634b410b5e3cf8c5a5f74d98b55d17d45d7014390cf696a7e693777" +
|
||||
"4c028517062a69276910cf5f139078e8ef6e77aa8b35aa55fd4f53e48ae6b4875d1732b286ffe8bf852b73af7b964fdf1aa4c4f16d9f14485a2b1a704c2615ac" +
|
||||
"8ac74eaaacec7e8e4e506e1b418d377e4d5a271dfab47b3d3c11a809beda596fdf37935dfe06c147dfe7d5be696ffb2a0cff907d1eb2a88477c261d5a7aba06c" +
|
||||
"d70dc52d00b9a9d851e849f86e1cba91b4c40d1ae3d4f21a2763369dde34d084adfc09d2a6cb5f09114cd8d6fa26d15f1ec428adc245064e5b8e80f21b0b3ff2" +
|
||||
"6690398d3080f5355fc082bc4bf3a38576c7da00efbc80839dc9a06fab2b998a152553c36fab42e03e3e4b54456ed954e53bd63902d89e2617a263e70146d1eb" +
|
||||
"71557baf43aeb0a681f600a784778c895afce26fe17e3ac33990c54cd96fcb2432de79d4f95ab2fb96effdd37f4e4247ae5b4c1fa461ca3269d45a90af090333" +
|
||||
"fc3ab5152bd5aed4445eab93466701382ba76fc8745abd911bdb45a494e1c62647670380c04377bcdb5e631318dfa79850469a988094acd48a4110bbc7289617" +
|
||||
"ce436294ff242302d154ad75437ae2f551df5b84f884c87497de0bb2ef7bd41a8c758e4b158084c78ef047389d88974faafa00ce7396e849509d39c403fdcca6" +
|
||||
"8f47e1d0fc294e5510a07af24c165e1a4b4ba9498e7b333c4e8624c552801079775fc684b6e98b72ff133164a2052c2aadcef168d9cdeab8a935c98f08e23b95" +
|
||||
"859277381a2ce23ea61fbe9ec1439a489523161ed370b0069aa6a5c7981e4a80c04e304ff2fd85f80b51e3de3484b53084f376cc72a390aaefc49baddf4d2545" +
|
||||
"93dcb5a49326c9c15c3d1c0e0709c9879d68bee07b956d018a995bf1e7f8fa03ef2079d01e0bec601519704cced98854c94f1f0ae837653f14c0221e12f2cbdb" +
|
||||
"1564066062bc1d4dcf7ed8b2c980b90e8101842d5844375cb370f402d858dffd9eb52572f8420d4d246462230ca0dbd567250e4f065730a6aecbd804b1acf949" +
|
||||
"30e2890a39fdd4c1eb693f7e345504dbad5ac207f1a649968c3a7b416bd972b6a6bfde04375337a93b0ac08f6fae62c0fa7df8ae9deeee421f7ac62d8cf5ecf3" +
|
||||
"b5ff39877ee4abaeb9db03d8a8f13f7925e54267a2651c55ecf580d5cbb24bf504fb01291e3e97ad1696ed995608fceda79f2441ca67bfe3c31f4f4bf0fffcd6" +
|
||||
"55408744524412cd4d3cbdbdd216694aa7477e88b25f7efeb34abf491a50695ff686829a8fea9e999877bcb37291b8dbeeddfd44465a2c28a215aa532590c487" +
|
||||
"d4747b6ece4e1aeaef725cb305d11b965a9647bef36a5c2fb45cc334d35ff4e308cd8813b6de3953b35a4ef6a3ae07794f8b54ef6365a573135320612bd1acfe" +
|
||||
"6cac5524c0e98b6f2a33a790b94f5134f0cba075a6fa93c191f4176ca62ea2e365557d6b3363a17b9ee52f3c347c82cd19f8432d16a934ae9c5d4d4505e7d20e" +
|
||||
"1ae31bb64ccb084f7a59744b27d58c2388d449ff4b63604878ae858240348ecfcb51761678265bd60d5dd7d51e25e91668fdf80f6b726b29ef6c3f0f229d8af4" +
|
||||
"b2cdadc3ca7fbadab49b28819b9c9b92b49cbe9a281e5891f4eae7616013777605a0623dd7a650baf9a9dad66ca9aae3c76ef1e27db32bd9514a2776eb0c8d05" +
|
||||
"65eec06fc4c8a69c417efa336842e248e5a51e3b5f3ba3227e3f78f1bd12d81595e03a01f4259c772fd481ab5f3d7a945e1c95fe0dc3c4742eeb7e15c9426ec3" +
|
||||
"ed4c90ee07d56acc78fecfd7c5ce1e04e7db1a970091f15c90f0aae2865d135395d27787aaf68c6a179064d82691e0b6c795f61875f317dc6d2e8feea55a28f2" +
|
||||
"461d74e14e350351720b6f536adfe3addd4111f08e3a84da2656fd4bc83989b329b383da9f01cf2392aa0b19577884d1281f2e6c106df451c078a472b36057d3" +
|
||||
"065dfc4bbb47ce4e5dce4acf6da095bdd10322f3ae12bcdd1f805e73b303f1fc7a7e16cf3ffd822bd8b25fbc93be065019e394113182713f1ad299ae6537f6bf" +
|
||||
"57116e8dc9ec775519b797ab4107c2ac5129ba85188852c3bc5f116044bbd8985b6dc8b8da4659589bf9d2351c4c3adaed87fe2ea20ef6bf62224c7af86fe8b4" +
|
||||
"973e558f39465dff43bf23cf1f78957514e4e82a3009d40d97bf8d8442a11deabde806e2fa84c1ba75273da75ce8dad3b2a34786b2958ac4bfd248ebe604a173" +
|
||||
"83c727b11dd922b1f72476af700b663fbd7033d0ac74b463d40a92d26c938b69f96fb4a9cb7a9ca2bd9496251270c0c5fcae6b3c2eda5377b897891648a97125" +
|
||||
"8ac71fed8dce8e02c30961a299cb7f3145845dbe8f4dfaaaf4baf0ca3fb730abdd258e98215f072a943d5aee8d8bc4c86023524f7b69186d99ad88ccdfc0b4bd" +
|
||||
"7db422bbad7eaa0824ce24b5c186e172c8c584f1cc5c126c901a69ebee8dbd230a653a3643b7875672d22fd86079daf8d834ba33664f5ad0b6eec767b4f58b45" +
|
||||
"e67b776b90e0a5e130aa5365003eb7fd78b757b1cf9133f6a1d51064b293cb42c8c41b15b7e95e2a39fa5dae19c6e20031d2bfa4632c37779163fdecc6b45624" +
|
||||
"4d6bfd01a8877f6fe7739591917a86e7dd795ad85cc3f256cff5961e8b62e92a0754a51f2c6d59819446eec8bdd08b87cf9f4fb5373e809d52240d2dd691cd50" +
|
||||
"37fc79d35b61d63851917cfdba164868a3f79e2061bd4610c1f5216ed77df00baa75f949ad37142db4fd282a5c7d2e2636ca590f92fc4781d4f51efa69f53947" +
|
||||
"d4fca1dc7dd2429837b6d7e5c9528effdecf6f731f676587785e5c4096bdb6f1f44e72f5f77d9025813e848881506f65bfb0f2b2d3ae6f9e00731929b5ac083d" +
|
||||
"b1c9c324703e63fef6319e1d8150aa0ff7d9a2049961df9158f3e1f2e540a91feb742625d2a859a452186d2ccaa3ec2ba086ee0868a4dc24ae6818fc02f9c1df" +
|
||||
"dc326cc31c46feefda97265238592f638968627ec24903b97513ab05ed58ce5b516decda0e2fbf01a70e6cb2e53c3e7b8855f80cd7e007b78da727ef0893e099" +
|
||||
"592ba684d62ae2d1f06ad148fa7f34cfc724d804149cda21aee7eac064ad20d29132b260c2c2867fa6a2e747739fc30df2f002c2a99da6c7e64ee51e806af7d9" +
|
||||
"768aec456b93a05002666cb61b2229c99f2cdef9afc9cea1c4ee3a85dd189823399781ee33cde2abedff09c47960c035e075a29156005d75845a11fa06abcc50" +
|
||||
"5f7f849a0caaf683f334e9e7bbbef90fe6cf94cbf87767219440d31713daef6ad1e4a1cc720ce59fee4cf7731e46bbba9ec1648908ea345030aa8f10ade10ffa" +
|
||||
"3d2acfd480b0b11eadc4fb2b740596b204e911456cb2f35ad9993ab7dd6a48b35ba0c207625384bb3c2ff24437810bd13c7ee96cd6f97f19ffd537ad182a3657" +
|
||||
"b0e83d42fd6e2ebac6cbf5ea1bde97465b7cec6954ff5b5be049e59a49ea25ed6667dbace765401bde12031e5cfabf2df7afb728d2a0b2a38b24d79bf23a313c" +
|
||||
"40fe5adef97487641c6088dd8712c0c352708e474b02c08fd2d71b6d44f16d82f291ccd61c43a339408379a8de54cfdbbae5e421e084112fbc17fb5561e084d1" +
|
||||
"4149bf4bb06fd161878d8574f856867cff974d5898e161923d55bdac8699c9df6a220bcb6c800d3ae7f107b8c4acab206d780aafaf6c2e2379de8c900700d9c9" +
|
||||
"c87d464772514c5aa3e5f5bfc00fb54f2b74702838b4731c5ac8a070b50783e81dd97fa8d55c739d026b607a2a78aba1bb79b1a7a3c22c78368672ac020061e6" +
|
||||
"d9683d57d6989c6c6f08b8d5d74720f5cb25505fbe81d2bf53a68e972a54784705b20f83fd1ab5afff30764ef89dba4465b56f48b325ab3810bf8dcbf4faa61f" +
|
||||
"676e2043ac8540df9e3af4c0f51d816e89c09bf67253be45fc5f75f64be97f6c7dc0c6392af6fa8e75aab58eda976b36773cd37d315771400a2cb846fdef3d8a" +
|
||||
"a15bce5dfda0379e526f87cf67767a2ab93d41c85b4ed016ed0a89d2f94737433a3ebade813def29eaa18a1fb925fca7d08d1020f64caebc562cb4ad2fb241e2" +
|
||||
"94923b2f2df5e6c4953c4b73be0f5568defe57ce49d16e2a205323e46cbb5a3e77fff1557671503bd7b5de5320f1fb951fbe26400cfa854af2d12fab0215310c" +
|
||||
"f070add34dc4565d1757d7e10a03e3bb73a607ed7e10861b1274ddee76183cf7e56c1de7162c805c2dba0e0331d36f3a4e2019a2e0705ee2747ed1e52bc3a6fb" +
|
||||
"3b061f784348204cdf8d643ff6c271fa72b56900edcc2f77201f3bd4fc296ad6534a7029ea66761bb9a3589a1f6ef566504c70297b98fbb603214fed2e4b7ca1" +
|
||||
"06e3f0e993118897fa641fb9722d4667fa98d07a6837e5ab2144e5ec1548a7dbca28c559f2a9a99d54b8e55f56d0e59bcef1ac45e2046835b60579da0d2261e7" +
|
||||
"30dab9009d138421c6458d146e870358b0b3fa20257e50b58f167c6b47edf7053513d58f33547d06ce52458baaa4dcf15f77b103565c66a81f183c827801b455" +
|
||||
"b61b6287a46a37a96884075a7eada9ba7f0ddcc14654bf87a26d2e27a978b415257773796923a220e06b25af16fb5aaded9b2d081a4c64106df460ddce9c3b2a" +
|
||||
"c8553e1521e501ad29a4b7f7681c9b60576a127087a5237c4c2bacf9b163dd590e63f2bc80e7f1e613773f87d034313064710404739d63363d204be7b14800c4" +
|
||||
"d8c1b6a2a21da70223be51d281fee302ef806454f9d7d28244ba537c1d9f8f1bcc5d47038d986a8f95ca48437ffe94fd44a90bb03014a259112a97508adb3db4" +
|
||||
"34f72a5268c1af6bc6d5801e579aab2228ca33600ebbf1a1959081c3a4ca99e444f97409f5e0ca4779241c9aacad1f4ee7fb4369bd6ae076378e4f63000b9a5c" +
|
||||
"849ba6e72e47e2454a44659149338ac0767cd25d8693c0d143e354bd600f1c1d3a44eeb024923ea659060665d5cd9a4ca1ca86162819556535fd59b9fde90caa" +
|
||||
"29920efe99479fe7e4b4e5371e13ccb43a1419cf023433239d840900d31bab37fabc3fd20e31bb7dbcb3ae8df66f67e2844944bcf544b658364f9e3d0b6d84b4" +
|
||||
"63ad4eb621644fd7d774b501407a1178814b15149345d551b474347188067db2ab4d7f4d1abd3027133039e855e129f3f5649550da8c04fe2db57cb89bf1bf4f" +
|
||||
"72eb35ccfe31afb92f6136d4c2a1c115b07b721b2da43151f11c356256230408979c5d95243714429e2c9500e7b043b20dac8a9763e5b487d1cbdb34ac379b9c" +
|
||||
"6409454c79385b6e562459c4fdaad1b7f9297c1473e9b90fffe6d1c5390e241a187a4cefa2eb0cb0c11f4ca6c5b961c18ceb57892295290dbc991692556bffa3" +
|
||||
"b8c405cf285e6bcb8a90246ad0ac15122f4cf73adc129d23aa2240733404beb6d74bf698e5589288a522573c774ce9f514b5d5c086382ea1dd4e89ff5facbf23" +
|
||||
"d36bc3d203941e17747ede4b82820351f4df278ddb787ce8f6f1cc468ef953399efb072ce706e253f1bab76444bb70be6443cd0db633e958dc57bd223e00418e" +
|
||||
"915a7c2e4d94c0623f9788276480cdfe798387d35e2ea2d304d066aec7627794cdd4200a44208d6c87f242c76e2d4a3f966b6fb96eaa63d892c1a177bef249b4" +
|
||||
"fdd1a4c06c791f677dd9919f739ccf318bd77835330b0219786249c9c9736161dac771a838724f2dd70afba46a6782fd27601cf8a7126ae95a66e526131a68d7" +
|
||||
"7a809e513533ed8021eecdbc5851dfcf95e10f1bbe47b5c7f079275a1837836245266b66d89fab25ac4bd6c1225560bea3259b67bf50a58ee056754d574da79e" +
|
||||
"f9a1a0df3a5defed0f74fe74ce0bf65a04086f17e94a8451828c723c97932f26f9349f1a2c7866c617a528602721de4f3cc8916bcfc66cdc106bafa26ea87a13" +
|
||||
"94dfa37e396365fb7f92df007b46a50ff04c7f85bfa679230ebedf18c2fb876fc7098dd1c4328adf85de71c31d94687a308053bfcdc158cfa7772170fbed63f5" +
|
||||
"37dda41f65196dfacdd1186b5de0f3369d841ce6502192292d05a19ce7464f5bcab3015c721cac13ddca561b92dc1ee25d3068dc1945a1b4e2bd1e6604c42e4c" +
|
||||
"3c04b490f6365828957990007394557854a903e19feb06906e41cbc8766bf37bd7dece90f4cdc987709b1129e84bfdc502543b5bfa887bf78553a5ec10ad68c5" +
|
||||
"d10eff75f7aa495e7d934a55577fdc0aead31aee4522db0259d7d4ea8438a7996d80a787465a2980457193d1c4bf1a0a1e01741d72e5fc4dfe58475c1c01026b" +
|
||||
"5a3bc973b902280753e9c3226db9cc778e2506c56ee86ae85b4d54dbf05394107329b2d1ee56522cb1ce562fb1aa4e592199d9c29f64cc3ab1d757531e209eec" +
|
||||
"aa138d8388169b5e28c45f5aba267eeaa57f69869f0b6855d82b0eafcde63895251f41e8e676a0ab12ef3f569bb7de91b79fa46ad9637da01ca004f4d30259c1" +
|
||||
"f5b00761f6ca9c17721a6718390624a10a11f7f52d7afb71ee5f8338828910e48f94a1347761abac87897b2dd0e23f1d325aec5031ef58f2972e8b402e05f8c1" +
|
||||
"ae7053a90380a1ae0d4d06645548c23e13afa31aac8ff83b10f8341418af4114632f6406d6e33076391696c9161d63c8bcfd1c625fc737f68198046212d1638a" +
|
||||
"d2d2d42ff7029c1fcc682a046edc4d4f24862d82c600180b1e8f57ff6a3865dfe9274f9886d00efa523a1b3b3757c4489200fec3dc5583854c955492336253dd" +
|
||||
"767f2a60ce3d224afcff9cdc19e9b28830d33affda6af99942a8fe39562055f3e884fd6c1ebc1908ac159061f35e9b0da80434ce9673d9c6b87265170077c670" +
|
||||
"743e37474d7605cd01c44af600f16d9ffaf24acf87fbe5ccf39bac41047a810d210051c87f06147a0bb8f1427a406700483679638f1af23f1dafb7aa0c468669" +
|
||||
"71c3a82f535c26cf6cb335e8e915fda393799d3dbe0e04b907ed3612d12ac95783a6876cd986d2a13b82192532e02c250eaa42f891d2481655fa4494c723fe00" +
|
||||
"87d224444245eb5b0eade5f741b025db1992a8ad0dce51b0c1af4a18a9e244f9f755891adf0f19179c7baa6c32bffc91e0b03c4ed3aaee1978b6a1f03b87ac6a" +
|
||||
"fc3b9e7030bb212b17de198edfccde29d04224798c1204e47ea235f048724fac62d637d1ba0ee3922048fcf79c746b6c0c036d882e3491fd72bad6e009c6403e" +
|
||||
"55876f4d31330caa02aedd0b0c121c3c41e736853a08071f0dd4ddc7412db0bbe274a9ac2932552bb37c40e72c2ef1d7cca8236942e480d709d3ea9d5ae0a1b7",
|
||||
),
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
cache := make([]uint32, tt.cacheSize/4)
|
||||
generateCache(cache, tt.epoch, seedHash(tt.epoch*epochLength+1))
|
||||
|
||||
dataset := make([]uint32, tt.datasetSize/4)
|
||||
generateDataset(dataset, tt.epoch, cache)
|
||||
|
||||
want := make([]uint32, tt.datasetSize/4)
|
||||
prepare(want, tt.dataset)
|
||||
|
||||
if !reflect.DeepEqual(dataset, want) {
|
||||
t.Errorf("dataset %d: content mismatch: have %x, want %x", i, dataset, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests whether the hashimoto lookup works for both light as well as the full
|
||||
// datasets.
|
||||
func TestHashimoto(t *testing.T) {
|
||||
// Create the verification cache and mining dataset
|
||||
cache := make([]uint32, 1024/4)
|
||||
generateCache(cache, 0, make([]byte, 32))
|
||||
|
||||
dataset := make([]uint32, 32*1024/4)
|
||||
generateDataset(dataset, 0, cache)
|
||||
|
||||
// Create a block to verify
|
||||
hash := hexutil.MustDecode("0xc9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f")
|
||||
nonce := uint64(0)
|
||||
|
||||
wantDigest := hexutil.MustDecode("0xe4073cffaef931d37117cefd9afd27ea0f1cad6a981dd2605c4a1ac97c519800")
|
||||
wantResult := hexutil.MustDecode("0xd3539235ee2e6f8db665c0a72169f55b7f6c605712330b778ec3944f0eb5a557")
|
||||
|
||||
digest, result := hashimotoLight(32*1024, cache, hash, nonce)
|
||||
if !bytes.Equal(digest, wantDigest) {
|
||||
t.Errorf("light hashimoto digest mismatch: have %x, want %x", digest, wantDigest)
|
||||
}
|
||||
if !bytes.Equal(result, wantResult) {
|
||||
t.Errorf("light hashimoto result mismatch: have %x, want %x", result, wantResult)
|
||||
}
|
||||
digest, result = hashimotoFull(dataset, hash, nonce)
|
||||
if !bytes.Equal(digest, wantDigest) {
|
||||
t.Errorf("full hashimoto digest mismatch: have %x, want %x", digest, wantDigest)
|
||||
}
|
||||
if !bytes.Equal(result, wantResult) {
|
||||
t.Errorf("full hashimoto result mismatch: have %x, want %x", result, wantResult)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that caches generated on disk may be done concurrently.
|
||||
func TestConcurrentDiskCacheGeneration(t *testing.T) {
|
||||
// Create a temp folder to generate the caches into
|
||||
cachedir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary cache dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(cachedir)
|
||||
|
||||
// Define a heavy enough block, one from mainnet should do
|
||||
block := types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(3311058),
|
||||
ParentHash: common.HexToHash("0xd783efa4d392943503f28438ad5830b2d5964696ffc285f338585e9fe0a37a05"),
|
||||
UncleHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
||||
Coinbase: common.HexToAddress("0xc0ea08a2d404d3172d2add29a45be56da40e2949"),
|
||||
Root: common.HexToHash("0x77d14e10470b5850332524f8cd6f69ad21f070ce92dca33ab2858300242ef2f1"),
|
||||
TxHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
|
||||
ReceiptHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
|
||||
Difficulty: big.NewInt(167925187834220),
|
||||
GasLimit: big.NewInt(4015682),
|
||||
GasUsed: big.NewInt(0),
|
||||
Time: big.NewInt(1488928920),
|
||||
Extra: []byte("www.bw.com"),
|
||||
MixDigest: common.HexToHash("0x3e140b0784516af5e5ec6730f2fb20cca22f32be399b9e4ad77d32541f798cd0"),
|
||||
Nonce: types.EncodeNonce(0xf400cd0006070c49),
|
||||
})
|
||||
// Simulate multiple processes sharing the same datadir
|
||||
var pend sync.WaitGroup
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
pend.Add(1)
|
||||
|
||||
go func(idx int) {
|
||||
defer pend.Done()
|
||||
|
||||
ethash := New(cachedir, 0, 1, "", 0, 0)
|
||||
if err := ethash.VerifySeal(nil, block.Header()); err != nil {
|
||||
t.Errorf("proc %d: block verification failed: %v", idx, err)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
pend.Wait()
|
||||
}
|
||||
|
||||
// Benchmarks the cache generation performance.
|
||||
func BenchmarkCacheGeneration(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
cache := make([]uint32, cacheSize(1)/4)
|
||||
generateCache(cache, 0, make([]byte, 32))
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks the dataset (small) generation performance.
|
||||
func BenchmarkSmallDatasetGeneration(b *testing.B) {
|
||||
cache := make([]uint32, 65536/4)
|
||||
generateCache(cache, 0, make([]byte, 32))
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
dataset := make([]uint32, 32*65536/4)
|
||||
generateDataset(dataset, 0, cache)
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks the light verification performance.
|
||||
func BenchmarkHashimotoLight(b *testing.B) {
|
||||
cache := make([]uint32, cacheSize(1)/4)
|
||||
generateCache(cache, 0, make([]byte, 32))
|
||||
|
||||
hash := hexutil.MustDecode("0xc9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f")
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
hashimotoLight(datasetSize(1), cache, hash, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks the full (small) verification performance.
|
||||
func BenchmarkHashimotoFullSmall(b *testing.B) {
|
||||
cache := make([]uint32, 65536/4)
|
||||
generateCache(cache, 0, make([]byte, 32))
|
||||
|
||||
dataset := make([]uint32, 32*65536/4)
|
||||
generateDataset(dataset, 0, cache)
|
||||
|
||||
hash := hexutil.MustDecode("0xc9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f")
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
hashimotoFull(dataset, hash, 0)
|
||||
}
|
||||
}
|
||||
496
consensus/ethash/consensus.go
Normal file
496
consensus/ethash/consensus.go
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
// Copyright 2017 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 ethash
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
set "gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
// Ethash proof-of-work protocol constants.
|
||||
var (
|
||||
blockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block
|
||||
maxUncles = 2 // Maximum number of uncles allowed in a single block
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidChain = errors.New("invalid header chain")
|
||||
ErrParentUnknown = errors.New("parent not known locally")
|
||||
ErrFutureBlock = errors.New("block in the future")
|
||||
ErrLargeBlockTimestamp = errors.New("timestamp too big")
|
||||
ErrZeroBlockTime = errors.New("timestamp equals parent's")
|
||||
ErrInvalidNumber = errors.New("invalid block number")
|
||||
ErrTooManyUncles = errors.New("too many uncles")
|
||||
ErrDuplicateUncle = errors.New("duplicate uncle")
|
||||
ErrUncleIsAncestor = errors.New("uncle is ancestor")
|
||||
ErrDanglingUncle = errors.New("uncle's parent is not ancestor")
|
||||
ErrNonceOutOfRange = errors.New("nonce out of range")
|
||||
ErrInvalidDifficulty = errors.New("non-positive difficulty")
|
||||
ErrInvalidMixDigest = errors.New("invalid mix digest")
|
||||
ErrInvalidPoW = errors.New("invalid proof-of-work")
|
||||
)
|
||||
|
||||
// VerifyHeader checks whether a header conforms to the consensus rules of the
|
||||
// stock Ethereum ethash engine.
|
||||
func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
|
||||
// If we're running a full engine faking, accept any input as valid
|
||||
if ethash.fakeFull {
|
||||
return nil
|
||||
}
|
||||
// Short circuit if the header is known, or it's parent not
|
||||
number := header.Number.Uint64()
|
||||
if chain.GetHeader(header.Hash(), number) != nil {
|
||||
return nil
|
||||
}
|
||||
parent := chain.GetHeader(header.ParentHash, number-1)
|
||||
if parent == nil {
|
||||
return ErrParentUnknown
|
||||
}
|
||||
// Sanity checks passed, do a proper verification
|
||||
return ethash.verifyHeader(chain, header, parent, false, seal)
|
||||
}
|
||||
|
||||
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
|
||||
// concurrently. The method returns a quit channel to abort the operations and
|
||||
// a results channel to retrieve the async verifications.
|
||||
func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
|
||||
// If we're running a full engine faking, accept any input as valid
|
||||
if ethash.fakeFull {
|
||||
abort, results := make(chan struct{}), make(chan error, len(headers))
|
||||
for i := 0; i < len(headers); i++ {
|
||||
results <- nil
|
||||
}
|
||||
return abort, results
|
||||
}
|
||||
// Spawn as many workers as allowed threads
|
||||
workers := runtime.GOMAXPROCS(0)
|
||||
if len(headers) < workers {
|
||||
workers = len(headers)
|
||||
}
|
||||
// Create a task channel and spawn the verifiers
|
||||
type result struct {
|
||||
index int
|
||||
err error
|
||||
}
|
||||
inputs := make(chan int, workers)
|
||||
outputs := make(chan result, len(headers))
|
||||
|
||||
var badblock uint64
|
||||
for i := 0; i < workers; i++ {
|
||||
go func() {
|
||||
for index := range inputs {
|
||||
// If we've found a bad block already before this, stop validating
|
||||
if bad := atomic.LoadUint64(&badblock); bad != 0 && bad <= headers[index].Number.Uint64() {
|
||||
outputs <- result{index: index, err: ErrInvalidChain}
|
||||
continue
|
||||
}
|
||||
// We need to look up the first parent
|
||||
var parent *types.Header
|
||||
if index == 0 {
|
||||
parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1)
|
||||
} else if headers[index-1].Hash() == headers[index].ParentHash {
|
||||
parent = headers[index-1]
|
||||
}
|
||||
// Ensure the validation is useful and execute it
|
||||
var failure error
|
||||
switch {
|
||||
case chain.GetHeader(headers[index].Hash(), headers[index].Number.Uint64()-1) != nil:
|
||||
outputs <- result{index: index, err: nil}
|
||||
case parent == nil:
|
||||
failure = ErrParentUnknown
|
||||
outputs <- result{index: index, err: failure}
|
||||
default:
|
||||
failure = ethash.verifyHeader(chain, headers[index], parent, false, seals[index])
|
||||
outputs <- result{index: index, err: failure}
|
||||
}
|
||||
// If a validation failure occurred, mark subsequent blocks invalid
|
||||
if failure != nil {
|
||||
number := headers[index].Number.Uint64()
|
||||
if prev := atomic.LoadUint64(&badblock); prev == 0 || prev > number {
|
||||
// This two step atomic op isn't thread-safe in that `badblock` might end
|
||||
// up slightly higher than the block number of the first failure (if many
|
||||
// workers try to write at the same time), but it's fine as we're mostly
|
||||
// interested to avoid large useless work, we don't care about 1-2 extra
|
||||
// runs. Doing "full thread safety" would involve mutexes, which would be
|
||||
// a noticeable sync overhead on the fast spinning worker routines.
|
||||
atomic.StoreUint64(&badblock, number)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
// Feed item indices to the workers until done, sorting and feeding the results to the caller
|
||||
dones := make([]bool, len(headers))
|
||||
errors := make([]error, len(headers))
|
||||
|
||||
abort := make(chan struct{})
|
||||
returns := make(chan error, len(headers))
|
||||
|
||||
go func() {
|
||||
defer close(inputs)
|
||||
|
||||
input, output := 0, 0
|
||||
for i := 0; i < len(headers)*2; i++ {
|
||||
var res result
|
||||
|
||||
// If there are tasks left, push to workers
|
||||
if input < len(headers) {
|
||||
select {
|
||||
case inputs <- input:
|
||||
input++
|
||||
continue
|
||||
case <-abort:
|
||||
return
|
||||
case res = <-outputs:
|
||||
}
|
||||
} else {
|
||||
// Otherwise keep waiting for results
|
||||
select {
|
||||
case <-abort:
|
||||
return
|
||||
case res = <-outputs:
|
||||
}
|
||||
}
|
||||
// A result arrived, save and propagate if next
|
||||
dones[res.index], errors[res.index] = true, res.err
|
||||
for output < len(headers) && dones[output] {
|
||||
returns <- errors[output]
|
||||
output++
|
||||
}
|
||||
}
|
||||
}()
|
||||
return abort, returns
|
||||
}
|
||||
|
||||
// VerifyUncles verifies that the given block's uncles conform to the consensus
|
||||
// rules of the stock Ethereum ethash engine.
|
||||
func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
|
||||
// If we're running a full engine faking, accept any input as valid
|
||||
if ethash.fakeFull {
|
||||
return nil
|
||||
}
|
||||
// Verify that there are at most 2 uncles included in this block
|
||||
if len(block.Uncles()) > maxUncles {
|
||||
return ErrTooManyUncles
|
||||
}
|
||||
// Gather the set of past uncles and ancestors
|
||||
uncles, ancestors := set.New(), make(map[common.Hash]*types.Header)
|
||||
|
||||
number, parent := block.NumberU64()-1, block.ParentHash()
|
||||
for i := 0; i < 7; i++ {
|
||||
ancestor := chain.GetBlock(parent, number)
|
||||
if ancestor == nil {
|
||||
break
|
||||
}
|
||||
ancestors[ancestor.Hash()] = ancestor.Header()
|
||||
for _, uncle := range ancestor.Uncles() {
|
||||
uncles.Add(uncle.Hash())
|
||||
}
|
||||
parent, number = ancestor.ParentHash(), number-1
|
||||
}
|
||||
ancestors[block.Hash()] = block.Header()
|
||||
uncles.Add(block.Hash())
|
||||
|
||||
// Verify each of the uncles that it's recent, but not an ancestor
|
||||
for _, uncle := range block.Uncles() {
|
||||
// Make sure every uncle is rewarded only once
|
||||
hash := uncle.Hash()
|
||||
if uncles.Has(hash) {
|
||||
return ErrDuplicateUncle
|
||||
}
|
||||
uncles.Add(hash)
|
||||
|
||||
// Make sure the uncle has a valid ancestry
|
||||
if ancestors[hash] != nil {
|
||||
return ErrUncleIsAncestor
|
||||
}
|
||||
if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == block.ParentHash() {
|
||||
return ErrDanglingUncle
|
||||
}
|
||||
if err := ethash.verifyHeader(chain, uncle, ancestors[uncle.ParentHash], true, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyHeader checks whether a header conforms to the consensus rules of the
|
||||
// stock Ethereum ethash engine.
|
||||
//
|
||||
// See YP section 4.3.4. "Block Header Validity"
|
||||
func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *types.Header, uncle bool, seal bool) error {
|
||||
// Ensure that the header's extra-data section is of a reasonable size
|
||||
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
|
||||
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
|
||||
}
|
||||
// Verify the header's timestamp
|
||||
if uncle {
|
||||
if header.Time.Cmp(math.MaxBig256) > 0 {
|
||||
return ErrLargeBlockTimestamp
|
||||
}
|
||||
} else {
|
||||
if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 {
|
||||
return ErrFutureBlock
|
||||
}
|
||||
}
|
||||
if header.Time.Cmp(parent.Time) <= 0 {
|
||||
return ErrZeroBlockTime
|
||||
}
|
||||
// Verify the block's difficulty based in it's timestamp and parent's difficulty
|
||||
expected := CalcDifficulty(chain.Config(), header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
|
||||
if expected.Cmp(header.Difficulty) != 0 {
|
||||
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected)
|
||||
}
|
||||
// Verify that the gas limit remains within allowed bounds
|
||||
diff := new(big.Int).Set(parent.GasLimit)
|
||||
diff = diff.Sub(diff, header.GasLimit)
|
||||
diff.Abs(diff)
|
||||
|
||||
limit := new(big.Int).Set(parent.GasLimit)
|
||||
limit = limit.Div(limit, params.GasLimitBoundDivisor)
|
||||
|
||||
if diff.Cmp(limit) >= 0 || header.GasLimit.Cmp(params.MinGasLimit) < 0 {
|
||||
return fmt.Errorf("invalid gas limit: have %v, want %v += %v", header.GasLimit, parent.GasLimit, limit)
|
||||
}
|
||||
// Verify that the block number is parent's +1
|
||||
if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 {
|
||||
return ErrInvalidNumber
|
||||
}
|
||||
// Verify the engine specific seal securing the block
|
||||
if seal {
|
||||
if err := ethash.VerifySeal(chain, header); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// If all checks passed, validate any special fields for hard forks
|
||||
if err := misc.VerifyDAOHeaderExtraData(chain.Config(), header); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := misc.VerifyForkHashes(chain.Config(), header, uncle); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
|
||||
// that a new block should have when created at time given the parent block's time
|
||||
// and difficulty.
|
||||
//
|
||||
// TODO (karalabe): Move the chain maker into this package and make this private!
|
||||
func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
|
||||
return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff)
|
||||
}
|
||||
return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff)
|
||||
}
|
||||
|
||||
// Some weird constants to avoid constant memory allocs for them.
|
||||
var (
|
||||
expDiffPeriod = big.NewInt(100000)
|
||||
big10 = big.NewInt(10)
|
||||
bigMinus99 = big.NewInt(-99)
|
||||
)
|
||||
|
||||
// calcDifficultyHomestead is the difficulty adjustment algorithm. It returns
|
||||
// the difficulty that a new block should have when created at time given the
|
||||
// parent block's time and difficulty. The calculation uses the Homestead rules.
|
||||
func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki
|
||||
// algorithm:
|
||||
// diff = (parent_diff +
|
||||
// (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
|
||||
// ) + 2^(periodCount - 2)
|
||||
|
||||
bigTime := new(big.Int).SetUint64(time)
|
||||
bigParentTime := new(big.Int).SetUint64(parentTime)
|
||||
|
||||
// holds intermediate values to make the algo easier to read & audit
|
||||
x := new(big.Int)
|
||||
y := new(big.Int)
|
||||
|
||||
// 1 - (block_timestamp -parent_timestamp) // 10
|
||||
x.Sub(bigTime, bigParentTime)
|
||||
x.Div(x, big10)
|
||||
x.Sub(common.Big1, x)
|
||||
|
||||
// max(1 - (block_timestamp - parent_timestamp) // 10, -99)))
|
||||
if x.Cmp(bigMinus99) < 0 {
|
||||
x.Set(bigMinus99)
|
||||
}
|
||||
// (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
|
||||
y.Div(parentDiff, params.DifficultyBoundDivisor)
|
||||
x.Mul(y, x)
|
||||
x.Add(parentDiff, x)
|
||||
|
||||
// minimum difficulty can ever be (before exponential factor)
|
||||
if x.Cmp(params.MinimumDifficulty) < 0 {
|
||||
x.Set(params.MinimumDifficulty)
|
||||
}
|
||||
// for the exponential factor
|
||||
periodCount := new(big.Int).Add(parentNumber, common.Big1)
|
||||
periodCount.Div(periodCount, expDiffPeriod)
|
||||
|
||||
// the exponential factor, commonly referred to as "the bomb"
|
||||
// diff = diff + 2^(periodCount - 2)
|
||||
if periodCount.Cmp(common.Big1) > 0 {
|
||||
y.Sub(periodCount, common.Big2)
|
||||
y.Exp(common.Big2, y, nil)
|
||||
x.Add(x, y)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// calcDifficultyFrontier is the difficulty adjustment algorithm. It returns the
|
||||
// difficulty that a new block should have when created at time given the parent
|
||||
// block's time and difficulty. The calculation uses the Frontier rules.
|
||||
func calcDifficultyFrontier(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
diff := new(big.Int)
|
||||
adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
|
||||
bigTime := new(big.Int)
|
||||
bigParentTime := new(big.Int)
|
||||
|
||||
bigTime.SetUint64(time)
|
||||
bigParentTime.SetUint64(parentTime)
|
||||
|
||||
if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 {
|
||||
diff.Add(parentDiff, adjust)
|
||||
} else {
|
||||
diff.Sub(parentDiff, adjust)
|
||||
}
|
||||
if diff.Cmp(params.MinimumDifficulty) < 0 {
|
||||
diff.Set(params.MinimumDifficulty)
|
||||
}
|
||||
|
||||
periodCount := new(big.Int).Add(parentNumber, common.Big1)
|
||||
periodCount.Div(periodCount, expDiffPeriod)
|
||||
if periodCount.Cmp(common.Big1) > 0 {
|
||||
// diff = diff + 2^(periodCount - 2)
|
||||
expDiff := periodCount.Sub(periodCount, common.Big2)
|
||||
expDiff.Exp(common.Big2, expDiff, nil)
|
||||
diff.Add(diff, expDiff)
|
||||
diff = math.BigMax(diff, params.MinimumDifficulty)
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
// VerifySeal implements consensus.Engine, checking whether the given block satisfies
|
||||
// the PoW difficulty requirements.
|
||||
func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
|
||||
// If we're running a fake PoW, accept any seal as valid
|
||||
if ethash.fakeMode {
|
||||
time.Sleep(ethash.fakeDelay)
|
||||
if ethash.fakeFail == header.Number.Uint64() {
|
||||
return ErrInvalidPoW
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// If we're running a shared PoW, delegate verification to it
|
||||
if ethash.shared != nil {
|
||||
return ethash.shared.VerifySeal(chain, header)
|
||||
}
|
||||
// Sanity check that the block number is below the lookup table size (60M blocks)
|
||||
number := header.Number.Uint64()
|
||||
if number/epochLength >= uint64(len(cacheSizes)) {
|
||||
// Go < 1.7 cannot calculate new cache/dataset sizes (no fast prime check)
|
||||
return ErrNonceOutOfRange
|
||||
}
|
||||
// Ensure that we have a valid difficulty for the block
|
||||
if header.Difficulty.Sign() <= 0 {
|
||||
return ErrInvalidDifficulty
|
||||
}
|
||||
// Recompute the digest and PoW value and verify against the header
|
||||
cache := ethash.cache(number)
|
||||
|
||||
size := datasetSize(number)
|
||||
if ethash.tester {
|
||||
size = 32 * 1024
|
||||
}
|
||||
digest, result := hashimotoLight(size, cache, header.HashNoNonce().Bytes(), header.Nonce.Uint64())
|
||||
if !bytes.Equal(header.MixDigest[:], digest) {
|
||||
return ErrInvalidMixDigest
|
||||
}
|
||||
target := new(big.Int).Div(maxUint256, header.Difficulty)
|
||||
if new(big.Int).SetBytes(result).Cmp(target) > 0 {
|
||||
return ErrInvalidPoW
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prepare implements consensus.Engine, initializing the difficulty field of a
|
||||
// header to conform to the ethash protocol. The changes are done inline.
|
||||
func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
||||
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
|
||||
if parent == nil {
|
||||
return ErrParentUnknown
|
||||
}
|
||||
header.Difficulty = CalcDifficulty(chain.Config(), header.Time.Uint64(),
|
||||
parent.Time.Uint64(), parent.Number, parent.Difficulty)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Finalize implements consensus.Engine, accumulating the block and uncle rewards,
|
||||
// setting the final state and assembling the block.
|
||||
func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
||||
// Accumulate any block and uncle rewards and commit the final state root
|
||||
AccumulateRewards(state, header, uncles)
|
||||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||
|
||||
// Header seems complete, assemble into a block and return
|
||||
return types.NewBlock(header, txs, uncles, receipts), nil
|
||||
}
|
||||
|
||||
// Some weird constants to avoid constant memory allocs for them.
|
||||
var (
|
||||
big8 = big.NewInt(8)
|
||||
big32 = big.NewInt(32)
|
||||
)
|
||||
|
||||
// AccumulateRewards credits the coinbase of the given block with the mining
|
||||
// reward. The total reward consists of the static block reward and rewards for
|
||||
// included uncles. The coinbase of each uncle block is also rewarded.
|
||||
//
|
||||
// TODO (karalabe): Move the chain maker into this package and make this private!
|
||||
func AccumulateRewards(state *state.StateDB, header *types.Header, uncles []*types.Header) {
|
||||
reward := new(big.Int).Set(blockReward)
|
||||
r := new(big.Int)
|
||||
for _, uncle := range uncles {
|
||||
r.Add(uncle.Number, big8)
|
||||
r.Sub(r, header.Number)
|
||||
r.Mul(r, blockReward)
|
||||
r.Div(r, big8)
|
||||
state.AddBalance(uncle.Coinbase, r)
|
||||
|
||||
r.Div(blockReward, big32)
|
||||
reward.Add(reward, r)
|
||||
}
|
||||
state.AddBalance(header.Coinbase, reward)
|
||||
}
|
||||
79
consensus/ethash/consensus_test.go
Normal file
79
consensus/ethash/consensus_test.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2017 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 ethash
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
type diffTest struct {
|
||||
ParentTimestamp uint64
|
||||
ParentDifficulty *big.Int
|
||||
CurrentTimestamp uint64
|
||||
CurrentBlocknumber *big.Int
|
||||
CurrentDifficulty *big.Int
|
||||
}
|
||||
|
||||
func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
|
||||
var ext struct {
|
||||
ParentTimestamp string
|
||||
ParentDifficulty string
|
||||
CurrentTimestamp string
|
||||
CurrentBlocknumber string
|
||||
CurrentDifficulty string
|
||||
}
|
||||
if err := json.Unmarshal(b, &ext); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.ParentTimestamp = math.MustParseUint64(ext.ParentTimestamp)
|
||||
d.ParentDifficulty = math.MustParseBig256(ext.ParentDifficulty)
|
||||
d.CurrentTimestamp = math.MustParseUint64(ext.CurrentTimestamp)
|
||||
d.CurrentBlocknumber = math.MustParseBig256(ext.CurrentBlocknumber)
|
||||
d.CurrentDifficulty = math.MustParseBig256(ext.CurrentDifficulty)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCalcDifficulty(t *testing.T) {
|
||||
file, err := os.Open("../../tests/files/BasicTests/difficulty.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
tests := make(map[string]diffTest)
|
||||
err = json.NewDecoder(file).Decode(&tests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1150000)}
|
||||
for name, test := range tests {
|
||||
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
|
||||
diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
|
||||
if diff.Cmp(test.CurrentDifficulty) != 0 {
|
||||
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
587
consensus/ethash/ethash.go
Normal file
587
consensus/ethash/ethash.go
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
// Copyright 2017 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 ethash implements the ethash proof-of-work consensus engine.
|
||||
package ethash
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
mmap "github.com/edsrzf/mmap-go"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
metrics "github.com/rcrowley/go-metrics"
|
||||
)
|
||||
|
||||
var ErrInvalidDumpMagic = errors.New("invalid dump magic")
|
||||
|
||||
var (
|
||||
// maxUint256 is a big integer representing 2^256-1
|
||||
maxUint256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
|
||||
|
||||
// sharedEthash is a full instance that can be shared between multiple users.
|
||||
sharedEthash = New("", 3, 0, "", 1, 0)
|
||||
|
||||
// algorithmRevision is the data structure version used for file naming.
|
||||
algorithmRevision = 23
|
||||
|
||||
// dumpMagic is a dataset dump header to sanity check a data dump.
|
||||
dumpMagic = []uint32{0xbaddcafe, 0xfee1dead}
|
||||
)
|
||||
|
||||
// isLittleEndian returns whether the local system is running in little or big
|
||||
// endian byte order.
|
||||
func isLittleEndian() bool {
|
||||
n := uint32(0x01020304)
|
||||
return *(*byte)(unsafe.Pointer(&n)) == 0x04
|
||||
}
|
||||
|
||||
// memoryMap tries to memory map a file of uint32s for read only access.
|
||||
func memoryMap(path string) (*os.File, mmap.MMap, []uint32, error) {
|
||||
file, err := os.OpenFile(path, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
mem, buffer, err := memoryMapFile(file, false)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
for i, magic := range dumpMagic {
|
||||
if buffer[i] != magic {
|
||||
mem.Unmap()
|
||||
file.Close()
|
||||
return nil, nil, nil, ErrInvalidDumpMagic
|
||||
}
|
||||
}
|
||||
return file, mem, buffer[len(dumpMagic):], err
|
||||
}
|
||||
|
||||
// memoryMapFile tries to memory map an already opened file descriptor.
|
||||
func memoryMapFile(file *os.File, write bool) (mmap.MMap, []uint32, error) {
|
||||
// Try to memory map the file
|
||||
flag := mmap.RDONLY
|
||||
if write {
|
||||
flag = mmap.RDWR
|
||||
}
|
||||
mem, err := mmap.Map(file, flag, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Yay, we managed to memory map the file, here be dragons
|
||||
header := *(*reflect.SliceHeader)(unsafe.Pointer(&mem))
|
||||
header.Len /= 4
|
||||
header.Cap /= 4
|
||||
|
||||
return mem, *(*[]uint32)(unsafe.Pointer(&header)), nil
|
||||
}
|
||||
|
||||
// memoryMapAndGenerate tries to memory map a temporary file of uint32s for write
|
||||
// access, fill it with the data from a generator and then move it into the final
|
||||
// path requested.
|
||||
func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint32)) (*os.File, mmap.MMap, []uint32, error) {
|
||||
// Ensure the data folder exists
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// Create a huge temporary empty file to fill with data
|
||||
temp := path + "." + strconv.Itoa(rand.Int())
|
||||
|
||||
dump, err := os.Create(temp)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if err = dump.Truncate(int64(len(dumpMagic))*4 + int64(size)); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// Memory map the file for writing and fill it with the generator
|
||||
mem, buffer, err := memoryMapFile(dump, true)
|
||||
if err != nil {
|
||||
dump.Close()
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
copy(buffer, dumpMagic)
|
||||
|
||||
data := buffer[len(dumpMagic):]
|
||||
generator(data)
|
||||
|
||||
if err := mem.Flush(); err != nil {
|
||||
mem.Unmap()
|
||||
dump.Close()
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
os.Rename(temp, path)
|
||||
return dump, mem, data, nil
|
||||
}
|
||||
|
||||
// cache wraps an ethash cache with some metadata to allow easier concurrent use.
|
||||
type cache struct {
|
||||
epoch uint64 // Epoch for which this cache is relevant
|
||||
|
||||
dump *os.File // File descriptor of the memory mapped cache
|
||||
mmap mmap.MMap // Memory map itself to unmap before releasing
|
||||
|
||||
cache []uint32 // The actual cache data content (may be memory mapped)
|
||||
used time.Time // Timestamp of the last use for smarter eviction
|
||||
once sync.Once // Ensures the cache is generated only once
|
||||
lock sync.Mutex // Ensures thread safety for updating the usage time
|
||||
}
|
||||
|
||||
// generate ensures that the cache content is generated before use.
|
||||
func (c *cache) generate(dir string, limit int, test bool) {
|
||||
c.once.Do(func() {
|
||||
// If we have a testing cache, generate and return
|
||||
if test {
|
||||
c.cache = make([]uint32, 1024/4)
|
||||
generateCache(c.cache, c.epoch, seedHash(c.epoch*epochLength+1))
|
||||
return
|
||||
}
|
||||
// If we don't store anything on disk, generate and return
|
||||
size := cacheSize(c.epoch*epochLength + 1)
|
||||
seed := seedHash(c.epoch*epochLength + 1)
|
||||
|
||||
if dir == "" {
|
||||
c.cache = make([]uint32, size/4)
|
||||
generateCache(c.cache, c.epoch, seed)
|
||||
return
|
||||
}
|
||||
// Disk storage is needed, this will get fancy
|
||||
var endian string
|
||||
if !isLittleEndian() {
|
||||
endian = ".be"
|
||||
}
|
||||
path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x%s", algorithmRevision, seed[:8], endian))
|
||||
logger := log.New("epoch", c.epoch)
|
||||
|
||||
// Try to load the file from disk and memory map it
|
||||
var err error
|
||||
c.dump, c.mmap, c.cache, err = memoryMap(path)
|
||||
if err == nil {
|
||||
logger.Debug("Loaded old ethash cache from disk")
|
||||
return
|
||||
}
|
||||
logger.Debug("Failed to load old ethash cache", "err", err)
|
||||
|
||||
// No previous cache available, create a new cache file to fill
|
||||
c.dump, c.mmap, c.cache, err = memoryMapAndGenerate(path, size, func(buffer []uint32) { generateCache(buffer, c.epoch, seed) })
|
||||
if err != nil {
|
||||
logger.Error("Failed to generate mapped ethash cache", "err", err)
|
||||
|
||||
c.cache = make([]uint32, size/4)
|
||||
generateCache(c.cache, c.epoch, seed)
|
||||
}
|
||||
// Iterate over all previous instances and delete old ones
|
||||
for ep := int(c.epoch) - limit; ep >= 0; ep-- {
|
||||
seed := seedHash(uint64(ep)*epochLength + 1)
|
||||
path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x%s", algorithmRevision, seed[:8], endian))
|
||||
os.Remove(path)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// release closes any file handlers and memory maps open.
|
||||
func (c *cache) release() {
|
||||
if c.mmap != nil {
|
||||
c.mmap.Unmap()
|
||||
c.mmap = nil
|
||||
}
|
||||
if c.dump != nil {
|
||||
c.dump.Close()
|
||||
c.dump = nil
|
||||
}
|
||||
}
|
||||
|
||||
// dataset wraps an ethash dataset with some metadata to allow easier concurrent use.
|
||||
type dataset struct {
|
||||
epoch uint64 // Epoch for which this cache is relevant
|
||||
|
||||
dump *os.File // File descriptor of the memory mapped cache
|
||||
mmap mmap.MMap // Memory map itself to unmap before releasing
|
||||
|
||||
dataset []uint32 // The actual cache data content
|
||||
used time.Time // Timestamp of the last use for smarter eviction
|
||||
once sync.Once // Ensures the cache is generated only once
|
||||
lock sync.Mutex // Ensures thread safety for updating the usage time
|
||||
}
|
||||
|
||||
// generate ensures that the dataset content is generated before use.
|
||||
func (d *dataset) generate(dir string, limit int, test bool) {
|
||||
d.once.Do(func() {
|
||||
// If we have a testing dataset, generate and return
|
||||
if test {
|
||||
cache := make([]uint32, 1024/4)
|
||||
generateCache(cache, d.epoch, seedHash(d.epoch*epochLength+1))
|
||||
|
||||
d.dataset = make([]uint32, 32*1024/4)
|
||||
generateDataset(d.dataset, d.epoch, cache)
|
||||
|
||||
return
|
||||
}
|
||||
// If we don't store anything on disk, generate and return
|
||||
csize := cacheSize(d.epoch*epochLength + 1)
|
||||
dsize := datasetSize(d.epoch*epochLength + 1)
|
||||
seed := seedHash(d.epoch*epochLength + 1)
|
||||
|
||||
if dir == "" {
|
||||
cache := make([]uint32, csize/4)
|
||||
generateCache(cache, d.epoch, seed)
|
||||
|
||||
d.dataset = make([]uint32, dsize/4)
|
||||
generateDataset(d.dataset, d.epoch, cache)
|
||||
}
|
||||
// Disk storage is needed, this will get fancy
|
||||
var endian string
|
||||
if !isLittleEndian() {
|
||||
endian = ".be"
|
||||
}
|
||||
path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian))
|
||||
logger := log.New("epoch", d.epoch)
|
||||
|
||||
// Try to load the file from disk and memory map it
|
||||
var err error
|
||||
d.dump, d.mmap, d.dataset, err = memoryMap(path)
|
||||
if err == nil {
|
||||
logger.Debug("Loaded old ethash dataset from disk")
|
||||
return
|
||||
}
|
||||
logger.Debug("Failed to load old ethash dataset", "err", err)
|
||||
|
||||
// No previous dataset available, create a new dataset file to fill
|
||||
cache := make([]uint32, csize/4)
|
||||
generateCache(cache, d.epoch, seed)
|
||||
|
||||
d.dump, d.mmap, d.dataset, err = memoryMapAndGenerate(path, dsize, func(buffer []uint32) { generateDataset(buffer, d.epoch, cache) })
|
||||
if err != nil {
|
||||
logger.Error("Failed to generate mapped ethash dataset", "err", err)
|
||||
|
||||
d.dataset = make([]uint32, dsize/2)
|
||||
generateDataset(d.dataset, d.epoch, cache)
|
||||
}
|
||||
// Iterate over all previous instances and delete old ones
|
||||
for ep := int(d.epoch) - limit; ep >= 0; ep-- {
|
||||
seed := seedHash(uint64(ep)*epochLength + 1)
|
||||
path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian))
|
||||
os.Remove(path)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// release closes any file handlers and memory maps open.
|
||||
func (d *dataset) release() {
|
||||
if d.mmap != nil {
|
||||
d.mmap.Unmap()
|
||||
d.mmap = nil
|
||||
}
|
||||
if d.dump != nil {
|
||||
d.dump.Close()
|
||||
d.dump = nil
|
||||
}
|
||||
}
|
||||
|
||||
// MakeCache generates a new ethash cache and optionally stores it to disk.
|
||||
func MakeCache(block uint64, dir string) {
|
||||
c := cache{epoch: block/epochLength + 1}
|
||||
c.generate(dir, math.MaxInt32, false)
|
||||
c.release()
|
||||
}
|
||||
|
||||
// MakeDataset generates a new ethash dataset and optionally stores it to disk.
|
||||
func MakeDataset(block uint64, dir string) {
|
||||
d := dataset{epoch: block/epochLength + 1}
|
||||
d.generate(dir, math.MaxInt32, false)
|
||||
d.release()
|
||||
}
|
||||
|
||||
// Ethash is a consensus engine based on proot-of-work implementing the ethash
|
||||
// algorithm.
|
||||
type Ethash struct {
|
||||
cachedir string // Data directory to store the verification caches
|
||||
cachesinmem int // Number of caches to keep in memory
|
||||
cachesondisk int // Number of caches to keep on disk
|
||||
dagdir string // Data directory to store full mining datasets
|
||||
dagsinmem int // Number of mining datasets to keep in memory
|
||||
dagsondisk int // Number of mining datasets to keep on disk
|
||||
|
||||
caches map[uint64]*cache // In memory caches to avoid regenerating too often
|
||||
fcache *cache // Pre-generated cache for the estimated future epoch
|
||||
datasets map[uint64]*dataset // In memory datasets to avoid regenerating too often
|
||||
fdataset *dataset // Pre-generated dataset for the estimated future epoch
|
||||
|
||||
// Mining related fields
|
||||
rand *rand.Rand // Properly seeded random source for nonces
|
||||
threads int // Number of threads to mine on if mining
|
||||
update chan struct{} // Notification channel to update mining parameters
|
||||
hashrate metrics.Meter // Meter tracking the average hashrate
|
||||
|
||||
// The fields below are hooks for testing
|
||||
tester bool // Flag whether to use a smaller test dataset
|
||||
shared *Ethash // Shared PoW verifier to avoid cache regeneration
|
||||
fakeMode bool // Flag whether to disable PoW checking
|
||||
fakeFull bool // Flag whether to disable all consensus rules
|
||||
fakeFail uint64 // Block number which fails PoW check even in fake mode
|
||||
fakeDelay time.Duration // Time delay to sleep for before returning from verify
|
||||
|
||||
lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
|
||||
}
|
||||
|
||||
// New creates a full sized ethash PoW scheme.
|
||||
func New(cachedir string, cachesinmem, cachesondisk int, dagdir string, dagsinmem, dagsondisk int) *Ethash {
|
||||
if cachesinmem <= 0 {
|
||||
log.Warn("One ethash cache must alwast be in memory", "requested", cachesinmem)
|
||||
cachesinmem = 1
|
||||
}
|
||||
if cachedir != "" && cachesondisk > 0 {
|
||||
log.Info("Disk storage enabled for ethash caches", "dir", cachedir, "count", cachesondisk)
|
||||
}
|
||||
if dagdir != "" && dagsondisk > 0 {
|
||||
log.Info("Disk storage enabled for ethash DAGs", "dir", dagdir, "count", dagsondisk)
|
||||
}
|
||||
return &Ethash{
|
||||
cachedir: cachedir,
|
||||
cachesinmem: cachesinmem,
|
||||
cachesondisk: cachesondisk,
|
||||
dagdir: dagdir,
|
||||
dagsinmem: dagsinmem,
|
||||
dagsondisk: dagsondisk,
|
||||
caches: make(map[uint64]*cache),
|
||||
datasets: make(map[uint64]*dataset),
|
||||
update: make(chan struct{}),
|
||||
hashrate: metrics.NewMeter(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewTester creates a small sized ethash PoW scheme useful only for testing
|
||||
// purposes.
|
||||
func NewTester() *Ethash {
|
||||
return &Ethash{
|
||||
cachesinmem: 1,
|
||||
caches: make(map[uint64]*cache),
|
||||
datasets: make(map[uint64]*dataset),
|
||||
tester: true,
|
||||
update: make(chan struct{}),
|
||||
hashrate: metrics.NewMeter(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFaker creates a ethash consensus engine with a fake PoW scheme that accepts
|
||||
// all blocks' seal as valid, though they still have to conform to the Ethereum
|
||||
// consensus rules.
|
||||
func NewFaker() *Ethash {
|
||||
return &Ethash{fakeMode: true}
|
||||
}
|
||||
|
||||
// NewFakeFailer creates a ethash consensus engine with a fake PoW scheme that
|
||||
// accepts all blocks as valid apart from the single one specified, though they
|
||||
// still have to conform to the Ethereum consensus rules.
|
||||
func NewFakeFailer(fail uint64) *Ethash {
|
||||
return &Ethash{fakeMode: true, fakeFail: fail}
|
||||
}
|
||||
|
||||
// NewFakeDelayer creates a ethash consensus engine with a fake PoW scheme that
|
||||
// accepts all blocks as valid, but delays verifications by some time, though
|
||||
// they still have to conform to the Ethereum consensus rules.
|
||||
func NewFakeDelayer(delay time.Duration) *Ethash {
|
||||
return &Ethash{fakeMode: true, fakeDelay: delay}
|
||||
}
|
||||
|
||||
// NewFullFaker creates a ethash consensus engine with a full fake scheme that
|
||||
// accepts all blocks as valid, without checking any consensus rules whatsoever.
|
||||
func NewFullFaker() *Ethash {
|
||||
return &Ethash{fakeMode: true, fakeFull: true}
|
||||
}
|
||||
|
||||
// NewShared creates a full sized ethash PoW shared between all requesters running
|
||||
// in the same process.
|
||||
func NewShared() *Ethash {
|
||||
return &Ethash{shared: sharedEthash}
|
||||
}
|
||||
|
||||
// cache tries to retrieve a verification cache for the specified block number
|
||||
// by first checking against a list of in-memory caches, then against caches
|
||||
// stored on disk, and finally generating one if none can be found.
|
||||
func (ethash *Ethash) cache(block uint64) []uint32 {
|
||||
epoch := block / epochLength
|
||||
|
||||
// If we have a PoW for that epoch, use that
|
||||
ethash.lock.Lock()
|
||||
|
||||
current, future := ethash.caches[epoch], (*cache)(nil)
|
||||
if current == nil {
|
||||
// No in-memory cache, evict the oldest if the cache limit was reached
|
||||
for len(ethash.caches) > 0 && len(ethash.caches) >= ethash.cachesinmem {
|
||||
var evict *cache
|
||||
for _, cache := range ethash.caches {
|
||||
if evict == nil || evict.used.After(cache.used) {
|
||||
evict = cache
|
||||
}
|
||||
}
|
||||
delete(ethash.caches, evict.epoch)
|
||||
evict.release()
|
||||
|
||||
log.Trace("Evicted ethash cache", "epoch", evict.epoch, "used", evict.used)
|
||||
}
|
||||
// If we have the new cache pre-generated, use that, otherwise create a new one
|
||||
if ethash.fcache != nil && ethash.fcache.epoch == epoch {
|
||||
log.Trace("Using pre-generated cache", "epoch", epoch)
|
||||
current, ethash.fcache = ethash.fcache, nil
|
||||
} else {
|
||||
log.Trace("Requiring new ethash cache", "epoch", epoch)
|
||||
current = &cache{epoch: epoch}
|
||||
}
|
||||
ethash.caches[epoch] = current
|
||||
|
||||
// If we just used up the future cache, or need a refresh, regenerate
|
||||
if ethash.fcache == nil || ethash.fcache.epoch <= epoch {
|
||||
if ethash.fcache != nil {
|
||||
ethash.fcache.release()
|
||||
}
|
||||
log.Trace("Requiring new future ethash cache", "epoch", epoch+1)
|
||||
future = &cache{epoch: epoch + 1}
|
||||
ethash.fcache = future
|
||||
}
|
||||
}
|
||||
current.used = time.Now()
|
||||
ethash.lock.Unlock()
|
||||
|
||||
// Wait for generation finish, bump the timestamp and finalize the cache
|
||||
current.generate(ethash.cachedir, ethash.cachesondisk, ethash.tester)
|
||||
|
||||
current.lock.Lock()
|
||||
current.used = time.Now()
|
||||
current.lock.Unlock()
|
||||
|
||||
// If we exhausted the future cache, now's a good time to regenerate it
|
||||
if future != nil {
|
||||
go future.generate(ethash.cachedir, ethash.cachesondisk, ethash.tester)
|
||||
}
|
||||
return current.cache
|
||||
}
|
||||
|
||||
// dataset tries to retrieve a mining dataset for the specified block number
|
||||
// by first checking against a list of in-memory datasets, then against DAGs
|
||||
// stored on disk, and finally generating one if none can be found.
|
||||
func (ethash *Ethash) dataset(block uint64) []uint32 {
|
||||
epoch := block / epochLength
|
||||
|
||||
// If we have a PoW for that epoch, use that
|
||||
ethash.lock.Lock()
|
||||
|
||||
current, future := ethash.datasets[epoch], (*dataset)(nil)
|
||||
if current == nil {
|
||||
// No in-memory dataset, evict the oldest if the dataset limit was reached
|
||||
for len(ethash.datasets) > 0 && len(ethash.datasets) >= ethash.dagsinmem {
|
||||
var evict *dataset
|
||||
for _, dataset := range ethash.datasets {
|
||||
if evict == nil || evict.used.After(dataset.used) {
|
||||
evict = dataset
|
||||
}
|
||||
}
|
||||
delete(ethash.datasets, evict.epoch)
|
||||
evict.release()
|
||||
|
||||
log.Trace("Evicted ethash dataset", "epoch", evict.epoch, "used", evict.used)
|
||||
}
|
||||
// If we have the new cache pre-generated, use that, otherwise create a new one
|
||||
if ethash.fdataset != nil && ethash.fdataset.epoch == epoch {
|
||||
log.Trace("Using pre-generated dataset", "epoch", epoch)
|
||||
current = &dataset{epoch: ethash.fdataset.epoch} // Reload from disk
|
||||
ethash.fdataset = nil
|
||||
} else {
|
||||
log.Trace("Requiring new ethash dataset", "epoch", epoch)
|
||||
current = &dataset{epoch: epoch}
|
||||
}
|
||||
ethash.datasets[epoch] = current
|
||||
|
||||
// If we just used up the future dataset, or need a refresh, regenerate
|
||||
if ethash.fdataset == nil || ethash.fdataset.epoch <= epoch {
|
||||
if ethash.fdataset != nil {
|
||||
ethash.fdataset.release()
|
||||
}
|
||||
log.Trace("Requiring new future ethash dataset", "epoch", epoch+1)
|
||||
future = &dataset{epoch: epoch + 1}
|
||||
ethash.fdataset = future
|
||||
}
|
||||
}
|
||||
current.used = time.Now()
|
||||
ethash.lock.Unlock()
|
||||
|
||||
// Wait for generation finish, bump the timestamp and finalize the cache
|
||||
current.generate(ethash.dagdir, ethash.dagsondisk, ethash.tester)
|
||||
|
||||
current.lock.Lock()
|
||||
current.used = time.Now()
|
||||
current.lock.Unlock()
|
||||
|
||||
// If we exhausted the future dataset, now's a good time to regenerate it
|
||||
if future != nil {
|
||||
go future.generate(ethash.dagdir, ethash.dagsondisk, ethash.tester)
|
||||
}
|
||||
return current.dataset
|
||||
}
|
||||
|
||||
// Threads returns the number of mining threads currently enabled. This doesn't
|
||||
// necessarily mean that mining is running!
|
||||
func (ethash *Ethash) Threads() int {
|
||||
ethash.lock.Lock()
|
||||
defer ethash.lock.Unlock()
|
||||
|
||||
return ethash.threads
|
||||
}
|
||||
|
||||
// SetThreads updates the number of mining threads currently enabled. Calling
|
||||
// this method does not start mining, only sets the thread count. If zero is
|
||||
// specified, the miner will use all cores of the machine.
|
||||
func (ethash *Ethash) SetThreads(threads int) {
|
||||
ethash.lock.Lock()
|
||||
defer ethash.lock.Unlock()
|
||||
|
||||
// Update the threads and ping any running seal to pull in any changes
|
||||
ethash.threads = threads
|
||||
select {
|
||||
case ethash.update <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Hashrate implements PoW, returning the measured rate of the search invocations
|
||||
// per second over the last minute.
|
||||
func (ethash *Ethash) Hashrate() float64 {
|
||||
return ethash.hashrate.Rate1()
|
||||
}
|
||||
|
||||
// APIs implements consensus.Engine, returning the user facing RPC APIs. Currently
|
||||
// that is empty.
|
||||
func (ethash *Ethash) APIs(chain consensus.ChainReader) []rpc.API {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SeedHash is the seed to use for generating a verification cache and the mining
|
||||
// dataset.
|
||||
func SeedHash(block uint64) []byte {
|
||||
return seedHash(block)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// Copyright 2017 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,13 +14,27 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Contains the wrappers from the math/big package that require Go 1.7 and above.
|
||||
package ethash
|
||||
|
||||
// +build go1.7
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
package geth
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// GetString returns the value of x as a formatted string in some number base.
|
||||
func (bi *BigInt) GetString(base int) string {
|
||||
return bi.bigint.Text(base)
|
||||
// Tests that ethash works correctly in test mode.
|
||||
func TestTestMode(t *testing.T) {
|
||||
head := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
||||
|
||||
ethash := NewTester()
|
||||
block, err := ethash.Seal(nil, types.NewBlockWithHeader(head), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to seal block: %v", err)
|
||||
}
|
||||
head.Nonce = types.EncodeNonce(block.Nonce())
|
||||
head.MixDigest = block.MixDigest()
|
||||
if err := ethash.VerifySeal(nil, head); err != nil {
|
||||
t.Fatalf("unexpected verification error: %v", err)
|
||||
}
|
||||
}
|
||||
146
consensus/ethash/sealer.go
Normal file
146
consensus/ethash/sealer.go
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright 2017 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 ethash
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
|
||||
// the block's difficulty requirements.
|
||||
func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) {
|
||||
// If we're running a fake PoW, simply return a 0 nonce immediately
|
||||
if ethash.fakeMode {
|
||||
header := block.Header()
|
||||
header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{}
|
||||
return block.WithSeal(header), nil
|
||||
}
|
||||
// If we're running a shared PoW, delegate sealing to it
|
||||
if ethash.shared != nil {
|
||||
return ethash.shared.Seal(chain, block, stop)
|
||||
}
|
||||
// Create a runner and the multiple search threads it directs
|
||||
abort := make(chan struct{})
|
||||
found := make(chan *types.Block)
|
||||
|
||||
ethash.lock.Lock()
|
||||
threads := ethash.threads
|
||||
if ethash.rand == nil {
|
||||
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
|
||||
if err != nil {
|
||||
ethash.lock.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
ethash.rand = rand.New(rand.NewSource(seed.Int64()))
|
||||
}
|
||||
ethash.lock.Unlock()
|
||||
if threads == 0 {
|
||||
threads = runtime.NumCPU()
|
||||
}
|
||||
var pend sync.WaitGroup
|
||||
for i := 0; i < threads; i++ {
|
||||
pend.Add(1)
|
||||
go func(id int, nonce uint64) {
|
||||
defer pend.Done()
|
||||
ethash.mine(block, id, nonce, abort, found)
|
||||
}(i, uint64(ethash.rand.Int63()))
|
||||
}
|
||||
// Wait until sealing is terminated or a nonce is found
|
||||
var result *types.Block
|
||||
select {
|
||||
case <-stop:
|
||||
// Outside abort, stop all miner threads
|
||||
close(abort)
|
||||
case result = <-found:
|
||||
// One of the threads found a block, abort all others
|
||||
close(abort)
|
||||
case <-ethash.update:
|
||||
// Thread count was changed on user request, restart
|
||||
close(abort)
|
||||
pend.Wait()
|
||||
return ethash.Seal(chain, block, stop)
|
||||
}
|
||||
// Wait for all miners to terminate and return the block
|
||||
pend.Wait()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// mine is the actual proof-of-work miner that searches for a nonce starting from
|
||||
// seed that results in correct final block difficulty.
|
||||
func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) {
|
||||
// Extract some data from the header
|
||||
var (
|
||||
header = block.Header()
|
||||
hash = header.HashNoNonce().Bytes()
|
||||
target = new(big.Int).Div(maxUint256, header.Difficulty)
|
||||
|
||||
number = header.Number.Uint64()
|
||||
dataset = ethash.dataset(number)
|
||||
)
|
||||
// Start generating random nonces until we abort or find a good one
|
||||
var (
|
||||
attempts = int64(0)
|
||||
nonce = seed
|
||||
)
|
||||
logger := log.New("miner", id)
|
||||
logger.Trace("Started ethash search for new nonces", "seed", seed)
|
||||
for {
|
||||
select {
|
||||
case <-abort:
|
||||
// Mining terminated, update stats and abort
|
||||
logger.Trace("Ethash nonce search aborted", "attempts", nonce-seed)
|
||||
ethash.hashrate.Mark(attempts)
|
||||
return
|
||||
|
||||
default:
|
||||
// We don't have to update hash rate on every nonce, so update after after 2^X nonces
|
||||
attempts++
|
||||
if (attempts % (1 << 15)) == 0 {
|
||||
ethash.hashrate.Mark(attempts)
|
||||
attempts = 0
|
||||
}
|
||||
// Compute the PoW value of this nonce
|
||||
digest, result := hashimotoFull(dataset, hash, nonce)
|
||||
if new(big.Int).SetBytes(result).Cmp(target) <= 0 {
|
||||
// Correct nonce found, create a new header with it
|
||||
header = types.CopyHeader(header)
|
||||
header.Nonce = types.EncodeNonce(nonce)
|
||||
header.MixDigest = common.BytesToHash(digest)
|
||||
|
||||
// Seal and return a block (if still needed)
|
||||
select {
|
||||
case found <- block.WithSeal(header):
|
||||
logger.Trace("Ethash nonce found and reported", "attempts", nonce-seed, "nonce", nonce)
|
||||
case <-abort:
|
||||
logger.Trace("Ethash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce)
|
||||
}
|
||||
return
|
||||
}
|
||||
nonce++
|
||||
}
|
||||
}
|
||||
}
|
||||
85
consensus/ethash/xor.go
Normal file
85
consensus/ethash/xor.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Source: https://golang.org/src/crypto/cipher/xor.go
|
||||
|
||||
package ethash
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
|
||||
|
||||
// fastXORBytes xors in bulk. It only works on architectures that
|
||||
// support unaligned read/writes.
|
||||
func fastXORBytes(dst, a, b []byte) int {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
n = len(b)
|
||||
}
|
||||
|
||||
w := n / wordSize
|
||||
if w > 0 {
|
||||
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
|
||||
aw := *(*[]uintptr)(unsafe.Pointer(&a))
|
||||
bw := *(*[]uintptr)(unsafe.Pointer(&b))
|
||||
for i := 0; i < w; i++ {
|
||||
dw[i] = aw[i] ^ bw[i]
|
||||
}
|
||||
}
|
||||
|
||||
for i := (n - n%wordSize); i < n; i++ {
|
||||
dst[i] = a[i] ^ b[i]
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func safeXORBytes(dst, a, b []byte) int {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
n = len(b)
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
dst[i] = a[i] ^ b[i]
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// xorBytes xors the bytes in a and b. The destination is assumed to have enough
|
||||
// space. Returns the number of bytes xor'd.
|
||||
func xorBytes(dst, a, b []byte) int {
|
||||
if supportsUnaligned {
|
||||
return fastXORBytes(dst, a, b)
|
||||
}
|
||||
// TODO(hanwen): if (dst, a, b) have common alignment
|
||||
// we could still try fastXORBytes. It is not clear
|
||||
// how often this happens, and it's only worth it if
|
||||
// the block encryption itself is hardware
|
||||
// accelerated.
|
||||
return safeXORBytes(dst, a, b)
|
||||
}
|
||||
|
||||
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
|
||||
// The arguments are assumed to be of equal length.
|
||||
func fastXORWords(dst, a, b []byte) {
|
||||
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
|
||||
aw := *(*[]uintptr)(unsafe.Pointer(&a))
|
||||
bw := *(*[]uintptr)(unsafe.Pointer(&b))
|
||||
n := len(b) / wordSize
|
||||
for i := 0; i < n; i++ {
|
||||
dw[i] = aw[i] ^ bw[i]
|
||||
}
|
||||
}
|
||||
|
||||
func xorWords(dst, a, b []byte) {
|
||||
if supportsUnaligned {
|
||||
fastXORWords(dst, a, b)
|
||||
} else {
|
||||
safeXORBytes(dst, a, b)
|
||||
}
|
||||
}
|
||||
85
consensus/misc/dao.go
Normal file
85
consensus/misc/dao.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// 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 misc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrBadProDAOExtra is returned if a header doens't support the DAO fork on a
|
||||
// pro-fork client.
|
||||
ErrBadProDAOExtra = errors.New("bad DAO pro-fork extra-data")
|
||||
|
||||
// ErrBadNoDAOExtra is returned if a header does support the DAO fork on a no-
|
||||
// fork client.
|
||||
ErrBadNoDAOExtra = errors.New("bad DAO no-fork extra-data")
|
||||
)
|
||||
|
||||
// VerifyDAOHeaderExtraData validates the extra-data field of a block header to
|
||||
// ensure it conforms to DAO hard-fork rules.
|
||||
//
|
||||
// DAO hard-fork extension to the header validity:
|
||||
// a) if the node is no-fork, do not accept blocks in the [fork, fork+10) range
|
||||
// with the fork specific extra-data set
|
||||
// b) if the node is pro-fork, require blocks in the specific range to have the
|
||||
// unique extra-data set.
|
||||
func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error {
|
||||
// Short circuit validation if the node doesn't care about the DAO fork
|
||||
if config.DAOForkBlock == nil {
|
||||
return nil
|
||||
}
|
||||
// Make sure the block is within the fork's modified extra-data range
|
||||
limit := new(big.Int).Add(config.DAOForkBlock, params.DAOForkExtraRange)
|
||||
if header.Number.Cmp(config.DAOForkBlock) < 0 || header.Number.Cmp(limit) >= 0 {
|
||||
return nil
|
||||
}
|
||||
// Depending whether we support or oppose the fork, validate the extra-data contents
|
||||
if config.DAOForkSupport {
|
||||
if !bytes.Equal(header.Extra, params.DAOForkBlockExtra) {
|
||||
return ErrBadProDAOExtra
|
||||
}
|
||||
} else {
|
||||
if bytes.Equal(header.Extra, params.DAOForkBlockExtra) {
|
||||
return ErrBadNoDAOExtra
|
||||
}
|
||||
}
|
||||
// All ok, header has the same extra-data we expect
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyDAOHardFork modifies the state database according to the DAO hard-fork
|
||||
// rules, transferring all balances of a set of DAO accounts to a single refund
|
||||
// contract.
|
||||
func ApplyDAOHardFork(statedb *state.StateDB) {
|
||||
// Retrieve the contract to refund balances into
|
||||
if !statedb.Exist(params.DAORefundContract) {
|
||||
statedb.CreateAccount(params.DAORefundContract)
|
||||
}
|
||||
|
||||
// Move every DAO account and extra-balance account funds into the refund contract
|
||||
for _, addr := range params.DAODrainList() {
|
||||
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr))
|
||||
statedb.SetBalance(addr, new(big.Int))
|
||||
}
|
||||
}
|
||||
43
consensus/misc/forks.go
Normal file
43
consensus/misc/forks.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2017 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 misc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// VerifyForkHashes verifies that blocks conforming to network hard-forks do have
|
||||
// the correct hashes, to avoid clients going off on different chains. This is an
|
||||
// optional feature.
|
||||
func VerifyForkHashes(config *params.ChainConfig, header *types.Header, uncle bool) error {
|
||||
// We don't care about uncles
|
||||
if uncle {
|
||||
return nil
|
||||
}
|
||||
// If the homestead reprice hash is set, validate it
|
||||
if config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 {
|
||||
if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() {
|
||||
return fmt.Errorf("homestead gas reprice fork: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash)
|
||||
}
|
||||
}
|
||||
// All ok, return
|
||||
return nil
|
||||
}
|
||||
|
|
@ -21,17 +21,16 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/internal/jsre"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -97,9 +96,9 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester {
|
|||
t.Fatalf("failed to create node: %v", err)
|
||||
}
|
||||
ethConf := ð.Config{
|
||||
ChainConfig: ¶ms.ChainConfig{HomesteadBlock: new(big.Int), ChainId: new(big.Int)},
|
||||
Etherbase: common.HexToAddress(testAddress),
|
||||
PowTest: true,
|
||||
Genesis: core.DevGenesisBlock(),
|
||||
Etherbase: common.HexToAddress(testAddress),
|
||||
PowTest: true,
|
||||
}
|
||||
if confOverride != nil {
|
||||
confOverride(ethConf)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ package chequebook
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
|
@ -43,7 +44,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/swarm/services/swap/swap"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// TODO(zelig): watch peer solvency and notify of bouncing cheques
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ var (
|
|||
)
|
||||
|
||||
func newTestBackend() *backends.SimulatedBackend {
|
||||
return backends.NewSimulatedBackend(
|
||||
core.GenesisAccount{Address: addr0, Balance: big.NewInt(1000000000)},
|
||||
core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000000)},
|
||||
core.GenesisAccount{Address: addr2, Balance: big.NewInt(1000000000)},
|
||||
)
|
||||
return backends.NewSimulatedBackend(core.GenesisAlloc{
|
||||
addr0: {Balance: big.NewInt(1000000000)},
|
||||
addr1: {Balance: big.NewInt(1000000000)},
|
||||
addr2: {Balance: big.NewInt(1000000000)},
|
||||
})
|
||||
}
|
||||
|
||||
func deploy(prvKey *ecdsa.PrivateKey, amount *big.Int, backend *backends.SimulatedBackend) (common.Address, error) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ var (
|
|||
)
|
||||
|
||||
func TestENS(t *testing.T) {
|
||||
contractBackend := backends.NewSimulatedBackend(core.GenesisAccount{Address: addr, Balance: big.NewInt(1000000000)})
|
||||
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}})
|
||||
transactOpts := bind.NewKeyedTransactor(key)
|
||||
// Workaround for bug estimating gas in the call to Register
|
||||
transactOpts.GasLimit = big.NewInt(1000000)
|
||||
|
|
|
|||
|
|
@ -35,11 +35,11 @@ func setupReleaseTest(t *testing.T, prefund ...*ecdsa.PrivateKey) (*ecdsa.Privat
|
|||
key, _ := crypto.GenerateKey()
|
||||
auth := bind.NewKeyedTransactor(key)
|
||||
|
||||
accounts := []core.GenesisAccount{{Address: auth.From, Balance: big.NewInt(10000000000)}}
|
||||
alloc := core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}
|
||||
for _, key := range prefund {
|
||||
accounts = append(accounts, core.GenesisAccount{Address: crypto.PubkeyToAddress(key.PublicKey), Balance: big.NewInt(10000000000)})
|
||||
alloc[crypto.PubkeyToAddress(key.PublicKey)] = core.GenesisAccount{Balance: big.NewInt(10000000000)}
|
||||
}
|
||||
sim := backends.NewSimulatedBackend(accounts...)
|
||||
sim := backends.NewSimulatedBackend(alloc)
|
||||
|
||||
// Deploy a version oracle contract, commit and return
|
||||
_, _, oracle, err := DeployReleaseOracle(auth, sim, []common.Address{auth.From})
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ package release
|
|||
//go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -33,7 +34,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Interval to check for new releases
|
||||
|
|
@ -116,47 +116,49 @@ func (r *ReleaseService) checker() {
|
|||
|
||||
for {
|
||||
select {
|
||||
// If the time arrived, check for a new release
|
||||
case <-timer.C:
|
||||
// Rechedule the timer before continuing
|
||||
timer.Reset(releaseRecheckInterval)
|
||||
|
||||
// Retrieve the current version, and handle missing contracts gracefully
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
|
||||
opts := &bind.CallOpts{Context: ctx}
|
||||
version, err := r.oracle.CurrentVersion(opts)
|
||||
if err != nil {
|
||||
if err == bind.ErrNoCode {
|
||||
log.Debug("Release oracle not found", "contract", r.config.Oracle)
|
||||
continue
|
||||
}
|
||||
log.Error("Failed to retrieve current release", "err", err)
|
||||
continue
|
||||
}
|
||||
// Version was successfully retrieved, notify if newer than ours
|
||||
if version.Major > r.config.Major ||
|
||||
(version.Major == r.config.Major && version.Minor > r.config.Minor) ||
|
||||
(version.Major == r.config.Major && version.Minor == r.config.Minor && version.Patch > r.config.Patch) {
|
||||
|
||||
warning := fmt.Sprintf("Client v%d.%d.%d-%x seems older than the latest upstream release v%d.%d.%d-%x",
|
||||
r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4])
|
||||
howtofix := fmt.Sprintf("Please check https://github.com/ethereum/go-ethereum/releases for new releases")
|
||||
separator := strings.Repeat("-", len(warning))
|
||||
|
||||
log.Warn(separator)
|
||||
log.Warn(warning)
|
||||
log.Warn(howtofix)
|
||||
log.Warn(separator)
|
||||
} else {
|
||||
log.Debug("Client seems up to date with upstream",
|
||||
"local", fmt.Sprintf("v%d.%d.%d-%x", r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4]),
|
||||
"upstream", fmt.Sprintf("v%d.%d.%d-%x", version.Major, version.Minor, version.Patch, version.Commit[:4]))
|
||||
}
|
||||
|
||||
// If termination was requested, return
|
||||
r.checkVersion()
|
||||
case errc := <-r.quit:
|
||||
errc <- nil
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReleaseService) checkVersion() {
|
||||
// Retrieve the current version, and handle missing contracts gracefully
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
opts := &bind.CallOpts{Context: ctx}
|
||||
defer cancel()
|
||||
|
||||
version, err := r.oracle.CurrentVersion(opts)
|
||||
if err != nil {
|
||||
if err == bind.ErrNoCode {
|
||||
log.Debug("Release oracle not found", "contract", r.config.Oracle)
|
||||
} else {
|
||||
log.Error("Failed to retrieve current release", "err", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
// Version was successfully retrieved, notify if newer than ours
|
||||
if version.Major > r.config.Major ||
|
||||
(version.Major == r.config.Major && version.Minor > r.config.Minor) ||
|
||||
(version.Major == r.config.Major && version.Minor == r.config.Minor && version.Patch > r.config.Patch) {
|
||||
|
||||
warning := fmt.Sprintf("Client v%d.%d.%d-%x seems older than the latest upstream release v%d.%d.%d-%x",
|
||||
r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4])
|
||||
howtofix := fmt.Sprintf("Please check https://github.com/ethereum/go-ethereum/releases for new releases")
|
||||
separator := strings.Repeat("-", len(warning))
|
||||
|
||||
log.Warn(separator)
|
||||
log.Warn(warning)
|
||||
log.Warn(howtofix)
|
||||
log.Warn(separator)
|
||||
} else {
|
||||
log.Debug("Client seems up to date with upstream",
|
||||
"local", fmt.Sprintf("v%d.%d.%d-%x", r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4]),
|
||||
"upstream", fmt.Sprintf("v%d.%d.%d-%x", version.Major, version.Minor, version.Patch, version.Commit[:4]))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,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/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
|
@ -165,13 +166,17 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
|||
|
||||
// Generate a chain of b.N blocks using the supplied block
|
||||
// generator function.
|
||||
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{benchRootAddr, benchRootFunds})
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, b.N, gen)
|
||||
gspec := Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
|
||||
}
|
||||
genesis := gspec.MustCommit(db)
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, db, b.N, gen)
|
||||
|
||||
// Time the insertion of the new chain.
|
||||
// State and blocks are stored in the same DB.
|
||||
evmux := new(event.TypeMux)
|
||||
chainman, _ := NewBlockChain(db, ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux, vm.Config{})
|
||||
chainman, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), evmux, vm.Config{})
|
||||
defer chainman.Stop()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
|
@ -281,7 +286,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
|||
if err != nil {
|
||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||
}
|
||||
chain, err := NewBlockChain(db, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
||||
chain, err := NewBlockChain(db, params.TestChainConfig, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
if err != nil {
|
||||
b.Fatalf("error creating chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,22 +19,12 @@ package core
|
|||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
var (
|
||||
ExpDiffPeriod = big.NewInt(100000)
|
||||
big10 = big.NewInt(10)
|
||||
bigMinus99 = big.NewInt(-99)
|
||||
)
|
||||
|
||||
// BlockValidator is responsible for validating block headers, uncles and
|
||||
|
|
@ -44,30 +34,24 @@ var (
|
|||
type BlockValidator struct {
|
||||
config *params.ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
Pow pow.PoW // Proof of work used for validating
|
||||
engine consensus.Engine // Consensus engine used for validating
|
||||
}
|
||||
|
||||
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator {
|
||||
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine) *BlockValidator {
|
||||
validator := &BlockValidator{
|
||||
config: config,
|
||||
Pow: pow,
|
||||
engine: engine,
|
||||
bc: blockchain,
|
||||
}
|
||||
return validator
|
||||
}
|
||||
|
||||
// ValidateBlock validates the given block's header and uncles and verifies the
|
||||
// the block header's transaction and uncle roots.
|
||||
//
|
||||
// ValidateBlock does not validate the header's pow. The pow work validated
|
||||
// separately so we can process them in parallel.
|
||||
//
|
||||
// ValidateBlock also validates and makes sure that any previous state (or present)
|
||||
// state that might or might not be present is checked to make sure that fast
|
||||
// sync has done it's job proper. This prevents the block validator from accepting
|
||||
// false positives where a header is present but the state is not.
|
||||
func (v *BlockValidator) ValidateBlock(block *types.Block) error {
|
||||
// ValidateBody validates the given block's uncles and verifies the the block
|
||||
// header's transaction and uncle roots. The headers are assumed to be already
|
||||
// validated at this point.
|
||||
func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||
// Check whether the block's known, and if not, that it's linkable
|
||||
if v.bc.HasBlock(block.Hash()) {
|
||||
if _, err := state.New(block.Root(), v.bc.chainDb); err == nil {
|
||||
return &KnownBlockError{block.Number(), block.Hash()}
|
||||
|
|
@ -80,30 +64,17 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
|
|||
if _, err := state.New(parent.Root(), v.bc.chainDb); err != nil {
|
||||
return ParentError(block.ParentHash())
|
||||
}
|
||||
|
||||
// Header validity is known at this point, check the uncles and transactions
|
||||
header := block.Header()
|
||||
// validate the block header
|
||||
if err := ValidateHeader(v.config, v.Pow, header, parent.Header(), false, false); err != nil {
|
||||
if err := v.engine.VerifyUncles(v.bc, block); err != nil {
|
||||
return err
|
||||
}
|
||||
// verify the uncles are correctly rewarded
|
||||
if err := v.VerifyUncles(block, parent); err != nil {
|
||||
return err
|
||||
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
|
||||
return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash)
|
||||
}
|
||||
|
||||
// Verify UncleHash before running other uncle validations
|
||||
unclesSha := types.CalcUncleHash(block.Uncles())
|
||||
if unclesSha != header.UncleHash {
|
||||
return fmt.Errorf("invalid uncles root hash (remote: %x local: %x)", header.UncleHash, unclesSha)
|
||||
if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash {
|
||||
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
|
||||
}
|
||||
|
||||
// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
|
||||
// can be used by light clients to make sure they've received the correct Txs
|
||||
txSha := types.DeriveSha(block.Transactions())
|
||||
if txSha != header.TxHash {
|
||||
return fmt.Errorf("invalid transaction root hash (remote: %x local: %x)", header.TxHash, txSha)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -135,222 +106,6 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
|
|||
return nil
|
||||
}
|
||||
|
||||
// VerifyUncles verifies the given block's uncles and applies the Ethereum
|
||||
// consensus rules to the various block headers included; it will return an
|
||||
// error if any of the included uncle headers were invalid. It returns an error
|
||||
// if the validation failed.
|
||||
func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error {
|
||||
// validate that there are at most 2 uncles included in this block
|
||||
if len(block.Uncles()) > 2 {
|
||||
return ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles()))
|
||||
}
|
||||
|
||||
uncles := set.New()
|
||||
ancestors := make(map[common.Hash]*types.Block)
|
||||
for _, ancestor := range v.bc.GetBlocksFromHash(block.ParentHash(), 7) {
|
||||
ancestors[ancestor.Hash()] = ancestor
|
||||
// Include ancestors uncles in the uncle set. Uncles must be unique.
|
||||
for _, uncle := range ancestor.Uncles() {
|
||||
uncles.Add(uncle.Hash())
|
||||
}
|
||||
}
|
||||
ancestors[block.Hash()] = block
|
||||
uncles.Add(block.Hash())
|
||||
|
||||
for i, uncle := range block.Uncles() {
|
||||
hash := uncle.Hash()
|
||||
if uncles.Has(hash) {
|
||||
// Error not unique
|
||||
return UncleError("uncle[%d](%x) not unique", i, hash[:4])
|
||||
}
|
||||
uncles.Add(hash)
|
||||
|
||||
if ancestors[hash] != nil {
|
||||
branch := fmt.Sprintf(" O - %x\n |\n", block.Hash())
|
||||
for h := range ancestors {
|
||||
branch += fmt.Sprintf(" O - %x\n |\n", h)
|
||||
}
|
||||
log.Warn(branch)
|
||||
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
|
||||
}
|
||||
|
||||
if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() {
|
||||
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
|
||||
}
|
||||
|
||||
if err := ValidateHeader(v.config, v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil {
|
||||
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateHeader validates the given header and, depending on the pow arg,
|
||||
// checks the proof of work of the given header. Returns an error if the
|
||||
// validation failed.
|
||||
func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error {
|
||||
// Short circuit if the parent is missing.
|
||||
if parent == nil {
|
||||
return ParentError(header.ParentHash)
|
||||
}
|
||||
// Short circuit if the header's already known or its parent is missing
|
||||
if v.bc.HasHeader(header.Hash()) {
|
||||
return nil
|
||||
}
|
||||
return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false)
|
||||
}
|
||||
|
||||
// Validates a header. Returns an error if the header is invalid.
|
||||
//
|
||||
// See YP section 4.3.4. "Block Header Validity"
|
||||
func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
|
||||
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
|
||||
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
|
||||
}
|
||||
|
||||
if uncle {
|
||||
if header.Time.Cmp(math.MaxBig256) == 1 {
|
||||
return BlockTSTooBigErr
|
||||
}
|
||||
} else {
|
||||
if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 {
|
||||
return BlockFutureErr
|
||||
}
|
||||
}
|
||||
if header.Time.Cmp(parent.Time) != 1 {
|
||||
return BlockEqualTSErr
|
||||
}
|
||||
|
||||
expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
|
||||
if expd.Cmp(header.Difficulty) != 0 {
|
||||
return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd)
|
||||
}
|
||||
|
||||
a := new(big.Int).Set(parent.GasLimit)
|
||||
a = a.Sub(a, header.GasLimit)
|
||||
a.Abs(a)
|
||||
b := new(big.Int).Set(parent.GasLimit)
|
||||
b = b.Div(b, params.GasLimitBoundDivisor)
|
||||
if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) {
|
||||
return fmt.Errorf("GasLimit check failed for header (remote: %v local_max: %v)", header.GasLimit, b)
|
||||
}
|
||||
|
||||
num := new(big.Int).Set(parent.Number)
|
||||
num.Sub(header.Number, num)
|
||||
if num.Cmp(big.NewInt(1)) != 0 {
|
||||
return BlockNumberErr
|
||||
}
|
||||
|
||||
if checkPow {
|
||||
// Verify the nonce of the header. Return an error if it's not valid
|
||||
if !pow.Verify(types.NewBlockWithHeader(header)) {
|
||||
return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()}
|
||||
}
|
||||
}
|
||||
// If all checks passed, validate the extra-data field for hard forks
|
||||
if err := ValidateDAOHeaderExtraData(config, header); err != nil {
|
||||
return err
|
||||
}
|
||||
if !uncle && config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 {
|
||||
if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() {
|
||||
return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns
|
||||
// the difficulty that a new block should have when created at time
|
||||
// given the parent block's time and difficulty.
|
||||
func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
|
||||
return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff)
|
||||
} else {
|
||||
return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff)
|
||||
}
|
||||
}
|
||||
|
||||
func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki
|
||||
// algorithm:
|
||||
// diff = (parent_diff +
|
||||
// (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
|
||||
// ) + 2^(periodCount - 2)
|
||||
|
||||
bigTime := new(big.Int).SetUint64(time)
|
||||
bigParentTime := new(big.Int).SetUint64(parentTime)
|
||||
|
||||
// holds intermediate values to make the algo easier to read & audit
|
||||
x := new(big.Int)
|
||||
y := new(big.Int)
|
||||
|
||||
// 1 - (block_timestamp -parent_timestamp) // 10
|
||||
x.Sub(bigTime, bigParentTime)
|
||||
x.Div(x, big10)
|
||||
x.Sub(common.Big1, x)
|
||||
|
||||
// max(1 - (block_timestamp - parent_timestamp) // 10, -99)))
|
||||
if x.Cmp(bigMinus99) < 0 {
|
||||
x.Set(bigMinus99)
|
||||
}
|
||||
|
||||
// (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
|
||||
y.Div(parentDiff, params.DifficultyBoundDivisor)
|
||||
x.Mul(y, x)
|
||||
x.Add(parentDiff, x)
|
||||
|
||||
// minimum difficulty can ever be (before exponential factor)
|
||||
if x.Cmp(params.MinimumDifficulty) < 0 {
|
||||
x.Set(params.MinimumDifficulty)
|
||||
}
|
||||
|
||||
// for the exponential factor
|
||||
periodCount := new(big.Int).Add(parentNumber, common.Big1)
|
||||
periodCount.Div(periodCount, ExpDiffPeriod)
|
||||
|
||||
// the exponential factor, commonly referred to as "the bomb"
|
||||
// diff = diff + 2^(periodCount - 2)
|
||||
if periodCount.Cmp(common.Big1) > 0 {
|
||||
y.Sub(periodCount, common.Big2)
|
||||
y.Exp(common.Big2, y, nil)
|
||||
x.Add(x, y)
|
||||
}
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
func calcDifficultyFrontier(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
diff := new(big.Int)
|
||||
adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
|
||||
bigTime := new(big.Int)
|
||||
bigParentTime := new(big.Int)
|
||||
|
||||
bigTime.SetUint64(time)
|
||||
bigParentTime.SetUint64(parentTime)
|
||||
|
||||
if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 {
|
||||
diff.Add(parentDiff, adjust)
|
||||
} else {
|
||||
diff.Sub(parentDiff, adjust)
|
||||
}
|
||||
if diff.Cmp(params.MinimumDifficulty) < 0 {
|
||||
diff.Set(params.MinimumDifficulty)
|
||||
}
|
||||
|
||||
periodCount := new(big.Int).Add(parentNumber, common.Big1)
|
||||
periodCount.Div(periodCount, ExpDiffPeriod)
|
||||
if periodCount.Cmp(common.Big1) > 0 {
|
||||
// diff = diff + 2^(periodCount - 2)
|
||||
expDiff := periodCount.Sub(periodCount, common.Big2)
|
||||
expDiff.Exp(common.Big2, expDiff, nil)
|
||||
diff.Add(diff, expDiff)
|
||||
diff = math.BigMax(diff, params.MinimumDifficulty)
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
// CalcGasLimit computes the gas limit of the next block after parent.
|
||||
// The result may be modified by the caller.
|
||||
// This is miner strategy, not consensus protocol.
|
||||
|
|
|
|||
|
|
@ -17,12 +17,11 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
|
|
@ -30,65 +29,167 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func testChainConfig() *params.ChainConfig {
|
||||
return params.TestChainConfig
|
||||
//return ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)}
|
||||
}
|
||||
|
||||
func proc() (Validator, *BlockChain) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
var mux event.TypeMux
|
||||
|
||||
WriteTestNetGenesisBlock(db)
|
||||
blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &mux, vm.Config{})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
// Tests that simple header verification works, for both good and bad blocks.
|
||||
func TestHeaderVerification(t *testing.T) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
}
|
||||
return blockchain.validator, blockchain
|
||||
}
|
||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
|
||||
func TestNumber(t *testing.T) {
|
||||
_, chain := proc()
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
for j, valid := range []bool{true, false} {
|
||||
var results <-chan error
|
||||
|
||||
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb)
|
||||
cfg := testChainConfig()
|
||||
header := makeHeader(cfg, chain.Genesis(), statedb)
|
||||
header.Number = big.NewInt(3)
|
||||
err := ValidateHeader(cfg, FakePow{}, header, chain.Genesis().Header(), false, false)
|
||||
if err != BlockNumberErr {
|
||||
t.Errorf("expected block number error, got %q", err)
|
||||
}
|
||||
|
||||
header = makeHeader(cfg, chain.Genesis(), statedb)
|
||||
err = ValidateHeader(cfg, FakePow{}, header, chain.Genesis().Header(), false, false)
|
||||
if err == BlockNumberErr {
|
||||
t.Errorf("didn't expect block number error")
|
||||
if valid {
|
||||
engine := ethash.NewFaker()
|
||||
_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true})
|
||||
} else {
|
||||
engine := ethash.NewFakeFailer(headers[i].Number.Uint64())
|
||||
_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true})
|
||||
}
|
||||
// Wait for the verification result
|
||||
select {
|
||||
case result := <-results:
|
||||
if (result == nil) != valid {
|
||||
t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, result, valid)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("test %d.%d: verification timeout", i, j)
|
||||
}
|
||||
// Make sure no more data is returned
|
||||
select {
|
||||
case result := <-results:
|
||||
t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result)
|
||||
case <-time.After(25 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
chain.InsertChain(blocks[i : i+1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestPutReceipt(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
// Tests that concurrent header verification works, for both good and bad blocks.
|
||||
func TestHeaderConcurrentVerification2(t *testing.T) { testHeaderConcurrentVerification(t, 2) }
|
||||
func TestHeaderConcurrentVerification8(t *testing.T) { testHeaderConcurrentVerification(t, 8) }
|
||||
func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVerification(t, 32) }
|
||||
|
||||
var addr common.Address
|
||||
addr[0] = 1
|
||||
var hash common.Hash
|
||||
hash[0] = 2
|
||||
func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
seals := make([]bool, len(blocks))
|
||||
|
||||
receipt := new(types.Receipt)
|
||||
receipt.Logs = []*types.Log{{
|
||||
Address: addr,
|
||||
Topics: []common.Hash{hash},
|
||||
Data: []byte("hi"),
|
||||
BlockNumber: 42,
|
||||
TxHash: hash,
|
||||
TxIndex: 0,
|
||||
BlockHash: hash,
|
||||
Index: 0,
|
||||
}}
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
seals[i] = true
|
||||
}
|
||||
// Set the number of threads to verify on
|
||||
old := runtime.GOMAXPROCS(threads)
|
||||
defer runtime.GOMAXPROCS(old)
|
||||
|
||||
WriteReceipts(db, types.Receipts{receipt})
|
||||
receipt = GetReceipt(db, common.Hash{})
|
||||
if receipt == nil {
|
||||
t.Error("expected to get 1 receipt, got none.")
|
||||
// Run the header checker for the entire block chain at once both for a valid and
|
||||
// also an invalid chain (enough if one arbitrary block is invalid).
|
||||
for i, valid := range []bool{true, false} {
|
||||
var results <-chan error
|
||||
|
||||
if valid {
|
||||
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
} else {
|
||||
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), new(event.TypeMux), vm.Config{})
|
||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
}
|
||||
// Wait for all the verification results
|
||||
checks := make(map[int]error)
|
||||
for j := 0; j < len(blocks); j++ {
|
||||
select {
|
||||
case result := <-results:
|
||||
checks[j] = result
|
||||
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("test %d.%d: verification timeout", i, j)
|
||||
}
|
||||
}
|
||||
// Check nonce check validity
|
||||
for j := 0; j < len(blocks); j++ {
|
||||
want := valid || (j < len(blocks)-2) // We chose the last-but-one nonce in the chain to fail
|
||||
if (checks[j] == nil) != want {
|
||||
t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, checks[j], want)
|
||||
}
|
||||
if !want {
|
||||
// A few blocks after the first error may pass verification due to concurrent
|
||||
// workers. We don't care about those in this test, just that the correct block
|
||||
// errors out.
|
||||
break
|
||||
}
|
||||
}
|
||||
// Make sure no more data is returned
|
||||
select {
|
||||
case result := <-results:
|
||||
t.Fatalf("test %d: unexpected result returned: %v", i, result)
|
||||
case <-time.After(25 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that aborting a header validation indeed prevents further checks from being
|
||||
// run, as well as checks that no left-over goroutines are leaked.
|
||||
func TestHeaderConcurrentAbortion2(t *testing.T) { testHeaderConcurrentAbortion(t, 2) }
|
||||
func TestHeaderConcurrentAbortion8(t *testing.T) { testHeaderConcurrentAbortion(t, 8) }
|
||||
func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion(t, 32) }
|
||||
|
||||
func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
seals := make([]bool, len(blocks))
|
||||
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
seals[i] = true
|
||||
}
|
||||
// Set the number of threads to verify on
|
||||
old := runtime.GOMAXPROCS(threads)
|
||||
defer runtime.GOMAXPROCS(old)
|
||||
|
||||
// Start the verifications and immediately abort
|
||||
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), new(event.TypeMux), vm.Config{})
|
||||
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
close(abort)
|
||||
|
||||
// Deplete the results channel
|
||||
verified := 0
|
||||
for depleted := false; !depleted; {
|
||||
select {
|
||||
case result := <-results:
|
||||
if result != nil {
|
||||
t.Errorf("header %d: validation failed: %v", verified, result)
|
||||
}
|
||||
verified++
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
depleted = true
|
||||
}
|
||||
}
|
||||
// Check that abortion was honored by not processing too many POWs
|
||||
if verified > 2*threads {
|
||||
t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
|
@ -39,7 +40,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
|
|
@ -104,7 +104,7 @@ type BlockChain struct {
|
|||
procInterrupt int32 // interrupt signaler for block processing
|
||||
wg sync.WaitGroup // chain processing wait group for shutting down
|
||||
|
||||
pow pow.PoW
|
||||
engine consensus.Engine
|
||||
processor Processor // block processor interface
|
||||
validator Validator // block and state validator interface
|
||||
vmConfig vm.Config
|
||||
|
|
@ -115,7 +115,7 @@ type BlockChain struct {
|
|||
// NewBlockChain returns a fully initialised block chain using information
|
||||
// available in the database. It initialiser the default Ethereum Validator and
|
||||
// Processor.
|
||||
func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux, vmConfig vm.Config) (*BlockChain, error) {
|
||||
func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, mux *event.TypeMux, vmConfig vm.Config) (*BlockChain, error) {
|
||||
bodyCache, _ := lru.New(bodyCacheLimit)
|
||||
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
||||
blockCache, _ := lru.New(blockCacheLimit)
|
||||
|
|
@ -131,25 +131,22 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P
|
|||
bodyRLPCache: bodyRLPCache,
|
||||
blockCache: blockCache,
|
||||
futureBlocks: futureBlocks,
|
||||
pow: pow,
|
||||
engine: engine,
|
||||
vmConfig: vmConfig,
|
||||
badBlocks: badBlocks,
|
||||
}
|
||||
bc.SetValidator(NewBlockValidator(config, bc, pow))
|
||||
bc.SetProcessor(NewStateProcessor(config, bc))
|
||||
bc.SetValidator(NewBlockValidator(config, bc, engine))
|
||||
bc.SetProcessor(NewStateProcessor(config, bc, engine))
|
||||
|
||||
gv := func() HeaderValidator { return bc.Validator() }
|
||||
var err error
|
||||
bc.hc, err = NewHeaderChain(chainDb, config, gv, bc.getProcInterrupt)
|
||||
bc.hc, err = NewHeaderChain(chainDb, config, engine, bc.getProcInterrupt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bc.genesisBlock = bc.GetBlockByNumber(0)
|
||||
if bc.genesisBlock == nil {
|
||||
return nil, ErrNoGenesis
|
||||
}
|
||||
|
||||
if err := bc.loadLastState(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -182,16 +179,25 @@ func (self *BlockChain) loadLastState() error {
|
|||
head := GetHeadBlockHash(self.chainDb)
|
||||
if head == (common.Hash{}) {
|
||||
// Corrupt or empty database, init from scratch
|
||||
self.Reset()
|
||||
} else {
|
||||
if block := self.GetBlockByHash(head); block != nil {
|
||||
// Block found, set as the current head
|
||||
self.currentBlock = block
|
||||
} else {
|
||||
// Corrupt or empty database, init from scratch
|
||||
self.Reset()
|
||||
}
|
||||
log.Warn("Empty database, resetting chain")
|
||||
return self.Reset()
|
||||
}
|
||||
// Make sure the entire head block is available
|
||||
currentBlock := self.GetBlockByHash(head)
|
||||
if currentBlock == nil {
|
||||
// Corrupt or empty database, init from scratch
|
||||
log.Warn("Head block missing, resetting chain", "hash", head)
|
||||
return self.Reset()
|
||||
}
|
||||
// Make sure the state associated with the block is available
|
||||
if _, err := state.New(currentBlock.Root(), self.chainDb); err != nil {
|
||||
// Dangling block without a state associated, init from scratch
|
||||
log.Warn("Head state missing, resetting chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
|
||||
return self.Reset()
|
||||
}
|
||||
// Everything seems to be fine, set as the head block
|
||||
self.currentBlock = currentBlock
|
||||
|
||||
// Restore the last known head header
|
||||
currentHeader := self.currentBlock.Header()
|
||||
if head := GetHeadHeaderHash(self.chainDb); head != (common.Hash{}) {
|
||||
|
|
@ -200,6 +206,7 @@ func (self *BlockChain) loadLastState() error {
|
|||
}
|
||||
}
|
||||
self.hc.SetCurrentHeader(currentHeader)
|
||||
|
||||
// Restore the last known head fast block
|
||||
self.currentFastBlock = self.currentBlock
|
||||
if head := GetHeadFastBlockHash(self.chainDb); head != (common.Hash{}) {
|
||||
|
|
@ -230,14 +237,18 @@ func (self *BlockChain) loadLastState() error {
|
|||
// above the new head will be deleted and the new one set. In the case of blocks
|
||||
// though, the head may be further rewound if block bodies are missing (non-archive
|
||||
// nodes after a fast sync).
|
||||
func (bc *BlockChain) SetHead(head uint64) {
|
||||
func (bc *BlockChain) SetHead(head uint64) error {
|
||||
log.Warn("Rewinding blockchain", "target", head)
|
||||
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
// Rewind the header chain, deleting all block bodies until then
|
||||
delFn := func(hash common.Hash, num uint64) {
|
||||
DeleteBody(bc.chainDb, hash, num)
|
||||
}
|
||||
bc.hc.SetHead(head, delFn)
|
||||
currentHeader := bc.hc.CurrentHeader()
|
||||
|
||||
// Clear out any stale content from the caches
|
||||
bc.bodyCache.Purge()
|
||||
|
|
@ -245,29 +256,34 @@ func (bc *BlockChain) SetHead(head uint64) {
|
|||
bc.blockCache.Purge()
|
||||
bc.futureBlocks.Purge()
|
||||
|
||||
// Update all computed fields to the new head
|
||||
currentHeader := bc.hc.CurrentHeader()
|
||||
// Rewind the block chain, ensuring we don't end up with a stateless head block
|
||||
if bc.currentBlock != nil && currentHeader.Number.Uint64() < bc.currentBlock.NumberU64() {
|
||||
bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
|
||||
}
|
||||
if bc.currentBlock != nil {
|
||||
if _, err := state.New(bc.currentBlock.Root(), bc.chainDb); err != nil {
|
||||
// Rewound state missing, rolled back to before pivot, reset to genesis
|
||||
bc.currentBlock = nil
|
||||
}
|
||||
}
|
||||
// Rewind the fast block in a simpleton way to the target head
|
||||
if bc.currentFastBlock != nil && currentHeader.Number.Uint64() < bc.currentFastBlock.NumberU64() {
|
||||
bc.currentFastBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
|
||||
}
|
||||
|
||||
// If either blocks reached nil, reset to the genesis state
|
||||
if bc.currentBlock == nil {
|
||||
bc.currentBlock = bc.genesisBlock
|
||||
}
|
||||
if bc.currentFastBlock == nil {
|
||||
bc.currentFastBlock = bc.genesisBlock
|
||||
}
|
||||
|
||||
if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil {
|
||||
log.Crit("Failed to reset head full block", "err", err)
|
||||
}
|
||||
if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil {
|
||||
log.Crit("Failed to reset head fast block", "err", err)
|
||||
}
|
||||
bc.loadLastState()
|
||||
return bc.loadLastState()
|
||||
}
|
||||
|
||||
// FastSyncCommitHead sets the current head block to the one defined by the hash
|
||||
|
|
@ -361,9 +377,6 @@ func (self *BlockChain) Processor() Processor {
|
|||
return self.processor
|
||||
}
|
||||
|
||||
// AuxValidator returns the auxiliary validator (Proof of work atm)
|
||||
func (self *BlockChain) AuxValidator() pow.PoW { return self.pow }
|
||||
|
||||
// State returns a new mutable state based on the current HEAD block.
|
||||
func (self *BlockChain) State() (*state.StateDB, error) {
|
||||
return self.StateAt(self.CurrentBlock().Root())
|
||||
|
|
@ -375,16 +388,17 @@ func (self *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
|||
}
|
||||
|
||||
// Reset purges the entire blockchain, restoring it to its genesis state.
|
||||
func (bc *BlockChain) Reset() {
|
||||
bc.ResetWithGenesisBlock(bc.genesisBlock)
|
||||
func (bc *BlockChain) Reset() error {
|
||||
return bc.ResetWithGenesisBlock(bc.genesisBlock)
|
||||
}
|
||||
|
||||
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
|
||||
// specified genesis state.
|
||||
func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) {
|
||||
func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
||||
// Dump the entire block chain and purge the caches
|
||||
bc.SetHead(0)
|
||||
|
||||
if err := bc.SetHead(0); err != nil {
|
||||
return err
|
||||
}
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
|
|
@ -401,6 +415,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) {
|
|||
bc.hc.SetGenesis(bc.genesisBlock.Header())
|
||||
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
|
||||
bc.currentFastBlock = bc.genesisBlock
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Export writes the active chain to the given writer.
|
||||
|
|
@ -787,12 +803,15 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain
|
|||
}
|
||||
// Update the head fast sync block if better
|
||||
self.mu.Lock()
|
||||
|
||||
head := blockChain[len(errs)-1]
|
||||
if self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64()).Cmp(self.GetTd(head.Hash(), head.NumberU64())) < 0 {
|
||||
if err := WriteHeadFastBlockHash(self.chainDb, head.Hash()); err != nil {
|
||||
log.Crit("Failed to update head fast block hash", "err", err)
|
||||
if td := self.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
|
||||
if self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64()).Cmp(td) < 0 {
|
||||
if err := WriteHeadFastBlockHash(self.chainDb, head.Hash()); err != nil {
|
||||
log.Crit("Failed to update head fast block hash", "err", err)
|
||||
}
|
||||
self.currentFastBlock = head
|
||||
}
|
||||
self.currentFastBlock = head
|
||||
}
|
||||
self.mu.Unlock()
|
||||
|
||||
|
|
@ -878,38 +897,38 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
stats = insertStats{startTime: mclock.Now()}
|
||||
events = make([]interface{}, 0, len(chain))
|
||||
coalescedLogs []*types.Log
|
||||
nonceChecked = make([]bool, len(chain))
|
||||
)
|
||||
|
||||
// Start the parallel nonce verifier.
|
||||
nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain)
|
||||
defer close(nonceAbort)
|
||||
// Start the parallel header verifier
|
||||
headers := make([]*types.Header, len(chain))
|
||||
seals := make([]bool, len(chain))
|
||||
|
||||
for i, block := range chain {
|
||||
headers[i] = block.Header()
|
||||
seals[i] = true
|
||||
}
|
||||
abort, results := self.engine.VerifyHeaders(self, headers, seals)
|
||||
defer close(abort)
|
||||
|
||||
// Iterate over the blocks and insert when the verifier permits
|
||||
for i, block := range chain {
|
||||
// If the chain is terminating, stop processing blocks
|
||||
if atomic.LoadInt32(&self.procInterrupt) == 1 {
|
||||
log.Debug("Premature abort during blocks processing")
|
||||
break
|
||||
}
|
||||
bstart := time.Now()
|
||||
// Wait for block i's nonce to be verified before processing
|
||||
// its state transition.
|
||||
for !nonceChecked[i] {
|
||||
r := <-nonceResults
|
||||
nonceChecked[r.index] = true
|
||||
if !r.valid {
|
||||
invalid := chain[r.index]
|
||||
return r.index, &BlockNonceErr{Hash: invalid.Hash(), Number: invalid.Number(), Nonce: invalid.Nonce()}
|
||||
}
|
||||
}
|
||||
|
||||
// If the header is a banned one, straight out abort
|
||||
if BadHashes[block.Hash()] {
|
||||
err := BadHashError(block.Hash())
|
||||
self.reportBlock(block, nil, err)
|
||||
return i, err
|
||||
}
|
||||
// Stage 1 validation of the block using the chain's validator
|
||||
// interface.
|
||||
err := self.Validator().ValidateBlock(block)
|
||||
// Wait for the block's verification to complete
|
||||
bstart := time.Now()
|
||||
|
||||
err := <-results
|
||||
if err == nil {
|
||||
err = self.Validator().ValidateBody(block)
|
||||
}
|
||||
if err != nil {
|
||||
if IsKnownBlockErr(err) {
|
||||
stats.ignored++
|
||||
|
|
@ -924,7 +943,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
if block.Time().Cmp(max) == 1 {
|
||||
return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max)
|
||||
}
|
||||
|
||||
self.futureBlocks.Add(block.Hash(), block)
|
||||
stats.queued++
|
||||
continue
|
||||
|
|
@ -1016,12 +1034,10 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
case SplitStatTy:
|
||||
events = append(events, ChainSplitEvent{block, logs})
|
||||
}
|
||||
|
||||
stats.processed++
|
||||
stats.usedGas += usedGas.Uint64()
|
||||
stats.report(chain, i)
|
||||
}
|
||||
|
||||
go self.postChainEvents(events, coalescedLogs)
|
||||
|
||||
return 0, nil
|
||||
|
|
@ -1287,6 +1303,11 @@ Error: %v
|
|||
// of the header retrieval mechanisms already need to verify nonces, as well as
|
||||
// because nonces can be verified sparsely, not needing to check each.
|
||||
func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
|
||||
start := time.Now()
|
||||
if i, err := self.hc.ValidateHeaderChain(chain, checkFreq); err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
// Make sure only one thread manipulates the chain at once
|
||||
self.chainmu.Lock()
|
||||
defer self.chainmu.Unlock()
|
||||
|
|
@ -1302,7 +1323,7 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
|
|||
return err
|
||||
}
|
||||
|
||||
return self.hc.InsertHeaderChain(chain, checkFreq, whFunc)
|
||||
return self.hc.InsertHeaderChain(chain, whFunc, start)
|
||||
}
|
||||
|
||||
// writeHeader writes a header into the local chain, given that its parent is
|
||||
|
|
|
|||
|
|
@ -20,15 +20,11 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
|
@ -36,30 +32,25 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
}
|
||||
|
||||
func thePow() pow.PoW {
|
||||
pow, _ := ethash.NewForTesting()
|
||||
return pow
|
||||
}
|
||||
|
||||
func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
|
||||
var eventMux event.TypeMux
|
||||
WriteTestNetGenesisBlock(db)
|
||||
blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &eventMux, vm.Config{})
|
||||
if err != nil {
|
||||
t.Error("failed creating blockchain:", err)
|
||||
t.FailNow()
|
||||
return nil
|
||||
// newTestBlockChain creates a blockchain without validation.
|
||||
func newTestBlockChain(fake bool) *BlockChain {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
gspec := &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Difficulty: big.NewInt(1),
|
||||
}
|
||||
|
||||
gspec.MustCommit(db)
|
||||
engine := ethash.NewFullFaker()
|
||||
if !fake {
|
||||
engine = ethash.NewTester()
|
||||
}
|
||||
blockchain, err := NewBlockChain(db, gspec.Config, engine, new(event.TypeMux), vm.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
blockchain.SetValidator(bproc{})
|
||||
return blockchain
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +121,10 @@ func printChain(bc *BlockChain) {
|
|||
func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
for _, block := range chain {
|
||||
// Try and process the block
|
||||
err := blockchain.Validator().ValidateBlock(block)
|
||||
err := blockchain.engine.VerifyHeader(blockchain, block.Header(), true)
|
||||
if err == nil {
|
||||
err = blockchain.validator.ValidateBody(block)
|
||||
}
|
||||
if err != nil {
|
||||
if IsKnownBlockErr(err) {
|
||||
continue
|
||||
|
|
@ -146,7 +140,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
|||
blockchain.reportBlock(block, receipts, err)
|
||||
return err
|
||||
}
|
||||
err = blockchain.Validator().ValidateState(block, blockchain.GetBlockByHash(block.ParentHash()), statedb, receipts, usedGas)
|
||||
err = blockchain.validator.ValidateState(block, blockchain.GetBlockByHash(block.ParentHash()), statedb, receipts, usedGas)
|
||||
if err != nil {
|
||||
blockchain.reportBlock(block, receipts, err)
|
||||
return err
|
||||
|
|
@ -165,7 +159,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
|||
func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
|
||||
for _, header := range chain {
|
||||
// Try and validate the header
|
||||
if err := blockchain.Validator().ValidateHeader(header, blockchain.GetHeaderByHash(header.ParentHash), false); err != nil {
|
||||
if err := blockchain.engine.VerifyHeader(blockchain, header, false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
|
||||
|
|
@ -177,21 +171,6 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
|
||||
fh, err := os.OpenFile(filepath.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
var chain types.Blocks
|
||||
if err := rlp.Decode(fh, &chain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *testing.T) {
|
||||
_, err := blockchain.InsertChain(chain)
|
||||
if err != nil {
|
||||
|
|
@ -202,12 +181,10 @@ func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *
|
|||
}
|
||||
|
||||
func TestLastBlock(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
bchain := theBlockChain(db, t)
|
||||
block := makeBlockChain(bchain.CurrentBlock(), 1, db, 0)[0]
|
||||
bchain := newTestBlockChain(false)
|
||||
block := makeBlockChain(bchain.CurrentBlock(), 1, bchain.chainDb, 0)[0]
|
||||
bchain.insert(block)
|
||||
if block.Hash() != GetHeadBlockHash(db) {
|
||||
if block.Hash() != GetHeadBlockHash(bchain.chainDb) {
|
||||
t.Errorf("Write/Get HeadBlockHash failed")
|
||||
}
|
||||
}
|
||||
|
|
@ -346,92 +323,9 @@ func testBrokenChain(t *testing.T, full bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestChainInsertions(t *testing.T) {
|
||||
t.Skip("Skipped: outdated test files")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
chain1, err := loadChain("valid1", t)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
chain2, err := loadChain("valid2", t)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
blockchain := theBlockChain(db, t)
|
||||
|
||||
const max = 2
|
||||
done := make(chan bool, max)
|
||||
|
||||
go insertChain(done, blockchain, chain1, t)
|
||||
go insertChain(done, blockchain, chain2, t)
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
if chain2[len(chain2)-1].Hash() != blockchain.CurrentBlock().Hash() {
|
||||
t.Error("chain2 is canonical and shouldn't be")
|
||||
}
|
||||
|
||||
if chain1[len(chain1)-1].Hash() != blockchain.CurrentBlock().Hash() {
|
||||
t.Error("chain1 isn't canonical and should be")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainMultipleInsertions(t *testing.T) {
|
||||
t.Skip("Skipped: outdated test files")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
const max = 4
|
||||
chains := make([]types.Blocks, max)
|
||||
var longest int
|
||||
for i := 0; i < max; i++ {
|
||||
var err error
|
||||
name := "valid" + strconv.Itoa(i+1)
|
||||
chains[i], err = loadChain(name, t)
|
||||
if len(chains[i]) >= len(chains[longest]) {
|
||||
longest = i
|
||||
}
|
||||
fmt.Println("loaded", name, "with a length of", len(chains[i]))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
blockchain := theBlockChain(db, t)
|
||||
|
||||
done := make(chan bool, max)
|
||||
for i, chain := range chains {
|
||||
// XXX the go routine would otherwise reference the same (chain[3]) variable and fail
|
||||
i := i
|
||||
chain := chain
|
||||
go func() {
|
||||
insertChain(done, blockchain, chain, t)
|
||||
fmt.Println(i, "done")
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
if chains[longest][len(chains[longest])-1].Hash() != blockchain.CurrentBlock().Hash() {
|
||||
t.Error("Invalid canonical chain")
|
||||
}
|
||||
}
|
||||
|
||||
type bproc struct{}
|
||||
|
||||
func (bproc) ValidateBlock(*types.Block) error { return nil }
|
||||
func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil }
|
||||
func (bproc) ValidateBody(*types.Block) error { return nil }
|
||||
func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -458,6 +352,7 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B
|
|||
UncleHash: types.EmptyUncleHash,
|
||||
TxHash: types.EmptyRootHash,
|
||||
ReceiptHash: types.EmptyRootHash,
|
||||
Time: big.NewInt(int64(i) + 1),
|
||||
}
|
||||
if i == 0 {
|
||||
header.ParentHash = genesis.Hash()
|
||||
|
|
@ -470,29 +365,6 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B
|
|||
return chain
|
||||
}
|
||||
|
||||
func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
|
||||
var eventMux event.TypeMux
|
||||
bc := &BlockChain{
|
||||
chainDb: db,
|
||||
genesisBlock: genesis,
|
||||
eventMux: &eventMux,
|
||||
pow: FakePow{},
|
||||
config: testChainConfig(),
|
||||
}
|
||||
valFn := func() HeaderValidator { return bc.Validator() }
|
||||
bc.hc, _ = NewHeaderChain(db, testChainConfig(), valFn, bc.getProcInterrupt)
|
||||
bc.bodyCache, _ = lru.New(100)
|
||||
bc.bodyRLPCache, _ = lru.New(100)
|
||||
bc.blockCache, _ = lru.New(100)
|
||||
bc.futureBlocks, _ = lru.New(100)
|
||||
bc.badBlocks, _ = lru.New(10)
|
||||
bc.SetValidator(bproc{})
|
||||
bc.SetProcessor(bproc{})
|
||||
bc.ResetWithGenesisBlock(genesis)
|
||||
|
||||
return bc
|
||||
}
|
||||
|
||||
// Tests that reorganising a long difficult chain after a short easy one
|
||||
// overwrites the canonical numbers and links in the database.
|
||||
func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) }
|
||||
|
|
@ -512,18 +384,15 @@ func testReorgShort(t *testing.T, full bool) {
|
|||
}
|
||||
|
||||
func testReorg(t *testing.T, first, second []int, td int64, full bool) {
|
||||
// Create a pristine block chain
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
||||
bc := chm(genesis, db)
|
||||
bc := newTestBlockChain(true)
|
||||
|
||||
// Insert an easy and a difficult chain afterwards
|
||||
if full {
|
||||
bc.InsertChain(makeBlockChainWithDiff(genesis, first, 11))
|
||||
bc.InsertChain(makeBlockChainWithDiff(genesis, second, 22))
|
||||
bc.InsertChain(makeBlockChainWithDiff(bc.genesisBlock, first, 11))
|
||||
bc.InsertChain(makeBlockChainWithDiff(bc.genesisBlock, second, 22))
|
||||
} else {
|
||||
bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, first, 11), 1)
|
||||
bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, second, 22), 1)
|
||||
bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1)
|
||||
bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1)
|
||||
}
|
||||
// Check that the chain is valid number and link wise
|
||||
if full {
|
||||
|
|
@ -542,7 +411,7 @@ func testReorg(t *testing.T, first, second []int, td int64, full bool) {
|
|||
}
|
||||
}
|
||||
// Make sure the chain total difficulty is the correct one
|
||||
want := new(big.Int).Add(genesis.Difficulty(), big.NewInt(td))
|
||||
want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td))
|
||||
if full {
|
||||
if have := bc.GetTdByHash(bc.CurrentBlock().Hash()); have.Cmp(want) != 0 {
|
||||
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
|
||||
|
|
@ -559,19 +428,16 @@ func TestBadHeaderHashes(t *testing.T) { testBadHashes(t, false) }
|
|||
func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) }
|
||||
|
||||
func testBadHashes(t *testing.T, full bool) {
|
||||
// Create a pristine block chain
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
||||
bc := chm(genesis, db)
|
||||
bc := newTestBlockChain(true)
|
||||
|
||||
// Create a chain, ban a hash and try to import
|
||||
var err error
|
||||
if full {
|
||||
blocks := makeBlockChainWithDiff(genesis, []int{1, 2, 4}, 10)
|
||||
blocks := makeBlockChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
|
||||
BadHashes[blocks[2].Header().Hash()] = true
|
||||
_, err = bc.InsertChain(blocks)
|
||||
} else {
|
||||
headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 4}, 10)
|
||||
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
|
||||
BadHashes[headers[2].Hash()] = true
|
||||
_, err = bc.InsertHeaderChain(headers, 1)
|
||||
}
|
||||
|
|
@ -586,14 +452,11 @@ func TestReorgBadHeaderHashes(t *testing.T) { testReorgBadHashes(t, false) }
|
|||
func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) }
|
||||
|
||||
func testReorgBadHashes(t *testing.T, full bool) {
|
||||
// Create a pristine block chain
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
||||
bc := chm(genesis, db)
|
||||
bc := newTestBlockChain(true)
|
||||
|
||||
// Create a chain, import and ban afterwards
|
||||
headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 3, 4}, 10)
|
||||
blocks := makeBlockChainWithDiff(genesis, []int{1, 2, 3, 4}, 10)
|
||||
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
|
||||
blocks := makeBlockChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
|
||||
|
||||
if full {
|
||||
if _, err := bc.InsertChain(blocks); err != nil {
|
||||
|
|
@ -614,8 +477,9 @@ func testReorgBadHashes(t *testing.T, full bool) {
|
|||
BadHashes[headers[3].Hash()] = true
|
||||
defer func() { delete(BadHashes, headers[3].Hash()) }()
|
||||
}
|
||||
// Create a new chain manager and check it rolled back the state
|
||||
ncm, err := NewBlockChain(db, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
||||
|
||||
// Create a new BlockChain and check that it rolled back the state.
|
||||
ncm, err := NewBlockChain(bc.chainDb, bc.config, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new chain manager: %v", err)
|
||||
}
|
||||
|
|
@ -646,46 +510,34 @@ func testInsertNonceError(t *testing.T, full bool) {
|
|||
}
|
||||
// Create and insert a chain with a failing nonce
|
||||
var (
|
||||
failAt int
|
||||
failRes int
|
||||
failNum uint64
|
||||
failHash common.Hash
|
||||
failAt int
|
||||
failRes int
|
||||
failNum uint64
|
||||
)
|
||||
if full {
|
||||
blocks := makeBlockChain(blockchain.CurrentBlock(), i, db, 0)
|
||||
|
||||
failAt = rand.Int() % len(blocks)
|
||||
failNum = blocks[failAt].NumberU64()
|
||||
failHash = blocks[failAt].Hash()
|
||||
|
||||
blockchain.pow = failPow{failNum}
|
||||
|
||||
blockchain.engine = ethash.NewFakeFailer(failNum)
|
||||
failRes, err = blockchain.InsertChain(blocks)
|
||||
} else {
|
||||
headers := makeHeaderChain(blockchain.CurrentHeader(), i, db, 0)
|
||||
|
||||
failAt = rand.Int() % len(headers)
|
||||
failNum = headers[failAt].Number.Uint64()
|
||||
failHash = headers[failAt].Hash()
|
||||
|
||||
blockchain.pow = failPow{failNum}
|
||||
blockchain.validator = NewBlockValidator(testChainConfig(), blockchain, failPow{failNum})
|
||||
|
||||
blockchain.engine = ethash.NewFakeFailer(failNum)
|
||||
blockchain.hc.engine = blockchain.engine
|
||||
failRes, err = blockchain.InsertHeaderChain(headers, 1)
|
||||
}
|
||||
// Check that the returned error indicates the nonce failure.
|
||||
if failRes != failAt {
|
||||
t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt)
|
||||
}
|
||||
if !IsBlockNonceErr(err) {
|
||||
t.Fatalf("test %d: error mismatch: have %v, want nonce error %T", i, err, err)
|
||||
}
|
||||
nerr := err.(*BlockNonceErr)
|
||||
if nerr.Number.Uint64() != failNum {
|
||||
t.Errorf("test %d: number mismatch: have %v, want %v", i, nerr.Number, failNum)
|
||||
}
|
||||
if nerr.Hash != failHash {
|
||||
t.Errorf("test %d: hash mismatch: have %x, want %x", i, nerr.Hash[:4], failHash[:4])
|
||||
if err != ethash.ErrInvalidPoW {
|
||||
t.Fatalf("test %d: error mismatch: have %v, want %v", i, err, ethash.ErrInvalidPoW)
|
||||
}
|
||||
// Check that all no blocks after the failing block have been inserted.
|
||||
for j := 0; j < i-failAt; j++ {
|
||||
|
|
@ -711,10 +563,14 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
genesis = GenesisBlockForTesting(gendb, address, funds)
|
||||
signer = types.NewEIP155Signer(big.NewInt(1))
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
}
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
)
|
||||
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, 1024, func(i int, block *BlockGen) {
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, gendb, 1024, func(i int, block *BlockGen) {
|
||||
block.SetCoinbase(common.Address{0x00})
|
||||
|
||||
// If the block number is multiple of 3, send a few bonus transactions to the miner
|
||||
|
|
@ -734,17 +590,17 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
})
|
||||
// Import the chain as an archive node for the comparison baseline
|
||||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
||||
gspec.MustCommit(archiveDb)
|
||||
archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to process block %d: %v", n, err)
|
||||
}
|
||||
|
||||
// Fast import the chain as a non-archive node to test
|
||||
fastDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
|
||||
fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
||||
gspec.MustCommit(fastDb)
|
||||
fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
|
|
@ -794,10 +650,11 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
genesis = GenesisBlockForTesting(gendb, address, funds)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
)
|
||||
height := uint64(1024)
|
||||
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, int(height), nil)
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, gendb, int(height), nil)
|
||||
|
||||
// Configure a subchain to roll back
|
||||
remove := []common.Hash{}
|
||||
|
|
@ -818,10 +675,9 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
}
|
||||
// Import the chain as an archive node and ensure all pointers are updated
|
||||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
||||
gspec.MustCommit(archiveDb)
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to process block %d: %v", n, err)
|
||||
}
|
||||
|
|
@ -831,8 +687,8 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
|
||||
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||
fastDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
|
||||
fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
||||
gspec.MustCommit(fastDb)
|
||||
fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
|
|
@ -850,9 +706,9 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
|
||||
// Import the chain as a light node and ensure all pointers are updated
|
||||
lightDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
|
||||
light, _ := NewBlockChain(lightDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
||||
gspec.MustCommit(lightDb)
|
||||
|
||||
light, _ := NewBlockChain(lightDb, gspec.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
||||
}
|
||||
|
|
@ -863,9 +719,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
|
||||
// Tests that chain reorganisations handle transaction removals and reinsertions.
|
||||
func TestChainTxReorgs(t *testing.T) {
|
||||
params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be.
|
||||
params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block.
|
||||
|
||||
var (
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
|
|
@ -874,13 +727,19 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
signer = types.NewEIP155Signer(big.NewInt(1))
|
||||
)
|
||||
genesis := WriteGenesisBlockForTesting(db,
|
||||
GenesisAccount{addr1, big.NewInt(1000000)},
|
||||
GenesisAccount{addr2, big.NewInt(1000000)},
|
||||
GenesisAccount{addr3, big.NewInt(1000000)},
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
GasLimit: 3141592,
|
||||
Alloc: GenesisAlloc{
|
||||
addr1: {Balance: big.NewInt(1000000)},
|
||||
addr2: {Balance: big.NewInt(1000000)},
|
||||
addr3: {Balance: big.NewInt(1000000)},
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
)
|
||||
|
||||
// Create two transactions shared between the chains:
|
||||
// - postponed: transaction included at a later block in the forked chain
|
||||
// - swapped: transaction included at the same block number in the forked chain
|
||||
|
|
@ -898,7 +757,7 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
// - futureAdd: transaction added after the reorg has already finished
|
||||
var pastAdd, freshAdd, futureAdd *types.Transaction
|
||||
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
|
||||
|
|
@ -917,13 +776,13 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
})
|
||||
// Import the chain. This runs all block validation rules.
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), evmux, vm.Config{})
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
||||
}
|
||||
|
||||
// overwrite the old chain
|
||||
chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
chain, _ = GenerateChain(gspec.Config, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
|
||||
|
|
@ -975,23 +834,20 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLogReorgs(t *testing.T) {
|
||||
params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be.
|
||||
params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block.
|
||||
|
||||
var (
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
// this code generates a log
|
||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||
signer = types.NewEIP155Signer(big.NewInt(1))
|
||||
)
|
||||
genesis := WriteGenesisBlockForTesting(db,
|
||||
GenesisAccount{addr1, big.NewInt(10000000000000)},
|
||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
)
|
||||
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{})
|
||||
var evmux event.TypeMux
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), &evmux, vm.Config{})
|
||||
|
||||
subs := evmux.Subscribe(RemovedLogsEvent{})
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) {
|
||||
|
|
@ -1023,19 +879,23 @@ func TestReorgSideEvent(t *testing.T) {
|
|||
db, _ = ethdb.NewMemDatabase()
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(10000000000000)})
|
||||
signer = types.NewEIP155Signer(big.NewInt(1))
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
)
|
||||
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), evmux, vm.Config{})
|
||||
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {})
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, gen *BlockGen) {})
|
||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert chain: %v", err)
|
||||
}
|
||||
|
||||
replacementBlocks, _ := GenerateChain(params.TestChainConfig, genesis, db, 4, func(i int, gen *BlockGen) {
|
||||
replacementBlocks, _ := GenerateChain(gspec.Config, genesis, db, 4, func(i int, gen *BlockGen) {
|
||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil), signer, key1)
|
||||
if i == 2 {
|
||||
gen.OffsetTime(-1)
|
||||
|
|
@ -1098,28 +958,21 @@ done:
|
|||
|
||||
// Tests if the canonical block can be fetched from the database during chain insertion.
|
||||
func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
genesis = WriteGenesisBlockForTesting(db)
|
||||
)
|
||||
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{})
|
||||
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *BlockGen) {})
|
||||
bc := newTestBlockChain(false)
|
||||
chain, _ := GenerateChain(bc.config, bc.genesisBlock, bc.chainDb, 10, func(i int, gen *BlockGen) {})
|
||||
|
||||
for i := range chain {
|
||||
go func(block *types.Block) {
|
||||
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
|
||||
for {
|
||||
ch := GetCanonicalHash(db, block.NumberU64())
|
||||
ch := GetCanonicalHash(bc.chainDb, 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(db, ch, block.NumberU64())
|
||||
fb := GetBlock(bc.chainDb, ch, block.NumberU64())
|
||||
if fb == nil {
|
||||
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
|
||||
}
|
||||
|
|
@ -1130,7 +983,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
|||
}
|
||||
}(chain[i])
|
||||
|
||||
blockchain.InsertChain(types.Blocks{chain[i]})
|
||||
bc.InsertChain(types.Blocks{chain[i]})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1142,13 +995,16 @@ func TestEIP155Transition(t *testing.T) {
|
|||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
deleteAddr = common.Address{1}
|
||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds}, GenesisAccount{deleteAddr, new(big.Int)})
|
||||
config = ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
mux event.TypeMux
|
||||
gspec = &Genesis{
|
||||
Config: ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)},
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
mux event.TypeMux
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, config, FakePow{}, &mux, vm.Config{})
|
||||
blocks, _ := GenerateChain(config, genesis, db, 4, func(i int, block *BlockGen) {
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), &mux, vm.Config{})
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, db, 4, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
|
|
@ -1170,7 +1026,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
}
|
||||
block.AddTx(tx)
|
||||
|
||||
tx, err = basicTx(types.NewEIP155Signer(config.ChainId))
|
||||
tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainId))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -1182,7 +1038,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
}
|
||||
block.AddTx(tx)
|
||||
|
||||
tx, err = basicTx(types.NewEIP155Signer(config.ChainId))
|
||||
tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainId))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -1210,7 +1066,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
}
|
||||
|
||||
// generate an invalid chain id transaction
|
||||
config = ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
config := ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], db, 4, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
|
|
@ -1242,22 +1098,24 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
|||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
theAddr = common.Address{1}
|
||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds})
|
||||
config = ¶ms.ChainConfig{
|
||||
ChainId: big.NewInt(1),
|
||||
HomesteadBlock: new(big.Int),
|
||||
EIP155Block: new(big.Int),
|
||||
EIP158Block: big.NewInt(2),
|
||||
gspec = &Genesis{
|
||||
Config: ¶ms.ChainConfig{
|
||||
ChainId: big.NewInt(1),
|
||||
HomesteadBlock: new(big.Int),
|
||||
EIP155Block: new(big.Int),
|
||||
EIP158Block: big.NewInt(2),
|
||||
},
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
}
|
||||
mux event.TypeMux
|
||||
|
||||
blockchain, _ = NewBlockChain(db, config, FakePow{}, &mux, vm.Config{})
|
||||
genesis = gspec.MustCommit(db)
|
||||
mux event.TypeMux
|
||||
blockchain, _ = NewBlockChain(db, gspec.Config, ethash.NewFaker(), &mux, vm.Config{})
|
||||
)
|
||||
blocks, _ := GenerateChain(config, genesis, db, 3, func(i int, block *BlockGen) {
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
signer = types.NewEIP155Signer(config.ChainId)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
)
|
||||
switch i {
|
||||
case 0:
|
||||
|
|
@ -1285,7 +1143,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
if blockchain.stateCache.Exist(theAddr) {
|
||||
t.Error("account should not expect")
|
||||
t.Error("account should not exist")
|
||||
}
|
||||
|
||||
// account musn't be created post eip 161
|
||||
|
|
@ -1293,6 +1151,6 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
if blockchain.stateCache.Exist(theAddr) {
|
||||
t.Error("account should not expect")
|
||||
t.Error("account should not exist")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,39 +21,16 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
)
|
||||
|
||||
/*
|
||||
* TODO: move this to another package.
|
||||
*/
|
||||
|
||||
// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings.
|
||||
func MakeChainConfig() *params.ChainConfig {
|
||||
return ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
}
|
||||
}
|
||||
|
||||
// FakePow is a non-validating proof of work implementation.
|
||||
// It returns true from Verify for any block.
|
||||
type FakePow struct{}
|
||||
|
||||
func (f FakePow) Search(block pow.Block, stop <-chan struct{}, index int) (uint64, []byte) {
|
||||
return 0, nil
|
||||
}
|
||||
func (f FakePow) Verify(block pow.Block) bool { return true }
|
||||
func (f FakePow) GetHashrate() int64 { return 0 }
|
||||
func (f FakePow) Turbo(bool) {}
|
||||
|
||||
// So we can deterministically seed different blockchains
|
||||
var (
|
||||
canonicalSeed = 1
|
||||
|
|
@ -165,7 +142,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
|||
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
|
||||
panic("block time out of range")
|
||||
}
|
||||
b.header.Difficulty = CalcDifficulty(MakeChainConfig(), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty())
|
||||
b.header.Difficulty = ethash.CalcDifficulty(b.config, b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty())
|
||||
}
|
||||
|
||||
// GenerateChain creates a chain of n blocks. The first block's
|
||||
|
|
@ -181,14 +158,13 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
|||
// values. Inserting them into BlockChain requires use of FakePow or
|
||||
// a similar non-validating proof of work implementation.
|
||||
func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
||||
if config == nil {
|
||||
config = params.TestChainConfig
|
||||
}
|
||||
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
|
||||
genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config}
|
||||
|
||||
// Mutate the state and block according to any hard-fork specs
|
||||
if config == nil {
|
||||
config = MakeChainConfig()
|
||||
}
|
||||
if daoBlock := config.DAOForkBlock; daoBlock != nil {
|
||||
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
|
||||
if h.Number.Cmp(daoBlock) >= 0 && h.Number.Cmp(limit) < 0 {
|
||||
|
|
@ -198,13 +174,13 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Dat
|
|||
}
|
||||
}
|
||||
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(h.Number) == 0 {
|
||||
ApplyDAOHardFork(statedb)
|
||||
misc.ApplyDAOHardFork(statedb)
|
||||
}
|
||||
// Execute any user modifications to the block and finalize it
|
||||
if gen != nil {
|
||||
gen(i, b)
|
||||
}
|
||||
AccumulateRewards(statedb, h, b.uncles)
|
||||
ethash.AccumulateRewards(statedb, h, b.uncles)
|
||||
root, err := statedb.Commit(config.IsEIP158(h.Number))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("state write error: %v", err))
|
||||
|
|
@ -237,7 +213,7 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St
|
|||
Root: state.IntermediateRoot(config.IsEIP158(parent.Number())),
|
||||
ParentHash: parent.Hash(),
|
||||
Coinbase: parent.Coinbase(),
|
||||
Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
|
||||
Difficulty: ethash.CalcDifficulty(config, time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
|
||||
GasLimit: CalcGasLimit(parent),
|
||||
GasUsed: new(big.Int),
|
||||
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||
|
|
@ -249,14 +225,12 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St
|
|||
// chain. Depending on the full flag, if creates either a full block chain or a
|
||||
// header only chain.
|
||||
func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
|
||||
// Create the new chain database
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
evmux := &event.TypeMux{}
|
||||
|
||||
// Initialize a fresh chain with only a genesis block
|
||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
||||
gspec := new(Genesis)
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis := gspec.MustCommit(db)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux, vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, params.AllProtocolChanges, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
// Create and inject the requested chain
|
||||
if n == 0 {
|
||||
return db, blockchain, nil
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
|
@ -29,9 +30,6 @@ import (
|
|||
)
|
||||
|
||||
func ExampleGenerateChain() {
|
||||
params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be.
|
||||
params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block.
|
||||
|
||||
var (
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
|
|
@ -40,19 +38,20 @@ func ExampleGenerateChain() {
|
|||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
signer = types.HomesteadSigner{}
|
||||
)
|
||||
|
||||
chainConfig := ¶ms.ChainConfig{
|
||||
HomesteadBlock: new(big.Int),
|
||||
}
|
||||
// Ensure that key1 has some funds in the genesis block.
|
||||
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(1000000)})
|
||||
gspec := &Genesis{
|
||||
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
|
||||
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
|
||||
}
|
||||
genesis := gspec.MustCommit(db)
|
||||
|
||||
// This call generates a chain of 5 blocks. The function runs for
|
||||
// each block and adds different features to gen based on the
|
||||
// block index.
|
||||
chain, _ := GenerateChain(chainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
signer := types.HomesteadSigner{}
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, addr1 sends addr2 some ether.
|
||||
|
|
@ -82,7 +81,7 @@ func ExampleGenerateChain() {
|
|||
|
||||
// Import the chain. This runs all block validation rules.
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, chainConfig, FakePow{}, evmux, vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), evmux, vm.Config{})
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,87 +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 (
|
||||
"runtime"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
)
|
||||
|
||||
// nonceCheckResult contains the result of a nonce verification.
|
||||
type nonceCheckResult struct {
|
||||
index int // Index of the item verified from an input array
|
||||
valid bool // Result of the nonce verification
|
||||
}
|
||||
|
||||
// verifyNoncesFromHeaders starts a concurrent header nonce verification,
|
||||
// returning a quit channel to abort the operations and a results channel
|
||||
// to retrieve the async verifications.
|
||||
func verifyNoncesFromHeaders(checker pow.PoW, headers []*types.Header) (chan<- struct{}, <-chan nonceCheckResult) {
|
||||
items := make([]pow.Block, len(headers))
|
||||
for i, header := range headers {
|
||||
items[i] = types.NewBlockWithHeader(header)
|
||||
}
|
||||
return verifyNonces(checker, items)
|
||||
}
|
||||
|
||||
// verifyNoncesFromBlocks starts a concurrent block nonce verification,
|
||||
// returning a quit channel to abort the operations and a results channel
|
||||
// to retrieve the async verifications.
|
||||
func verifyNoncesFromBlocks(checker pow.PoW, blocks []*types.Block) (chan<- struct{}, <-chan nonceCheckResult) {
|
||||
items := make([]pow.Block, len(blocks))
|
||||
for i, block := range blocks {
|
||||
items[i] = block
|
||||
}
|
||||
return verifyNonces(checker, items)
|
||||
}
|
||||
|
||||
// verifyNonces starts a concurrent nonce verification, returning a quit channel
|
||||
// to abort the operations and a results channel to retrieve the async checks.
|
||||
func verifyNonces(checker pow.PoW, items []pow.Block) (chan<- struct{}, <-chan nonceCheckResult) {
|
||||
// Spawn as many workers as allowed threads
|
||||
workers := runtime.GOMAXPROCS(0)
|
||||
if len(items) < workers {
|
||||
workers = len(items)
|
||||
}
|
||||
// Create a task channel and spawn the verifiers
|
||||
tasks := make(chan int, workers)
|
||||
results := make(chan nonceCheckResult, len(items)) // Buffered to make sure all workers stop
|
||||
for i := 0; i < workers; i++ {
|
||||
go func() {
|
||||
for index := range tasks {
|
||||
results <- nonceCheckResult{index: index, valid: checker.Verify(items[index])}
|
||||
}
|
||||
}()
|
||||
}
|
||||
// Feed item indices to the workers until done or aborted
|
||||
abort := make(chan struct{})
|
||||
go func() {
|
||||
defer close(tasks)
|
||||
|
||||
for i := range items {
|
||||
select {
|
||||
case tasks <- i:
|
||||
continue
|
||||
case <-abort:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return abort, results
|
||||
}
|
||||
|
|
@ -1,234 +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 (
|
||||
"math/big"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
)
|
||||
|
||||
// failPow is a non-validating proof of work implementation, that returns true
|
||||
// from Verify for all but one block.
|
||||
type failPow struct {
|
||||
failing uint64
|
||||
}
|
||||
|
||||
func (pow failPow) Search(pow.Block, <-chan struct{}, int) (uint64, []byte) {
|
||||
return 0, nil
|
||||
}
|
||||
func (pow failPow) Verify(block pow.Block) bool { return block.NumberU64() != pow.failing }
|
||||
func (pow failPow) GetHashrate() int64 { return 0 }
|
||||
func (pow failPow) Turbo(bool) {}
|
||||
|
||||
// delayedPow is a non-validating proof of work implementation, that returns true
|
||||
// from Verify for all blocks, but delays them the configured amount of time.
|
||||
type delayedPow struct {
|
||||
delay time.Duration
|
||||
}
|
||||
|
||||
func (pow delayedPow) Search(pow.Block, <-chan struct{}, int) (uint64, []byte) {
|
||||
return 0, nil
|
||||
}
|
||||
func (pow delayedPow) Verify(block pow.Block) bool { time.Sleep(pow.delay); return true }
|
||||
func (pow delayedPow) GetHashrate() int64 { return 0 }
|
||||
func (pow delayedPow) Turbo(bool) {}
|
||||
|
||||
// Tests that simple POW verification works, for both good and bad blocks.
|
||||
func TestPowVerification(t *testing.T) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
}
|
||||
// Run the POW checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
for j, full := range []bool{true, false} {
|
||||
for k, valid := range []bool{true, false} {
|
||||
var results <-chan nonceCheckResult
|
||||
|
||||
switch {
|
||||
case full && valid:
|
||||
_, results = verifyNoncesFromBlocks(FakePow{}, []*types.Block{blocks[i]})
|
||||
case full && !valid:
|
||||
_, results = verifyNoncesFromBlocks(failPow{blocks[i].NumberU64()}, []*types.Block{blocks[i]})
|
||||
case !full && valid:
|
||||
_, results = verifyNoncesFromHeaders(FakePow{}, []*types.Header{headers[i]})
|
||||
case !full && !valid:
|
||||
_, results = verifyNoncesFromHeaders(failPow{headers[i].Number.Uint64()}, []*types.Header{headers[i]})
|
||||
}
|
||||
// Wait for the verification result
|
||||
select {
|
||||
case result := <-results:
|
||||
if result.index != 0 {
|
||||
t.Errorf("test %d.%d.%d: invalid index: have %d, want 0", i, j, k, result.index)
|
||||
}
|
||||
if result.valid != valid {
|
||||
t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, result.valid, valid)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("test %d.%d.%d: verification timeout", i, j, k)
|
||||
}
|
||||
// Make sure no more data is returned
|
||||
select {
|
||||
case result := <-results:
|
||||
t.Fatalf("test %d.%d.%d: unexpected result returned: %v", i, j, k, result)
|
||||
case <-time.After(25 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that concurrent POW verification works, for both good and bad blocks.
|
||||
func TestPowConcurrentVerification2(t *testing.T) { testPowConcurrentVerification(t, 2) }
|
||||
func TestPowConcurrentVerification8(t *testing.T) { testPowConcurrentVerification(t, 8) }
|
||||
func TestPowConcurrentVerification32(t *testing.T) { testPowConcurrentVerification(t, 32) }
|
||||
|
||||
func testPowConcurrentVerification(t *testing.T, threads int) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
}
|
||||
// Set the number of threads to verify on
|
||||
old := runtime.GOMAXPROCS(threads)
|
||||
defer runtime.GOMAXPROCS(old)
|
||||
|
||||
// Run the POW checker for the entire block chain at once both for a valid and
|
||||
// also an invalid chain (enough if one is invalid, last but one (arbitrary)).
|
||||
for i, full := range []bool{true, false} {
|
||||
for j, valid := range []bool{true, false} {
|
||||
var results <-chan nonceCheckResult
|
||||
|
||||
switch {
|
||||
case full && valid:
|
||||
_, results = verifyNoncesFromBlocks(FakePow{}, blocks)
|
||||
case full && !valid:
|
||||
_, results = verifyNoncesFromBlocks(failPow{uint64(len(blocks) - 1)}, blocks)
|
||||
case !full && valid:
|
||||
_, results = verifyNoncesFromHeaders(FakePow{}, headers)
|
||||
case !full && !valid:
|
||||
_, results = verifyNoncesFromHeaders(failPow{uint64(len(headers) - 1)}, headers)
|
||||
}
|
||||
// Wait for all the verification results
|
||||
checks := make(map[int]bool)
|
||||
for k := 0; k < len(blocks); k++ {
|
||||
select {
|
||||
case result := <-results:
|
||||
if _, ok := checks[result.index]; ok {
|
||||
t.Fatalf("test %d.%d.%d: duplicate results for %d", i, j, k, result.index)
|
||||
}
|
||||
if result.index < 0 || result.index >= len(blocks) {
|
||||
t.Fatalf("test %d.%d.%d: result %d out of bounds [%d, %d]", i, j, k, result.index, 0, len(blocks)-1)
|
||||
}
|
||||
checks[result.index] = result.valid
|
||||
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("test %d.%d.%d: verification timeout", i, j, k)
|
||||
}
|
||||
}
|
||||
// Check nonce check validity
|
||||
for k := 0; k < len(blocks); k++ {
|
||||
want := valid || (k != len(blocks)-2) // We chose the last but one nonce in the chain to fail
|
||||
if checks[k] != want {
|
||||
t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, checks[k], want)
|
||||
}
|
||||
}
|
||||
// Make sure no more data is returned
|
||||
select {
|
||||
case result := <-results:
|
||||
t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result)
|
||||
case <-time.After(25 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that aborting a POW validation indeed prevents further checks from being
|
||||
// run, as well as checks that no left-over goroutines are leaked.
|
||||
func TestPowConcurrentAbortion2(t *testing.T) { testPowConcurrentAbortion(t, 2) }
|
||||
func TestPowConcurrentAbortion8(t *testing.T) { testPowConcurrentAbortion(t, 8) }
|
||||
func TestPowConcurrentAbortion32(t *testing.T) { testPowConcurrentAbortion(t, 32) }
|
||||
|
||||
func testPowConcurrentAbortion(t *testing.T, threads int) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
}
|
||||
// Set the number of threads to verify on
|
||||
old := runtime.GOMAXPROCS(threads)
|
||||
defer runtime.GOMAXPROCS(old)
|
||||
|
||||
// Run the POW checker for the entire block chain at once
|
||||
for i, full := range []bool{true, false} {
|
||||
var abort chan<- struct{}
|
||||
var results <-chan nonceCheckResult
|
||||
|
||||
// Start the verifications and immediately abort
|
||||
if full {
|
||||
abort, results = verifyNoncesFromBlocks(delayedPow{time.Millisecond}, blocks)
|
||||
} else {
|
||||
abort, results = verifyNoncesFromHeaders(delayedPow{time.Millisecond}, headers)
|
||||
}
|
||||
close(abort)
|
||||
|
||||
// Deplete the results channel
|
||||
verified := make(map[int]struct{})
|
||||
for depleted := false; !depleted; {
|
||||
select {
|
||||
case result := <-results:
|
||||
verified[result.index] = struct{}{}
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
depleted = true
|
||||
}
|
||||
}
|
||||
// Check that abortion was honored by not processing too many POWs
|
||||
if len(verified) > 2*threads {
|
||||
t.Errorf("test %d: verification count too large: have %d, want below %d", i, len(verified), 2*threads)
|
||||
}
|
||||
// Check that there are no gaps in the results
|
||||
for j := 0; j < len(verified); j++ {
|
||||
if _, ok := verified[j]; !ok {
|
||||
t.Errorf("test %d.%d: gap found in verification results", i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ func ApplyDAOHardFork(statedb *state.StateDB) {
|
|||
}
|
||||
|
||||
// Move every DAO account and extra-balance account funds into the refund contract
|
||||
for _, addr := range params.DAODrainList {
|
||||
for _, addr := range params.DAODrainList() {
|
||||
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr))
|
||||
statedb.SetBalance(addr, new(big.Int))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
|
|
@ -33,19 +34,20 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
|
||||
// Generate a common prefix for both pro-forkers and non-forkers
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis := WriteGenesisBlockForTesting(db)
|
||||
gspec := new(Genesis)
|
||||
genesis := gspec.MustCommit(db)
|
||||
prefix, _ := GenerateChain(params.TestChainConfig, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
||||
|
||||
// Create the concurrent, conflicting two nodes
|
||||
proDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(proDb)
|
||||
gspec.MustCommit(proDb)
|
||||
proConf := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true}
|
||||
proBc, _ := NewBlockChain(proDb, proConf, new(FakePow), new(event.TypeMux), vm.Config{})
|
||||
proBc, _ := NewBlockChain(proDb, proConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
|
||||
conDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(conDb)
|
||||
gspec.MustCommit(conDb)
|
||||
conConf := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false}
|
||||
conBc, _ := NewBlockChain(conDb, conConf, new(FakePow), new(event.TypeMux), vm.Config{})
|
||||
conBc, _ := NewBlockChain(conDb, conConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
|
||||
if _, err := proBc.InsertChain(prefix); err != nil {
|
||||
t.Fatalf("pro-fork: failed to import chain prefix: %v", err)
|
||||
|
|
@ -57,8 +59,8 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
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()
|
||||
WriteGenesisBlockForTesting(db)
|
||||
bc, _ := NewBlockChain(db, conConf, new(FakePow), new(event.TypeMux), vm.Config{})
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, conConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
|
||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1))
|
||||
for j := 0; j < len(blocks)/2; j++ {
|
||||
|
|
@ -78,8 +80,8 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
}
|
||||
// Create a no-fork block, and try to feed into the pro-fork chain
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(db)
|
||||
bc, _ = NewBlockChain(db, proConf, new(FakePow), new(event.TypeMux), vm.Config{})
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, proConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
|
||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1))
|
||||
for j := 0; j < len(blocks)/2; j++ {
|
||||
|
|
@ -100,8 +102,8 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
}
|
||||
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(db)
|
||||
bc, _ := NewBlockChain(db, conConf, new(FakePow), new(event.TypeMux), vm.Config{})
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, conConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
|
||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1))
|
||||
for j := 0; j < len(blocks)/2; j++ {
|
||||
|
|
@ -116,8 +118,8 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
}
|
||||
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(db)
|
||||
bc, _ = NewBlockChain(db, proConf, new(FakePow), new(event.TypeMux), vm.Config{})
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, proConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{})
|
||||
|
||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1))
|
||||
for j := 0; j < len(blocks)/2; j++ {
|
||||
|
|
|
|||
|
|
@ -18,14 +18,12 @@ package core
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
|
|
@ -34,58 +32,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
type diffTest struct {
|
||||
ParentTimestamp uint64
|
||||
ParentDifficulty *big.Int
|
||||
CurrentTimestamp uint64
|
||||
CurrentBlocknumber *big.Int
|
||||
CurrentDifficulty *big.Int
|
||||
}
|
||||
|
||||
func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
|
||||
var ext struct {
|
||||
ParentTimestamp string
|
||||
ParentDifficulty string
|
||||
CurrentTimestamp string
|
||||
CurrentBlocknumber string
|
||||
CurrentDifficulty string
|
||||
}
|
||||
if err := json.Unmarshal(b, &ext); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.ParentTimestamp = math.MustParseUint64(ext.ParentTimestamp)
|
||||
d.ParentDifficulty = math.MustParseBig256(ext.ParentDifficulty)
|
||||
d.CurrentTimestamp = math.MustParseUint64(ext.CurrentTimestamp)
|
||||
d.CurrentBlocknumber = math.MustParseBig256(ext.CurrentBlocknumber)
|
||||
d.CurrentDifficulty = math.MustParseBig256(ext.CurrentDifficulty)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCalcDifficulty(t *testing.T) {
|
||||
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
tests := make(map[string]diffTest)
|
||||
err = json.NewDecoder(file).Decode(&tests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1150000)}
|
||||
for name, test := range tests {
|
||||
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
|
||||
diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
|
||||
if diff.Cmp(test.CurrentDifficulty) != 0 {
|
||||
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests block header storage and retrieval operations.
|
||||
func TestHeaderStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
|
@ -562,7 +508,11 @@ func TestMipmapChain(t *testing.T) {
|
|||
)
|
||||
defer db.Close()
|
||||
|
||||
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr, big.NewInt(1000000)})
|
||||
gspec := &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{addr: {Balance: big.NewInt(1000000)}},
|
||||
}
|
||||
genesis := gspec.MustCommit(db)
|
||||
chain, receipts := GenerateChain(params.TestChainConfig, genesis, db, 1010, func(i int, gen *BlockGen) {
|
||||
var receipts types.Receipts
|
||||
switch i {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
104
core/gen_genesis.go
Normal file
104
core/gen_genesis.go
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"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/params"
|
||||
)
|
||||
|
||||
func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||
type GenesisJSON struct {
|
||||
Config *params.ChainConfig `json:"config" optional:"true"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce" optional:"true"`
|
||||
Timestamp math.HexOrDecimal64 `json:"timestamp" optional:"true"`
|
||||
ParentHash common.Hash `json:"parentHash" optional:"true"`
|
||||
ExtraData hexutil.Bytes `json:"extraData" optional:"true"`
|
||||
GasLimit math.HexOrDecimal64 `json:"gasLimit"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
|
||||
Mixhash common.Hash `json:"mixHash" optional:"true"`
|
||||
Coinbase common.Address `json:"coinbase" optional:"true"`
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc"`
|
||||
}
|
||||
var enc GenesisJSON
|
||||
enc.Config = g.Config
|
||||
enc.Nonce = math.HexOrDecimal64(g.Nonce)
|
||||
enc.Timestamp = math.HexOrDecimal64(g.Timestamp)
|
||||
enc.ParentHash = g.ParentHash
|
||||
enc.ExtraData = g.ExtraData
|
||||
enc.GasLimit = math.HexOrDecimal64(g.GasLimit)
|
||||
enc.Difficulty = (*math.HexOrDecimal256)(g.Difficulty)
|
||||
enc.Mixhash = g.Mixhash
|
||||
enc.Coinbase = g.Coinbase
|
||||
if g.Alloc != nil {
|
||||
enc.Alloc = make(map[common.UnprefixedAddress]GenesisAccount, len(g.Alloc))
|
||||
for k, v := range g.Alloc {
|
||||
enc.Alloc[common.UnprefixedAddress(k)] = v
|
||||
}
|
||||
}
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||
type GenesisJSON struct {
|
||||
Config *params.ChainConfig `json:"config" optional:"true"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce" optional:"true"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"timestamp" optional:"true"`
|
||||
ParentHash *common.Hash `json:"parentHash" optional:"true"`
|
||||
ExtraData hexutil.Bytes `json:"extraData" optional:"true"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"gasLimit"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
|
||||
Mixhash *common.Hash `json:"mixHash" optional:"true"`
|
||||
Coinbase *common.Address `json:"coinbase" optional:"true"`
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc"`
|
||||
}
|
||||
var dec GenesisJSON
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
var x Genesis
|
||||
if dec.Config != nil {
|
||||
x.Config = dec.Config
|
||||
}
|
||||
if dec.Nonce != nil {
|
||||
x.Nonce = uint64(*dec.Nonce)
|
||||
}
|
||||
if dec.Timestamp != nil {
|
||||
x.Timestamp = uint64(*dec.Timestamp)
|
||||
}
|
||||
if dec.ParentHash != nil {
|
||||
x.ParentHash = *dec.ParentHash
|
||||
}
|
||||
if dec.ExtraData != nil {
|
||||
x.ExtraData = dec.ExtraData
|
||||
}
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'gasLimit' for Genesis")
|
||||
}
|
||||
x.GasLimit = uint64(*dec.GasLimit)
|
||||
if dec.Difficulty == nil {
|
||||
return errors.New("missing required field 'difficulty' for Genesis")
|
||||
}
|
||||
x.Difficulty = (*big.Int)(dec.Difficulty)
|
||||
if dec.Mixhash != nil {
|
||||
x.Mixhash = *dec.Mixhash
|
||||
}
|
||||
if dec.Coinbase != nil {
|
||||
x.Coinbase = *dec.Coinbase
|
||||
}
|
||||
if dec.Alloc == nil {
|
||||
return errors.New("missing required field 'alloc' for Genesis")
|
||||
}
|
||||
x.Alloc = make(GenesisAlloc, len(dec.Alloc))
|
||||
for k, v := range dec.Alloc {
|
||||
x.Alloc[common.Address(k)] = v
|
||||
}
|
||||
*g = x
|
||||
return nil
|
||||
}
|
||||
57
core/gen_genesis_account.go
Normal file
57
core/gen_genesis_account.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
func (g GenesisAccount) MarshalJSON() ([]byte, error) {
|
||||
type GenesisAccountJSON struct {
|
||||
Code hexutil.Bytes `json:"code,omitempty" optional:"true"`
|
||||
Storage map[common.Hash]common.Hash `json:"storage,omitempty" optional:"true"`
|
||||
Balance *math.HexOrDecimal256 `json:"balance"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce,omitempty" optional:"true"`
|
||||
}
|
||||
var enc GenesisAccountJSON
|
||||
enc.Code = g.Code
|
||||
enc.Storage = g.Storage
|
||||
enc.Balance = (*math.HexOrDecimal256)(g.Balance)
|
||||
enc.Nonce = math.HexOrDecimal64(g.Nonce)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
|
||||
type GenesisAccountJSON struct {
|
||||
Code hexutil.Bytes `json:"code,omitempty" optional:"true"`
|
||||
Storage map[common.Hash]common.Hash `json:"storage,omitempty" optional:"true"`
|
||||
Balance *math.HexOrDecimal256 `json:"balance"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce,omitempty" optional:"true"`
|
||||
}
|
||||
var dec GenesisAccountJSON
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
var x GenesisAccount
|
||||
if dec.Code != nil {
|
||||
x.Code = dec.Code
|
||||
}
|
||||
if dec.Storage != nil {
|
||||
x.Storage = dec.Storage
|
||||
}
|
||||
if dec.Balance == nil {
|
||||
return errors.New("missing required field 'balance' for GenesisAccount")
|
||||
}
|
||||
x.Balance = (*big.Int)(dec.Balance)
|
||||
if dec.Nonce != nil {
|
||||
x.Nonce = uint64(*dec.Nonce)
|
||||
}
|
||||
*g = x
|
||||
return nil
|
||||
}
|
||||
388
core/genesis.go
388
core/genesis.go
|
|
@ -17,230 +17,286 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"compress/bzip2"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"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/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// WriteGenesisBlock writes the genesis block to the database as block number 0
|
||||
func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, error) {
|
||||
contents, err := ioutil.ReadAll(reader)
|
||||
//go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
|
||||
//go:generate gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go
|
||||
|
||||
var errGenesisNoConfig = errors.New("genesis has no chain configuration")
|
||||
|
||||
// Genesis specifies the header fields, state of a genesis block. It also defines hard
|
||||
// fork switch-over blocks through the chain configuration.
|
||||
type Genesis struct {
|
||||
Config *params.ChainConfig `json:"config" optional:"true"`
|
||||
Nonce uint64 `json:"nonce" optional:"true"`
|
||||
Timestamp uint64 `json:"timestamp" optional:"true"`
|
||||
ParentHash common.Hash `json:"parentHash" optional:"true"`
|
||||
ExtraData []byte `json:"extraData" optional:"true"`
|
||||
GasLimit uint64 `json:"gasLimit"`
|
||||
Difficulty *big.Int `json:"difficulty"`
|
||||
Mixhash common.Hash `json:"mixHash" optional:"true"`
|
||||
Coinbase common.Address `json:"coinbase" optional:"true"`
|
||||
Alloc GenesisAlloc `json:"alloc"`
|
||||
}
|
||||
|
||||
// GenesisAlloc specifies the initial state that is part of the genesis block.
|
||||
type GenesisAlloc map[common.Address]GenesisAccount
|
||||
|
||||
// GenesisAccount is an account in the state of the genesis block.
|
||||
type GenesisAccount struct {
|
||||
Code []byte `json:"code,omitempty" optional:"true"`
|
||||
Storage map[common.Hash]common.Hash `json:"storage,omitempty" optional:"true"`
|
||||
Balance *big.Int `json:"balance"`
|
||||
Nonce uint64 `json:"nonce,omitempty" optional:"true"`
|
||||
}
|
||||
|
||||
// field type overrides for gencodec
|
||||
type genesisSpecMarshaling struct {
|
||||
Nonce math.HexOrDecimal64
|
||||
Timestamp math.HexOrDecimal64
|
||||
ExtraData hexutil.Bytes
|
||||
GasLimit math.HexOrDecimal64
|
||||
Difficulty *math.HexOrDecimal256
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount
|
||||
}
|
||||
type genesisAccountMarshaling struct {
|
||||
Code hexutil.Bytes
|
||||
Balance *math.HexOrDecimal256
|
||||
Nonce math.HexOrDecimal64
|
||||
}
|
||||
|
||||
// GenesisMismatchError is raised when trying to overwrite an existing
|
||||
// genesis block with an incompatible one.
|
||||
type GenesisMismatchError struct {
|
||||
Stored, New common.Hash
|
||||
}
|
||||
|
||||
func (e *GenesisMismatchError) Error() string {
|
||||
return fmt.Sprintf("wrong genesis block in database (have %x, new %x)", e.Stored[:8], e.New[:8])
|
||||
}
|
||||
|
||||
// SetupGenesisBlock writes or updates the genesis block in db.
|
||||
// The block that will be used is:
|
||||
//
|
||||
// genesis == nil genesis != nil
|
||||
// +------------------------------------------
|
||||
// db has no genesis | main-net default | genesis
|
||||
// db has genesis | from DB | genesis (if compatible)
|
||||
//
|
||||
// The stored chain configuration will be updated if it is compatible (i.e. does not
|
||||
// specify a fork block below the local head block). In case of a conflict, the
|
||||
// error is a *params.ConfigCompatError and the new, unwritten config is returned.
|
||||
//
|
||||
// The returned chain configuration is never nil.
|
||||
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
|
||||
if genesis != nil && genesis.Config == nil {
|
||||
return params.AllProtocolChanges, common.Hash{}, errGenesisNoConfig
|
||||
}
|
||||
|
||||
// Just commit the new block if there is no stored genesis block.
|
||||
stored := GetCanonicalHash(db, 0)
|
||||
if (stored == common.Hash{}) {
|
||||
if genesis == nil {
|
||||
log.Info("Writing default main-net genesis block")
|
||||
genesis = DefaultGenesisBlock()
|
||||
} else {
|
||||
log.Info("Writing custom genesis block")
|
||||
}
|
||||
block, err := genesis.Commit(db)
|
||||
return genesis.Config, block.Hash(), err
|
||||
}
|
||||
|
||||
// Check whether the genesis block is already written.
|
||||
if genesis != nil {
|
||||
block, _ := genesis.ToBlock()
|
||||
hash := block.Hash()
|
||||
if hash != stored {
|
||||
return genesis.Config, block.Hash(), &GenesisMismatchError{stored, hash}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the existing chain configuration.
|
||||
newcfg := genesis.configOrDefault(stored)
|
||||
storedcfg, err := GetChainConfig(db, stored)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var genesis struct {
|
||||
ChainConfig *params.ChainConfig `json:"config"`
|
||||
Nonce string
|
||||
Timestamp string
|
||||
ParentHash string
|
||||
ExtraData string
|
||||
GasLimit string
|
||||
Difficulty string
|
||||
Mixhash string
|
||||
Coinbase string
|
||||
Alloc map[string]struct {
|
||||
Code string
|
||||
Storage map[string]string
|
||||
Balance string
|
||||
Nonce string
|
||||
if err == ChainConfigNotFoundErr {
|
||||
// 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
|
||||
}
|
||||
// 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)
|
||||
// if we just continued here.
|
||||
if genesis == nil && stored != params.MainNetGenesisHash {
|
||||
return storedcfg, stored, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(contents, &genesis); err != nil {
|
||||
return nil, err
|
||||
// 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 {
|
||||
return newcfg, stored, fmt.Errorf("missing block number for head header hash")
|
||||
}
|
||||
if genesis.ChainConfig == nil {
|
||||
genesis.ChainConfig = params.AllProtocolChanges
|
||||
compatErr := storedcfg.CheckCompatible(newcfg, height)
|
||||
if compatErr != nil && height != 0 && compatErr.RewindTo != 0 {
|
||||
return newcfg, stored, compatErr
|
||||
}
|
||||
return newcfg, stored, WriteChainConfig(db, stored, newcfg)
|
||||
}
|
||||
|
||||
// creating with empty hash always works
|
||||
statedb, _ := state.New(common.Hash{}, chainDb)
|
||||
for addr, account := range genesis.Alloc {
|
||||
balance, ok := math.ParseBig256(account.Balance)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid balance for account %s: %q", addr, account.Balance)
|
||||
}
|
||||
nonce, ok := math.ParseUint64(account.Nonce)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid nonce for account %s: %q", addr, account.Nonce)
|
||||
}
|
||||
func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||
switch {
|
||||
case g != nil:
|
||||
return g.Config
|
||||
case ghash == params.MainNetGenesisHash:
|
||||
return params.MainnetChainConfig
|
||||
case ghash == params.TestNetGenesisHash:
|
||||
return params.TestnetChainConfig
|
||||
default:
|
||||
return params.AllProtocolChanges
|
||||
}
|
||||
}
|
||||
|
||||
address := common.HexToAddress(addr)
|
||||
statedb.AddBalance(address, balance)
|
||||
statedb.SetCode(address, common.FromHex(account.Code))
|
||||
statedb.SetNonce(address, nonce)
|
||||
// ToBlock creates the block and state of a genesis specification.
|
||||
func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
for addr, account := range g.Alloc {
|
||||
statedb.AddBalance(addr, account.Balance)
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
for key, value := range account.Storage {
|
||||
statedb.SetState(address, common.HexToHash(key), common.HexToHash(value))
|
||||
statedb.SetState(addr, key, value)
|
||||
}
|
||||
}
|
||||
root, stateBatch := statedb.CommitBatch(false)
|
||||
|
||||
difficulty, ok := math.ParseBig256(genesis.Difficulty)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid difficulty: %q", genesis.Difficulty)
|
||||
}
|
||||
gaslimit, ok := math.ParseUint64(genesis.GasLimit)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid gas limit: %q", genesis.GasLimit)
|
||||
}
|
||||
nonce, ok := math.ParseUint64(genesis.Nonce)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid nonce: %q", genesis.Nonce)
|
||||
}
|
||||
timestamp, ok := math.ParseBig256(genesis.Timestamp)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid timestamp: %q", genesis.Timestamp)
|
||||
}
|
||||
|
||||
block := types.NewBlock(&types.Header{
|
||||
Nonce: types.EncodeNonce(nonce),
|
||||
Time: timestamp,
|
||||
ParentHash: common.HexToHash(genesis.ParentHash),
|
||||
Extra: common.FromHex(genesis.ExtraData),
|
||||
GasLimit: new(big.Int).SetUint64(gaslimit),
|
||||
Difficulty: difficulty,
|
||||
MixDigest: common.HexToHash(genesis.Mixhash),
|
||||
Coinbase: common.HexToAddress(genesis.Coinbase),
|
||||
root := statedb.IntermediateRoot(false)
|
||||
head := &types.Header{
|
||||
Nonce: types.EncodeNonce(g.Nonce),
|
||||
Time: new(big.Int).SetUint64(g.Timestamp),
|
||||
ParentHash: g.ParentHash,
|
||||
Extra: g.ExtraData,
|
||||
GasLimit: new(big.Int).SetUint64(g.GasLimit),
|
||||
Difficulty: g.Difficulty,
|
||||
MixDigest: g.Mixhash,
|
||||
Coinbase: g.Coinbase,
|
||||
Root: root,
|
||||
}, nil, nil, nil)
|
||||
|
||||
if block := GetBlock(chainDb, block.Hash(), block.NumberU64()); block != nil {
|
||||
log.Info("Genesis block known, writing canonical number")
|
||||
err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
if g.GasLimit == 0 {
|
||||
head.GasLimit = params.GenesisGasLimit
|
||||
}
|
||||
if g.Difficulty == nil {
|
||||
head.Difficulty = params.GenesisDifficulty
|
||||
}
|
||||
return types.NewBlock(head, nil, nil, nil), statedb
|
||||
}
|
||||
|
||||
if err := stateBatch.Write(); err != nil {
|
||||
// Commit writes the block and state of a genesis specification to the database.
|
||||
// The block is committed as the canonical head block.
|
||||
func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
|
||||
block, statedb := g.ToBlock()
|
||||
if _, err := statedb.CommitTo(db, false); err != nil {
|
||||
return nil, fmt.Errorf("cannot write state: %v", err)
|
||||
}
|
||||
if err := WriteTd(chainDb, block.Hash(), block.NumberU64(), difficulty); err != nil {
|
||||
if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteBlock(chainDb, block); err != nil {
|
||||
if err := WriteBlock(db, block); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteBlockReceipts(chainDb, block.Hash(), block.NumberU64(), nil); err != nil {
|
||||
if err := WriteBlockReceipts(db, block.Hash(), block.NumberU64(), nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()); err != nil {
|
||||
if err := WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteHeadBlockHash(chainDb, block.Hash()); err != nil {
|
||||
if err := WriteHeadBlockHash(db, block.Hash()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := WriteChainConfig(chainDb, block.Hash(), genesis.ChainConfig); err != nil {
|
||||
if err := WriteHeadHeaderHash(db, block.Hash()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return block, nil
|
||||
config := g.Config
|
||||
if config == nil {
|
||||
config = params.AllProtocolChanges
|
||||
}
|
||||
return block, WriteChainConfig(db, block.Hash(), config)
|
||||
}
|
||||
|
||||
// GenesisBlockForTesting creates a block in which addr has the given wei balance.
|
||||
// The state trie of the block is written to db. the passed db needs to contain a state root
|
||||
func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
obj := statedb.GetOrNewStateObject(addr)
|
||||
obj.SetBalance(balance)
|
||||
root, err := statedb.Commit(false)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot write state: %v", err))
|
||||
}
|
||||
block := types.NewBlock(&types.Header{
|
||||
Difficulty: params.GenesisDifficulty,
|
||||
GasLimit: params.GenesisGasLimit,
|
||||
Root: root,
|
||||
}, nil, nil, nil)
|
||||
return block
|
||||
}
|
||||
|
||||
type GenesisAccount struct {
|
||||
Address common.Address
|
||||
Balance *big.Int
|
||||
}
|
||||
|
||||
func WriteGenesisBlockForTesting(db ethdb.Database, accounts ...GenesisAccount) *types.Block {
|
||||
accountJson := "{"
|
||||
for i, account := range accounts {
|
||||
if i != 0 {
|
||||
accountJson += ","
|
||||
}
|
||||
accountJson += fmt.Sprintf(`"0x%x":{"balance":"%d"}`, account.Address, account.Balance)
|
||||
}
|
||||
accountJson += "}"
|
||||
|
||||
testGenesis := fmt.Sprintf(`{
|
||||
"nonce":"0x%x",
|
||||
"gasLimit":"0x%x",
|
||||
"difficulty":"0x%x",
|
||||
"alloc": %s
|
||||
}`, types.EncodeNonce(0), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes(), accountJson)
|
||||
block, err := WriteGenesisBlock(db, strings.NewReader(testGenesis))
|
||||
// MustCommit writes the genesis block and state to db, panicking on error.
|
||||
// The block is committed as the canonical head block.
|
||||
func (g *Genesis) MustCommit(db ethdb.Database) *types.Block {
|
||||
block, err := g.Commit(db)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
// WriteDefaultGenesisBlock assembles the official Ethereum genesis block and
|
||||
// writes it - along with all associated state - into a chain database.
|
||||
func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(DefaultGenesisBlock()))
|
||||
// GenesisBlockForTesting creates and writes a block in which addr has the given wei balance.
|
||||
func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
|
||||
g := Genesis{Alloc: GenesisAlloc{addr: {Balance: balance}}}
|
||||
return g.MustCommit(db)
|
||||
}
|
||||
|
||||
// WriteTestNetGenesisBlock assembles the test network genesis block and
|
||||
// writes it - along with all associated state - into a chain database.
|
||||
func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(DefaultTestnetGenesisBlock()))
|
||||
// DefaultGenesisBlock returns the Ethereum main net genesis block.
|
||||
func DefaultGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
Config: params.MainnetChainConfig,
|
||||
Nonce: 66,
|
||||
ExtraData: hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"),
|
||||
GasLimit: 5000,
|
||||
Difficulty: big.NewInt(17179869184),
|
||||
Alloc: decodePrealloc(mainnetAllocData),
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultGenesisBlock assembles a JSON string representing the default Ethereum
|
||||
// genesis block.
|
||||
func DefaultGenesisBlock() string {
|
||||
reader, err := gzip.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultGenesisBlock)))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to access default genesis: %v", err))
|
||||
// DefaultTestnetGenesisBlock returns the Ropsten network genesis block.
|
||||
func DefaultTestnetGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
Config: params.TestnetChainConfig,
|
||||
Nonce: 66,
|
||||
ExtraData: hexutil.MustDecode("0x3535353535353535353535353535353535353535353535353535353535353535"),
|
||||
GasLimit: 16777216,
|
||||
Difficulty: big.NewInt(1048576),
|
||||
Alloc: decodePrealloc(testnetAllocData),
|
||||
}
|
||||
blob, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to load default genesis: %v", err))
|
||||
}
|
||||
return string(blob)
|
||||
}
|
||||
|
||||
// DefaultTestnetGenesisBlock assembles a JSON string representing the default Ethereum
|
||||
// test network genesis block.
|
||||
func DefaultTestnetGenesisBlock() string {
|
||||
reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultTestnetGenesisBlock)))
|
||||
blob, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to load default genesis: %v", err))
|
||||
// DevGenesisBlock returns the 'geth --dev' genesis block.
|
||||
func DevGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
Config: params.AllProtocolChanges,
|
||||
Nonce: 42,
|
||||
GasLimit: 4712388,
|
||||
Difficulty: big.NewInt(131072),
|
||||
Alloc: decodePrealloc(devAllocData),
|
||||
}
|
||||
return string(blob)
|
||||
}
|
||||
|
||||
// DevGenesisBlock assembles a JSON string representing a local dev genesis block.
|
||||
func DevGenesisBlock() string {
|
||||
reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultDevnetGenesisBlock)))
|
||||
blob, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to load dev genesis: %v", err))
|
||||
func decodePrealloc(data string) GenesisAlloc {
|
||||
var p []struct{ Addr, Balance *big.Int }
|
||||
if err := rlp.NewStream(strings.NewReader(data), 0).Decode(&p); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(blob)
|
||||
ga := make(GenesisAlloc, len(p))
|
||||
for _, account := range p {
|
||||
ga[common.BigToAddress(account.Addr)] = GenesisAccount{Balance: account.Balance}
|
||||
}
|
||||
return ga
|
||||
}
|
||||
|
|
|
|||
25
core/genesis_alloc.go
Normal file
25
core/genesis_alloc.go
Normal file
File diff suppressed because one or more lines are too long
161
core/genesis_test.go
Normal file
161
core/genesis_test.go
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2017 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 (
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"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/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func TestDefaultGenesisBlock(t *testing.T) {
|
||||
block, _ := DefaultGenesisBlock().ToBlock()
|
||||
if block.Hash() != params.MainNetGenesisHash {
|
||||
t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainNetGenesisHash)
|
||||
}
|
||||
block, _ = DefaultTestnetGenesisBlock().ToBlock()
|
||||
if block.Hash() != params.TestNetGenesisHash {
|
||||
t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestNetGenesisHash)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupGenesis(t *testing.T) {
|
||||
var (
|
||||
customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
|
||||
customg = Genesis{
|
||||
Config: ¶ms.ChainConfig{HomesteadBlock: big.NewInt(3)},
|
||||
Alloc: GenesisAlloc{
|
||||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
||||
},
|
||||
}
|
||||
oldcustomg = customg
|
||||
)
|
||||
oldcustomg.Config = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(2)}
|
||||
tests := []struct {
|
||||
name string
|
||||
fn func(ethdb.Database) (*params.ChainConfig, common.Hash, error)
|
||||
wantConfig *params.ChainConfig
|
||||
wantHash common.Hash
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "genesis without ChainConfig",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
return SetupGenesisBlock(db, new(Genesis))
|
||||
},
|
||||
wantErr: errGenesisNoConfig,
|
||||
wantConfig: params.AllProtocolChanges,
|
||||
},
|
||||
{
|
||||
name: "no block in DB, genesis == nil",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
return SetupGenesisBlock(db, nil)
|
||||
},
|
||||
wantHash: params.MainNetGenesisHash,
|
||||
wantConfig: params.MainnetChainConfig,
|
||||
},
|
||||
{
|
||||
name: "mainnet block in DB, genesis == nil",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
DefaultGenesisBlock().MustCommit(db)
|
||||
return SetupGenesisBlock(db, nil)
|
||||
},
|
||||
wantHash: params.MainNetGenesisHash,
|
||||
wantConfig: params.MainnetChainConfig,
|
||||
},
|
||||
{
|
||||
name: "custom block in DB, genesis == nil",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
customg.MustCommit(db)
|
||||
return SetupGenesisBlock(db, nil)
|
||||
},
|
||||
wantHash: customghash,
|
||||
wantConfig: customg.Config,
|
||||
},
|
||||
{
|
||||
name: "custom block in DB, genesis == testnet",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
customg.MustCommit(db)
|
||||
return SetupGenesisBlock(db, DefaultTestnetGenesisBlock())
|
||||
},
|
||||
wantErr: &GenesisMismatchError{Stored: customghash, New: params.TestNetGenesisHash},
|
||||
wantHash: params.TestNetGenesisHash,
|
||||
wantConfig: params.TestnetChainConfig,
|
||||
},
|
||||
{
|
||||
name: "compatible config in DB",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
oldcustomg.MustCommit(db)
|
||||
return SetupGenesisBlock(db, &customg)
|
||||
},
|
||||
wantHash: customghash,
|
||||
wantConfig: customg.Config,
|
||||
},
|
||||
{
|
||||
name: "incompatible config in DB",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
// Commit the 'old' genesis block with Homestead transition at #2.
|
||||
// Advance to block #4, past the homestead transition block of customg.
|
||||
genesis := oldcustomg.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, oldcustomg.Config, ethash.NewFullFaker(), new(event.TypeMux), vm.Config{})
|
||||
bc.SetValidator(bproc{})
|
||||
bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0))
|
||||
bc.CurrentBlock()
|
||||
// This should return a compatibility error.
|
||||
return SetupGenesisBlock(db, &customg)
|
||||
},
|
||||
wantHash: customghash,
|
||||
wantConfig: customg.Config,
|
||||
wantErr: ¶ms.ConfigCompatError{
|
||||
What: "Homestead fork block",
|
||||
StoredConfig: big.NewInt(2),
|
||||
NewConfig: big.NewInt(3),
|
||||
RewindTo: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
config, hash, err := test.fn(db)
|
||||
// Check the return values.
|
||||
if !reflect.DeepEqual(err, test.wantErr) {
|
||||
spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true}
|
||||
t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(err), spew.NewFormatter(test.wantErr))
|
||||
}
|
||||
if !reflect.DeepEqual(config, test.wantConfig) {
|
||||
t.Errorf("%s:\nreturned %v\nwant %v", test.name, config, test.wantConfig)
|
||||
}
|
||||
if hash != test.wantHash {
|
||||
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)
|
||||
if stored.Hash() != test.wantHash {
|
||||
t.Errorf("%s: block in DB has hash %s, want %s", test.name, stored.Hash(), test.wantHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,21 +18,19 @@ package core
|
|||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
mrand "math/rand"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
|
|
@ -62,18 +60,15 @@ type HeaderChain struct {
|
|||
|
||||
procInterrupt func() bool
|
||||
|
||||
rand *mrand.Rand
|
||||
getValidator getHeaderValidatorFn
|
||||
rand *mrand.Rand
|
||||
engine consensus.Engine
|
||||
}
|
||||
|
||||
// getHeaderValidatorFn returns a HeaderValidator interface
|
||||
type getHeaderValidatorFn func() HeaderValidator
|
||||
|
||||
// NewHeaderChain creates a new HeaderChain structure.
|
||||
// getValidator should return the parent's validator
|
||||
// procInterrupt points to the parent's interrupt semaphore
|
||||
// wg points to the parent's shutdown wait group
|
||||
func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) {
|
||||
func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) {
|
||||
headerCache, _ := lru.New(headerCacheLimit)
|
||||
tdCache, _ := lru.New(tdCacheLimit)
|
||||
numberCache, _ := lru.New(numberCacheLimit)
|
||||
|
|
@ -92,17 +87,12 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValid
|
|||
numberCache: numberCache,
|
||||
procInterrupt: procInterrupt,
|
||||
rand: mrand.New(mrand.NewSource(seed.Int64())),
|
||||
getValidator: getValidator,
|
||||
engine: engine,
|
||||
}
|
||||
|
||||
hc.genesisHeader = hc.GetHeaderByNumber(0)
|
||||
if hc.genesisHeader == nil {
|
||||
genesisBlock, err := WriteDefaultGenesisBlock(chainDb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Warn("Wrote default Ethereum genesis block")
|
||||
hc.genesisHeader = genesisBlock.Header()
|
||||
return nil, ErrNoGenesis
|
||||
}
|
||||
|
||||
hc.currentHeader = hc.genesisHeader
|
||||
|
|
@ -219,7 +209,8 @@ type WhCallback func(*types.Header) error
|
|||
// should be done or not. The reason behind the optional check is because some
|
||||
// of the header retrieval mechanisms already need to verfy nonces, as well as
|
||||
// because nonces can be verified sparsely, not needing to check each.
|
||||
func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, writeHeader WhCallback) (int, error) {
|
||||
|
||||
func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
|
||||
// Do a sanity check that the provided chain is actually ordered and linked
|
||||
for i := 1; i < len(chain); i++ {
|
||||
if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() {
|
||||
|
|
@ -231,95 +222,53 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, w
|
|||
chain[i-1].Hash().Bytes()[:4], i, chain[i].Number, chain[i].Hash().Bytes()[:4], chain[i].ParentHash[:4])
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the list of seal verification requests, and start the parallel verifier
|
||||
seals := make([]bool, len(chain))
|
||||
for i := 0; i < len(seals)/checkFreq; i++ {
|
||||
index := i*checkFreq + hc.rand.Intn(checkFreq)
|
||||
if index >= len(seals) {
|
||||
index = len(seals) - 1
|
||||
}
|
||||
seals[index] = true
|
||||
}
|
||||
seals[len(seals)-1] = true // Last should always be verified to avoid junk
|
||||
|
||||
abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
|
||||
defer close(abort)
|
||||
|
||||
// Iterate over the headers and ensure they all check out
|
||||
for i, header := range chain {
|
||||
// If the chain is terminating, stop processing blocks
|
||||
if hc.procInterrupt() {
|
||||
log.Debug("Premature abort during headers verification")
|
||||
return 0, errors.New("aborted")
|
||||
}
|
||||
// If the header is a banned one, straight out abort
|
||||
if BadHashes[header.Hash()] {
|
||||
return i, BadHashError(header.Hash())
|
||||
}
|
||||
// Otherwise wait for headers checks and ensure they pass
|
||||
if err := <-results; err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, writeHeader WhCallback, start time.Time) (int, error) {
|
||||
// Collect some import statistics to report on
|
||||
stats := struct{ processed, ignored int }{}
|
||||
start := time.Now()
|
||||
|
||||
// Generate the list of headers that should be POW verified
|
||||
verify := make([]bool, len(chain))
|
||||
for i := 0; i < len(verify)/checkFreq; i++ {
|
||||
index := i*checkFreq + hc.rand.Intn(checkFreq)
|
||||
if index >= len(verify) {
|
||||
index = len(verify) - 1
|
||||
}
|
||||
verify[index] = true
|
||||
}
|
||||
verify[len(verify)-1] = true // Last should always be verified to avoid junk
|
||||
|
||||
// Create the header verification task queue and worker functions
|
||||
tasks := make(chan int, len(chain))
|
||||
for i := 0; i < len(chain); i++ {
|
||||
tasks <- i
|
||||
}
|
||||
close(tasks)
|
||||
|
||||
errs, failed := make([]error, len(tasks)), int32(0)
|
||||
process := func(worker int) {
|
||||
for index := range tasks {
|
||||
header, hash := chain[index], chain[index].Hash()
|
||||
|
||||
// Short circuit insertion if shutting down or processing failed
|
||||
if hc.procInterrupt() {
|
||||
return
|
||||
}
|
||||
if atomic.LoadInt32(&failed) > 0 {
|
||||
return
|
||||
}
|
||||
// Short circuit if the header is bad or already known
|
||||
if BadHashes[hash] {
|
||||
errs[index] = BadHashError(hash)
|
||||
atomic.AddInt32(&failed, 1)
|
||||
return
|
||||
}
|
||||
if hc.HasHeader(hash) {
|
||||
continue
|
||||
}
|
||||
// Verify that the header honors the chain parameters
|
||||
checkPow := verify[index]
|
||||
|
||||
var err error
|
||||
if index == 0 {
|
||||
err = hc.getValidator().ValidateHeader(header, hc.GetHeader(header.ParentHash, header.Number.Uint64()-1), checkPow)
|
||||
} else {
|
||||
err = hc.getValidator().ValidateHeader(header, chain[index-1], checkPow)
|
||||
}
|
||||
if err != nil {
|
||||
errs[index] = err
|
||||
atomic.AddInt32(&failed, 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// Start as many worker threads as goroutines allowed
|
||||
pending := new(sync.WaitGroup)
|
||||
for i := 0; i < runtime.GOMAXPROCS(0); i++ {
|
||||
pending.Add(1)
|
||||
go func(id int) {
|
||||
defer pending.Done()
|
||||
process(id)
|
||||
}(i)
|
||||
}
|
||||
pending.Wait()
|
||||
|
||||
// If anything failed, report
|
||||
if failed > 0 {
|
||||
for i, err := range errs {
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
}
|
||||
// All headers passed verification, import them into the database
|
||||
for i, header := range chain {
|
||||
// Short circuit insertion if shutting down
|
||||
if hc.procInterrupt() {
|
||||
log.Debug("Premature abort during headers processing")
|
||||
break
|
||||
log.Debug("Premature abort during headers import")
|
||||
return i, errors.New("aborted")
|
||||
}
|
||||
hash := header.Hash()
|
||||
|
||||
// If the header's already known, skip it, otherwise store
|
||||
if hc.HasHeader(hash) {
|
||||
if hc.GetHeader(header.Hash(), header.Number.Uint64()) != nil {
|
||||
stats.ignored++
|
||||
continue
|
||||
}
|
||||
|
|
@ -490,35 +439,11 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) {
|
|||
hc.genesisHeader = head
|
||||
}
|
||||
|
||||
// headerValidator is responsible for validating block headers
|
||||
//
|
||||
// headerValidator implements HeaderValidator.
|
||||
type headerValidator struct {
|
||||
config *params.ChainConfig
|
||||
hc *HeaderChain // Canonical header chain
|
||||
Pow pow.PoW // Proof of work used for validating
|
||||
}
|
||||
// Config retrieves the header chain's chain configuration.
|
||||
func (hc *HeaderChain) Config() *params.ChainConfig { return hc.config }
|
||||
|
||||
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||
func NewHeaderValidator(config *params.ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator {
|
||||
return &headerValidator{
|
||||
config: config,
|
||||
Pow: pow,
|
||||
hc: chain,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateHeader validates the given header and, depending on the pow arg,
|
||||
// checks the proof of work of the given header. Returns an error if the
|
||||
// validation failed.
|
||||
func (v *headerValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error {
|
||||
// Short circuit if the parent is missing.
|
||||
if parent == nil {
|
||||
return ParentError(header.ParentHash)
|
||||
}
|
||||
// Short circuit if the header's already known or its parent missing
|
||||
if v.hc.HasHeader(header.Hash()) {
|
||||
return nil
|
||||
}
|
||||
return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false)
|
||||
// GetBlock implements consensus.ChainReader, and returns nil for every input as
|
||||
// a header chain does not have blocks available for retrieval.
|
||||
func (hc *HeaderChain) GetBlock(hash common.Hash, number uint64) *types.Block {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
85
core/mkalloc.go
Normal file
85
core/mkalloc.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2017 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
|
||||
|
||||
/*
|
||||
|
||||
The mkalloc tool creates the genesis allocation constants in genesis_alloc.go
|
||||
It outputs a const declaration that contains an RLP-encoded list of (address, balance) tuples.
|
||||
|
||||
go run mkalloc.go genesis.json
|
||||
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
type allocItem struct{ Addr, Balance *big.Int }
|
||||
|
||||
type allocList []allocItem
|
||||
|
||||
func (a allocList) Len() int { return len(a) }
|
||||
func (a allocList) Less(i, j int) bool { return a[i].Addr.Cmp(a[j].Addr) < 0 }
|
||||
func (a allocList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
func makelist(g *core.Genesis) allocList {
|
||||
a := make(allocList, 0, len(g.Alloc))
|
||||
for addr, account := range g.Alloc {
|
||||
if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 {
|
||||
panic(fmt.Sprintf("can't encode account %x", addr))
|
||||
}
|
||||
a = append(a, allocItem{addr.Big(), account.Balance})
|
||||
}
|
||||
sort.Sort(a)
|
||||
return a
|
||||
}
|
||||
|
||||
func makealloc(g *core.Genesis) string {
|
||||
a := makelist(g)
|
||||
data, err := rlp.EncodeToBytes(a)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return strconv.QuoteToASCII(string(data))
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Fprintln(os.Stderr, "Usage: mkalloc genesis.json")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
g := new(core.Genesis)
|
||||
file, err := os.Open(os.Args[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := json.NewDecoder(file).Decode(g); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("const allocData =", makealloc(g))
|
||||
}
|
||||
|
|
@ -73,6 +73,7 @@ type (
|
|||
touchChange struct {
|
||||
account *common.Address
|
||||
prev bool
|
||||
prevDirty bool
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -97,8 +98,10 @@ var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
|
|||
|
||||
func (ch touchChange) undo(s *StateDB) {
|
||||
if !ch.prev && *ch.account != ripemd {
|
||||
delete(s.stateObjects, *ch.account)
|
||||
delete(s.stateObjectsDirty, *ch.account)
|
||||
s.getStateObject(*ch.account).touched = ch.prev
|
||||
if !ch.prevDirty {
|
||||
delete(s.stateObjectsDirty, *ch.account)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -137,8 +137,9 @@ func (self *stateObject) markSuicided() {
|
|||
|
||||
func (c *stateObject) touch() {
|
||||
c.db.journal = append(c.db.journal, touchChange{
|
||||
account: &c.address,
|
||||
prev: c.touched,
|
||||
account: &c.address,
|
||||
prev: c.touched,
|
||||
prevDirty: c.onDirty == nil,
|
||||
})
|
||||
if c.onDirty != nil {
|
||||
c.onDirty(c.Address())
|
||||
|
|
|
|||
|
|
@ -611,7 +611,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
|
|||
// the root hash stored in a block.
|
||||
func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) {
|
||||
batch = s.db.NewBatch()
|
||||
root, _ = s.commit(batch, deleteEmptyObjects)
|
||||
root, _ = s.CommitTo(batch, deleteEmptyObjects)
|
||||
|
||||
log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
|
||||
return root, batch
|
||||
|
|
@ -623,7 +623,8 @@ func (s *StateDB) clearJournalAndRefund() {
|
|||
s.refund = new(big.Int)
|
||||
}
|
||||
|
||||
func (s *StateDB) commit(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) {
|
||||
// CommitTo writes the state to the given database.
|
||||
func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) {
|
||||
defer s.clearJournalAndRefund()
|
||||
|
||||
// Commit objects to the trie.
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ func TestIntermediateLeaks(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSnapshotRandom(t *testing.T) {
|
||||
t.Skip("@fjl fix me please")
|
||||
config := &quick.Config{MaxCount: 1000}
|
||||
err := quick.Check((*snapshotTest).run, config)
|
||||
if cerr, ok := err.(*quick.CheckError); ok {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ package core
|
|||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
|
@ -26,25 +28,22 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
big8 = big.NewInt(8)
|
||||
big32 = big.NewInt(32)
|
||||
)
|
||||
|
||||
// StateProcessor is a basic Processor, which takes care of transitioning
|
||||
// state from one point to another.
|
||||
//
|
||||
// StateProcessor implements Processor.
|
||||
type StateProcessor struct {
|
||||
config *params.ChainConfig
|
||||
bc *BlockChain
|
||||
config *params.ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
engine consensus.Engine // Consensus engine used for block rewards
|
||||
}
|
||||
|
||||
// NewStateProcessor initialises a new StateProcessor.
|
||||
func NewStateProcessor(config *params.ChainConfig, bc *BlockChain) *StateProcessor {
|
||||
func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor {
|
||||
return &StateProcessor{
|
||||
config: config,
|
||||
bc: bc,
|
||||
engine: engine,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,18 +58,16 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||
var (
|
||||
receipts types.Receipts
|
||||
totalUsedGas = big.NewInt(0)
|
||||
err error
|
||||
header = block.Header()
|
||||
allLogs []*types.Log
|
||||
gp = new(GasPool).AddGas(block.GasLimit())
|
||||
)
|
||||
// Mutate the the block and state according to any hard-fork specs
|
||||
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||
ApplyDAOHardFork(statedb)
|
||||
misc.ApplyDAOHardFork(statedb)
|
||||
}
|
||||
// Iterate over and process the individual transactions
|
||||
for i, tx := range block.Transactions() {
|
||||
//fmt.Println("tx:", i)
|
||||
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
||||
receipt, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
||||
if err != nil {
|
||||
|
|
@ -79,9 +76,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||
receipts = append(receipts, receipt)
|
||||
allLogs = append(allLogs, receipt.Logs...)
|
||||
}
|
||||
AccumulateRewards(statedb, header, block.Uncles())
|
||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts)
|
||||
|
||||
return receipts, allLogs, totalUsedGas, err
|
||||
return receipts, allLogs, totalUsedGas, nil
|
||||
}
|
||||
|
||||
// ApplyTransaction attempts to apply a transaction to the given state database
|
||||
|
|
@ -122,23 +120,3 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s
|
|||
|
||||
return receipt, gas, err
|
||||
}
|
||||
|
||||
// AccumulateRewards credits the coinbase of the given block with the
|
||||
// mining reward. The total reward consists of the static block reward
|
||||
// and rewards for included uncles. The coinbase of each uncle block is
|
||||
// also rewarded.
|
||||
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) {
|
||||
reward := new(big.Int).Set(BlockReward)
|
||||
r := new(big.Int)
|
||||
for _, uncle := range uncles {
|
||||
r.Add(uncle.Number, big8)
|
||||
r.Sub(r, header.Number)
|
||||
r.Mul(r, BlockReward)
|
||||
r.Div(r, big8)
|
||||
statedb.AddBalance(uncle.Coinbase, r)
|
||||
|
||||
r.Div(BlockReward, big32)
|
||||
reward.Add(reward, r)
|
||||
}
|
||||
statedb.AddBalance(header.Coinbase, reward)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
|||
statedb, _ := state.New(common.Hash{}, db)
|
||||
|
||||
key, _ := crypto.GenerateKey()
|
||||
newPool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
newPool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
newPool.resetState()
|
||||
|
||||
return newPool, key
|
||||
|
|
@ -91,7 +91,7 @@ func TestStateChangeDuringPoolReset(t *testing.T) {
|
|||
|
||||
gasLimitFunc := func() *big.Int { return big.NewInt(1000000000) }
|
||||
|
||||
txpool := NewTxPool(testChainConfig(), mux, stateFunc, gasLimitFunc)
|
||||
txpool := NewTxPool(params.TestChainConfig, mux, stateFunc, gasLimitFunc)
|
||||
txpool.resetState()
|
||||
|
||||
nonce := txpool.State().GetNonce(address)
|
||||
|
|
@ -564,7 +564,7 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
|
|||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
|
||||
pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
pool.resetState()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
|
|
@ -713,7 +713,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
|
|||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
|
||||
pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
pool.resetState()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
|
|
@ -759,7 +759,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
|
|||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
|
||||
pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
pool.resetState()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
|
|
|
|||
|
|
@ -24,31 +24,17 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
// Validator is an interface which defines the standard for block validation.
|
||||
// Validator is an interface which defines the standard for block validation. It
|
||||
// is only responsible for validating block contents, as the header validation is
|
||||
// done by the specific consensus engines.
|
||||
//
|
||||
// The validator is responsible for validating incoming block or, if desired,
|
||||
// validates headers for fast validation.
|
||||
//
|
||||
// ValidateBlock validates the given block and should return an error if it
|
||||
// failed to do so and should be used for "full" validation.
|
||||
//
|
||||
// ValidateHeader validates the given header and parent and returns an error
|
||||
// if it failed to do so.
|
||||
//
|
||||
// ValidateState validates the given statedb and optionally the receipts and
|
||||
// gas used. The implementer should decide what to do with the given input.
|
||||
type Validator interface {
|
||||
HeaderValidator
|
||||
ValidateBlock(block *types.Block) error
|
||||
ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error
|
||||
}
|
||||
// ValidateBody validates the given block's content.
|
||||
ValidateBody(block *types.Block) error
|
||||
|
||||
// HeaderValidator is an interface for validating headers only
|
||||
//
|
||||
// ValidateHeader validates the given header and parent and returns an error
|
||||
// if it failed to do so.
|
||||
type HeaderValidator interface {
|
||||
ValidateHeader(header, parent *types.Header, checkPow bool) error
|
||||
// ValidateState validates the given statedb and optionally the receipts and
|
||||
// gas used.
|
||||
ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error
|
||||
}
|
||||
|
||||
// Processor is an interface for processing blocks using a given initial state.
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ package types
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
|
@ -39,12 +37,6 @@ var (
|
|||
EmptyUncleHash = CalcUncleHash(nil)
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingHeaderMixDigest = errors.New("missing mixHash in JSON block header")
|
||||
errMissingHeaderFields = errors.New("missing required JSON block header fields")
|
||||
errBadNonceSize = errors.New("invalid block nonce size, want 8 bytes")
|
||||
)
|
||||
|
||||
// A BlockNonce is a 64-bit hash which proves (combined with the
|
||||
// mix-hash) that a sufficient amount of computation has been carried
|
||||
// out on a block.
|
||||
|
|
@ -72,41 +64,35 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
|
|||
return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
|
||||
}
|
||||
|
||||
//go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
|
||||
|
||||
// Header represents a block header in the Ethereum blockchain.
|
||||
type Header struct {
|
||||
ParentHash common.Hash // Hash to the previous block
|
||||
UncleHash common.Hash // Uncles of this block
|
||||
Coinbase common.Address // The coin base address
|
||||
Root common.Hash // Block Trie state
|
||||
TxHash common.Hash // Tx sha
|
||||
ReceiptHash common.Hash // Receipt sha
|
||||
Bloom Bloom // Bloom
|
||||
Difficulty *big.Int // Difficulty for the current block
|
||||
Number *big.Int // The block number
|
||||
GasLimit *big.Int // Gas limit
|
||||
GasUsed *big.Int // Gas used
|
||||
Time *big.Int // Creation time
|
||||
Extra []byte // Extra data
|
||||
MixDigest common.Hash // for quick difficulty verification
|
||||
Nonce BlockNonce
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
UncleHash common.Hash `json:"sha3Uncles"`
|
||||
Coinbase common.Address `json:"miner"`
|
||||
Root common.Hash `json:"stateRoot"`
|
||||
TxHash common.Hash `json:"transactionsRoot"`
|
||||
ReceiptHash common.Hash `json:"receiptsRoot"`
|
||||
Bloom Bloom `json:"logsBloom"`
|
||||
Difficulty *big.Int `json:"difficulty"`
|
||||
Number *big.Int `json:"number"`
|
||||
GasLimit *big.Int `json:"gasLimit"`
|
||||
GasUsed *big.Int `json:"gasUsed"`
|
||||
Time *big.Int `json:"timestamp"`
|
||||
Extra []byte `json:"extraData"`
|
||||
MixDigest common.Hash `json:"mixHash"`
|
||||
Nonce BlockNonce `json:"nonce"`
|
||||
}
|
||||
|
||||
type jsonHeader struct {
|
||||
ParentHash *common.Hash `json:"parentHash"`
|
||||
UncleHash *common.Hash `json:"sha3Uncles"`
|
||||
Coinbase *common.Address `json:"miner"`
|
||||
Root *common.Hash `json:"stateRoot"`
|
||||
TxHash *common.Hash `json:"transactionsRoot"`
|
||||
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
||||
Bloom *Bloom `json:"logsBloom"`
|
||||
Difficulty *hexutil.Big `json:"difficulty"`
|
||||
Number *hexutil.Big `json:"number"`
|
||||
GasLimit *hexutil.Big `json:"gasLimit"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
Time *hexutil.Big `json:"timestamp"`
|
||||
Extra *hexutil.Bytes `json:"extraData"`
|
||||
MixDigest *common.Hash `json:"mixHash"`
|
||||
Nonce *BlockNonce `json:"nonce"`
|
||||
// field type overrides for gencodec
|
||||
type headerMarshaling struct {
|
||||
Difficulty *hexutil.Big
|
||||
Number *hexutil.Big
|
||||
GasLimit *hexutil.Big
|
||||
GasUsed *hexutil.Big
|
||||
Time *hexutil.Big
|
||||
Extra hexutil.Bytes
|
||||
}
|
||||
|
||||
// Hash returns the block hash of the header, which is simply the keccak256 hash of its
|
||||
|
|
@ -134,65 +120,6 @@ func (h *Header) HashNoNonce() common.Hash {
|
|||
})
|
||||
}
|
||||
|
||||
// MarshalJSON encodes headers into the web3 RPC response block format.
|
||||
func (h *Header) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&jsonHeader{
|
||||
ParentHash: &h.ParentHash,
|
||||
UncleHash: &h.UncleHash,
|
||||
Coinbase: &h.Coinbase,
|
||||
Root: &h.Root,
|
||||
TxHash: &h.TxHash,
|
||||
ReceiptHash: &h.ReceiptHash,
|
||||
Bloom: &h.Bloom,
|
||||
Difficulty: (*hexutil.Big)(h.Difficulty),
|
||||
Number: (*hexutil.Big)(h.Number),
|
||||
GasLimit: (*hexutil.Big)(h.GasLimit),
|
||||
GasUsed: (*hexutil.Big)(h.GasUsed),
|
||||
Time: (*hexutil.Big)(h.Time),
|
||||
Extra: (*hexutil.Bytes)(&h.Extra),
|
||||
MixDigest: &h.MixDigest,
|
||||
Nonce: &h.Nonce,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes headers from the web3 RPC response block format.
|
||||
func (h *Header) UnmarshalJSON(input []byte) error {
|
||||
var dec jsonHeader
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
// Ensure that all fields are set. MixDigest is checked separately because
|
||||
// it is a recent addition to the spec (as of August 2016) and older RPC server
|
||||
// implementations might not provide it.
|
||||
if dec.MixDigest == nil {
|
||||
return errMissingHeaderMixDigest
|
||||
}
|
||||
if dec.ParentHash == nil || dec.UncleHash == nil || dec.Coinbase == nil ||
|
||||
dec.Root == nil || dec.TxHash == nil || dec.ReceiptHash == nil ||
|
||||
dec.Bloom == nil || dec.Difficulty == nil || dec.Number == nil ||
|
||||
dec.GasLimit == nil || dec.GasUsed == nil || dec.Time == nil ||
|
||||
dec.Extra == nil || dec.Nonce == nil {
|
||||
return errMissingHeaderFields
|
||||
}
|
||||
// Assign all values.
|
||||
h.ParentHash = *dec.ParentHash
|
||||
h.UncleHash = *dec.UncleHash
|
||||
h.Coinbase = *dec.Coinbase
|
||||
h.Root = *dec.Root
|
||||
h.TxHash = *dec.TxHash
|
||||
h.ReceiptHash = *dec.ReceiptHash
|
||||
h.Bloom = *dec.Bloom
|
||||
h.Difficulty = (*big.Int)(dec.Difficulty)
|
||||
h.Number = (*big.Int)(dec.Number)
|
||||
h.GasLimit = (*big.Int)(dec.GasLimit)
|
||||
h.GasUsed = (*big.Int)(dec.GasUsed)
|
||||
h.Time = (*big.Int)(dec.Time)
|
||||
h.Extra = *dec.Extra
|
||||
h.MixDigest = *dec.MixDigest
|
||||
h.Nonce = *dec.Nonce
|
||||
return nil
|
||||
}
|
||||
|
||||
func rlpHash(x interface{}) (h common.Hash) {
|
||||
hw := sha3.NewKeccak256()
|
||||
rlp.Encode(hw, x)
|
||||
|
|
@ -421,12 +348,11 @@ func CalcUncleHash(uncles []*Header) common.Hash {
|
|||
return rlpHash(uncles)
|
||||
}
|
||||
|
||||
// WithMiningResult returns a new block with the data from b
|
||||
// where nonce and mix digest are set to the provided values.
|
||||
func (b *Block) WithMiningResult(nonce BlockNonce, mixDigest common.Hash) *Block {
|
||||
cpy := *b.header
|
||||
cpy.Nonce = nonce
|
||||
cpy.MixDigest = mixDigest
|
||||
// WithSeal returns a new block with the data from b but the header replaced with
|
||||
// the sealed one.
|
||||
func (b *Block) WithSeal(header *Header) *Block {
|
||||
cpy := *header
|
||||
|
||||
return &Block{
|
||||
header: &cpy,
|
||||
transactions: b.transactions,
|
||||
|
|
|
|||
136
core/types/gen_header_json.go
Normal file
136
core/types/gen_header_json.go
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func (h Header) MarshalJSON() ([]byte, error) {
|
||||
type HeaderJSON struct {
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
UncleHash common.Hash `json:"sha3Uncles"`
|
||||
Coinbase common.Address `json:"miner"`
|
||||
Root common.Hash `json:"stateRoot"`
|
||||
TxHash common.Hash `json:"transactionsRoot"`
|
||||
ReceiptHash common.Hash `json:"receiptsRoot"`
|
||||
Bloom Bloom `json:"logsBloom"`
|
||||
Difficulty *hexutil.Big `json:"difficulty"`
|
||||
Number *hexutil.Big `json:"number"`
|
||||
GasLimit *hexutil.Big `json:"gasLimit"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
Time *hexutil.Big `json:"timestamp"`
|
||||
Extra hexutil.Bytes `json:"extraData"`
|
||||
MixDigest common.Hash `json:"mixHash"`
|
||||
Nonce BlockNonce `json:"nonce"`
|
||||
}
|
||||
var enc HeaderJSON
|
||||
enc.ParentHash = h.ParentHash
|
||||
enc.UncleHash = h.UncleHash
|
||||
enc.Coinbase = h.Coinbase
|
||||
enc.Root = h.Root
|
||||
enc.TxHash = h.TxHash
|
||||
enc.ReceiptHash = h.ReceiptHash
|
||||
enc.Bloom = h.Bloom
|
||||
enc.Difficulty = (*hexutil.Big)(h.Difficulty)
|
||||
enc.Number = (*hexutil.Big)(h.Number)
|
||||
enc.GasLimit = (*hexutil.Big)(h.GasLimit)
|
||||
enc.GasUsed = (*hexutil.Big)(h.GasUsed)
|
||||
enc.Time = (*hexutil.Big)(h.Time)
|
||||
enc.Extra = h.Extra
|
||||
enc.MixDigest = h.MixDigest
|
||||
enc.Nonce = h.Nonce
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (h *Header) UnmarshalJSON(input []byte) error {
|
||||
type HeaderJSON struct {
|
||||
ParentHash *common.Hash `json:"parentHash"`
|
||||
UncleHash *common.Hash `json:"sha3Uncles"`
|
||||
Coinbase *common.Address `json:"miner"`
|
||||
Root *common.Hash `json:"stateRoot"`
|
||||
TxHash *common.Hash `json:"transactionsRoot"`
|
||||
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
||||
Bloom *Bloom `json:"logsBloom"`
|
||||
Difficulty *hexutil.Big `json:"difficulty"`
|
||||
Number *hexutil.Big `json:"number"`
|
||||
GasLimit *hexutil.Big `json:"gasLimit"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
Time *hexutil.Big `json:"timestamp"`
|
||||
Extra hexutil.Bytes `json:"extraData"`
|
||||
MixDigest *common.Hash `json:"mixHash"`
|
||||
Nonce *BlockNonce `json:"nonce"`
|
||||
}
|
||||
var dec HeaderJSON
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
var x Header
|
||||
if dec.ParentHash == nil {
|
||||
return errors.New("missing required field 'parentHash' for Header")
|
||||
}
|
||||
x.ParentHash = *dec.ParentHash
|
||||
if dec.UncleHash == nil {
|
||||
return errors.New("missing required field 'sha3Uncles' for Header")
|
||||
}
|
||||
x.UncleHash = *dec.UncleHash
|
||||
if dec.Coinbase == nil {
|
||||
return errors.New("missing required field 'miner' for Header")
|
||||
}
|
||||
x.Coinbase = *dec.Coinbase
|
||||
if dec.Root == nil {
|
||||
return errors.New("missing required field 'stateRoot' for Header")
|
||||
}
|
||||
x.Root = *dec.Root
|
||||
if dec.TxHash == nil {
|
||||
return errors.New("missing required field 'transactionsRoot' for Header")
|
||||
}
|
||||
x.TxHash = *dec.TxHash
|
||||
if dec.ReceiptHash == nil {
|
||||
return errors.New("missing required field 'receiptsRoot' for Header")
|
||||
}
|
||||
x.ReceiptHash = *dec.ReceiptHash
|
||||
if dec.Bloom == nil {
|
||||
return errors.New("missing required field 'logsBloom' for Header")
|
||||
}
|
||||
x.Bloom = *dec.Bloom
|
||||
if dec.Difficulty == nil {
|
||||
return errors.New("missing required field 'difficulty' for Header")
|
||||
}
|
||||
x.Difficulty = (*big.Int)(dec.Difficulty)
|
||||
if dec.Number == nil {
|
||||
return errors.New("missing required field 'number' for Header")
|
||||
}
|
||||
x.Number = (*big.Int)(dec.Number)
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'gasLimit' for Header")
|
||||
}
|
||||
x.GasLimit = (*big.Int)(dec.GasLimit)
|
||||
if dec.GasUsed == nil {
|
||||
return errors.New("missing required field 'gasUsed' for Header")
|
||||
}
|
||||
x.GasUsed = (*big.Int)(dec.GasUsed)
|
||||
if dec.Time == nil {
|
||||
return errors.New("missing required field 'timestamp' for Header")
|
||||
}
|
||||
x.Time = (*big.Int)(dec.Time)
|
||||
if dec.Extra == nil {
|
||||
return errors.New("missing required field 'extraData' for Header")
|
||||
}
|
||||
x.Extra = dec.Extra
|
||||
if dec.MixDigest == nil {
|
||||
return errors.New("missing required field 'mixHash' for Header")
|
||||
}
|
||||
x.MixDigest = *dec.MixDigest
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' for Header")
|
||||
}
|
||||
x.Nonce = *dec.Nonce
|
||||
*h = x
|
||||
return nil
|
||||
}
|
||||
90
core/types/gen_log_json.go
Normal file
90
core/types/gen_log_json.go
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func (l Log) MarshalJSON() ([]byte, error) {
|
||||
type LogJSON struct {
|
||||
Address common.Address `json:"address"`
|
||||
Topics []common.Hash `json:"topics"`
|
||||
Data hexutil.Bytes `json:"data"`
|
||||
BlockNumber hexutil.Uint64 `json:"blockNumber" optional:"yes"`
|
||||
TxHash common.Hash `json:"transactionHash"`
|
||||
TxIndex hexutil.Uint `json:"transactionIndex"`
|
||||
BlockHash common.Hash `json:"blockHash" optional:"yes"`
|
||||
Index hexutil.Uint `json:"logIndex"`
|
||||
Removed bool `json:"removed" optional:"yes"`
|
||||
}
|
||||
var enc LogJSON
|
||||
enc.Address = l.Address
|
||||
enc.Topics = l.Topics
|
||||
enc.Data = l.Data
|
||||
enc.BlockNumber = hexutil.Uint64(l.BlockNumber)
|
||||
enc.TxHash = l.TxHash
|
||||
enc.TxIndex = hexutil.Uint(l.TxIndex)
|
||||
enc.BlockHash = l.BlockHash
|
||||
enc.Index = hexutil.Uint(l.Index)
|
||||
enc.Removed = l.Removed
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (l *Log) UnmarshalJSON(input []byte) error {
|
||||
type LogJSON struct {
|
||||
Address *common.Address `json:"address"`
|
||||
Topics []common.Hash `json:"topics"`
|
||||
Data hexutil.Bytes `json:"data"`
|
||||
BlockNumber *hexutil.Uint64 `json:"blockNumber" optional:"yes"`
|
||||
TxHash *common.Hash `json:"transactionHash"`
|
||||
TxIndex *hexutil.Uint `json:"transactionIndex"`
|
||||
BlockHash *common.Hash `json:"blockHash" optional:"yes"`
|
||||
Index *hexutil.Uint `json:"logIndex"`
|
||||
Removed *bool `json:"removed" optional:"yes"`
|
||||
}
|
||||
var dec LogJSON
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
var x Log
|
||||
if dec.Address == nil {
|
||||
return errors.New("missing required field 'address' for Log")
|
||||
}
|
||||
x.Address = *dec.Address
|
||||
if dec.Topics == nil {
|
||||
return errors.New("missing required field 'topics' for Log")
|
||||
}
|
||||
x.Topics = dec.Topics
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'data' for Log")
|
||||
}
|
||||
x.Data = dec.Data
|
||||
if dec.BlockNumber != nil {
|
||||
x.BlockNumber = uint64(*dec.BlockNumber)
|
||||
}
|
||||
if dec.TxHash == nil {
|
||||
return errors.New("missing required field 'transactionHash' for Log")
|
||||
}
|
||||
x.TxHash = *dec.TxHash
|
||||
if dec.TxIndex == nil {
|
||||
return errors.New("missing required field 'transactionIndex' for Log")
|
||||
}
|
||||
x.TxIndex = uint(*dec.TxIndex)
|
||||
if dec.BlockHash != nil {
|
||||
x.BlockHash = *dec.BlockHash
|
||||
}
|
||||
if dec.Index == nil {
|
||||
return errors.New("missing required field 'logIndex' for Log")
|
||||
}
|
||||
x.Index = uint(*dec.Index)
|
||||
if dec.Removed != nil {
|
||||
x.Removed = *dec.Removed
|
||||
}
|
||||
*l = x
|
||||
return nil
|
||||
}
|
||||
79
core/types/gen_receipt_json.go
Normal file
79
core/types/gen_receipt_json.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
type ReceiptJSON struct {
|
||||
PostState hexutil.Bytes `json:"root"`
|
||||
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"`
|
||||
Bloom Bloom `json:"logsBloom"`
|
||||
Logs []*Log `json:"logs"`
|
||||
TxHash common.Hash `json:"transactionHash"`
|
||||
ContractAddress common.Address `json:"contractAddress" optional:"true"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
}
|
||||
var enc ReceiptJSON
|
||||
enc.PostState = r.PostState
|
||||
enc.CumulativeGasUsed = (*hexutil.Big)(r.CumulativeGasUsed)
|
||||
enc.Bloom = r.Bloom
|
||||
enc.Logs = r.Logs
|
||||
enc.TxHash = r.TxHash
|
||||
enc.ContractAddress = r.ContractAddress
|
||||
enc.GasUsed = (*hexutil.Big)(r.GasUsed)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
type ReceiptJSON struct {
|
||||
PostState hexutil.Bytes `json:"root"`
|
||||
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"`
|
||||
Bloom *Bloom `json:"logsBloom"`
|
||||
Logs []*Log `json:"logs"`
|
||||
TxHash *common.Hash `json:"transactionHash"`
|
||||
ContractAddress *common.Address `json:"contractAddress" optional:"true"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
}
|
||||
var dec ReceiptJSON
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
var x Receipt
|
||||
if dec.PostState == nil {
|
||||
return errors.New("missing required field 'root' for Receipt")
|
||||
}
|
||||
x.PostState = dec.PostState
|
||||
if dec.CumulativeGasUsed == nil {
|
||||
return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
|
||||
}
|
||||
x.CumulativeGasUsed = (*big.Int)(dec.CumulativeGasUsed)
|
||||
if dec.Bloom == nil {
|
||||
return errors.New("missing required field 'logsBloom' for Receipt")
|
||||
}
|
||||
x.Bloom = *dec.Bloom
|
||||
if dec.Logs == nil {
|
||||
return errors.New("missing required field 'logs' for Receipt")
|
||||
}
|
||||
x.Logs = dec.Logs
|
||||
if dec.TxHash == nil {
|
||||
return errors.New("missing required field 'transactionHash' for Receipt")
|
||||
}
|
||||
x.TxHash = *dec.TxHash
|
||||
if dec.ContractAddress != nil {
|
||||
x.ContractAddress = *dec.ContractAddress
|
||||
}
|
||||
if dec.GasUsed == nil {
|
||||
return errors.New("missing required field 'gasUsed' for Receipt")
|
||||
}
|
||||
x.GasUsed = (*big.Int)(dec.GasUsed)
|
||||
*r = x
|
||||
return nil
|
||||
}
|
||||
99
core/types/gen_tx_json.go
Normal file
99
core/types/gen_tx_json.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func (t txdata) MarshalJSON() ([]byte, error) {
|
||||
type txdataJSON struct {
|
||||
AccountNonce hexutil.Uint64 `json:"nonce"`
|
||||
Price *hexutil.Big `json:"gasPrice"`
|
||||
GasLimit *hexutil.Big `json:"gas"`
|
||||
Recipient *common.Address `json:"to" optional:"yes" rlp:"nil"`
|
||||
Amount *hexutil.Big `json:"value"`
|
||||
Payload hexutil.Bytes `json:"input"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
Hash *common.Hash `json:"hash" optional:"yes" rlp:"-"`
|
||||
}
|
||||
var enc txdataJSON
|
||||
enc.AccountNonce = hexutil.Uint64(t.AccountNonce)
|
||||
enc.Price = (*hexutil.Big)(t.Price)
|
||||
enc.GasLimit = (*hexutil.Big)(t.GasLimit)
|
||||
enc.Recipient = t.Recipient
|
||||
enc.Amount = (*hexutil.Big)(t.Amount)
|
||||
enc.Payload = t.Payload
|
||||
enc.V = (*hexutil.Big)(t.V)
|
||||
enc.R = (*hexutil.Big)(t.R)
|
||||
enc.S = (*hexutil.Big)(t.S)
|
||||
enc.Hash = t.Hash
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (t *txdata) UnmarshalJSON(input []byte) error {
|
||||
type txdataJSON struct {
|
||||
AccountNonce *hexutil.Uint64 `json:"nonce"`
|
||||
Price *hexutil.Big `json:"gasPrice"`
|
||||
GasLimit *hexutil.Big `json:"gas"`
|
||||
Recipient *common.Address `json:"to" optional:"yes" rlp:"nil"`
|
||||
Amount *hexutil.Big `json:"value"`
|
||||
Payload hexutil.Bytes `json:"input"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
Hash *common.Hash `json:"hash" optional:"yes" rlp:"-"`
|
||||
}
|
||||
var dec txdataJSON
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
var x txdata
|
||||
if dec.AccountNonce == nil {
|
||||
return errors.New("missing required field 'nonce' for txdata")
|
||||
}
|
||||
x.AccountNonce = uint64(*dec.AccountNonce)
|
||||
if dec.Price == nil {
|
||||
return errors.New("missing required field 'gasPrice' for txdata")
|
||||
}
|
||||
x.Price = (*big.Int)(dec.Price)
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'gas' for txdata")
|
||||
}
|
||||
x.GasLimit = (*big.Int)(dec.GasLimit)
|
||||
if dec.Recipient != nil {
|
||||
x.Recipient = dec.Recipient
|
||||
}
|
||||
if dec.Amount == nil {
|
||||
return errors.New("missing required field 'value' for txdata")
|
||||
}
|
||||
x.Amount = (*big.Int)(dec.Amount)
|
||||
if dec.Payload == nil {
|
||||
return errors.New("missing required field 'input' for txdata")
|
||||
}
|
||||
x.Payload = dec.Payload
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' for txdata")
|
||||
}
|
||||
x.V = (*big.Int)(dec.V)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' for txdata")
|
||||
}
|
||||
x.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' for txdata")
|
||||
}
|
||||
x.S = (*big.Int)(dec.S)
|
||||
if dec.Hash != nil {
|
||||
x.Hash = dec.Hash
|
||||
}
|
||||
*t = x
|
||||
return nil
|
||||
}
|
||||
|
|
@ -17,8 +17,6 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
|
|
@ -27,27 +25,42 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
var errMissingLogFields = errors.New("missing required JSON log fields")
|
||||
//go:generate gencodec -type Log -field-override logMarshaling -out gen_log_json.go
|
||||
|
||||
// Log represents a contract log event. These events are generated by the LOG opcode and
|
||||
// stored/indexed by the node.
|
||||
type Log struct {
|
||||
// Consensus fields.
|
||||
Address common.Address // address of the contract that generated the event
|
||||
Topics []common.Hash // list of topics provided by the contract.
|
||||
Data []byte // supplied by the contract, usually ABI-encoded
|
||||
// Consensus fields:
|
||||
// address of the contract that generated the event
|
||||
Address common.Address `json:"address"`
|
||||
// list of topics provided by the contract.
|
||||
Topics []common.Hash `json:"topics"`
|
||||
// supplied by the contract, usually ABI-encoded
|
||||
Data []byte `json:"data"`
|
||||
|
||||
// Derived fields. These fields are filled in by the node
|
||||
// but not secured by consensus.
|
||||
BlockNumber uint64 // block in which the transaction was included
|
||||
TxHash common.Hash // hash of the transaction
|
||||
TxIndex uint // index of the transaction in the block
|
||||
BlockHash common.Hash // hash of the block in which the transaction was included
|
||||
Index uint // index of the log in the receipt
|
||||
// block in which the transaction was included
|
||||
BlockNumber uint64 `json:"blockNumber" optional:"yes"`
|
||||
// hash of the transaction
|
||||
TxHash common.Hash `json:"transactionHash"`
|
||||
// index of the transaction in the block
|
||||
TxIndex uint `json:"transactionIndex"`
|
||||
// hash of the block in which the transaction was included
|
||||
BlockHash common.Hash `json:"blockHash" optional:"yes"`
|
||||
// index of the log in the receipt
|
||||
Index uint `json:"logIndex"`
|
||||
|
||||
// The Removed field is true if this log was reverted due to a chain reorganisation.
|
||||
// You must pay attention to this field if you receive logs through a filter query.
|
||||
Removed bool
|
||||
Removed bool `json:"removed" optional:"yes"`
|
||||
}
|
||||
|
||||
type logMarshaling struct {
|
||||
Data hexutil.Bytes
|
||||
BlockNumber hexutil.Uint64
|
||||
TxIndex hexutil.Uint
|
||||
Index hexutil.Uint
|
||||
}
|
||||
|
||||
type rlpLog struct {
|
||||
|
|
@ -67,18 +80,6 @@ type rlpStorageLog struct {
|
|||
Index uint
|
||||
}
|
||||
|
||||
type jsonLog struct {
|
||||
Address *common.Address `json:"address"`
|
||||
Topics *[]common.Hash `json:"topics"`
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
BlockNumber *hexutil.Uint64 `json:"blockNumber"`
|
||||
TxIndex *hexutil.Uint `json:"transactionIndex"`
|
||||
TxHash *common.Hash `json:"transactionHash"`
|
||||
BlockHash *common.Hash `json:"blockHash"`
|
||||
Index *hexutil.Uint `json:"logIndex"`
|
||||
Removed bool `json:"removed"`
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (l *Log) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
|
||||
|
|
@ -98,54 +99,6 @@ func (l *Log) String() string {
|
|||
return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index)
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (l *Log) MarshalJSON() ([]byte, error) {
|
||||
jslog := &jsonLog{
|
||||
Address: &l.Address,
|
||||
Topics: &l.Topics,
|
||||
Data: (*hexutil.Bytes)(&l.Data),
|
||||
TxIndex: (*hexutil.Uint)(&l.TxIndex),
|
||||
TxHash: &l.TxHash,
|
||||
Index: (*hexutil.Uint)(&l.Index),
|
||||
Removed: l.Removed,
|
||||
}
|
||||
// Set block information for mined logs.
|
||||
if (l.BlockHash != common.Hash{}) {
|
||||
jslog.BlockHash = &l.BlockHash
|
||||
jslog.BlockNumber = (*hexutil.Uint64)(&l.BlockNumber)
|
||||
}
|
||||
return json.Marshal(jslog)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Umarshaler.
|
||||
func (l *Log) UnmarshalJSON(input []byte) error {
|
||||
var dec jsonLog
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Address == nil || dec.Topics == nil || dec.Data == nil ||
|
||||
dec.TxIndex == nil || dec.TxHash == nil || dec.Index == nil {
|
||||
return errMissingLogFields
|
||||
}
|
||||
declog := Log{
|
||||
Address: *dec.Address,
|
||||
Topics: *dec.Topics,
|
||||
Data: *dec.Data,
|
||||
TxHash: *dec.TxHash,
|
||||
TxIndex: uint(*dec.TxIndex),
|
||||
Index: uint(*dec.Index),
|
||||
Removed: dec.Removed,
|
||||
}
|
||||
// Block information may be missing if the log is received through
|
||||
// the pending log filter, so it's handled specially here.
|
||||
if dec.BlockHash != nil && dec.BlockNumber != nil {
|
||||
declog.BlockHash = *dec.BlockHash
|
||||
declog.BlockNumber = uint64(*dec.BlockNumber)
|
||||
}
|
||||
*l = declog
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogForStorage is a wrapper around a Log that flattens and parses the entire content of
|
||||
// a log including non-consensus fields.
|
||||
type LogForStorage Log
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package types
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
|
@ -96,7 +97,7 @@ var unmarshalLogTests = map[string]struct {
|
|||
},
|
||||
"missing data": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||
wantError: errMissingLogFields,
|
||||
wantError: fmt.Errorf("missing required field 'data' for Log"),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
|
@ -28,33 +26,26 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingReceiptPostState = errors.New("missing post state root in JSON receipt")
|
||||
errMissingReceiptFields = errors.New("missing required JSON receipt fields")
|
||||
)
|
||||
//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
|
||||
|
||||
// Receipt represents the results of a transaction.
|
||||
type Receipt struct {
|
||||
// Consensus fields
|
||||
PostState []byte
|
||||
CumulativeGasUsed *big.Int
|
||||
Bloom Bloom
|
||||
Logs []*Log
|
||||
PostState []byte `json:"root"`
|
||||
CumulativeGasUsed *big.Int `json:"cumulativeGasUsed"`
|
||||
Bloom Bloom `json:"logsBloom"`
|
||||
Logs []*Log `json:"logs"`
|
||||
|
||||
// Implementation fields (don't reorder!)
|
||||
TxHash common.Hash
|
||||
ContractAddress common.Address
|
||||
GasUsed *big.Int
|
||||
TxHash common.Hash `json:"transactionHash"`
|
||||
ContractAddress common.Address `json:"contractAddress" optional:"true"`
|
||||
GasUsed *big.Int `json:"gasUsed"`
|
||||
}
|
||||
|
||||
type jsonReceipt struct {
|
||||
PostState *common.Hash `json:"root"`
|
||||
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"`
|
||||
Bloom *Bloom `json:"logsBloom"`
|
||||
Logs []*Log `json:"logs"`
|
||||
TxHash *common.Hash `json:"transactionHash"`
|
||||
ContractAddress *common.Address `json:"contractAddress"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
type receiptMarshaling struct {
|
||||
PostState hexutil.Bytes
|
||||
CumulativeGasUsed *hexutil.Big
|
||||
GasUsed *hexutil.Big
|
||||
}
|
||||
|
||||
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||
|
|
@ -84,51 +75,6 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON encodes receipts into the web3 RPC response block format.
|
||||
func (r *Receipt) MarshalJSON() ([]byte, error) {
|
||||
root := common.BytesToHash(r.PostState)
|
||||
|
||||
return json.Marshal(&jsonReceipt{
|
||||
PostState: &root,
|
||||
CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed),
|
||||
Bloom: &r.Bloom,
|
||||
Logs: r.Logs,
|
||||
TxHash: &r.TxHash,
|
||||
ContractAddress: &r.ContractAddress,
|
||||
GasUsed: (*hexutil.Big)(r.GasUsed),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes the web3 RPC receipt format.
|
||||
func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
var dec jsonReceipt
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
// Ensure that all fields are set. PostState is checked separately because it is a
|
||||
// recent addition to the RPC spec (as of August 2016) and older implementations might
|
||||
// not provide it. Note that ContractAddress is not checked because it can be null.
|
||||
if dec.PostState == nil {
|
||||
return errMissingReceiptPostState
|
||||
}
|
||||
if dec.CumulativeGasUsed == nil || dec.Bloom == nil ||
|
||||
dec.Logs == nil || dec.TxHash == nil || dec.GasUsed == nil {
|
||||
return errMissingReceiptFields
|
||||
}
|
||||
*r = Receipt{
|
||||
PostState: (*dec.PostState)[:],
|
||||
CumulativeGasUsed: (*big.Int)(dec.CumulativeGasUsed),
|
||||
Bloom: *dec.Bloom,
|
||||
Logs: dec.Logs,
|
||||
TxHash: *dec.TxHash,
|
||||
GasUsed: (*big.Int)(dec.GasUsed),
|
||||
}
|
||||
if dec.ContractAddress != nil {
|
||||
r.ContractAddress = *dec.ContractAddress
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (r *Receipt) String() string {
|
||||
return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package types
|
|||
|
||||
import (
|
||||
"container/heap"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
|
@ -28,22 +27,20 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
var ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
||||
//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
|
||||
|
||||
var (
|
||||
errMissingTxSignatureFields = errors.New("missing required JSON transaction signature fields")
|
||||
errMissingTxFields = errors.New("missing required JSON transaction fields")
|
||||
errNoSigner = errors.New("missing signing methods")
|
||||
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
||||
errNoSigner = errors.New("missing signing methods")
|
||||
)
|
||||
|
||||
// deriveSigner makes a *best* guess about which signer to use.
|
||||
func deriveSigner(V *big.Int) Signer {
|
||||
if V.Sign() != 0 && isProtectedV(V) {
|
||||
return EIP155Signer{chainId: deriveChainId(V)}
|
||||
return NewEIP155Signer(deriveChainId(V))
|
||||
} else {
|
||||
return HomesteadSigner{}
|
||||
}
|
||||
|
|
@ -58,26 +55,31 @@ type Transaction struct {
|
|||
}
|
||||
|
||||
type txdata struct {
|
||||
AccountNonce uint64
|
||||
Price, GasLimit *big.Int
|
||||
Recipient *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Amount *big.Int
|
||||
Payload []byte
|
||||
V *big.Int // signature
|
||||
R, S *big.Int // signature
|
||||
AccountNonce uint64 `json:"nonce"`
|
||||
Price *big.Int `json:"gasPrice"`
|
||||
GasLimit *big.Int `json:"gas"`
|
||||
Recipient *common.Address `json:"to" optional:"yes" rlp:"nil"` // nil means contract creation
|
||||
Amount *big.Int `json:"value"`
|
||||
Payload []byte `json:"input"`
|
||||
|
||||
// Signature values
|
||||
V *big.Int `json:"v"`
|
||||
R *big.Int `json:"r"`
|
||||
S *big.Int `json:"s"`
|
||||
|
||||
// This is only used when marshaling to JSON.
|
||||
Hash *common.Hash `json:"hash" optional:"yes" rlp:"-"`
|
||||
}
|
||||
|
||||
type jsonTransaction struct {
|
||||
Hash *common.Hash `json:"hash"`
|
||||
AccountNonce *hexutil.Uint64 `json:"nonce"`
|
||||
Price *hexutil.Big `json:"gasPrice"`
|
||||
GasLimit *hexutil.Big `json:"gas"`
|
||||
Recipient *common.Address `json:"to"`
|
||||
Amount *hexutil.Big `json:"value"`
|
||||
Payload *hexutil.Bytes `json:"input"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
type txdataMarshaling struct {
|
||||
AccountNonce hexutil.Uint64
|
||||
Price *hexutil.Big
|
||||
GasLimit *hexutil.Big
|
||||
Amount *hexutil.Big
|
||||
Payload hexutil.Bytes
|
||||
V *hexutil.Big
|
||||
R *hexutil.Big
|
||||
S *hexutil.Big
|
||||
}
|
||||
|
||||
func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||
|
|
@ -116,19 +118,6 @@ func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice
|
|||
return &Transaction{data: d}
|
||||
}
|
||||
|
||||
func pickSigner(rules params.Rules) Signer {
|
||||
var signer Signer
|
||||
switch {
|
||||
case rules.IsEIP155:
|
||||
signer = NewEIP155Signer(rules.ChainId)
|
||||
case rules.IsHomestead:
|
||||
signer = HomesteadSigner{}
|
||||
default:
|
||||
signer = FrontierSigner{}
|
||||
}
|
||||
return signer
|
||||
}
|
||||
|
||||
// ChainId returns which chain id this transaction was signed for (if at all)
|
||||
func (tx *Transaction) ChainId() *big.Int {
|
||||
return deriveChainId(tx.data.V)
|
||||
|
|
@ -164,66 +153,30 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// MarshalJSON encodes transactions into the web3 RPC response block format.
|
||||
func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
||||
hash := tx.Hash()
|
||||
|
||||
return json.Marshal(&jsonTransaction{
|
||||
Hash: &hash,
|
||||
AccountNonce: (*hexutil.Uint64)(&tx.data.AccountNonce),
|
||||
Price: (*hexutil.Big)(tx.data.Price),
|
||||
GasLimit: (*hexutil.Big)(tx.data.GasLimit),
|
||||
Recipient: tx.data.Recipient,
|
||||
Amount: (*hexutil.Big)(tx.data.Amount),
|
||||
Payload: (*hexutil.Bytes)(&tx.data.Payload),
|
||||
V: (*hexutil.Big)(tx.data.V),
|
||||
R: (*hexutil.Big)(tx.data.R),
|
||||
S: (*hexutil.Big)(tx.data.S),
|
||||
})
|
||||
data := tx.data
|
||||
data.Hash = &hash
|
||||
return data.MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes the web3 RPC transaction format.
|
||||
func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
||||
var dec jsonTransaction
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
var dec txdata
|
||||
if err := dec.UnmarshalJSON(input); err != nil {
|
||||
return err
|
||||
}
|
||||
// Ensure that all fields are set. V, R, S are checked separately because they're a
|
||||
// recent addition to the RPC spec (as of August 2016) and older implementations might
|
||||
// not provide them. Note that Recipient is not checked because it can be missing for
|
||||
// contract creations.
|
||||
if dec.V == nil || dec.R == nil || dec.S == nil {
|
||||
return errMissingTxSignatureFields
|
||||
}
|
||||
|
||||
var V byte
|
||||
if isProtectedV((*big.Int)(dec.V)) {
|
||||
chainId := deriveChainId((*big.Int)(dec.V)).Uint64()
|
||||
V = byte(dec.V.ToInt().Uint64() - 35 - 2*chainId)
|
||||
if isProtectedV(dec.V) {
|
||||
chainId := deriveChainId(dec.V).Uint64()
|
||||
V = byte(dec.V.Uint64() - 35 - 2*chainId)
|
||||
} else {
|
||||
V = byte(((*big.Int)(dec.V)).Uint64() - 27)
|
||||
V = byte(dec.V.Uint64() - 27)
|
||||
}
|
||||
if !crypto.ValidateSignatureValues(V, (*big.Int)(dec.R), (*big.Int)(dec.S), false) {
|
||||
if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
|
||||
return ErrInvalidSig
|
||||
}
|
||||
|
||||
if dec.AccountNonce == nil || dec.Price == nil || dec.GasLimit == nil || dec.Amount == nil || dec.Payload == nil {
|
||||
return errMissingTxFields
|
||||
}
|
||||
// Assign the fields. This is not atomic but reusing transactions
|
||||
// for decoding isn't thread safe anyway.
|
||||
*tx = Transaction{}
|
||||
tx.data = txdata{
|
||||
AccountNonce: uint64(*dec.AccountNonce),
|
||||
Recipient: dec.Recipient,
|
||||
Amount: (*big.Int)(dec.Amount),
|
||||
GasLimit: (*big.Int)(dec.GasLimit),
|
||||
Price: (*big.Int)(dec.Price),
|
||||
Payload: *dec.Payload,
|
||||
V: (*big.Int)(dec.V),
|
||||
R: (*big.Int)(dec.R),
|
||||
S: (*big.Int)(dec.S),
|
||||
}
|
||||
*tx = Transaction{data: dec}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,9 @@ type EIP155Signer struct {
|
|||
}
|
||||
|
||||
func NewEIP155Signer(chainId *big.Int) EIP155Signer {
|
||||
if chainId == nil {
|
||||
chainId = new(big.Int)
|
||||
}
|
||||
return EIP155Signer{
|
||||
chainId: chainId,
|
||||
chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package vm
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
|
|
@ -117,9 +116,7 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e
|
|||
if err != nil && evm.cfg.Debug {
|
||||
// XXX For debugging
|
||||
//fmt.Printf("%04d: %8v cost = %-8d stack = %-8d ERR = %v\n", pc, op, cost, stack.len(), err)
|
||||
// TODO update the tracer
|
||||
g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost)
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err)
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
@ -177,8 +174,7 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e
|
|||
}
|
||||
|
||||
if evm.cfg.Debug {
|
||||
g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost)
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err)
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err)
|
||||
}
|
||||
// XXX For debugging
|
||||
//fmt.Printf("%04d: %8v cost = %-8d stack = %-8d\n", pc, op, cost, stack.len())
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ type LogConfig struct {
|
|||
type StructLog struct {
|
||||
Pc uint64
|
||||
Op OpCode
|
||||
Gas *big.Int
|
||||
GasCost *big.Int
|
||||
Gas uint64
|
||||
GasCost uint64
|
||||
Memory []byte
|
||||
Stack []*big.Int
|
||||
Storage map[common.Hash]common.Hash
|
||||
|
|
@ -67,7 +67,7 @@ type StructLog struct {
|
|||
// Note that reference types are actual VM data structures; make copies
|
||||
// if you need to retain them beyond the current call.
|
||||
type Tracer interface {
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
}
|
||||
|
||||
// StructLogger is an EVM state logger and implements Tracer.
|
||||
|
|
@ -96,7 +96,7 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
|||
// captureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// captureState also tracks SSTORE ops to track dirty values.
|
||||
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
// check if already accumulated the specified number of logs
|
||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
||||
return ErrTraceLimitReached
|
||||
|
|
@ -158,7 +158,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *b
|
|||
}
|
||||
}
|
||||
// create a new snaptshot of the EVM.
|
||||
log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.depth, err}
|
||||
log := StructLog{pc, op, gas, cost, mem, stck, storage, env.depth, err}
|
||||
|
||||
l.logs = append(l.logs, log)
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ func TestStoreCapture(t *testing.T) {
|
|||
|
||||
var index common.Hash
|
||||
|
||||
logger.CaptureState(env, 0, SSTORE, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
|
||||
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil)
|
||||
if len(logger.changedValues[contract.Address()]) == 0 {
|
||||
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))
|
||||
}
|
||||
|
|
@ -81,13 +81,13 @@ func TestStorageCapture(t *testing.T) {
|
|||
stack = newstack()
|
||||
)
|
||||
|
||||
logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
|
||||
logger.CaptureState(env, 0, STOP, 0, 0, mem, stack, contract, 0, nil)
|
||||
if ref.calledForEach {
|
||||
t.Error("didn't expect for each to be called")
|
||||
}
|
||||
|
||||
logger = NewStructLogger(&LogConfig{FullStorage: true})
|
||||
logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
|
||||
logger.CaptureState(env, 0, STOP, 0, 0, mem, stack, contract, 0, nil)
|
||||
if !ref.calledForEach {
|
||||
t.Error("expected for each to be called")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ var (
|
|||
secp256k1_halfN = new(big.Int).Div(secp256k1_N, big.NewInt(2))
|
||||
)
|
||||
|
||||
// Keccak256 calculates and returns the Keccak256 hash of the input data.
|
||||
func Keccak256(data ...[]byte) []byte {
|
||||
d := sha3.NewKeccak256()
|
||||
for _, b := range data {
|
||||
|
|
@ -45,6 +46,8 @@ func Keccak256(data ...[]byte) []byte {
|
|||
return d.Sum(nil)
|
||||
}
|
||||
|
||||
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
|
||||
// converting it to an internal Hash data structure.
|
||||
func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
||||
d := sha3.NewKeccak256()
|
||||
for _, b := range data {
|
||||
|
|
@ -54,6 +57,15 @@ func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
|||
return h
|
||||
}
|
||||
|
||||
// Keccak512 calculates and returns the Keccak512 hash of the input data.
|
||||
func Keccak512(data ...[]byte) []byte {
|
||||
d := sha3.NewKeccak512()
|
||||
for _, b := range data {
|
||||
d.Write(b)
|
||||
}
|
||||
return d.Sum(nil)
|
||||
}
|
||||
|
||||
// Deprecated: For backward compatibility as other packages depend on these
|
||||
func Sha3Hash(data ...[]byte) common.Hash { return Keccak256Hash(data...) }
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ import (
|
|||
// NewKeccak256 creates a new Keccak-256 hash.
|
||||
func NewKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} }
|
||||
|
||||
// NewKeccak512 creates a new Keccak-512 hash.
|
||||
func NewKeccak512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x01} }
|
||||
|
||||
// New224 creates a new SHA3-224 hash.
|
||||
// Its generic security strength is 224 bits against preimage attacks,
|
||||
// and 112 bits against collision attacks.
|
||||
|
|
|
|||
|
|
@ -322,7 +322,6 @@
|
|||
// func keccakF1600(state *[25]uint64)
|
||||
TEXT ·keccakF1600(SB), 0, $200-8
|
||||
MOVQ state+0(FP), rpState
|
||||
SUBQ $(8*25), SP
|
||||
|
||||
// Convert the user state into an internal state
|
||||
NOTQ _be(rpState)
|
||||
|
|
@ -388,5 +387,4 @@ TEXT ·keccakF1600(SB), 0, $200-8
|
|||
NOTQ _mi(rpState)
|
||||
NOTQ _sa(rpState)
|
||||
|
||||
ADDQ $(8*25), SP
|
||||
RET
|
||||
|
|
|
|||
149
eth/api.go
149
eth/api.go
|
|
@ -19,17 +19,16 @@ package eth
|
|||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
|
|
@ -37,11 +36,11 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const defaultTraceTimeout = 5 * time.Second
|
||||
|
|
@ -58,18 +57,18 @@ func NewPublicEthereumAPI(e *Ethereum) *PublicEthereumAPI {
|
|||
}
|
||||
|
||||
// Etherbase is the address that mining rewards will be send to
|
||||
func (s *PublicEthereumAPI) Etherbase() (common.Address, error) {
|
||||
return s.e.Etherbase()
|
||||
func (api *PublicEthereumAPI) Etherbase() (common.Address, error) {
|
||||
return api.e.Etherbase()
|
||||
}
|
||||
|
||||
// Coinbase is the address that mining rewards will be send to (alias for Etherbase)
|
||||
func (s *PublicEthereumAPI) Coinbase() (common.Address, error) {
|
||||
return s.Etherbase()
|
||||
func (api *PublicEthereumAPI) Coinbase() (common.Address, error) {
|
||||
return api.Etherbase()
|
||||
}
|
||||
|
||||
// Hashrate returns the POW hashrate
|
||||
func (s *PublicEthereumAPI) Hashrate() hexutil.Uint64 {
|
||||
return hexutil.Uint64(s.e.Miner().HashRate())
|
||||
func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 {
|
||||
return hexutil.Uint64(api.e.Miner().HashRate())
|
||||
}
|
||||
|
||||
// PublicMinerAPI provides an API to control the miner.
|
||||
|
|
@ -81,34 +80,34 @@ type PublicMinerAPI struct {
|
|||
|
||||
// NewPublicMinerAPI create a new PublicMinerAPI instance.
|
||||
func NewPublicMinerAPI(e *Ethereum) *PublicMinerAPI {
|
||||
agent := miner.NewRemoteAgent(e.Pow())
|
||||
agent := miner.NewRemoteAgent(e.BlockChain(), e.Engine())
|
||||
e.Miner().Register(agent)
|
||||
|
||||
return &PublicMinerAPI{e, agent}
|
||||
}
|
||||
|
||||
// Mining returns an indication if this node is currently mining.
|
||||
func (s *PublicMinerAPI) Mining() bool {
|
||||
return s.e.IsMining()
|
||||
func (api *PublicMinerAPI) Mining() bool {
|
||||
return api.e.IsMining()
|
||||
}
|
||||
|
||||
// SubmitWork can be used by external miner to submit their POW solution. It returns an indication if the work was
|
||||
// accepted. Note, this is not an indication if the provided work was valid!
|
||||
func (s *PublicMinerAPI) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) bool {
|
||||
return s.agent.SubmitWork(nonce, digest, solution)
|
||||
func (api *PublicMinerAPI) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) bool {
|
||||
return api.agent.SubmitWork(nonce, digest, solution)
|
||||
}
|
||||
|
||||
// GetWork returns a work package for external miner. The work package consists of 3 strings
|
||||
// result[0], 32 bytes hex encoded current block header pow-hash
|
||||
// result[1], 32 bytes hex encoded seed hash used for DAG
|
||||
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
||||
func (s *PublicMinerAPI) GetWork() ([3]string, error) {
|
||||
if !s.e.IsMining() {
|
||||
if err := s.e.StartMining(0); err != nil {
|
||||
func (api *PublicMinerAPI) GetWork() ([3]string, error) {
|
||||
if !api.e.IsMining() {
|
||||
if err := api.e.StartMining(); err != nil {
|
||||
return [3]string{}, err
|
||||
}
|
||||
}
|
||||
work, err := s.agent.GetWork()
|
||||
work, err := api.agent.GetWork()
|
||||
if err != nil {
|
||||
return work, fmt.Errorf("mining not ready: %v", err)
|
||||
}
|
||||
|
|
@ -118,8 +117,8 @@ func (s *PublicMinerAPI) GetWork() ([3]string, error) {
|
|||
// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined
|
||||
// hash rate of all miners which submit work through this node. It accepts the miner hash rate and an identifier which
|
||||
// must be unique between nodes.
|
||||
func (s *PublicMinerAPI) SubmitHashrate(hashrate hexutil.Uint64, id common.Hash) bool {
|
||||
s.agent.SubmitHashrate(id, uint64(hashrate))
|
||||
func (api *PublicMinerAPI) SubmitHashrate(hashrate hexutil.Uint64, id common.Hash) bool {
|
||||
api.agent.SubmitHashrate(id, uint64(hashrate))
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -134,64 +133,59 @@ func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI {
|
|||
return &PrivateMinerAPI{e: e}
|
||||
}
|
||||
|
||||
// Start the miner with the given number of threads. If threads is nil the number of
|
||||
// workers started is equal to the number of logical CPU's that are usable by this process.
|
||||
func (s *PrivateMinerAPI) Start(threads *int) (bool, error) {
|
||||
s.e.StartAutoDAG()
|
||||
var err error
|
||||
if threads == nil {
|
||||
err = s.e.StartMining(runtime.NumCPU())
|
||||
} else {
|
||||
err = s.e.StartMining(*threads)
|
||||
// Start the miner with the given number of threads. If threads is nil the number
|
||||
// of workers started is equal to the number of logical CPUs that are usable by
|
||||
// this process. If mining is already running, this method adjust the number of
|
||||
// threads allowed to use.
|
||||
func (api *PrivateMinerAPI) Start(threads *int) error {
|
||||
// Set the number of threads if the seal engine supports it
|
||||
if threads != nil {
|
||||
type threaded interface {
|
||||
SetThreads(threads int)
|
||||
}
|
||||
if th, ok := api.e.engine.(threaded); ok {
|
||||
log.Info("Updated mining threads", "threads", *threads)
|
||||
th.SetThreads(*threads)
|
||||
} else {
|
||||
log.Warn("Current seal engine isn't threaded")
|
||||
}
|
||||
}
|
||||
return err == nil, err
|
||||
// Start the miner and return
|
||||
if !api.e.IsMining() {
|
||||
return api.e.StartMining()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop the miner
|
||||
func (s *PrivateMinerAPI) Stop() bool {
|
||||
s.e.StopMining()
|
||||
func (api *PrivateMinerAPI) Stop() bool {
|
||||
api.e.StopMining()
|
||||
return true
|
||||
}
|
||||
|
||||
// SetExtra sets the extra data string that is included when this miner mines a block.
|
||||
func (s *PrivateMinerAPI) SetExtra(extra string) (bool, error) {
|
||||
if err := s.e.Miner().SetExtra([]byte(extra)); err != nil {
|
||||
func (api *PrivateMinerAPI) SetExtra(extra string) (bool, error) {
|
||||
if err := api.e.Miner().SetExtra([]byte(extra)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// SetGasPrice sets the minimum accepted gas price for the miner.
|
||||
func (s *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool {
|
||||
s.e.Miner().SetGasPrice((*big.Int)(&gasPrice))
|
||||
func (api *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool {
|
||||
api.e.Miner().SetGasPrice((*big.Int)(&gasPrice))
|
||||
return true
|
||||
}
|
||||
|
||||
// SetEtherbase sets the etherbase of the miner
|
||||
func (s *PrivateMinerAPI) SetEtherbase(etherbase common.Address) bool {
|
||||
s.e.SetEtherbase(etherbase)
|
||||
func (api *PrivateMinerAPI) SetEtherbase(etherbase common.Address) bool {
|
||||
api.e.SetEtherbase(etherbase)
|
||||
return true
|
||||
}
|
||||
|
||||
// StartAutoDAG starts auto DAG generation. This will prevent the DAG generating on epoch change
|
||||
// which will cause the node to stop mining during the generation process.
|
||||
func (s *PrivateMinerAPI) StartAutoDAG() bool {
|
||||
s.e.StartAutoDAG()
|
||||
return true
|
||||
}
|
||||
|
||||
// StopAutoDAG stops auto DAG generation
|
||||
func (s *PrivateMinerAPI) StopAutoDAG() bool {
|
||||
s.e.StopAutoDAG()
|
||||
return true
|
||||
}
|
||||
|
||||
// MakeDAG creates the new DAG for the given block number
|
||||
func (s *PrivateMinerAPI) MakeDAG(blockNr rpc.BlockNumber) (bool, error) {
|
||||
if err := ethash.MakeDAG(uint64(blockNr.Int64()), ""); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
// GetHashrate returns the current hashrate of the miner.
|
||||
func (api *PrivateMinerAPI) GetHashrate() uint64 {
|
||||
return uint64(api.e.miner.HashRate())
|
||||
}
|
||||
|
||||
// PrivateAdminAPI is the collection of Etheruem full node-related APIs
|
||||
|
|
@ -300,10 +294,22 @@ func NewPublicDebugAPI(eth *Ethereum) *PublicDebugAPI {
|
|||
}
|
||||
|
||||
// DumpBlock retrieves the entire state of the database at a given block.
|
||||
func (api *PublicDebugAPI) DumpBlock(number uint64) (state.Dump, error) {
|
||||
block := api.eth.BlockChain().GetBlockByNumber(number)
|
||||
func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
|
||||
if blockNr == rpc.PendingBlockNumber {
|
||||
// If we're dumping the pending state, we need to request
|
||||
// both the pending block as well as the pending state from
|
||||
// the miner and operate on those
|
||||
_, stateDb := api.eth.miner.Pending()
|
||||
return stateDb.RawDump(), nil
|
||||
}
|
||||
var block *types.Block
|
||||
if blockNr == rpc.LatestBlockNumber {
|
||||
block = api.eth.blockchain.CurrentBlock()
|
||||
} else {
|
||||
block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr))
|
||||
}
|
||||
if block == nil {
|
||||
return state.Dump{}, fmt.Errorf("block #%d not found", number)
|
||||
return state.Dump{}, fmt.Errorf("block #%d not found", blockNr)
|
||||
}
|
||||
stateDb, err := api.eth.BlockChain().StateAt(block.Root())
|
||||
if err != nil {
|
||||
|
|
@ -340,7 +346,7 @@ type TraceArgs struct {
|
|||
Timeout *string
|
||||
}
|
||||
|
||||
// TraceBlock processes the given block's RLP but does not import the block in to
|
||||
// TraceBlock processes the given block'api RLP but does not import the block in to
|
||||
// the chain.
|
||||
func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.LogConfig) BlockTraceResult {
|
||||
var block types.Block
|
||||
|
|
@ -357,7 +363,7 @@ func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.LogConfig) Bl
|
|||
}
|
||||
}
|
||||
|
||||
// TraceBlockFromFile loads the block's RLP from the given file name and attempts to
|
||||
// TraceBlockFromFile loads the block'api RLP from the given file name and attempts to
|
||||
// process it but does not import the block in to the chain.
|
||||
func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.LogConfig) BlockTraceResult {
|
||||
blockRlp, err := ioutil.ReadFile(file)
|
||||
|
|
@ -368,11 +374,21 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.LogConfig
|
|||
}
|
||||
|
||||
// TraceBlockByNumber processes the block by canonical block number.
|
||||
func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.LogConfig) BlockTraceResult {
|
||||
func (api *PrivateDebugAPI) TraceBlockByNumber(blockNr rpc.BlockNumber, config *vm.LogConfig) BlockTraceResult {
|
||||
// Fetch the block that we aim to reprocess
|
||||
block := api.eth.BlockChain().GetBlockByNumber(number)
|
||||
var block *types.Block
|
||||
switch blockNr {
|
||||
case rpc.PendingBlockNumber:
|
||||
// Pending block is only known by the miner
|
||||
block = api.eth.miner.PendingBlock()
|
||||
case rpc.LatestBlockNumber:
|
||||
block = api.eth.blockchain.CurrentBlock()
|
||||
default:
|
||||
block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr))
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
return BlockTraceResult{Error: fmt.Sprintf("block #%d not found", number)}
|
||||
return BlockTraceResult{Error: fmt.Sprintf("block #%d not found", blockNr)}
|
||||
}
|
||||
|
||||
validated, logs, err := api.traceBlock(block, config)
|
||||
|
|
@ -414,8 +430,7 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConf
|
|||
Debug: true,
|
||||
Tracer: structLogger,
|
||||
}
|
||||
|
||||
if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash(), block.NumberU64()-1), true, false); err != nil {
|
||||
if err := api.eth.engine.VerifyHeader(blockchain, block.Header(), true); err != nil {
|
||||
return false, structLogger.StructLogs(), err
|
||||
}
|
||||
statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root())
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
|
|
@ -33,7 +34,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// EthApiBackend implements ethapi.Backend for full nodes
|
||||
|
|
@ -51,6 +51,7 @@ func (b *EthApiBackend) CurrentBlock() *types.Block {
|
|||
}
|
||||
|
||||
func (b *EthApiBackend) SetHead(number uint64) {
|
||||
b.eth.protocolManager.downloader.Cancel()
|
||||
b.eth.blockchain.SetHead(number)
|
||||
}
|
||||
|
||||
|
|
|
|||
229
eth/backend.go
229
eth/backend.go
|
|
@ -18,19 +18,15 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
|
@ -45,45 +41,44 @@ import (
|
|||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
const (
|
||||
epochLength = 30000
|
||||
ethashRevision = 23
|
||||
|
||||
autoDAGcheckInterval = 10 * time.Hour
|
||||
autoDAGepochHeight = epochLength / 2
|
||||
)
|
||||
|
||||
var (
|
||||
datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
|
||||
portInUseErrRE = regexp.MustCompile("address already in use")
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ChainConfig *params.ChainConfig // chain configuration
|
||||
// The genesis block, which is inserted if the database is empty.
|
||||
// If nil, the Ethereum main net block is used.
|
||||
Genesis *core.Genesis
|
||||
|
||||
NetworkId int // Network ID to use for selecting peers to connect to
|
||||
Genesis string // Genesis JSON to seed the chain database with
|
||||
FastSync bool // Enables the state download based fast synchronisation algorithm
|
||||
LightMode bool // Running in light client mode
|
||||
LightServ int // Maximum percentage of time allowed for serving LES requests
|
||||
LightPeers int // Maximum number of LES client peers
|
||||
MaxPeers int // Maximum number of global peers
|
||||
NetworkId int // Network ID to use for selecting peers to connect to
|
||||
|
||||
FastSync bool // Enables the state download based fast synchronisation algorithm
|
||||
LightMode bool // Running in light client mode
|
||||
LightServ int // Maximum percentage of time allowed for serving LES requests
|
||||
LightPeers int // Maximum number of LES client peers
|
||||
MaxPeers int // Maximum number of global peers
|
||||
|
||||
SkipBcVersionCheck bool // e.g. blockchain export
|
||||
DatabaseCache int
|
||||
DatabaseHandles int
|
||||
|
||||
DocRoot string
|
||||
AutoDAG bool
|
||||
PowFake bool
|
||||
PowTest bool
|
||||
PowShared bool
|
||||
ExtraData []byte
|
||||
|
||||
EthashCacheDir string
|
||||
EthashCachesInMem int
|
||||
EthashCachesOnDisk int
|
||||
EthashDatasetDir string
|
||||
EthashDatasetsInMem int
|
||||
EthashDatasetsOnDisk int
|
||||
|
||||
Etherbase common.Address
|
||||
GasPrice *big.Int
|
||||
MinerThreads int
|
||||
|
|
@ -97,9 +92,6 @@ type Config struct {
|
|||
GpobaseCorrectionFactor int
|
||||
|
||||
EnablePreimageRecording bool
|
||||
|
||||
TestGenesisBlock *types.Block // Genesis block to seed the chain database with (testing only!)
|
||||
TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!)
|
||||
}
|
||||
|
||||
type LesServer interface {
|
||||
|
|
@ -124,7 +116,7 @@ type Ethereum struct {
|
|||
chainDb ethdb.Database // Block chain database
|
||||
|
||||
eventMux *event.TypeMux
|
||||
pow pow.PoW
|
||||
engine consensus.Engine
|
||||
accountManager *accounts.Manager
|
||||
|
||||
ApiBackend *EthApiBackend
|
||||
|
|
@ -132,8 +124,6 @@ type Ethereum struct {
|
|||
miner *miner.Miner
|
||||
Mining bool
|
||||
MinerThreads int
|
||||
AutoDAG bool
|
||||
autodagquit chan bool
|
||||
etherbase common.Address
|
||||
solcPath string
|
||||
|
||||
|
|
@ -154,25 +144,23 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
return nil, err
|
||||
}
|
||||
stopDbUpgrade := upgradeSequentialKeys(chainDb)
|
||||
if err := SetupGenesisBlock(&chainDb, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pow, err := CreatePoW(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
|
||||
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
|
||||
return nil, genesisErr
|
||||
}
|
||||
log.Info("Initialised chain configuration", "config", chainConfig)
|
||||
|
||||
eth := &Ethereum{
|
||||
chainDb: chainDb,
|
||||
chainConfig: chainConfig,
|
||||
eventMux: ctx.EventMux,
|
||||
accountManager: ctx.AccountManager,
|
||||
pow: pow,
|
||||
engine: CreateConsensusEngine(ctx, config, chainConfig, chainDb),
|
||||
shutdownChan: make(chan bool),
|
||||
stopDbUpgrade: stopDbUpgrade,
|
||||
netVersionId: config.NetworkId,
|
||||
etherbase: config.Etherbase,
|
||||
MinerThreads: config.MinerThreads,
|
||||
AutoDAG: config.AutoDAG,
|
||||
solcPath: config.SolcPath,
|
||||
}
|
||||
|
||||
|
|
@ -189,33 +177,18 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
|
||||
}
|
||||
|
||||
// load the genesis block or write a new one if no genesis
|
||||
// block is prenent in the database.
|
||||
genesis := core.GetBlock(chainDb, core.GetCanonicalHash(chainDb, 0), 0)
|
||||
if genesis == nil {
|
||||
genesis, err = core.WriteDefaultGenesisBlock(chainDb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Warn("Wrote default Ethereum genesis block")
|
||||
}
|
||||
|
||||
if config.ChainConfig == nil {
|
||||
return nil, errors.New("missing chain config")
|
||||
}
|
||||
core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig)
|
||||
|
||||
eth.chainConfig = config.ChainConfig
|
||||
|
||||
log.Info("Initialised chain configuration", "config", eth.chainConfig)
|
||||
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux(), vm.Config{EnablePreimageRecording: config.EnablePreimageRecording})
|
||||
vmConfig := vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.engine, eth.eventMux, vmConfig)
|
||||
if err != nil {
|
||||
if err == core.ErrNoGenesis {
|
||||
return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
// Rewind the chain in case of an incompatible config upgrade.
|
||||
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)
|
||||
}
|
||||
|
||||
newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
|
||||
eth.txPool = newPool
|
||||
|
||||
|
|
@ -230,10 +203,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil {
|
||||
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow)
|
||||
|
||||
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
|
||||
eth.miner.SetGasPrice(config.GasPrice)
|
||||
eth.miner.SetExtra(config.ExtraData)
|
||||
|
||||
|
|
@ -260,50 +234,34 @@ func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Data
|
|||
return db, err
|
||||
}
|
||||
|
||||
// SetupGenesisBlock initializes the genesis block for an Ethereum service
|
||||
func SetupGenesisBlock(chainDb *ethdb.Database, config *Config) error {
|
||||
// Load up any custom genesis block if requested
|
||||
if len(config.Genesis) > 0 {
|
||||
block, err := core.WriteGenesisBlock(*chainDb, strings.NewReader(config.Genesis))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Successfully wrote custom genesis block", "hash", block.Hash())
|
||||
}
|
||||
// Load up a test setup if directly injected
|
||||
if config.TestGenesisState != nil {
|
||||
*chainDb = config.TestGenesisState
|
||||
}
|
||||
if config.TestGenesisBlock != nil {
|
||||
core.WriteTd(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64(), config.TestGenesisBlock.Difficulty())
|
||||
core.WriteBlock(*chainDb, config.TestGenesisBlock)
|
||||
core.WriteCanonicalHash(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64())
|
||||
core.WriteHeadBlockHash(*chainDb, config.TestGenesisBlock.Hash())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatePoW creates the required type of PoW instance for an Ethereum service
|
||||
func CreatePoW(config *Config) (pow.PoW, error) {
|
||||
// CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service
|
||||
func CreateConsensusEngine(ctx *node.ServiceContext, config *Config, chainConfig *params.ChainConfig, db ethdb.Database) consensus.Engine {
|
||||
switch {
|
||||
case config.PowFake:
|
||||
log.Warn("Ethash used in fake mode")
|
||||
return pow.PoW(core.FakePow{}), nil
|
||||
return ethash.NewFaker()
|
||||
case config.PowTest:
|
||||
log.Warn("Ethash used in test mode")
|
||||
return ethash.NewForTesting()
|
||||
return ethash.NewTester()
|
||||
case config.PowShared:
|
||||
log.Warn("Ethash used in shared mode")
|
||||
return ethash.NewShared(), nil
|
||||
return ethash.NewShared()
|
||||
default:
|
||||
return ethash.New(), nil
|
||||
return ethash.New(ctx.ResolvePath(config.EthashCacheDir), config.EthashCachesInMem, config.EthashCachesOnDisk,
|
||||
config.EthashDatasetDir, config.EthashDatasetsInMem, config.EthashDatasetsOnDisk)
|
||||
}
|
||||
}
|
||||
|
||||
// APIs returns 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 {
|
||||
return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{
|
||||
apis := ethapi.GetAPIs(s.ApiBackend, s.solcPath)
|
||||
|
||||
// Append any APIs exposed explicitly by the consensus engine
|
||||
apis = append(apis, s.engine.APIs(s.BlockChain())...)
|
||||
|
||||
// Append all the local APIs and return
|
||||
return append(apis, []rpc.API{
|
||||
{
|
||||
Namespace: "eth",
|
||||
Version: "1.0",
|
||||
|
|
@ -373,13 +331,13 @@ func (self *Ethereum) SetEtherbase(etherbase common.Address) {
|
|||
self.miner.SetEtherbase(etherbase)
|
||||
}
|
||||
|
||||
func (s *Ethereum) StartMining(threads int) error {
|
||||
func (s *Ethereum) StartMining() error {
|
||||
eb, err := s.Etherbase()
|
||||
if err != nil {
|
||||
log.Error("Cannot start mining without etherbase", "err", err)
|
||||
return fmt.Errorf("etherbase missing: %v", err)
|
||||
}
|
||||
go s.miner.Start(eb, threads)
|
||||
go s.miner.Start(eb)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -391,7 +349,7 @@ func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager
|
|||
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
||||
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
||||
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||
func (s *Ethereum) Pow() pow.PoW { return s.pow }
|
||||
func (s *Ethereum) Engine() consensus.Engine { return s.engine }
|
||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
|
||||
|
|
@ -412,9 +370,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
|
|||
// Ethereum protocol implementation.
|
||||
func (s *Ethereum) Start(srvr *p2p.Server) error {
|
||||
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion())
|
||||
if s.AutoDAG {
|
||||
s.StartAutoDAG()
|
||||
}
|
||||
|
||||
s.protocolManager.Start()
|
||||
if s.lesServer != nil {
|
||||
s.lesServer.Start(srvr)
|
||||
|
|
@ -437,8 +393,6 @@ func (s *Ethereum) Stop() error {
|
|||
s.miner.Stop()
|
||||
s.eventMux.Stop()
|
||||
|
||||
s.StopAutoDAG()
|
||||
|
||||
s.chainDb.Close()
|
||||
close(s.shutdownChan)
|
||||
|
||||
|
|
@ -449,74 +403,3 @@ func (s *Ethereum) Stop() error {
|
|||
func (s *Ethereum) WaitForShutdown() {
|
||||
<-s.shutdownChan
|
||||
}
|
||||
|
||||
// StartAutoDAG() spawns a go routine that checks the DAG every autoDAGcheckInterval
|
||||
// by default that is 10 times per epoch
|
||||
// in epoch n, if we past autoDAGepochHeight within-epoch blocks,
|
||||
// it calls ethash.MakeDAG to pregenerate the DAG for the next epoch n+1
|
||||
// if it does not exist yet as well as remove the DAG for epoch n-1
|
||||
// the loop quits if autodagquit channel is closed, it can safely restart and
|
||||
// stop any number of times.
|
||||
// For any more sophisticated pattern of DAG generation, use CLI subcommand
|
||||
// makedag
|
||||
func (self *Ethereum) StartAutoDAG() {
|
||||
if self.autodagquit != nil {
|
||||
return // already started
|
||||
}
|
||||
go func() {
|
||||
log.Info("Pre-generation of ethash DAG on", "dir", ethash.DefaultDir)
|
||||
var nextEpoch uint64
|
||||
timer := time.After(0)
|
||||
self.autodagquit = make(chan bool)
|
||||
for {
|
||||
select {
|
||||
case <-timer:
|
||||
log.Info("Checking DAG availability", "dir", ethash.DefaultDir)
|
||||
currentBlock := self.BlockChain().CurrentBlock().NumberU64()
|
||||
thisEpoch := currentBlock / epochLength
|
||||
if nextEpoch <= thisEpoch {
|
||||
if currentBlock%epochLength > autoDAGepochHeight {
|
||||
if thisEpoch > 0 {
|
||||
previousDag, previousDagFull := dagFiles(thisEpoch - 1)
|
||||
os.Remove(filepath.Join(ethash.DefaultDir, previousDag))
|
||||
os.Remove(filepath.Join(ethash.DefaultDir, previousDagFull))
|
||||
log.Info("Removed previous DAG", "epoch", thisEpoch-1, "dag", previousDag)
|
||||
}
|
||||
nextEpoch = thisEpoch + 1
|
||||
dag, _ := dagFiles(nextEpoch)
|
||||
if _, err := os.Stat(dag); os.IsNotExist(err) {
|
||||
log.Info("Pre-generating next DAG", "epoch", nextEpoch, "dag", dag)
|
||||
err := ethash.MakeDAG(nextEpoch*epochLength, "") // "" -> ethash.DefaultDir
|
||||
if err != nil {
|
||||
log.Error("Error generating DAG", "epoch", nextEpoch, "dag", dag, "err", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
log.Warn("DAG already exists", "epoch", nextEpoch, "dag", dag)
|
||||
}
|
||||
}
|
||||
}
|
||||
timer = time.After(autoDAGcheckInterval)
|
||||
case <-self.autodagquit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// stopAutoDAG stops automatic DAG pregeneration by quitting the loop
|
||||
func (self *Ethereum) StopAutoDAG() {
|
||||
if self.autodagquit != nil {
|
||||
close(self.autodagquit)
|
||||
self.autodagquit = nil
|
||||
}
|
||||
log.Info("Pre-generation of ethash DAG off", "dir", ethash.DefaultDir)
|
||||
}
|
||||
|
||||
// dagFiles(epoch) returns the two alternative DAG filenames (not a path)
|
||||
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
|
||||
func dagFiles(epoch uint64) (string, string) {
|
||||
seedHash, _ := ethash.GetSeedHash(epoch * epochLength)
|
||||
dag := fmt.Sprintf("full-R%d-%x", ethashRevision, seedHash[:8])
|
||||
return dag, "full-R" + dag
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import (
|
|||
func TestMipmapUpgrade(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
addr := common.BytesToAddress([]byte("jeff"))
|
||||
genesis := core.WriteGenesisBlockForTesting(db)
|
||||
genesis := new(core.Genesis).MustCommit(db)
|
||||
|
||||
chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {
|
||||
var receipts types.Receipts
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue