From 1858463d8edbaa40313c5316166306aabe6dbbc9 Mon Sep 17 00:00:00 2001 From: pingke Date: Tue, 21 Feb 2023 16:21:25 +0800 Subject: [PATCH 01/23] add miner --- sstorminer/miner.go | 249 +++++++ sstorminer/miner_test.go | 282 ++++++++ sstorminer/unconfirmed.go | 136 ++++ sstorminer/unconfirmed_test.go | 87 +++ sstorminer/worker.go | 1172 ++++++++++++++++++++++++++++++++ sstorminer/worker_test.go | 669 ++++++++++++++++++ 6 files changed, 2595 insertions(+) create mode 100644 sstorminer/miner.go create mode 100644 sstorminer/miner_test.go create mode 100644 sstorminer/unconfirmed.go create mode 100644 sstorminer/unconfirmed_test.go create mode 100644 sstorminer/worker.go create mode 100644 sstorminer/worker_test.go diff --git a/sstorminer/miner.go b/sstorminer/miner.go new file mode 100644 index 000000000000..204ef652624b --- /dev/null +++ b/sstorminer/miner.go @@ -0,0 +1,249 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package miner implements Ethereum block creation and mining. +package sstorminer + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" +) + +// Backend wraps all methods required for mining. Only full node is capable +// to offer all the functions here. +type Backend interface { + BlockChain() *core.BlockChain + TxPool() *core.TxPool + StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) +} + +// Config is the configuration parameters of mining. +type Config struct { + Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards (default = first account) + Notify []string `toml:",omitempty"` // HTTP URL list to be notified of new work packages (only useful in ethash). + NotifyFull bool `toml:",omitempty"` // Notify with pending block headers instead of work packages + ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner + GasFloor uint64 // Target gas floor for mined blocks. + GasCeil uint64 // Target gas ceiling for mined blocks. + GasPrice *big.Int // Minimum gas price for mining a transaction + Recommit time.Duration // The time interval for miner to re-create mining work. + Noverify bool // Disable remote mining solution verification(only useful in ethash). +} + +// Miner creates blocks and searches for proof-of-work values. +type Miner struct { + mux *event.TypeMux + worker *worker + coinbase common.Address + eth Backend + engine consensus.Engine + exitCh chan struct{} + startCh chan common.Address + stopCh chan struct{} + + wg sync.WaitGroup +} + +func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(header *types.Header) bool) *Miner { + miner := &Miner{ + eth: eth, + mux: mux, + engine: engine, + exitCh: make(chan struct{}), + startCh: make(chan common.Address), + stopCh: make(chan struct{}), + worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true), + } + miner.wg.Add(1) + go miner.update() + return miner +} + +// update keeps track of the downloader events. Please be aware that this is a one shot type of update loop. +// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and +// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks +// and halt your mining operation for as long as the DOS continues. +func (miner *Miner) update() { + defer miner.wg.Done() + + events := miner.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) + defer func() { + if !events.Closed() { + events.Unsubscribe() + } + }() + + shouldStart := false + canStart := true + dlEventCh := events.Chan() + for { + select { + case ev := <-dlEventCh: + if ev == nil { + // Unsubscription done, stop listening + dlEventCh = nil + continue + } + switch ev.Data.(type) { + case downloader.StartEvent: + wasMining := miner.Mining() + miner.worker.stop() + canStart = false + if wasMining { + // Resume mining after sync was finished + shouldStart = true + log.Info("Mining aborted due to sync") + } + case downloader.FailedEvent: + canStart = true + if shouldStart { + miner.SetEtherbase(miner.coinbase) + miner.worker.start() + } + case downloader.DoneEvent: + canStart = true + if shouldStart { + miner.SetEtherbase(miner.coinbase) + miner.worker.start() + } + // Stop reacting to downloader events + events.Unsubscribe() + } + case addr := <-miner.startCh: + miner.SetEtherbase(addr) + miner.worker.init() + if canStart { + miner.worker.start() + } + shouldStart = true + case <-miner.stopCh: + shouldStart = false + miner.worker.stop() + case <-miner.exitCh: + miner.worker.close() + return + } + } +} + +func (miner *Miner) Start(coinbase common.Address) { + miner.startCh <- coinbase +} + +func (miner *Miner) Stop() { + miner.stopCh <- struct{}{} +} + +func (miner *Miner) Close() { + close(miner.exitCh) + miner.wg.Wait() +} + +func (miner *Miner) Mining() bool { + return miner.worker.isRunning() +} + +func (miner *Miner) Hashrate() uint64 { + if pow, ok := miner.engine.(consensus.PoW); ok { + return uint64(pow.Hashrate()) + } + return 0 +} + +func (miner *Miner) SetExtra(extra []byte) error { + if uint64(len(extra)) > params.MaximumExtraDataSize { + return fmt.Errorf("extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize) + } + miner.worker.setExtra(extra) + return nil +} + +// SetRecommitInterval sets the interval for sealing work resubmitting. +func (miner *Miner) SetRecommitInterval(interval time.Duration) { + miner.worker.setRecommitInterval(interval) +} + +// Pending returns the currently pending block and associated state. +func (miner *Miner) Pending() (*types.Block, *state.StateDB) { + return miner.worker.pending() +} + +// PendingBlock returns the currently pending block. +// +// Note, to access both the pending block and the pending state +// simultaneously, please use Pending(), as the pending state can +// change between multiple method calls +func (miner *Miner) PendingBlock() *types.Block { + return miner.worker.pendingBlock() +} + +// PendingBlockAndReceipts returns the currently pending block and corresponding receipts. +func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) { + return miner.worker.pendingBlockAndReceipts() +} + +func (miner *Miner) SetEtherbase(addr common.Address) { + miner.coinbase = addr + miner.worker.setEtherbase(addr) +} + +// SetGasCeil sets the gaslimit to strive for when mining blocks post 1559. +// For pre-1559 blocks, it sets the ceiling. +func (miner *Miner) SetGasCeil(ceil uint64) { + miner.worker.setGasCeil(ceil) +} + +// EnablePreseal turns on the preseal mining feature. It's enabled by default. +// Note this function shouldn't be exposed to API, it's unnecessary for users +// (miners) to actually know the underlying detail. It's only for outside project +// which uses this library. +func (miner *Miner) EnablePreseal() { + miner.worker.enablePreseal() +} + +// DisablePreseal turns off the preseal mining feature. It's necessary for some +// fake consensus engine which can seal blocks instantaneously. +// Note this function shouldn't be exposed to API, it's unnecessary for users +// (miners) to actually know the underlying detail. It's only for outside project +// which uses this library. +func (miner *Miner) DisablePreseal() { + miner.worker.disablePreseal() +} + +// GetSealingBlock retrieves a sealing block based on the given parameters. +// The returned block is not sealed but all other fields should be filled. +func (miner *Miner) GetSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash) (*types.Block, error) { + return miner.worker.getSealingBlock(parent, timestamp, coinbase, random) +} + +// SubscribePendingLogs starts delivering logs from pending transactions +// to the given channel. +func (miner *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscription { + return miner.worker.pendingLogsFeed.Subscribe(ch) +} diff --git a/sstorminer/miner_test.go b/sstorminer/miner_test.go new file mode 100644 index 000000000000..ac3795444a72 --- /dev/null +++ b/sstorminer/miner_test.go @@ -0,0 +1,282 @@ +// Copyright 2020 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 . + +// Package miner implements Ethereum block creation and mining. +package sstorminer + +import ( + "errors" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/clique" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/ethdb/memorydb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/trie" +) + +type mockBackend struct { + bc *core.BlockChain + txPool *core.TxPool +} + +func NewMockBackend(bc *core.BlockChain, txPool *core.TxPool) *mockBackend { + return &mockBackend{ + bc: bc, + txPool: txPool, + } +} + +func (m *mockBackend) BlockChain() *core.BlockChain { + return m.bc +} + +func (m *mockBackend) TxPool() *core.TxPool { + return m.txPool +} + +func (m *mockBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) { + return nil, errors.New("not supported") +} + +type testBlockChain struct { + statedb *state.StateDB + gasLimit uint64 + chainHeadFeed *event.Feed +} + +func (bc *testBlockChain) CurrentBlock() *types.Block { + return types.NewBlock(&types.Header{ + GasLimit: bc.gasLimit, + }, nil, nil, nil, trie.NewStackTrie(nil)) +} + +func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { + return bc.CurrentBlock() +} + +func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { + return bc.statedb, nil +} + +func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { + return bc.chainHeadFeed.Subscribe(ch) +} + +func TestMiner(t *testing.T) { + miner, mux, cleanup := createMiner(t) + defer cleanup(false) + miner.Start(common.HexToAddress("0x12345")) + waitForMiningState(t, miner, true) + // Start the downloader + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, false) + // Stop the downloader and wait for the update loop to run + mux.Post(downloader.DoneEvent{}) + waitForMiningState(t, miner, true) + + // Subsequent downloader events after a successful DoneEvent should not cause the + // miner to start or stop. This prevents a security vulnerability + // that would allow entities to present fake high blocks that would + // stop mining operations by causing a downloader sync + // until it was discovered they were invalid, whereon mining would resume. + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, true) + + mux.Post(downloader.FailedEvent{}) + waitForMiningState(t, miner, true) +} + +// TestMinerDownloaderFirstFails tests that mining is only +// permitted to run indefinitely once the downloader sees a DoneEvent (success). +// An initial FailedEvent should allow mining to stop on a subsequent +// downloader StartEvent. +func TestMinerDownloaderFirstFails(t *testing.T) { + miner, mux, cleanup := createMiner(t) + defer cleanup(false) + miner.Start(common.HexToAddress("0x12345")) + waitForMiningState(t, miner, true) + // Start the downloader + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, false) + + // Stop the downloader and wait for the update loop to run + mux.Post(downloader.FailedEvent{}) + waitForMiningState(t, miner, true) + + // Since the downloader hasn't yet emitted a successful DoneEvent, + // we expect the miner to stop on next StartEvent. + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, false) + + // Downloader finally succeeds. + mux.Post(downloader.DoneEvent{}) + waitForMiningState(t, miner, true) + + // Downloader starts again. + // Since it has achieved a DoneEvent once, we expect miner + // state to be unchanged. + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, true) + + mux.Post(downloader.FailedEvent{}) + waitForMiningState(t, miner, true) +} + +func TestMinerStartStopAfterDownloaderEvents(t *testing.T) { + miner, mux, cleanup := createMiner(t) + defer cleanup(false) + miner.Start(common.HexToAddress("0x12345")) + waitForMiningState(t, miner, true) + // Start the downloader + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, false) + + // Downloader finally succeeds. + mux.Post(downloader.DoneEvent{}) + waitForMiningState(t, miner, true) + + miner.Stop() + waitForMiningState(t, miner, false) + + miner.Start(common.HexToAddress("0x678910")) + waitForMiningState(t, miner, true) + + miner.Stop() + waitForMiningState(t, miner, false) +} + +func TestStartWhileDownload(t *testing.T) { + miner, mux, cleanup := createMiner(t) + defer cleanup(false) + waitForMiningState(t, miner, false) + miner.Start(common.HexToAddress("0x12345")) + waitForMiningState(t, miner, true) + // Stop the downloader and wait for the update loop to run + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, false) + // Starting the miner after the downloader should not work + miner.Start(common.HexToAddress("0x12345")) + waitForMiningState(t, miner, false) +} + +func TestStartStopMiner(t *testing.T) { + miner, _, cleanup := createMiner(t) + defer cleanup(false) + waitForMiningState(t, miner, false) + miner.Start(common.HexToAddress("0x12345")) + waitForMiningState(t, miner, true) + miner.Stop() + waitForMiningState(t, miner, false) + +} + +func TestCloseMiner(t *testing.T) { + miner, _, cleanup := createMiner(t) + defer cleanup(true) + waitForMiningState(t, miner, false) + miner.Start(common.HexToAddress("0x12345")) + waitForMiningState(t, miner, true) + // Terminate the miner and wait for the update loop to run + miner.Close() + waitForMiningState(t, miner, false) +} + +// TestMinerSetEtherbase checks that etherbase becomes set even if mining isn't +// possible at the moment +func TestMinerSetEtherbase(t *testing.T) { + miner, mux, cleanup := createMiner(t) + defer cleanup(false) + // Start with a 'bad' mining address + miner.Start(common.HexToAddress("0xdead")) + waitForMiningState(t, miner, true) + // Start the downloader + mux.Post(downloader.StartEvent{}) + waitForMiningState(t, miner, false) + // Now user tries to configure proper mining address + miner.Start(common.HexToAddress("0x1337")) + // Stop the downloader and wait for the update loop to run + mux.Post(downloader.DoneEvent{}) + + waitForMiningState(t, miner, true) + // The miner should now be using the good address + if got, exp := miner.coinbase, common.HexToAddress("0x1337"); got != exp { + t.Fatalf("Wrong coinbase, got %x expected %x", got, exp) + } +} + +// waitForMiningState waits until either +// * the desired mining state was reached +// * a timeout was reached which fails the test +func waitForMiningState(t *testing.T, m *Miner, mining bool) { + t.Helper() + + var state bool + for i := 0; i < 100; i++ { + time.Sleep(10 * time.Millisecond) + if state = m.Mining(); state == mining { + return + } + } + t.Fatalf("Mining() == %t, want %t", state, mining) +} + +func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) { + // Create Ethash config + config := Config{ + Etherbase: common.HexToAddress("123456789"), + } + // Create chainConfig + memdb := memorydb.New() + chainDB := rawdb.NewDatabase(memdb) + genesis := core.DeveloperGenesisBlock(15, 11_500_000, common.HexToAddress("12345")) + chainConfig, _, err := core.SetupGenesisBlock(chainDB, genesis) + if err != nil { + t.Fatalf("can't create new chain config: %v", err) + } + // Create consensus engine + engine := clique.New(chainConfig.Clique, chainDB) + // Create Ethereum backend + bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("can't create new chain %v", err) + } + statedb, _ := state.New(common.Hash{}, state.NewDatabase(chainDB), nil) + blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)} + + pool := core.NewTxPool(testTxPoolConfig, chainConfig, blockchain) + backend := NewMockBackend(bc, pool) + // Create event Mux + mux := new(event.TypeMux) + // Create Miner + miner := New(backend, &config, chainConfig, mux, engine, nil) + cleanup := func(skipMiner bool) { + bc.Stop() + engine.Close() + pool.Stop() + if !skipMiner { + miner.Close() + } + } + return miner, mux, cleanup +} diff --git a/sstorminer/unconfirmed.go b/sstorminer/unconfirmed.go new file mode 100644 index 000000000000..d7c371d816c9 --- /dev/null +++ b/sstorminer/unconfirmed.go @@ -0,0 +1,136 @@ +// 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 . + +package sstorminer + +import ( + "container/ring" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" +) + +// chainRetriever is used by the unconfirmed block set to verify whether a previously +// mined block is part of the canonical chain or not. +type chainRetriever interface { + // GetHeaderByNumber retrieves the canonical header associated with a block number. + GetHeaderByNumber(number uint64) *types.Header + + // GetBlockByNumber retrieves the canonical block associated with a block number. + GetBlockByNumber(number uint64) *types.Block +} + +// unconfirmedBlock is a small collection of metadata about a locally mined block +// that is placed into a unconfirmed set for canonical chain inclusion tracking. +type unconfirmedBlock struct { + index uint64 + hash common.Hash +} + +// unconfirmedBlocks implements a data structure to maintain locally mined blocks +// have not yet reached enough maturity to guarantee chain inclusion. It is +// used by the miner to provide logs to the user when a previously mined block +// has a high enough guarantee to not be reorged out of the canonical chain. +type unconfirmedBlocks struct { + chain chainRetriever // Blockchain to verify canonical status through + depth uint // Depth after which to discard previous blocks + blocks *ring.Ring // Block infos to allow canonical chain cross checks + lock sync.Mutex // Protects the fields from concurrent access +} + +// newUnconfirmedBlocks returns new data structure to track currently unconfirmed blocks. +func newUnconfirmedBlocks(chain chainRetriever, depth uint) *unconfirmedBlocks { + return &unconfirmedBlocks{ + chain: chain, + depth: depth, + } +} + +// Insert adds a new block to the set of unconfirmed ones. +func (set *unconfirmedBlocks) Insert(index uint64, hash common.Hash) { + // If a new block was mined locally, shift out any old enough blocks + set.Shift(index) + + // Create the new item as its own ring + item := ring.New(1) + item.Value = &unconfirmedBlock{ + index: index, + hash: hash, + } + // Set as the initial ring or append to the end + set.lock.Lock() + defer set.lock.Unlock() + + if set.blocks == nil { + set.blocks = item + } else { + set.blocks.Move(-1).Link(item) + } + // Display a log for the user to notify of a new mined block unconfirmed + log.Info("🔨 mined potential block", "number", index, "hash", hash) +} + +// Shift drops all unconfirmed blocks from the set which exceed the unconfirmed sets depth +// allowance, checking them against the canonical chain for inclusion or staleness +// report. +func (set *unconfirmedBlocks) Shift(height uint64) { + set.lock.Lock() + defer set.lock.Unlock() + + for set.blocks != nil { + // Retrieve the next unconfirmed block and abort if too fresh + next := set.blocks.Value.(*unconfirmedBlock) + if next.index+uint64(set.depth) > height { + break + } + // Block seems to exceed depth allowance, check for canonical status + header := set.chain.GetHeaderByNumber(next.index) + switch { + case header == nil: + log.Warn("Failed to retrieve header of mined block", "number", next.index, "hash", next.hash) + case header.Hash() == next.hash: + log.Info("🔗 block reached canonical chain", "number", next.index, "hash", next.hash) + default: + // Block is not canonical, check whether we have an uncle or a lost block + included := false + for number := next.index; !included && number < next.index+uint64(set.depth) && number <= height; number++ { + if block := set.chain.GetBlockByNumber(number); block != nil { + for _, uncle := range block.Uncles() { + if uncle.Hash() == next.hash { + included = true + break + } + } + } + } + if included { + log.Info("â‘‚ block became an uncle", "number", next.index, "hash", next.hash) + } else { + log.Info("😱 block lost", "number", next.index, "hash", next.hash) + } + } + // Drop the block out of the ring + if set.blocks.Value == set.blocks.Next().Value { + set.blocks = nil + } else { + set.blocks = set.blocks.Move(-1) + set.blocks.Unlink(1) + set.blocks = set.blocks.Move(1) + } + } +} diff --git a/sstorminer/unconfirmed_test.go b/sstorminer/unconfirmed_test.go new file mode 100644 index 000000000000..6126c377784a --- /dev/null +++ b/sstorminer/unconfirmed_test.go @@ -0,0 +1,87 @@ +// 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 . + +package sstorminer + +import ( + "testing" + + "github.com/ethereum/go-ethereum/core/types" +) + +// noopChainRetriever is an implementation of headerRetriever that always +// returns nil for any requested headers. +type noopChainRetriever struct{} + +func (r *noopChainRetriever) GetHeaderByNumber(number uint64) *types.Header { + return nil +} +func (r *noopChainRetriever) GetBlockByNumber(number uint64) *types.Block { + return nil +} + +// Tests that inserting blocks into the unconfirmed set accumulates them until +// the desired depth is reached, after which they begin to be dropped. +func TestUnconfirmedInsertBounds(t *testing.T) { + limit := uint(10) + + pool := newUnconfirmedBlocks(new(noopChainRetriever), limit) + for depth := uint64(0); depth < 2*uint64(limit); depth++ { + // Insert multiple blocks for the same level just to stress it + for i := 0; i < int(depth); i++ { + pool.Insert(depth, [32]byte{byte(depth), byte(i)}) + } + // Validate that no blocks below the depth allowance are left in + pool.blocks.Do(func(block interface{}) { + if block := block.(*unconfirmedBlock); block.index+uint64(limit) <= depth { + t.Errorf("depth %d: block %x not dropped", depth, block.hash) + } + }) + } +} + +// Tests that shifting blocks out of the unconfirmed set works both for normal +// cases as well as for corner cases such as empty sets, empty shifts or full +// shifts. +func TestUnconfirmedShifts(t *testing.T) { + // Create a pool with a few blocks on various depths + limit, start := uint(10), uint64(25) + + pool := newUnconfirmedBlocks(new(noopChainRetriever), limit) + for depth := start; depth < start+uint64(limit); depth++ { + pool.Insert(depth, [32]byte{byte(depth)}) + } + // Try to shift below the limit and ensure no blocks are dropped + pool.Shift(start + uint64(limit) - 1) + if n := pool.blocks.Len(); n != int(limit) { + t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit) + } + // Try to shift half the blocks out and verify remainder + pool.Shift(start + uint64(limit) - 1 + uint64(limit/2)) + if n := pool.blocks.Len(); n != int(limit)/2 { + t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit/2) + } + // Try to shift all the remaining blocks out and verify emptyness + pool.Shift(start + 2*uint64(limit)) + if n := pool.blocks.Len(); n != 0 { + t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) + } + // Try to shift out from the empty set and make sure it doesn't break + pool.Shift(start + 3*uint64(limit)) + if n := pool.blocks.Len(); n != 0 { + t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) + } +} diff --git a/sstorminer/worker.go b/sstorminer/worker.go new file mode 100644 index 000000000000..d9ce0b5b01fe --- /dev/null +++ b/sstorminer/worker.go @@ -0,0 +1,1172 @@ +// 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 . + +package sstorminer + +import ( + "errors" + "fmt" + "math/big" + "sync" + "sync/atomic" + "time" + + mapset "github.com/deckarep/golang-set" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/misc" + "github.com/ethereum/go-ethereum/consensus/tendermint" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie" +) + +const ( + // resultQueueSize is the size of channel listening to sealing result. + resultQueueSize = 10 + + // txChanSize is the size of channel listening to NewTxsEvent. + // The number is referenced from the size of tx pool. + txChanSize = 4096 + + // chainHeadChanSize is the size of channel listening to ChainHeadEvent. + chainHeadChanSize = 10 + + // chainSideChanSize is the size of channel listening to ChainSideEvent. + chainSideChanSize = 10 + + // resubmitAdjustChanSize is the size of resubmitting interval adjustment channel. + resubmitAdjustChanSize = 10 + + // sealingLogAtDepth is the number of confirmations before logging successful sealing. + sealingLogAtDepth = 7 + + // minRecommitInterval is the minimal time interval to recreate the sealing block with + // any newly arrived transactions. + minRecommitInterval = 1 * time.Second + + // maxRecommitInterval is the maximum time interval to recreate the sealing block with + // any newly arrived transactions. + maxRecommitInterval = 15 * time.Second + + // intervalAdjustRatio is the impact a single interval adjustment has on sealing work + // resubmitting interval. + intervalAdjustRatio = 0.1 + + // intervalAdjustBias is applied during the new resubmit interval calculation in favor of + // increasing upper limit or decreasing lower limit so that the limit can be reachable. + intervalAdjustBias = 200 * 1000.0 * 1000.0 + + // staleThreshold is the maximum depth of the acceptable stale block. + staleThreshold = 7 +) + +// environment is the worker's current environment and holds all +// information of the sealing block generation. +type environment struct { + signer types.Signer + + state *state.StateDB // apply state changes here + ancestors mapset.Set // ancestor set (used for checking uncle parent validity) + family mapset.Set // family set (used for checking uncle invalidity) + tcount int // tx count in cycle + totalCalldata int // total calldata in cycle + gasPool *core.GasPool // available gas used to pack transactions + coinbase common.Address + + header *types.Header + txs []*types.Transaction + receipts []*types.Receipt + uncles map[common.Hash]*types.Header +} + +// copy creates a deep copy of environment. +func (env *environment) copy() *environment { + cpy := &environment{ + signer: env.signer, + state: env.state.Copy(), + ancestors: env.ancestors.Clone(), + family: env.family.Clone(), + tcount: env.tcount, + totalCalldata: env.totalCalldata, + coinbase: env.coinbase, + header: types.CopyHeader(env.header), + receipts: copyReceipts(env.receipts), + } + if env.gasPool != nil { + gasPool := *env.gasPool + cpy.gasPool = &gasPool + } + // The content of txs and uncles are immutable, unnecessary + // to do the expensive deep copy for them. + cpy.txs = make([]*types.Transaction, len(env.txs)) + copy(cpy.txs, env.txs) + cpy.uncles = make(map[common.Hash]*types.Header) + for hash, uncle := range env.uncles { + cpy.uncles[hash] = uncle + } + return cpy +} + +// unclelist returns the contained uncles as the list format. +func (env *environment) unclelist() []*types.Header { + var uncles []*types.Header + for _, uncle := range env.uncles { + uncles = append(uncles, uncle) + } + return uncles +} + +// discard terminates the background prefetcher go-routine. It should +// always be called for all created environment instances otherwise +// the go-routine leak can happen. +func (env *environment) discard() { + if env.state == nil { + return + } + env.state.StopPrefetcher() +} + +// task contains all information for consensus engine sealing and result submitting. +type task struct { + receipts []*types.Receipt + state *state.StateDB + block *types.Block + createdAt time.Time +} + +const ( + commitInterruptNone int32 = iota + commitInterruptNewHead + commitInterruptResubmit +) + +// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier. +type newWorkReq struct { + interrupt *int32 + noempty bool + timestamp int64 +} + +// getWorkReq represents a request for getting a new sealing work with provided parameters. +type getWorkReq struct { + params *generateParams + err error + result chan *types.Block +} + +// intervalAdjust represents a resubmitting interval adjustment. +type intervalAdjust struct { + ratio float64 + inc bool +} + +// worker is the main object which takes care of submitting new work to consensus engine +// and gathering the sealing result. +type worker struct { + config *Config + chainConfig *params.ChainConfig + engine consensus.Engine + eth Backend + chain *core.BlockChain + + // Subscriptions + mux *event.TypeMux + chainHeadCh chan core.ChainHeadEvent + chainHeadSub event.Subscription + + // Channels + newWorkCh chan *newWorkReq + getWorkCh chan *getWorkReq + taskCh chan *task + resultCh chan *types.Block + startCh chan struct{} + exitCh chan struct{} + resubmitIntervalCh chan time.Duration + resubmitAdjustCh chan *intervalAdjust + + wg sync.WaitGroup + + current *environment // An environment for current running cycle. + localUncles map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks. + remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks. + unconfirmed *unconfirmedBlocks // A set of locally mined blocks pending canonicalness confirmations. + + mu sync.RWMutex // The lock used to protect the coinbase and extra fields + coinbase common.Address + extra []byte + + pendingMu sync.RWMutex + pendingTasks map[common.Hash]*task + + snapshotMu sync.RWMutex // The lock used to protect the snapshots below + snapshotBlock *types.Block + snapshotReceipts types.Receipts + snapshotState *state.StateDB + + // atomic status counters + running int32 // The indicator whether the consensus engine is running or not. + newTxs int32 // New arrival transaction count since last sealing work submitting. + + // noempty is the flag used to control whether the feature of pre-seal empty + // block is enabled. The default value is false(pre-seal is enabled by default). + // But in some special scenario the consensus engine will seal blocks instantaneously, + // in this case this feature will add all empty blocks into canonical chain + // non-stop and no real transaction will be included. + noempty uint32 + + // External functions + isLocalBlock func(header *types.Header) bool // Function used to determine whether the specified block is mined by local miner. + + // Test hooks + newTaskHook func(*task) // Method to call upon receiving a new sealing task. + skipSealHook func(*task) bool // Method to decide whether skipping the sealing. + fullTaskHook func() // Method to call before pushing the full sealing task. + resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval. +} + +func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(header *types.Header) bool, init bool) *worker { + worker := &worker{ + config: config, + chainConfig: chainConfig, + engine: engine, + eth: eth, + mux: mux, + chain: eth.BlockChain(), + isLocalBlock: isLocalBlock, + localUncles: make(map[common.Hash]*types.Block), + remoteUncles: make(map[common.Hash]*types.Block), + unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), sealingLogAtDepth), + pendingTasks: make(map[common.Hash]*task), + chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), + newWorkCh: make(chan *newWorkReq), + getWorkCh: make(chan *getWorkReq), + taskCh: make(chan *task), + resultCh: make(chan *types.Block, resultQueueSize), + exitCh: make(chan struct{}), + startCh: make(chan struct{}, 1), + resubmitIntervalCh: make(chan time.Duration), + resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), + } + // Subscribe events for blockchain + worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) + + // Sanitize recommit interval if the user-specified one is too short. + recommit := worker.config.Recommit + if recommit < minRecommitInterval { + log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval) + recommit = minRecommitInterval + } + + worker.wg.Add(4) + go worker.mainLoop() + go worker.newWorkLoop(recommit) + go worker.resultLoop() + go worker.taskLoop() + + // Submit first work to initialize pending state. + if init { + worker.startCh <- struct{}{} + } + return worker +} + +// setEtherbase sets the etherbase used to initialize the block coinbase field. +func (w *worker) setEtherbase(addr common.Address) { + w.mu.Lock() + defer w.mu.Unlock() + w.coinbase = addr +} + +func (w *worker) setGasCeil(ceil uint64) { + w.mu.Lock() + defer w.mu.Unlock() + w.config.GasCeil = ceil +} + +// setExtra sets the content used to initialize the block extra field. +func (w *worker) setExtra(extra []byte) { + w.mu.Lock() + defer w.mu.Unlock() + w.extra = extra +} + +// setRecommitInterval updates the interval for miner sealing work recommitting. +func (w *worker) setRecommitInterval(interval time.Duration) { + select { + case w.resubmitIntervalCh <- interval: + case <-w.exitCh: + } +} + +// disablePreseal disables pre-sealing feature +func (w *worker) disablePreseal() { + atomic.StoreUint32(&w.noempty, 1) +} + +// enablePreseal enables pre-sealing feature +func (w *worker) enablePreseal() { + atomic.StoreUint32(&w.noempty, 0) +} + +// pending returns the pending state and corresponding block. +func (w *worker) pending() (*types.Block, *state.StateDB) { + // return a snapshot to avoid contention on currentMu mutex + w.snapshotMu.RLock() + defer w.snapshotMu.RUnlock() + if w.snapshotState == nil { + return nil, nil + } + return w.snapshotBlock, w.snapshotState.Copy() +} + +// pendingBlock returns pending block. +func (w *worker) pendingBlock() *types.Block { + // return a snapshot to avoid contention on currentMu mutex + w.snapshotMu.RLock() + defer w.snapshotMu.RUnlock() + return w.snapshotBlock +} + +// pendingBlockAndReceipts returns pending block and corresponding receipts. +func (w *worker) pendingBlockAndReceipts() (*types.Block, types.Receipts) { + // return a snapshot to avoid contention on currentMu mutex + w.snapshotMu.RLock() + defer w.snapshotMu.RUnlock() + return w.snapshotBlock, w.snapshotReceipts +} + +func (w *worker) init() { + tm, isTm := w.engine.(*tendermint.Tendermint) + if isTm { + err := tm.Init(w.chain, func(parent common.Hash, coinbase common.Address, timestamp uint64) (*types.Block, error) { + return w.getSealingBlock(parent, timestamp, coinbase, common.Hash{}) + }, w.mux) + if err != nil { + log.Crit("tm.Init", "err", err) + } + } +} + +// start sets the running status as 1 and triggers new work submitting. +func (w *worker) start() { + atomic.StoreInt32(&w.running, 1) + w.startCh <- struct{}{} +} + +// stop sets the running status as 0. +func (w *worker) stop() { + atomic.StoreInt32(&w.running, 0) +} + +// isRunning returns an indicator whether worker is running or not. +func (w *worker) isRunning() bool { + return atomic.LoadInt32(&w.running) == 1 +} + +// close terminates all background threads maintained by the worker. +// Note the worker does not support being closed multiple times. +func (w *worker) close() { + atomic.StoreInt32(&w.running, 0) + close(w.exitCh) + w.wg.Wait() +} + +// recalcRecommit recalculates the resubmitting interval upon feedback. +func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) time.Duration { + var ( + prevF = float64(prev.Nanoseconds()) + next float64 + ) + if inc { + next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target+intervalAdjustBias) + max := float64(maxRecommitInterval.Nanoseconds()) + if next > max { + next = max + } + } else { + next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target-intervalAdjustBias) + min := float64(minRecommit.Nanoseconds()) + if next < min { + next = min + } + } + return time.Duration(int64(next)) +} + +// newWorkLoop is a standalone goroutine to submit new sealing work upon received events. +func (w *worker) newWorkLoop(recommit time.Duration) { + defer w.wg.Done() + var ( + interrupt *int32 + minRecommit = recommit // minimal resubmit interval specified by user. + timestamp int64 // timestamp for each round of sealing. + ) + + timer := time.NewTimer(0) + defer timer.Stop() + <-timer.C // discard the initial tick + + // commit aborts in-flight transaction execution with given signal and resubmits a new one. + commit := func(noempty bool, s int32) { + if interrupt != nil { + atomic.StoreInt32(interrupt, s) + } + interrupt = new(int32) + select { + case w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp}: + case <-w.exitCh: + return + } + timer.Reset(recommit) + atomic.StoreInt32(&w.newTxs, 0) + } + // clearPending cleans the stale pending tasks. + clearPending := func(number uint64) { + w.pendingMu.Lock() + for h, t := range w.pendingTasks { + if t.block.NumberU64()+staleThreshold <= number { + delete(w.pendingTasks, h) + } + } + w.pendingMu.Unlock() + } + + for { + select { + case <-w.startCh: + clearPending(w.chain.CurrentBlock().NumberU64()) + + timestamp = time.Now().Unix() + commit(false, commitInterruptNewHead) + + case head := <-w.chainHeadCh: + clearPending(head.Block.NumberU64()) + + timestamp = time.Now().Unix() + commit(false, commitInterruptNewHead) + + case <-timer.C: + // If sealing is running resubmit a new work cycle periodically to pull in + // higher priced transactions. Disable this overhead for pending blocks. + if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) { + // Short circuit if no new transaction arrives. + if atomic.LoadInt32(&w.newTxs) == 0 { + timer.Reset(recommit) + continue + } + commit(true, commitInterruptResubmit) + } + + case interval := <-w.resubmitIntervalCh: + // Adjust resubmit interval explicitly by user. + if interval < minRecommitInterval { + log.Warn("Sanitizing miner recommit interval", "provided", interval, "updated", minRecommitInterval) + interval = minRecommitInterval + } + log.Info("Miner recommit interval update", "from", minRecommit, "to", interval) + minRecommit, recommit = interval, interval + + if w.resubmitHook != nil { + w.resubmitHook(minRecommit, recommit) + } + + case adjust := <-w.resubmitAdjustCh: + // Adjust resubmit interval by feedback. + if adjust.inc { + before := recommit + target := float64(recommit.Nanoseconds()) / adjust.ratio + recommit = recalcRecommit(minRecommit, recommit, target, true) + log.Trace("Increase miner recommit interval", "from", before, "to", recommit) + } else { + before := recommit + recommit = recalcRecommit(minRecommit, recommit, float64(minRecommit.Nanoseconds()), false) + log.Trace("Decrease miner recommit interval", "from", before, "to", recommit) + } + + if w.resubmitHook != nil { + w.resubmitHook(minRecommit, recommit) + } + + case <-w.exitCh: + return + } + } +} + +// mainLoop is responsible for generating and submitting sealing work based on +// the received event. It can support two modes: automatically generate task and +// submit it or return task according to given parameters for various proposes. +func (w *worker) mainLoop() { + defer w.wg.Done() + defer w.chainHeadSub.Unsubscribe() + defer func() { + if w.current != nil { + w.current.discard() + } + }() + + cleanTicker := time.NewTicker(time.Second * 10) + defer cleanTicker.Stop() + + for { + select { + case req := <-w.newWorkCh: + w.commitWork(req.interrupt, req.noempty, req.timestamp) + + case req := <-w.getWorkCh: + block, err := w.generateWork(req.params) + if err != nil { + req.err = err + req.result <- nil + } else { + req.result <- block + } + + case <-cleanTicker.C: + chainHead := w.chain.CurrentBlock() + for hash, uncle := range w.localUncles { + if uncle.NumberU64()+staleThreshold <= chainHead.NumberU64() { + delete(w.localUncles, hash) + } + } + for hash, uncle := range w.remoteUncles { + if uncle.NumberU64()+staleThreshold <= chainHead.NumberU64() { + delete(w.remoteUncles, hash) + } + } + + // System stopped + case <-w.exitCh: + return + case <-w.chainHeadSub.Err(): + return + } + } +} + +// taskLoop is a standalone goroutine to fetch sealing task from the generator and +// push them to consensus engine. +func (w *worker) taskLoop() { + defer w.wg.Done() + var ( + stopCh chan struct{} + prev common.Hash + ) + + // interrupt aborts the in-flight sealing task. + interrupt := func() { + if stopCh != nil { + close(stopCh) + stopCh = nil + } + } + for { + select { + case task := <-w.taskCh: + if w.newTaskHook != nil { + w.newTaskHook(task) + } + // Reject duplicate sealing work due to resubmitting. + sealHash := w.engine.SealHash(task.block.Header()) + if sealHash == prev { + continue + } + // Interrupt previous sealing operation + interrupt() + stopCh, prev = make(chan struct{}), sealHash + + if w.skipSealHook != nil && w.skipSealHook(task) { + continue + } + w.pendingMu.Lock() + w.pendingTasks[sealHash] = task + w.pendingMu.Unlock() + + if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil { + log.Warn("Block sealing failed", "err", err) + w.pendingMu.Lock() + delete(w.pendingTasks, sealHash) + w.pendingMu.Unlock() + } + case <-w.exitCh: + interrupt() + return + } + } +} + +// resultLoop is a standalone goroutine to handle sealing result submitting +// and flush relative data to the database. +func (w *worker) resultLoop() { + defer w.wg.Done() + for { + select { + case block := <-w.resultCh: + // Short circuit when receiving empty result. + if block == nil { + continue + } + // Short circuit when receiving duplicate result caused by resubmitting. + if w.chain.HasBlock(block.Hash(), block.NumberU64()) { + continue + } + var ( + sealhash = w.engine.SealHash(block.Header()) + hash = block.Hash() + ) + w.pendingMu.RLock() + task, exist := w.pendingTasks[sealhash] + w.pendingMu.RUnlock() + if !exist { + log.Error("Block found but no relative pending task", "number", block.Number(), "sealhash", sealhash, "hash", hash) + continue + } + // Different block could share same sealhash, deep copy here to prevent write-write conflict. + var ( + receipts = make([]*types.Receipt, len(task.receipts)) + logs []*types.Log + ) + for i, taskReceipt := range task.receipts { + receipt := new(types.Receipt) + receipts[i] = receipt + *receipt = *taskReceipt + + // add block location fields + receipt.BlockHash = hash + receipt.BlockNumber = block.Number() + receipt.TransactionIndex = uint(i) + + // Update the block hash in all logs since it is now available and not when the + // receipt/log of individual transactions were created. + receipt.Logs = make([]*types.Log, len(taskReceipt.Logs)) + for i, taskLog := range taskReceipt.Logs { + log := new(types.Log) + receipt.Logs[i] = log + *log = *taskLog + log.BlockHash = hash + } + logs = append(logs, receipt.Logs...) + } + // Commit block and state to database. + _, err := w.chain.WriteBlockAndSetHead(block, receipts, logs, task.state, true) + if err != nil { + log.Error("Failed writing block to chain", "err", err) + continue + } + log.Info("Successfully sealed new block", "number", block.Number(), "sealhash", sealhash, "hash", hash, + "elapsed", common.PrettyDuration(time.Since(task.createdAt))) + + // Broadcast the block and announce chain insertion event + w.mux.Post(core.NewMinedBlockEvent{Block: block}) + + // Insert the block into the set of pending ones to resultLoop for confirmations + w.unconfirmed.Insert(block.NumberU64(), block.Hash()) + + case <-w.exitCh: + return + } + } +} + +// makeEnv creates a new environment for the sealing block. +func (w *worker) makeEnv(parent *types.Block, header *types.Header, coinbase common.Address) (*environment, error) { + // Retrieve the parent state to execute on top and start a prefetcher for + // the miner to speed block sealing up a bit. + state, err := w.chain.StateAt(parent.Root()) + if err != nil { + // Note since the sealing block can be created upon the arbitrary parent + // block, but the state of parent block may already be pruned, so the necessary + // state recovery is needed here in the future. + // + // The maximum acceptable reorg depth can be limited by the finalised block + // somehow. TODO(rjl493456442) fix the hard-coded number here later. + state, err = w.eth.StateAtBlock(parent, 1024, nil, false, false) + log.Warn("Recovered mining state", "root", parent.Root(), "err", err) + } + if err != nil { + return nil, err + } + state.StartPrefetcher("miner") + + // Note the passed coinbase may be different with header.Coinbase. + env := &environment{ + signer: types.MakeSigner(w.chainConfig, header.Number), + state: state, + coinbase: coinbase, + ancestors: mapset.NewSet(), + family: mapset.NewSet(), + header: header, + uncles: make(map[common.Hash]*types.Header), + } + // when 08 is processed ancestors contain 07 (quick block) + for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { + for _, uncle := range ancestor.Uncles() { + env.family.Add(uncle.Hash()) + } + env.family.Add(ancestor.Hash()) + env.ancestors.Add(ancestor.Hash()) + } + // Keep track of transactions which return errors so they can be removed + env.tcount = 0 + env.totalCalldata = 0 + return env, nil +} + +// commitUncle adds the given block to uncle block set, returns error if failed to add. +func (w *worker) commitUncle(env *environment, uncle *types.Header) error { + if w.isTTDReached(env.header) { + return errors.New("ignore uncle for beacon block") + } + hash := uncle.Hash() + if _, exist := env.uncles[hash]; exist { + return errors.New("uncle not unique") + } + if env.header.ParentHash == uncle.ParentHash { + return errors.New("uncle is sibling") + } + if !env.ancestors.Contains(uncle.ParentHash) { + return errors.New("uncle's parent unknown") + } + if env.family.Contains(hash) { + return errors.New("uncle already included") + } + env.uncles[hash] = uncle + return nil +} + +// updateSnapshot updates pending snapshot block, receipts and state. +func (w *worker) updateSnapshot(env *environment) { + w.snapshotMu.Lock() + defer w.snapshotMu.Unlock() + + w.snapshotBlock = types.NewBlock( + env.header, + env.txs, + env.unclelist(), + env.receipts, + trie.NewStackTrie(nil), + ) + w.snapshotReceipts = copyReceipts(env.receipts) + w.snapshotState = env.state.Copy() +} + +func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) { + snap := env.state.Snapshot() + + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig()) + if err != nil { + env.state.RevertToSnapshot(snap) + return nil, err + } + env.txs = append(env.txs, tx) + env.receipts = append(env.receipts, receipt) + + return receipt.Logs, nil +} + +func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32) bool { + gasLimit := env.header.GasLimit + if env.gasPool == nil { + env.gasPool = new(core.GasPool).AddGas(gasLimit) + } + var coalescedLogs []*types.Log + + for { + // In the following three cases, we will interrupt the execution of the transaction. + // (1) new head block event arrival, the interrupt signal is 1 + // (2) worker start or restart, the interrupt signal is 1 + // (3) worker recreate the sealing block with any newly arrived transactions, the interrupt signal is 2. + // For the first two cases, the semi-finished work will be discarded. + // For the third case, the semi-finished work will be submitted to the consensus engine. + if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone { + // Notify resubmit loop to increase resubmitting interval due to too frequent commits. + if atomic.LoadInt32(interrupt) == commitInterruptResubmit { + ratio := float64(gasLimit-env.gasPool.Gas()) / float64(gasLimit) + if ratio < 0.1 { + ratio = 0.1 + } + w.resubmitAdjustCh <- &intervalAdjust{ + ratio: ratio, + inc: true, + } + } + return atomic.LoadInt32(interrupt) == commitInterruptNewHead + } + // If we don't have enough gas for any further transactions then we're done + if env.gasPool.Gas() < params.TxGas { + log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) + break + } + + // Retrieve the next transaction and abort if all done + tx := txs.Peek() + if tx == nil { + break + } + + if w.chainConfig.IsPisa(env.header.Number) && env.totalCalldata+len(tx.Data()) > core.MaxCalldataEIP4488(env.tcount+1) { + log.Trace("Total transaction calldata exceeded, ignoring transaction", "hash", tx.Hash(), "eip4488", w.chainConfig.PisaBlock) + + break + } + + // Error may be ignored here. The error has already been checked + // during transaction acceptance is the transaction pool. + // + // We use the eip155 signer regardless of the current hf. + from, _ := types.Sender(env.signer, tx) + // Check whether the tx is replay protected. If we're not in the EIP155 hf + // phase, start ignoring the sender until we do. + if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { + log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) + + txs.Pop() + continue + } + // Start executing the transaction + env.state.Prepare(tx.Hash(), env.tcount) + + logs, err := w.commitTransaction(env, tx) + switch { + case errors.Is(err, core.ErrGasLimitReached): + // Pop the current out-of-gas transaction without shifting in the next from the account + log.Trace("Gas limit exceeded for current block", "sender", from) + txs.Pop() + + case errors.Is(err, core.ErrNonceTooLow): + // New head notification data race between the transaction pool and miner, shift + log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) + txs.Shift() + + case errors.Is(err, core.ErrNonceTooHigh): + // Reorg notification data race between the transaction pool and miner, skip account = + log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) + txs.Pop() + + case errors.Is(err, nil): + // Everything ok, collect the logs and shift in the next transaction from the same account + coalescedLogs = append(coalescedLogs, logs...) + env.tcount++ + env.totalCalldata += len(tx.Data()) + txs.Shift() + + case errors.Is(err, core.ErrTxTypeNotSupported): + // Pop the unsupported transaction without shifting in the next from the account + log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) + txs.Pop() + + default: + // Strange error, discard the transaction and get the next in line (note, the + // nonce-too-high clause will prevent us from executing in vain). + log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) + txs.Shift() + } + } + + if !w.isRunning() && len(coalescedLogs) > 0 { + // We don't push the pendingLogsEvent while we are sealing. The reason is that + // when we are sealing, the worker will regenerate a sealing block every 3 seconds. + // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. + + // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined + // logs by filling in the block hash when the block was mined by the local miner. This can + // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. + cpy := make([]*types.Log, len(coalescedLogs)) + for i, l := range coalescedLogs { + cpy[i] = new(types.Log) + *cpy[i] = *l + } + w.pendingLogsFeed.Send(cpy) + } + // Notify resubmit loop to decrease resubmitting interval if current interval is larger + // than the user-specified one. + if interrupt != nil { + w.resubmitAdjustCh <- &intervalAdjust{inc: false} + } + return false +} + +// generateParams wraps various of settings for generating sealing task. +type generateParams struct { + timestamp uint64 // The timstamp for sealing task + forceTime bool // Flag whether the given timestamp is immutable or not + parentHash common.Hash // Parent block hash, empty means the latest chain head + coinbase common.Address // The fee recipient address for including transaction + random common.Hash // The randomness generated by beacon chain, empty before the merge + noUncle bool // Flag whether the uncle block inclusion is allowed + noExtra bool // Flag whether the extra field assignment is allowed +} + +// prepareWork constructs the sealing task according to the given parameters, +// either based on the last chain head or specified parent. In this function +// the pending transactions are not filled yet, only the empty task returned. +func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { + w.mu.RLock() + defer w.mu.RUnlock() + + // Find the parent block for sealing task + parent := w.chain.CurrentBlock() + if genParams.parentHash != (common.Hash{}) { + parent = w.chain.GetBlockByHash(genParams.parentHash) + } + if parent == nil { + return nil, fmt.Errorf("missing parent") + } + // Sanity check the timestamp correctness, recap the timestamp + // to parent+1 if the mutation is allowed. + timestamp := genParams.timestamp + if parent.Time() >= timestamp { + if genParams.forceTime { + return nil, fmt.Errorf("invalid timestamp, parent %d given %d", parent.Time(), timestamp) + } + timestamp = parent.Time() + 1 + } + // Construct the sealing block header, set the extra field if it's allowed + num := parent.Number() + header := &types.Header{ + ParentHash: parent.Hash(), + Number: num.Add(num, common.Big1), + GasLimit: core.CalcGasLimit(parent.GasLimit(), w.config.GasCeil), + Time: timestamp, + Coinbase: genParams.coinbase, + } + if !genParams.noExtra && len(w.extra) != 0 { + header.Extra = w.extra + } + // Set the randomness field from the beacon chain if it's available. + if genParams.random != (common.Hash{}) { + header.MixDigest = genParams.random + } + // Set baseFee and GasLimit if we are on an EIP-1559 chain + if w.chainConfig.IsLondon(header.Number) { + header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header()) + if !w.chainConfig.IsLondon(parent.Number()) { + parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier + header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil) + } + } + // Run the consensus preparation with the default or customized consensus engine. + if err := w.engine.Prepare(w.chain, header); err != nil { + log.Error("Failed to prepare header for sealing", "err", err) + return nil, err + } + // Could potentially happen if starting to mine in an odd state. + // Note genParams.coinbase can be different with header.Coinbase + // since clique algorithm can modify the coinbase field in header. + env, err := w.makeEnv(parent, header, genParams.coinbase) + if err != nil { + log.Error("Failed to create sealing context", "err", err) + return nil, err + } + // Accumulate the uncles for the sealing work only if it's allowed. + if !genParams.noUncle { + commitUncles := func(blocks map[common.Hash]*types.Block) { + for hash, uncle := range blocks { + if len(env.uncles) == 2 { + break + } + if err := w.commitUncle(env, uncle.Header()); err != nil { + log.Trace("Possible uncle rejected", "hash", hash, "reason", err) + } else { + log.Debug("Committing new uncle to block", "hash", hash) + } + } + } + // Prefer to locally generated uncle + commitUncles(w.localUncles) + commitUncles(w.remoteUncles) + } + return env, nil +} + +// fillTransactions retrieves the pending transactions from the txpool and fills them +// into the given sealing block. The transaction selection and ordering strategy can +// be customized with the plugin in the future. +func (w *worker) fillTransactions(interrupt *int32, env *environment) { + // Split the pending transactions into locals and remotes + // Fill the block with all available pending transactions. + pending := w.eth.TxPool().Pending(true) + localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending + for _, account := range w.eth.TxPool().Locals() { + if txs := remoteTxs[account]; len(txs) > 0 { + delete(remoteTxs, account) + localTxs[account] = txs + } + } + if len(localTxs) > 0 { + txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee) + if w.commitTransactions(env, txs, interrupt) { + return + } + } + if len(remoteTxs) > 0 { + txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee) + if w.commitTransactions(env, txs, interrupt) { + return + } + } +} + +// generateWork generates a sealing block based on the given parameters. +func (w *worker) generateWork(params *generateParams) (*types.Block, error) { + work, err := w.prepareWork(params) + if err != nil { + return nil, err + } + defer work.discard() + + w.fillTransactions(nil, work) + return w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts) +} + +// commitWork generates several new sealing tasks based on the parent block +// and submit them to the sealer. +func (w *worker) commitWork(interrupt *int32, noempty bool, timestamp int64) { + start := time.Now() + + // Set the coinbase if the worker is running or it's required + var coinbase common.Address + if w.isRunning() { + if w.coinbase == (common.Address{}) { + log.Error("Refusing to mine without etherbase") + return + } + coinbase = w.coinbase // Use the preset address as the fee recipient + } + work, err := w.prepareWork(&generateParams{ + timestamp: uint64(timestamp), + coinbase: coinbase, + }) + if err != nil { + return + } + // Create an empty block based on temporary copied state for + // sealing in advance without waiting block execution finished. + if !noempty && atomic.LoadUint32(&w.noempty) == 0 { + w.commit(work.copy(), nil, false, start) + } + // Fill pending transactions from the txpool + w.fillTransactions(interrupt, work) + w.commit(work.copy(), w.fullTaskHook, true, start) + + // Swap out the old work with the new one, terminating any leftover + // prefetcher processes in the mean time and starting a new one. + if w.current != nil { + w.current.discard() + } + w.current = work +} + +// commit runs any post-transaction state modifications, assembles the final block +// and commits new work if consensus engine is running. +// Note the assumption is held that the mutation is allowed to the passed env, do +// the deep copy first. +func (w *worker) commit(env *environment, interval func(), update bool, start time.Time) error { + _, isTm := w.engine.(*tendermint.Tendermint) + if w.isRunning() && !isTm { + if interval != nil { + interval() + } + // Create a local environment copy, avoid the data race with snapshot state. + // https://github.com/ethereum/go-ethereum/issues/24299 + env := env.copy() + block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts) + if err != nil { + return err + } + // If we're post merge, just ignore + if !w.isTTDReached(block.Header()) { + select { + case w.taskCh <- &task{receipts: env.receipts, state: env.state, block: block, createdAt: time.Now()}: + w.unconfirmed.Shift(block.NumberU64() - 1) + log.Info("Commit new sealing work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()), + "uncles", len(env.uncles), "txs", env.tcount, "totalCalldata", env.totalCalldata, + "gas", block.GasUsed(), "fees", totalFees(block, env.receipts), + "elapsed", common.PrettyDuration(time.Since(start))) + + case <-w.exitCh: + log.Info("Worker has exited") + } + } + } + return nil +} + +// getSealingBlock generates the sealing block based on the given parameters. +func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash) (*types.Block, error) { + req := &getWorkReq{ + params: &generateParams{ + timestamp: timestamp, + forceTime: true, + parentHash: parent, + coinbase: coinbase, + random: random, + noUncle: true, + noExtra: true, + }, + result: make(chan *types.Block, 1), + } + select { + case w.getWorkCh <- req: + block := <-req.result + if block == nil { + return nil, req.err + } + return block, nil + case <-w.exitCh: + return nil, errors.New("miner closed") + } +} + +// isTTDReached returns the indicator if the given block has reached the total +// terminal difficulty for The Merge transition. +func (w *worker) isTTDReached(header *types.Header) bool { + td, ttd := w.chain.GetTd(header.ParentHash, header.Number.Uint64()-1), w.chain.Config().TerminalTotalDifficulty + return td != nil && ttd != nil && td.Cmp(ttd) >= 0 +} + +// copyReceipts makes a deep copy of the given receipts. +func copyReceipts(receipts []*types.Receipt) []*types.Receipt { + result := make([]*types.Receipt, len(receipts)) + for i, l := range receipts { + cpy := *l + result[i] = &cpy + } + return result +} + +// postSideBlock fires a side chain event, only use it for testing. +func (w *worker) postSideBlock(event core.ChainSideEvent) { + select { + case w.chainSideCh <- event: + case <-w.exitCh: + } +} + +// totalFees computes total consumed miner fees in ETH. Block transactions and receipts have to have the same order. +func totalFees(block *types.Block, receipts []*types.Receipt) *big.Float { + feesWei := new(big.Int) + for i, tx := range block.Transactions() { + minerFee, _ := tx.EffectiveGasTip(block.BaseFee()) + feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), minerFee)) + } + return new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether))) +} diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go new file mode 100644 index 000000000000..255a2675158d --- /dev/null +++ b/sstorminer/worker_test.go @@ -0,0 +1,669 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package sstorminer + +import ( + "errors" + "math/big" + "math/rand" + "sync/atomic" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/clique" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" +) + +const ( + // testCode is the testing contract binary code which will initialises some + // variables in constructor + testCode = "0x60806040527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060005534801561003457600080fd5b5060fc806100436000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c4dae8814603757806398a213cf146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506084565b005b60005481565b806000819055507fe9e44f9f7da8c559de847a3232b57364adc0354f15a2cd8dc636d54396f9587a6000546040518082815260200191505060405180910390a15056fea265627a7a723058208ae31d9424f2d0bc2a3da1a5dd659db2d71ec322a17db8f87e19e209e3a1ff4a64736f6c634300050a0032" + + // testGas is the gas required for contract deployment. + testGas = 144109 +) + +var ( + // Test chain configurations + testTxPoolConfig core.TxPoolConfig + ethashChainConfig *params.ChainConfig + cliqueChainConfig *params.ChainConfig + + // Test accounts + testBankKey, _ = crypto.GenerateKey() + testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) + testBankFunds = big.NewInt(1000000000000000000) + + testUserKey, _ = crypto.GenerateKey() + testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey) + + // Test transactions + pendingTxs []*types.Transaction + newTxs []*types.Transaction + + testConfig = &Config{ + Recommit: time.Second, + GasCeil: params.GenesisGasLimit, + } +) + +func init() { + testTxPoolConfig = core.DefaultTxPoolConfig + testTxPoolConfig.Journal = "" + ethashChainConfig = new(params.ChainConfig) + *ethashChainConfig = *params.TestChainConfig + cliqueChainConfig = new(params.ChainConfig) + *cliqueChainConfig = *params.TestChainConfig + cliqueChainConfig.Clique = ¶ms.CliqueConfig{ + Period: 10, + Epoch: 30000, + } + + signer := types.LatestSigner(params.TestChainConfig) + tx1 := types.MustSignNewTx(testBankKey, signer, &types.AccessListTx{ + ChainID: params.TestChainConfig.ChainID, + Nonce: 0, + To: &testUserAddress, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: big.NewInt(params.InitialBaseFee), + }) + pendingTxs = append(pendingTxs, tx1) + + tx2 := types.MustSignNewTx(testBankKey, signer, &types.LegacyTx{ + Nonce: 1, + To: &testUserAddress, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: big.NewInt(params.InitialBaseFee), + }) + newTxs = append(newTxs, tx2) + + rand.Seed(time.Now().UnixNano()) +} + +// testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing. +type testWorkerBackend struct { + db ethdb.Database + txPool *core.TxPool + chain *core.BlockChain + testTxFeed event.Feed + genesis *core.Genesis + uncleBlock *types.Block +} + +func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend { + var gspec = core.Genesis{ + Config: chainConfig, + Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, + } + + switch e := engine.(type) { + case *clique.Clique: + gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength) + copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes()) + e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), testBankKey) + }) + case *ethash.Ethash: + default: + t.Fatalf("unexpected consensus engine type: %T", engine) + } + genesis := gspec.MustCommit(db) + + chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil) + txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain) + + // Generate a small n-block chain and an uncle block for it + if n > 0 { + blocks, _ := core.GenerateChain(chainConfig, genesis, engine, db, n, func(i int, gen *core.BlockGen) { + gen.SetCoinbase(testBankAddress) + }) + if _, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("failed to insert origin chain: %v", err) + } + } + parent := genesis + if n > 0 { + parent = chain.GetBlockByHash(chain.CurrentBlock().ParentHash()) + } + blocks, _ := core.GenerateChain(chainConfig, parent, engine, db, 1, func(i int, gen *core.BlockGen) { + gen.SetCoinbase(testUserAddress) + }) + + return &testWorkerBackend{ + db: db, + chain: chain, + txPool: txpool, + genesis: &gspec, + uncleBlock: blocks[0], + } +} + +func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain } +func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool } +func (b *testWorkerBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) { + return nil, errors.New("not supported") +} + +func (b *testWorkerBackend) newRandomUncle() *types.Block { + var parent *types.Block + cur := b.chain.CurrentBlock() + if cur.NumberU64() == 0 { + parent = b.chain.Genesis() + } else { + parent = b.chain.GetBlockByHash(b.chain.CurrentBlock().ParentHash()) + } + blocks, _ := core.GenerateChain(b.chain.Config(), parent, b.chain.Engine(), b.db, 1, func(i int, gen *core.BlockGen) { + var addr = make([]byte, common.AddressLength) + rand.Read(addr) + gen.SetCoinbase(common.BytesToAddress(addr)) + }) + return blocks[0] +} + +func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction { + var tx *types.Transaction + gasPrice := big.NewInt(10 * params.InitialBaseFee) + if creation { + tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(testBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(testCode)), types.HomesteadSigner{}, testBankKey) + } else { + tx, _ = types.SignTx(types.NewTransaction(b.txPool.Nonce(testBankAddress), testUserAddress, big.NewInt(1000), params.TxGas, gasPrice, nil), types.HomesteadSigner{}, testBankKey) + } + return tx +} + +func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) { + backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks) + backend.txPool.AddLocals(pendingTxs) + w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false) + w.setEtherbase(testBankAddress) + return w, backend +} + +func TestGenerateBlockAndImportEthash(t *testing.T) { + testGenerateBlockAndImport(t, false) +} + +func TestGenerateBlockAndImportClique(t *testing.T) { + testGenerateBlockAndImport(t, true) +} + +func testGenerateBlockAndImport(t *testing.T, isClique bool) { + var ( + engine consensus.Engine + chainConfig *params.ChainConfig + db = rawdb.NewMemoryDatabase() + ) + if isClique { + chainConfig = params.AllCliqueProtocolChanges + chainConfig.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000} + engine = clique.New(chainConfig.Clique, db) + } else { + chainConfig = params.AllEthashProtocolChanges + engine = ethash.NewFaker() + } + + chainConfig.LondonBlock = big.NewInt(0) + w, b := newTestWorker(t, chainConfig, engine, db, 0) + defer w.close() + + // This test chain imports the mined blocks. + db2 := rawdb.NewMemoryDatabase() + b.genesis.MustCommit(db2) + chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil) + defer chain.Stop() + + // Ignore empty commit here for less noise. + w.skipSealHook = func(task *task) bool { + return len(task.receipts) == 0 + } + + // Wait for mined blocks. + sub := w.mux.Subscribe(core.NewMinedBlockEvent{}) + defer sub.Unsubscribe() + + // Start mining! + w.start() + + for i := 0; i < 5; i++ { + b.txPool.AddLocal(b.newRandomTx(true)) + b.txPool.AddLocal(b.newRandomTx(false)) + w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()}) + w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()}) + + select { + case ev := <-sub.Chan(): + block := ev.Data.(core.NewMinedBlockEvent).Block + if _, err := chain.InsertChain([]*types.Block{block}); err != nil { + t.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err) + } + case <-time.After(3 * time.Second): // Worker needs 1s to include new changes. + t.Fatalf("timeout") + } + } +} + +func TestEmptyWorkEthash(t *testing.T) { + testEmptyWork(t, ethashChainConfig, ethash.NewFaker()) +} +func TestEmptyWorkClique(t *testing.T) { + testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) +} + +func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { + defer engine.Close() + + w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) + defer w.close() + + var ( + taskIndex int + taskCh = make(chan struct{}, 2) + ) + checkEqual := func(t *testing.T, task *task, index int) { + // The first empty work without any txs included + receiptLen, balance := 0, big.NewInt(0) + if index == 1 { + // The second full work with 1 tx included + receiptLen, balance = 1, big.NewInt(1000) + } + if len(task.receipts) != receiptLen { + t.Fatalf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) + } + if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { + t.Fatalf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) + } + } + w.newTaskHook = func(task *task) { + if task.block.NumberU64() == 1 { + checkEqual(t, task, taskIndex) + taskIndex += 1 + taskCh <- struct{}{} + } + } + w.skipSealHook = func(task *task) bool { return true } + w.fullTaskHook = func() { + time.Sleep(100 * time.Millisecond) + } + w.start() // Start mining! + for i := 0; i < 2; i += 1 { + select { + case <-taskCh: + case <-time.NewTimer(3 * time.Second).C: + t.Error("new task timeout") + } + } +} + +func TestStreamUncleBlock(t *testing.T) { + ethash := ethash.NewFaker() + defer ethash.Close() + + w, b := newTestWorker(t, ethashChainConfig, ethash, rawdb.NewMemoryDatabase(), 1) + defer w.close() + + var taskCh = make(chan struct{}) + + taskIndex := 0 + w.newTaskHook = func(task *task) { + if task.block.NumberU64() == 2 { + // The first task is an empty task, the second + // one has 1 pending tx, the third one has 1 tx + // and 1 uncle. + if taskIndex == 2 { + have := task.block.Header().UncleHash + want := types.CalcUncleHash([]*types.Header{b.uncleBlock.Header()}) + if have != want { + t.Errorf("uncle hash mismatch: have %s, want %s", have.Hex(), want.Hex()) + } + } + taskCh <- struct{}{} + taskIndex += 1 + } + } + w.skipSealHook = func(task *task) bool { + return true + } + w.fullTaskHook = func() { + time.Sleep(100 * time.Millisecond) + } + w.start() + + for i := 0; i < 2; i += 1 { + select { + case <-taskCh: + case <-time.NewTimer(time.Second).C: + t.Error("new task timeout") + } + } + + w.postSideBlock(core.ChainSideEvent{Block: b.uncleBlock}) + + select { + case <-taskCh: + case <-time.NewTimer(time.Second).C: + t.Error("new task timeout") + } +} + +func TestRegenerateMiningBlockEthash(t *testing.T) { + testRegenerateMiningBlock(t, ethashChainConfig, ethash.NewFaker()) +} + +func TestRegenerateMiningBlockClique(t *testing.T) { + testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) +} + +func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { + defer engine.Close() + + w, b := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) + defer w.close() + + var taskCh = make(chan struct{}, 3) + + taskIndex := 0 + w.newTaskHook = func(task *task) { + if task.block.NumberU64() == 1 { + // The first task is an empty task, the second + // one has 1 pending tx, the third one has 2 txs + if taskIndex == 2 { + receiptLen, balance := 2, big.NewInt(2000) + if len(task.receipts) != receiptLen { + t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) + } + if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { + t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) + } + } + taskCh <- struct{}{} + taskIndex += 1 + } + } + w.skipSealHook = func(task *task) bool { + return true + } + w.fullTaskHook = func() { + time.Sleep(100 * time.Millisecond) + } + + w.start() + // Ignore the first two works + for i := 0; i < 2; i += 1 { + select { + case <-taskCh: + case <-time.NewTimer(time.Second).C: + t.Error("new task timeout") + } + } + b.txPool.AddLocals(newTxs) + time.Sleep(time.Second) + + select { + case <-taskCh: + case <-time.NewTimer(time.Second).C: + t.Error("new task timeout") + } +} + +func TestAdjustIntervalEthash(t *testing.T) { + testAdjustInterval(t, ethashChainConfig, ethash.NewFaker()) +} + +func TestAdjustIntervalClique(t *testing.T) { + testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) +} + +func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { + defer engine.Close() + + w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) + defer w.close() + + w.skipSealHook = func(task *task) bool { + return true + } + w.fullTaskHook = func() { + time.Sleep(100 * time.Millisecond) + } + var ( + progress = make(chan struct{}, 10) + result = make([]float64, 0, 10) + index = 0 + start uint32 + ) + w.resubmitHook = func(minInterval time.Duration, recommitInterval time.Duration) { + // Short circuit if interval checking hasn't started. + if atomic.LoadUint32(&start) == 0 { + return + } + var wantMinInterval, wantRecommitInterval time.Duration + + switch index { + case 0: + wantMinInterval, wantRecommitInterval = 3*time.Second, 3*time.Second + case 1: + origin := float64(3 * time.Second.Nanoseconds()) + estimate := origin*(1-intervalAdjustRatio) + intervalAdjustRatio*(origin/0.8+intervalAdjustBias) + wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond + case 2: + estimate := result[index-1] + min := float64(3 * time.Second.Nanoseconds()) + estimate = estimate*(1-intervalAdjustRatio) + intervalAdjustRatio*(min-intervalAdjustBias) + wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond + case 3: + wantMinInterval, wantRecommitInterval = time.Second, time.Second + } + + // Check interval + if minInterval != wantMinInterval { + t.Errorf("resubmit min interval mismatch: have %v, want %v ", minInterval, wantMinInterval) + } + if recommitInterval != wantRecommitInterval { + t.Errorf("resubmit interval mismatch: have %v, want %v", recommitInterval, wantRecommitInterval) + } + result = append(result, float64(recommitInterval.Nanoseconds())) + index += 1 + progress <- struct{}{} + } + w.start() + + time.Sleep(time.Second) // Ensure two tasks have been summitted due to start opt + atomic.StoreUint32(&start, 1) + + w.setRecommitInterval(3 * time.Second) + select { + case <-progress: + case <-time.NewTimer(time.Second).C: + t.Error("interval reset timeout") + } + + w.resubmitAdjustCh <- &intervalAdjust{inc: true, ratio: 0.8} + select { + case <-progress: + case <-time.NewTimer(time.Second).C: + t.Error("interval reset timeout") + } + + w.resubmitAdjustCh <- &intervalAdjust{inc: false} + select { + case <-progress: + case <-time.NewTimer(time.Second).C: + t.Error("interval reset timeout") + } + + w.setRecommitInterval(500 * time.Millisecond) + select { + case <-progress: + case <-time.NewTimer(time.Second).C: + t.Error("interval reset timeout") + } +} + +func TestGetSealingWorkEthash(t *testing.T) { + testGetSealingWork(t, ethashChainConfig, ethash.NewFaker(), false) +} + +func TestGetSealingWorkClique(t *testing.T) { + testGetSealingWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()), false) +} + +func TestGetSealingWorkPostMerge(t *testing.T) { + local := new(params.ChainConfig) + *local = *ethashChainConfig + local.TerminalTotalDifficulty = big.NewInt(0) + testGetSealingWork(t, local, ethash.NewFaker(), true) +} + +func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, postMerge bool) { + defer engine.Close() + + w, b := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) + defer w.close() + + w.setExtra([]byte{0x01, 0x02}) + w.postSideBlock(core.ChainSideEvent{Block: b.uncleBlock}) + + w.skipSealHook = func(task *task) bool { + return true + } + w.fullTaskHook = func() { + time.Sleep(100 * time.Millisecond) + } + timestamp := uint64(time.Now().Unix()) + assertBlock := func(block *types.Block, number uint64, coinbase common.Address, random common.Hash) { + if block.Time() != timestamp { + // Sometime the timestamp will be mutated if the timestamp + // is even smaller than parent block's. It's OK. + t.Logf("Invalid timestamp, want %d, get %d", timestamp, block.Time()) + } + if len(block.Uncles()) != 0 { + t.Error("Unexpected uncle block") + } + _, isClique := engine.(*clique.Clique) + if !isClique { + if len(block.Extra()) != 0 { + t.Error("Unexpected extra field") + } + if block.Coinbase() != coinbase { + t.Errorf("Unexpected coinbase got %x want %x", block.Coinbase(), coinbase) + } + } else { + if block.Coinbase() != (common.Address{}) { + t.Error("Unexpected coinbase") + } + } + if !isClique { + if block.MixDigest() != random { + t.Error("Unexpected mix digest") + } + } + if block.Nonce() != 0 { + t.Error("Unexpected block nonce") + } + if block.NumberU64() != number { + t.Errorf("Mismatched block number, want %d got %d", number, block.NumberU64()) + } + } + var cases = []struct { + parent common.Hash + coinbase common.Address + random common.Hash + expectNumber uint64 + expectErr bool + }{ + { + b.chain.Genesis().Hash(), + common.HexToAddress("0xdeadbeef"), + common.HexToHash("0xcafebabe"), + uint64(1), + false, + }, + { + b.chain.CurrentBlock().Hash(), + common.HexToAddress("0xdeadbeef"), + common.HexToHash("0xcafebabe"), + b.chain.CurrentBlock().NumberU64() + 1, + false, + }, + { + b.chain.CurrentBlock().Hash(), + common.Address{}, + common.HexToHash("0xcafebabe"), + b.chain.CurrentBlock().NumberU64() + 1, + false, + }, + { + b.chain.CurrentBlock().Hash(), + common.Address{}, + common.Hash{}, + b.chain.CurrentBlock().NumberU64() + 1, + false, + }, + { + common.HexToHash("0xdeadbeef"), + common.HexToAddress("0xdeadbeef"), + common.HexToHash("0xcafebabe"), + 0, + true, + }, + } + + // This API should work even when the automatic sealing is not enabled + for _, c := range cases { + block, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random) + if c.expectErr { + if err == nil { + t.Error("Expect error but get nil") + } + } else { + if err != nil { + t.Errorf("Unexpected error %v", err) + } + assertBlock(block, c.expectNumber, c.coinbase, c.random) + } + } + + // This API should work even when the automatic sealing is enabled + w.start() + for _, c := range cases { + block, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random) + if c.expectErr { + if err == nil { + t.Error("Expect error but get nil") + } + } else { + if err != nil { + t.Errorf("Unexpected error %v", err) + } + assertBlock(block, c.expectNumber, c.coinbase, c.random) + } + } +} From 4a468848f473e4c146700d340d9dc9d30e794bf5 Mon Sep 17 00:00:00 2001 From: pingke Date: Thu, 23 Feb 2023 09:32:53 +0800 Subject: [PATCH 02/23] update --- sstorminer/miner.go | 59 --------------------------- sstorminer/worker.go | 95 +------------------------------------------- 2 files changed, 2 insertions(+), 152 deletions(-) diff --git a/sstorminer/miner.go b/sstorminer/miner.go index 204ef652624b..6ce7e666b37d 100644 --- a/sstorminer/miner.go +++ b/sstorminer/miner.go @@ -184,66 +184,7 @@ func (miner *Miner) SetExtra(extra []byte) error { return nil } -// SetRecommitInterval sets the interval for sealing work resubmitting. -func (miner *Miner) SetRecommitInterval(interval time.Duration) { - miner.worker.setRecommitInterval(interval) -} - -// Pending returns the currently pending block and associated state. -func (miner *Miner) Pending() (*types.Block, *state.StateDB) { - return miner.worker.pending() -} - -// PendingBlock returns the currently pending block. -// -// Note, to access both the pending block and the pending state -// simultaneously, please use Pending(), as the pending state can -// change between multiple method calls -func (miner *Miner) PendingBlock() *types.Block { - return miner.worker.pendingBlock() -} - -// PendingBlockAndReceipts returns the currently pending block and corresponding receipts. -func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) { - return miner.worker.pendingBlockAndReceipts() -} - func (miner *Miner) SetEtherbase(addr common.Address) { miner.coinbase = addr miner.worker.setEtherbase(addr) } - -// SetGasCeil sets the gaslimit to strive for when mining blocks post 1559. -// For pre-1559 blocks, it sets the ceiling. -func (miner *Miner) SetGasCeil(ceil uint64) { - miner.worker.setGasCeil(ceil) -} - -// EnablePreseal turns on the preseal mining feature. It's enabled by default. -// Note this function shouldn't be exposed to API, it's unnecessary for users -// (miners) to actually know the underlying detail. It's only for outside project -// which uses this library. -func (miner *Miner) EnablePreseal() { - miner.worker.enablePreseal() -} - -// DisablePreseal turns off the preseal mining feature. It's necessary for some -// fake consensus engine which can seal blocks instantaneously. -// Note this function shouldn't be exposed to API, it's unnecessary for users -// (miners) to actually know the underlying detail. It's only for outside project -// which uses this library. -func (miner *Miner) DisablePreseal() { - miner.worker.disablePreseal() -} - -// GetSealingBlock retrieves a sealing block based on the given parameters. -// The returned block is not sealed but all other fields should be filled. -func (miner *Miner) GetSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash) (*types.Block, error) { - return miner.worker.getSealingBlock(parent, timestamp, coinbase, random) -} - -// SubscribePendingLogs starts delivering logs from pending transactions -// to the given channel. -func (miner *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscription { - return miner.worker.pendingLogsFeed.Subscribe(ch) -} diff --git a/sstorminer/worker.go b/sstorminer/worker.go index d9ce0b5b01fe..8be27f625c0d 100644 --- a/sstorminer/worker.go +++ b/sstorminer/worker.go @@ -94,7 +94,6 @@ type environment struct { header *types.Header txs []*types.Transaction receipts []*types.Receipt - uncles map[common.Hash]*types.Header } // copy creates a deep copy of environment. @@ -108,7 +107,6 @@ func (env *environment) copy() *environment { totalCalldata: env.totalCalldata, coinbase: env.coinbase, header: types.CopyHeader(env.header), - receipts: copyReceipts(env.receipts), } if env.gasPool != nil { gasPool := *env.gasPool @@ -118,22 +116,12 @@ func (env *environment) copy() *environment { // to do the expensive deep copy for them. cpy.txs = make([]*types.Transaction, len(env.txs)) copy(cpy.txs, env.txs) - cpy.uncles = make(map[common.Hash]*types.Header) for hash, uncle := range env.uncles { cpy.uncles[hash] = uncle } return cpy } -// unclelist returns the contained uncles as the list format. -func (env *environment) unclelist() []*types.Header { - var uncles []*types.Header - for _, uncle := range env.uncles { - uncles = append(uncles, uncle) - } - return uncles -} - // discard terminates the background prefetcher go-routine. It should // always be called for all created environment instances otherwise // the go-routine leak can happen. @@ -204,10 +192,8 @@ type worker struct { wg sync.WaitGroup - current *environment // An environment for current running cycle. - localUncles map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks. - remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks. - unconfirmed *unconfirmedBlocks // A set of locally mined blocks pending canonicalness confirmations. + current *environment // An environment for current running cycle. + unconfirmed *unconfirmedBlocks // A set of locally mined blocks pending canonicalness confirmations. mu sync.RWMutex // The lock used to protect the coinbase and extra fields coinbase common.Address @@ -251,8 +237,6 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus mux: mux, chain: eth.BlockChain(), isLocalBlock: isLocalBlock, - localUncles: make(map[common.Hash]*types.Block), - remoteUncles: make(map[common.Hash]*types.Block), unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), sealingLogAtDepth), pendingTasks: make(map[common.Hash]*task), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), @@ -308,14 +292,6 @@ func (w *worker) setExtra(extra []byte) { w.extra = extra } -// setRecommitInterval updates the interval for miner sealing work recommitting. -func (w *worker) setRecommitInterval(interval time.Duration) { - select { - case w.resubmitIntervalCh <- interval: - case <-w.exitCh: - } -} - // disablePreseal disables pre-sealing feature func (w *worker) disablePreseal() { atomic.StoreUint32(&w.noempty, 1) @@ -326,33 +302,6 @@ func (w *worker) enablePreseal() { atomic.StoreUint32(&w.noempty, 0) } -// pending returns the pending state and corresponding block. -func (w *worker) pending() (*types.Block, *state.StateDB) { - // return a snapshot to avoid contention on currentMu mutex - w.snapshotMu.RLock() - defer w.snapshotMu.RUnlock() - if w.snapshotState == nil { - return nil, nil - } - return w.snapshotBlock, w.snapshotState.Copy() -} - -// pendingBlock returns pending block. -func (w *worker) pendingBlock() *types.Block { - // return a snapshot to avoid contention on currentMu mutex - w.snapshotMu.RLock() - defer w.snapshotMu.RUnlock() - return w.snapshotBlock -} - -// pendingBlockAndReceipts returns pending block and corresponding receipts. -func (w *worker) pendingBlockAndReceipts() (*types.Block, types.Receipts) { - // return a snapshot to avoid contention on currentMu mutex - w.snapshotMu.RLock() - defer w.snapshotMu.RUnlock() - return w.snapshotBlock, w.snapshotReceipts -} - func (w *worker) init() { tm, isTm := w.engine.(*tendermint.Tendermint) if isTm { @@ -730,28 +679,6 @@ func (w *worker) makeEnv(parent *types.Block, header *types.Header, coinbase com return env, nil } -// commitUncle adds the given block to uncle block set, returns error if failed to add. -func (w *worker) commitUncle(env *environment, uncle *types.Header) error { - if w.isTTDReached(env.header) { - return errors.New("ignore uncle for beacon block") - } - hash := uncle.Hash() - if _, exist := env.uncles[hash]; exist { - return errors.New("uncle not unique") - } - if env.header.ParentHash == uncle.ParentHash { - return errors.New("uncle is sibling") - } - if !env.ancestors.Contains(uncle.ParentHash) { - return errors.New("uncle's parent unknown") - } - if env.family.Contains(hash) { - return errors.New("uncle already included") - } - env.uncles[hash] = uncle - return nil -} - // updateSnapshot updates pending snapshot block, receipts and state. func (w *worker) updateSnapshot(env *environment) { w.snapshotMu.Lock() @@ -1143,24 +1070,6 @@ func (w *worker) isTTDReached(header *types.Header) bool { return td != nil && ttd != nil && td.Cmp(ttd) >= 0 } -// copyReceipts makes a deep copy of the given receipts. -func copyReceipts(receipts []*types.Receipt) []*types.Receipt { - result := make([]*types.Receipt, len(receipts)) - for i, l := range receipts { - cpy := *l - result[i] = &cpy - } - return result -} - -// postSideBlock fires a side chain event, only use it for testing. -func (w *worker) postSideBlock(event core.ChainSideEvent) { - select { - case w.chainSideCh <- event: - case <-w.exitCh: - } -} - // totalFees computes total consumed miner fees in ETH. Block transactions and receipts have to have the same order. func totalFees(block *types.Block, receipts []*types.Receipt) *big.Float { feesWei := new(big.Int) From 8ec46748c53e6c15a1f15d9a27405fa81a275af5 Mon Sep 17 00:00:00 2001 From: pingke Date: Wed, 22 Mar 2023 17:08:08 +0800 Subject: [PATCH 03/23] add sstorage miner --- cmd/geth/main.go | 2 + cmd/utils/flags.go | 14 + core/blockchain.go | 126 ++- eth/api.go | 15 + eth/backend.go | 40 +- eth/ethconfig/config.go | 18 +- eth/protocols/sstorage/sync_test.go | 100 ++- sstorage/data_file.go | 3 +- sstorage/data_shard.go | 32 + sstorage/shard_manager.go | 74 +- sstorminer/merklelib.go | 90 ++ sstorminer/miner.go | 70 +- sstorminer/miner_test.go | 47 +- sstorminer/unconfirmed.go | 136 --- sstorminer/unconfirmed_test.go | 87 -- sstorminer/worker.go | 1255 ++++++++++----------------- sstorminer/worker_test.go | 897 +++++++++---------- 17 files changed, 1382 insertions(+), 1624 deletions(-) create mode 100644 sstorminer/merklelib.go delete mode 100644 sstorminer/unconfirmed.go delete mode 100644 sstorminer/unconfirmed_test.go diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 08188a2006cb..20aadaef3e76 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -133,6 +133,8 @@ var ( utils.MinerNoVerifyFlag, utils.SstorageShardFlag, utils.SstorageFileFlag, + utils.SstorageMineFlag, + utils.SstorageNodeKeyFlag, utils.NATFlag, utils.NoDiscoverFlag, utils.DiscoveryV5Flag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index e0ea19bd6e59..01d9ae3fca80 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -566,6 +566,14 @@ var ( Usage: "Add sharded storage data file", Value: nil, } + SstorageMineFlag = cli.BoolFlag{ + Name: "sstorage.mine", + Usage: "Enable sstorage mining", + } + SstorageNodeKeyFlag = cli.StringFlag{ + Name: "sstorage.nodekey", + Usage: "Sstorage miner node key file", + } // Logging and debug settings EthStatsURLFlag = cli.StringFlag{ Name: "ethstats", @@ -1125,6 +1133,12 @@ func setSstorage(ctx *cli.Context, cfg *ethconfig.Config) { if ctx.GlobalIsSet(SstorageFileFlag.Name) { cfg.SstorageFiles = ctx.GlobalStringSlice(SstorageFileFlag.Name) } + if ctx.GlobalIsSet(SstorageMineFlag.Name) { + cfg.SstorageMine = ctx.GlobalBool(SstorageMineFlag.Name) + } + if ctx.GlobalIsSet(SstorageNodeKeyFlag.Name) { + cfg.SstorageNodeKey = ctx.GlobalString(SstorageNodeKeyFlag.Name) + } sstorage.InitializeConfig() for _, s := range cfg.SstorageShards { diff --git a/core/blockchain.go b/core/blockchain.go index eacb123e39c3..faa8460bae59 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -19,6 +19,7 @@ package core import ( "bytes" + "encoding/binary" "errors" "fmt" "io" @@ -48,7 +49,6 @@ import ( "github.com/ethereum/go-ethereum/trie" lru "github.com/hashicorp/golang-lru" "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) var ( @@ -2352,7 +2352,10 @@ func (bc *BlockChain) PreExecuteBlock(block *types.Block) (err error) { return } -var emptyHash = common.Hash{} +var ( + emptyHash = common.Hash{} + defaultHashBytes = crypto.Keccak256Hash().Bytes() +) type SstorageMetadata struct { KVIdx uint64 @@ -2379,13 +2382,7 @@ func getSlotHash(slotIdx uint64, key common.Hash) common.Hash { slotdata := slot[:] data := append(keydata, slotdata...) - hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) - hasher.Write(data) - - hashRes := common.Hash{} - hasher.Read(hashRes[:]) - - return hashRes + return crypto.Keccak256Hash(data) } // GetSstorageMetadata get sstorage metadata for a given kv (specified by contract address and index) @@ -2395,19 +2392,20 @@ func GetSstorageMetadata(s *state.StateDB, contract common.Address, index uint64 // then get SstorageMetadata from kvMap (slot 1) using skey. the SstorageMetadata struct is as following // struct PhyAddr { // uint40 KVIdx; - // uint24 KVSize; + // uint24 kvSize; // bytes24 hash; // } + defaultHash, defaultMeta := GetDefaultMetadata(index) position := getSlotHash(2, uint256.NewInt(index).Bytes32()) skey := s.GetState(contract, position) if skey == emptyHash { - return emptyHash, nil, fmt.Errorf("fail to get skey for index %d", index) + return defaultHash, defaultMeta, fmt.Errorf("fail to get skey for index %d", index) } position = getSlotHash(1, skey) meta := s.GetState(contract, position) if meta == emptyHash { - return emptyHash, nil, fmt.Errorf("fail to get SstorageMetadata for skey %s", skey.Hex()) + return defaultHash, defaultMeta, fmt.Errorf("fail to get SstorageMetadata for skey %s", skey.Hex()) } return meta, &SstorageMetadata{ @@ -2417,6 +2415,18 @@ func GetSstorageMetadata(s *state.StateDB, contract common.Address, index uint64 nil } +func GetDefaultMetadata(index uint64) (common.Hash, *SstorageMetadata) { + meta := &SstorageMetadata{ + KVIdx: index, + KVSize: uint64(0), + HashInMeta: defaultHashBytes[:24], + } + hashBytes := make([]byte, 32) + copy(hashBytes[:24], meta.HashInMeta) + binary.BigEndian.PutUint32(hashBytes[27:], uint32(index)) + return common.BytesToHash(hashBytes), meta +} + // VerifyKV verify kv using SstorageMetadata func VerifyKV(sm *sstorage.ShardManager, idx uint64, val []byte, meta *SstorageMetadata, isEncoded bool) ([]byte, error) { if idx != meta.KVIdx { @@ -2434,7 +2444,7 @@ func VerifyKV(sm *sstorage.ShardManager, idx uint64, val []byte, meta *SstorageM } if meta.KVSize != uint64(len(data)) { - return nil, fmt.Errorf("verifyKV fail: size error; Data size: %d; MetaHash KVSize: %d", len(val), meta.KVSize) + return nil, fmt.Errorf("verifyKV fail: size error; Data size: %d; MetaHash kvSize: %d", len(val), meta.KVSize) } data = d } @@ -2519,7 +2529,43 @@ func (bc *BlockChain) VerifyAndWriteKV(contract common.Address, data map[uint64] return synced, syncedBytes, inserted, nil } -// ReadEncodedKVsByIndexList Read the masked KVs by a list of KV index. +// ReadKVsByIndexList Read the KVs by a list of KV index. +func (bc *BlockChain) ReadKVsByIndexList(contract common.Address, indexes []uint64, useMaxKVsize bool) ([]*KV, error) { + stateDB, err := bc.StateAt(bc.CurrentBlock().Root()) + if err != nil { + return nil, err + } + + return bc.ReadKVsByIndexListWithState(stateDB, contract, indexes, useMaxKVsize) +} + +func (bc *BlockChain) ReadKVsByIndexListWithState(stateDB *state.StateDB, contract common.Address, indexes []uint64, useMaxKVsize bool) ([]*KV, error) { + sm := sstorage.ContractToShardManager[contract] + if sm == nil { + return nil, fmt.Errorf("shard manager for contract %s is not support", contract.Hex()) + } + + res := make([]*KV, 0) + for _, idx := range indexes { + _, meta, err := GetSstorageMetadata(stateDB, contract, idx) + if err != nil { + continue + } + l := int(meta.KVSize) + if useMaxKVsize { + l = int(sm.MaxKvSize()) + } + data, ok, err := sm.TryRead(idx, l, common.BytesToHash(meta.HashInMeta)) + if ok && err == nil { + kv := KV{idx, data} + res = append(res, &kv) + } + } + + return res, nil +} + +// ReadEncodedKVsByIndexList Read the encoded KVs by a list of KV index. func (bc *BlockChain) ReadEncodedKVsByIndexList(contract common.Address, indexes []uint64) ([]*KV, error) { sm := sstorage.ContractToShardManager[contract] if sm == nil { @@ -2590,3 +2636,55 @@ func (bc *BlockChain) GetSstorageLastKvIdx(contract common.Address) (uint64, err log.Warn("GetSstorageLastKvIdx", "val", common.Bytes2Hex(val.Bytes())) return new(big.Int).SetBytes(val.Bytes()).Uint64(), nil } + +type MiningInfo struct { + MiningHash common.Hash + LastMineTime uint64 + Difficulty *big.Int + BlockMined *big.Int +} + +func (a *MiningInfo) Equal(b *MiningInfo) bool { + if b == nil { + return false + } + if a.LastMineTime != b.LastMineTime { + return false + } + if !bytes.Equal(a.MiningHash.Bytes(), b.MiningHash.Bytes()) { + return false + } + if a.BlockMined.Cmp(b.BlockMined) != 0 { + return false + } + if a.Difficulty.Cmp(b.Difficulty) != 0 { + return false + } + return true +} + +func (bc *BlockChain) GetSstorageMiningInfo(root common.Hash, contract common.Address, shardId uint64) (*MiningInfo, error) { + stateDB, err := bc.StateAt(root) + if err != nil { + return nil, err + } + + return bc.GetSstorageMiningInfoWithStateDB(stateDB, contract, shardId) +} + +func (bc *BlockChain) GetSstorageMiningInfoWithStateDB(stateDB *state.StateDB, contract common.Address, shardId uint64) (*MiningInfo, error) { + info := new(MiningInfo) + position := getSlotHash(0, uint256.NewInt(shardId).Bytes32()) + info.MiningHash = stateDB.GetState(contract, position) + if info.MiningHash == emptyHash { + return nil, fmt.Errorf("fail to get mining info for shard %d", shardId) + } + info.LastMineTime = stateDB.GetState(contract, hashAdd(position, 1)).Big().Uint64() + info.Difficulty = stateDB.GetState(contract, hashAdd(position, 2)).Big() + info.BlockMined = stateDB.GetState(contract, hashAdd(position, 3)).Big() + return info, nil +} + +func hashAdd(hash common.Hash, i uint64) common.Hash { + return common.BytesToHash(new(big.Int).Add(hash.Big(), new(big.Int).SetUint64(i)).Bytes()) +} diff --git a/eth/api.go b/eth/api.go index f81dfa922b7a..30aa12feaa80 100644 --- a/eth/api.go +++ b/eth/api.go @@ -94,6 +94,21 @@ func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI { return &PrivateMinerAPI{e: e} } +// StartSstorMining starts the Sstorage miner. If mining is already running, this method just return. +func (api *PrivateMinerAPI) StartSstorMining() { + api.e.StartSstorMining() +} + +// StopSstorMining terminates the Sstorage miner. +func (api *PrivateMinerAPI) StopSstorMining() { + api.e.StopSstorMining() +} + +// SetSstorRecommitInterval updates the interval for sstorage miner sealing work recommitting. +func (api *PrivateMinerAPI) SetSstorRecommitInterval(interval int) { + api.e.SstorMiner().SetRecommitInterval(time.Duration(interval) * time.Millisecond) +} + // Start starts 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 diff --git a/eth/backend.go b/eth/backend.go index 4beb7b228f40..699158bde1b9 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -20,6 +20,8 @@ package eth import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/sstorminer" "math/big" "runtime" "sync" @@ -92,9 +94,10 @@ type Ethereum struct { APIBackend *EthAPIBackend - miner *miner.Miner - gasPrice *big.Int - etherbase common.Address + miner *miner.Miner + sstorMiner *sstorminer.Miner + gasPrice *big.Int + etherbase common.Address networkID uint64 netRPCService *ethapi.PublicNetAPI @@ -273,6 +276,17 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) + if config.SstorageMine { + privKey, err := crypto.LoadECDSA(config.SstorageNodeKey) + if err != nil { + return nil, err + } + if len(sstorage.Shards()) == 0 { + return nil, fmt.Errorf("no shards is exist") + } + eth.sstorMiner = sstorminer.New(eth, &config.SStorMiner, chainConfig, eth.EventMux(), privKey) + } + eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} if eth.APIBackend.allowUnprotectedTxs { log.Info("Unprotected transactions allowed") @@ -476,6 +490,26 @@ func (s *Ethereum) SetEtherbase(etherbase common.Address) { s.miner.SetEtherbase(etherbase) } +// todo add start / stop mining for special shard + +// StartSstorMining starts the sstorage miner. If mining +// is already running, this method just return. +func (s *Ethereum) StartSstorMining() { + // If the miner was not running, initialize it + if !s.IsMining() { + go s.sstorMiner.Start() + } +} + +// StopSstorMining terminates the sstorage miner. +func (s *Ethereum) StopSstorMining() { + s.miner.Stop() +} + +func (s *Ethereum) IsSstorMining() bool { return s.miner.Mining() } + +func (s *Ethereum) SstorMiner() *sstorminer.Miner { return s.sstorMiner } + // StartMining starts the miner with the given number of CPU threads. If mining // is already running, this method adjust the number of threads allowed to use // and updates the minimum price required by the transaction pool. diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index f7176d36de94..4d21b9107e92 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -18,6 +18,7 @@ package ethconfig import ( + "github.com/ethereum/go-ethereum/sstorminer" "math/big" "os" "os/user" @@ -89,6 +90,14 @@ var Defaults = Config{ GasPrice: big.NewInt(params.GWei), Recommit: 3 * time.Second, }, + SStorMiner: sstorminer.Config{ + RandomChecks: 16, + MinimumDiff: new(big.Int).SetUint64(1), + TargetIntervalSec: new(big.Int).SetUint64(60), + Cutoff: new(big.Int).SetUint64(40), + DiffAdjDivisor: new(big.Int).SetUint64(1024), + Recommit: 15 * time.Second, + }, TxPool: core.DefaultTxPoolConfig, RPCGasCap: 50000000, RPCEVMTimeout: 5 * time.Second, @@ -174,6 +183,9 @@ type Config struct { // Mining options Miner miner.Config + // Sstorage Mining options + SStorMiner sstorminer.Config + // Ethash options Ethash ethash.Config @@ -220,8 +232,10 @@ type Config struct { ValidatorChangeEpochId uint64 // Sstorage config - SstorageFiles []string `toml:",omitempty"` - SstorageShards []string `toml:",omitempty"` + SstorageFiles []string `toml:",omitempty"` + SstorageShards []string `toml:",omitempty"` + SstorageMine bool + SstorageNodeKey string } // CreateConsensusEngine creates a consensus engine for the given chain configuration. diff --git a/eth/protocols/sstorage/sync_test.go b/eth/protocols/sstorage/sync_test.go index e6c66a1b2d24..144cf644a68e 100644 --- a/eth/protocols/sstorage/sync_test.go +++ b/eth/protocols/sstorage/sync_test.go @@ -40,8 +40,9 @@ import ( ) var ( - contract = common.HexToAddress("0x0000000000000000000000000000000003330001") - kvEntries = uint64(512) + contract = common.HexToAddress("0x0000000000000000000000000000000003330001") + kvEntriesBits = uint64(9) + kvEntries = uint64(1) << 9 ) type ( @@ -417,10 +418,12 @@ func makeKVStorage(stateDB *state.StateDB, contract common.Address, shards []uin return shardData, shardList } -func createSstorage(contract common.Address, shardIdxList []uint64, kvSize, - kvEntries, filePerShard uint64, miner common.Address) (map[common.Address][]uint64, []string) { - sm := sstorage.NewShardManager(contract, kvSize, kvEntries) +func createSstorage(contract common.Address, shardIdxList []uint64, kvSizeBits, + kvEntriesBits, filePerShard uint64, miner common.Address) (map[common.Address][]uint64, []string) { + sm := sstorage.NewShardManager(contract, kvSizeBits, kvEntriesBits) sstorage.ContractToShardManager[contract] = sm + kvSize := uint64(1) << kvSizeBits + kvEntries := uint64(1) << kvEntriesBits files := make([]string, 0) for _, shardIdx := range shardIdxList { @@ -628,7 +631,7 @@ func checkTasksWithBaskTasks(baseTasks, tasks []*kvTask) error { // TestReadWrite tests a basic sstorage read/wrtie func TestReadWrite(t *testing.T) { - shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) if shards == nil { t.Fatalf("createSstorage failed") } @@ -667,7 +670,7 @@ func TestSync(t *testing.T) { } ) - shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) if shards == nil { t.Fatalf("createSstorage failed") } @@ -686,8 +689,8 @@ func TestSync(t *testing.T) { return source } - data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntries) - syncer := setupSyncer(shards, stateDB, kvEntries, mkSource("source", shards, data)) + data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntriesBits) + syncer := setupSyncer(shards, stateDB, kvEntriesBits, mkSource("source", shards, data)) done := checkStall(t, term) if err := syncer.Sync(cancel); err != nil { t.Fatalf("sync failed: %v", err) @@ -701,7 +704,8 @@ func TestMultiSubTasksSync(t *testing.T) { var ( once sync.Once cancel = make(chan struct{}) - entries = uint64(1024) + entriesBits = uint64(10) + entries = uint64(1) << 10 destroyedList = make(map[uint64]struct{}) stateDB, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) term = func() { @@ -711,7 +715,7 @@ func TestMultiSubTasksSync(t *testing.T) { } ) - shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE, entries, 1, common.Address{}) + shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE_BITS, entriesBits, 1, common.Address{}) if shards == nil { t.Fatalf("createSstorage failed") } @@ -754,7 +758,7 @@ func TestMultiSync(t *testing.T) { } ) - shards, files := createSstorage(contract, []uint64{0, 1, 2, 3, 4}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + shards, files := createSstorage(contract, []uint64{0, 1, 2, 3, 4}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) if shards == nil { t.Fatalf("createSstorage failed") } @@ -807,7 +811,7 @@ func TestSyncWithEmptyResponse(t *testing.T) { } ) - shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) if shards == nil { t.Fatalf("createSstorage failed") } @@ -827,9 +831,9 @@ func TestSyncWithEmptyResponse(t *testing.T) { return source } - data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntries) - syncer := setupSyncer(shards, stateDB, kvEntries, mkSource("source", shards, data)) - for i := uint64(0); i < kvEntries; i++ { + data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntriesBits) + syncer := setupSyncer(shards, stateDB, kvEntriesBits, mkSource("source", shards, data)) + for i := uint64(0); i < kvEntriesBits; i++ { destroyedList[i] = struct{}{} } done := checkStall(t, term) @@ -854,7 +858,7 @@ func TestSyncWithNoResponse(t *testing.T) { } ) - shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) if shards == nil { t.Fatalf("createSstorage failed") } @@ -874,9 +878,9 @@ func TestSyncWithNoResponse(t *testing.T) { return source } - data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntries) - syncer := setupSyncer(shards, stateDB, kvEntries, mkSource("source", shards, data)) - for i := uint64(0); i < kvEntries; i++ { + data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntriesBits) + syncer := setupSyncer(shards, stateDB, kvEntriesBits, mkSource("source", shards, data)) + for i := uint64(0); i < kvEntriesBits; i++ { destroyedList[i] = struct{}{} } done := checkStall(t, term) @@ -899,10 +903,10 @@ func TestSyncWithFewerResult(t *testing.T) { close(cancel) }) } - reduce = rand.Uint64()%(kvEntries/2) - 1 + reduce = rand.Uint64()%(kvEntriesBits/2) - 1 ) - shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) if shards == nil { t.Fatalf("createSstorage failed") } @@ -921,8 +925,8 @@ func TestSyncWithFewerResult(t *testing.T) { return source } - data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntries-reduce) - syncer := setupSyncer(shards, stateDB, kvEntries-reduce, mkSource("source", shards, data)) + data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntriesBits-reduce) + syncer := setupSyncer(shards, stateDB, kvEntriesBits-reduce, mkSource("source", shards, data)) done := checkStall(t, term) if err := syncer.Sync(cancel); err != nil { t.Fatalf("sync failed: %v", err) @@ -944,7 +948,7 @@ func TestSyncMismatchWithMeta(t *testing.T) { } ) - shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + shards, files := createSstorage(contract, []uint64{0}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) if shards == nil { t.Fatalf("createSstorage failed") } @@ -963,9 +967,9 @@ func TestSyncMismatchWithMeta(t *testing.T) { return source } - data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntries) - destroyedList := destoryData(data, make(map[uint64]struct{}), 0, kvEntries, 8) - syncer := setupSyncer(shards, stateDB, kvEntries, mkSource("source", shards, data)) + data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntriesBits) + destroyedList := destoryData(data, make(map[uint64]struct{}), 0, kvEntriesBits, 8) + syncer := setupSyncer(shards, stateDB, kvEntriesBits, mkSource("source", shards, data)) done := checkStall(t, term) if err := syncer.Sync(cancel); err != ErrCancelled { t.Fatalf("sync cancelled error is expected: %v", err) @@ -988,7 +992,7 @@ func TestMultiSyncWithDataOverlay(t *testing.T) { } ) - _, files := createSstorage(contract, []uint64{0, 1, 2, 3}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + _, files := createSstorage(contract, []uint64{0, 1, 2, 3}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) defer func(files []string) { for _, file := range files { @@ -1009,7 +1013,7 @@ func TestMultiSyncWithDataOverlay(t *testing.T) { peer0 := mkSource("source_0", shards, data) data, shards = makeKVStorage(nil, contract, []uint64{2, 3}, kvEntries) peer1 := mkSource("source_1", shards, data) - /* data, shards = makeKVStorage(nil, contract, []uint64{2, 3}, kvEntries) + /* data, shards = makeKVStorage(nil, contract, []uint64{2, 3}, kvEntriesBits) peer2 := mkSource("source_2", shards, data)*/ syncer := setupSyncer(localShards, stateDB, kvEntries*4, peer0, peer1) @@ -1035,7 +1039,7 @@ func TestMultiSyncWithDataOverlayWithDestroyed(t *testing.T) { ) requestTimeoutInMillisecond = 50 * time.Millisecond // Millisecond - _, files := createSstorage(contract, []uint64{0, 1, 2}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + _, files := createSstorage(contract, []uint64{0, 1, 2}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) defer func(files []string) { for _, file := range files { @@ -1051,15 +1055,15 @@ func TestMultiSyncWithDataOverlayWithDestroyed(t *testing.T) { return source } - expectedData, localShards := makeKVStorage(stateDB, contract, []uint64{0, 1, 2}, kvEntries) - data, shards := makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntries) - list := destoryData(data, make(map[uint64]struct{}), 0, kvEntries*3, 8) + expectedData, localShards := makeKVStorage(stateDB, contract, []uint64{0, 1, 2}, kvEntriesBits) + data, shards := makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntriesBits) + list := destoryData(data, make(map[uint64]struct{}), 0, kvEntriesBits*3, 8) peer0 := mkSource("source_0", shards, data) - data, shards = makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntries) - _ = destoryData(data, list, 0, kvEntries*3, 8) + data, shards = makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntriesBits) + _ = destoryData(data, list, 0, kvEntriesBits*3, 8) peer1 := mkSource("source_1", shards, data) - syncer := setupSyncer(localShards, stateDB, kvEntries*3, peer0, peer1) + syncer := setupSyncer(localShards, stateDB, kvEntriesBits*3, peer0, peer1) done := checkStall(t, term) if err := syncer.Sync(cancel); err != nil { t.Fatalf("sync failed: %v", err) @@ -1082,7 +1086,7 @@ func TestAddPeerDuringSyncing(t *testing.T) { ) requestTimeoutInMillisecond = 50 * time.Millisecond // Millisecond - _, files := createSstorage(contract, []uint64{0, 1, 2}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + _, files := createSstorage(contract, []uint64{0, 1, 2}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) defer func(files []string) { for _, file := range files { @@ -1098,15 +1102,15 @@ func TestAddPeerDuringSyncing(t *testing.T) { return source } - expectedData, localShards := makeKVStorage(stateDB, contract, []uint64{0, 1, 2}, kvEntries) - data, shards := makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntries) - list := destoryData(data, make(map[uint64]struct{}), 0, kvEntries*3, 8) + expectedData, localShards := makeKVStorage(stateDB, contract, []uint64{0, 1, 2}, kvEntriesBits) + data, shards := makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntriesBits) + list := destoryData(data, make(map[uint64]struct{}), 0, kvEntriesBits*3, 8) peer0 := mkSource("source_0", shards, data) - data, shards = makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntries) - _ = destoryData(data, list, 0, kvEntries*3, 8) + data, shards = makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntriesBits) + _ = destoryData(data, list, 0, kvEntriesBits*3, 8) peer1 := mkSource("source_1", shards, data) - syncer := setupSyncer(localShards, stateDB, kvEntries*3, peer0) + syncer := setupSyncer(localShards, stateDB, kvEntriesBits*3, peer0) done := checkStall(t, term) go func() { time.Sleep(100 * time.Millisecond) @@ -1126,10 +1130,10 @@ func TestAddPeerDuringSyncing(t *testing.T) { func TestSaveAndLoadSyncStatus(t *testing.T) { var ( stateDB, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - entries = kvEntries * 10 - lastKvIndex = entries*3 - kvEntries - 9 + entries = kvEntriesBits * 10 + lastKvIndex = entries*3 - kvEntriesBits - 9 ) - shards, files := createSstorage(contract, []uint64{0, 1, 2}, sstorage.CHUNK_SIZE, kvEntries, 1, common.Address{}) + shards, files := createSstorage(contract, []uint64{0, 1, 2}, sstorage.CHUNK_SIZE_BITS, kvEntriesBits, 1, common.Address{}) if shards == nil { t.Fatalf("createSstorage failed") } @@ -1140,7 +1144,7 @@ func TestSaveAndLoadSyncStatus(t *testing.T) { } }(files) - syncer := setupSyncer(shards, stateDB, kvEntries) + syncer := setupSyncer(shards, stateDB, kvEntriesBits) task0 := createKvTask(contract, entries, 0, lastKvIndex, 20) task0.KvSubTasks = make([]*kvSubTask, 0) task1 := createKvTask(contract, entries, 1, lastKvIndex, 4) diff --git a/sstorage/data_file.go b/sstorage/data_file.go index 7a873926f192..597f58087ce2 100644 --- a/sstorage/data_file.go +++ b/sstorage/data_file.go @@ -21,7 +21,8 @@ const ( MAGIC = uint64(0xcf20bd770c22b2e1) VERSION = uint64(1) - CHUNK_SIZE = uint64(4096) + CHUNK_SIZE = uint64(4096) + CHUNK_SIZE_BITS = uint64(12) ) // A DataFile represents a local file for a consective chunks diff --git a/sstorage/data_shard.go b/sstorage/data_shard.go index 96d1372a7f78..78e08db4f845 100644 --- a/sstorage/data_shard.go +++ b/sstorage/data_shard.go @@ -105,6 +105,38 @@ func (ds *DataShard) ReadEncoded(kvIdx uint64, readLen int) ([]byte, error) { }) } +// Read the encoded data from storage and return it. +func (ds *DataShard) ReadChunkEncoded(kvIdx uint64, chunkIdx uint64) ([]byte, error) { + return ds.readChunkWith(kvIdx, chunkIdx, func(cdata []byte, chunkIdx uint64) []byte { + return cdata + }) +} + +// Read the encoded data from storage and decode it. +func (ds *DataShard) ReadChunk(kvIdx uint64, chunkIdx uint64, commit common.Hash) ([]byte, error) { + return ds.readChunkWith(kvIdx, chunkIdx, func(cdata []byte, chunkIdx uint64) []byte { + encodeKey := calcEncodeKey(commit, chunkIdx, ds.dataFiles[0].miner) + return decodeChunk(cdata, ds.dataFiles[0].encodeType, encodeKey) + }) +} + +// Read the encoded data from storage with a decoder. +func (ds *DataShard) readChunkWith(kvIdx uint64, chunkIdx uint64, decoder func([]byte, uint64) []byte) ([]byte, error) { + if !ds.Contains(kvIdx) { + return nil, fmt.Errorf("kv not found") + } + if chunkIdx > ds.chunksPerKv { + return nil, fmt.Errorf("chunkIdx out of range, chunkIdx: %d vs chunksPerKv %d", chunkIdx, ds.chunksPerKv) + } + idx := kvIdx*ds.chunksPerKv + chunkIdx + data, err := ds.readChunk(idx, int(CHUNK_SIZE)) + if err != nil { + return nil, err + } + data = decoder(data, chunkIdx) + return data, nil +} + // Read the encoded data from storage and decode it. func (ds *DataShard) Read(kvIdx uint64, readLen int, commit common.Hash) ([]byte, error) { return ds.readWith(kvIdx, readLen, func(cdata []byte, chunkIdx uint64) []byte { diff --git a/sstorage/shard_manager.go b/sstorage/shard_manager.go index 8b3b226ff087..9c5822cf9425 100644 --- a/sstorage/shard_manager.go +++ b/sstorage/shard_manager.go @@ -12,26 +12,52 @@ type ShardManager struct { kvSize uint64 chunksPerKv uint64 kvEntries uint64 + kvSizeBits uint64 + chunksPerKvBits uint64 + kvEntriesBits uint64 } -func NewShardManager(contractAddress common.Address, kvSize uint64, kvEntries uint64) *ShardManager { +func NewShardManager(contractAddress common.Address, kvSizeBits uint64, kvEntriesBits uint64) *ShardManager { return &ShardManager{ shardMap: make(map[uint64]*DataShard), contractAddress: contractAddress, - kvSize: kvSize, - chunksPerKv: kvSize / CHUNK_SIZE, - kvEntries: kvEntries, + kvSize: 1 << kvSizeBits, + chunksPerKv: (1 << kvSizeBits) / CHUNK_SIZE, + kvEntries: 1 << kvEntriesBits, + kvSizeBits: kvSizeBits, + chunksPerKvBits: kvSizeBits - CHUNK_SIZE_BITS, + kvEntriesBits: kvEntriesBits, } } +func (sm *ShardManager) ShardMap() map[uint64]*DataShard { + return sm.shardMap +} + +func (sm *ShardManager) ChunksPerKv() uint64 { + return sm.chunksPerKv +} + +func (sm *ShardManager) ChunksPerKvBits() uint64 { + return sm.chunksPerKvBits +} + func (sm *ShardManager) KvEntries() uint64 { return sm.kvEntries } +func (sm *ShardManager) KvEntriesBits() uint64 { + return sm.kvEntriesBits +} + func (sm *ShardManager) MaxKvSize() uint64 { return sm.kvSize } +func (sm *ShardManager) MaxKvSizeBits() uint64 { + return sm.kvSizeBits +} + func (sm *ShardManager) AddDataShard(shardIdx uint64) error { if _, ok := sm.shardMap[shardIdx]; !ok { ds := NewDataShard(shardIdx, sm.kvSize, sm.kvEntries) @@ -53,7 +79,7 @@ func (sm *ShardManager) AddDataFile(df *DataFile) error { return ds.AddDataFile(df) } -// Encode a raw KV data, and write it to the underly storage file. +// TryWrite Encode a raw KV data, and write it to the underly storage file. // Return error if the write IO fails. // Return false if the data is not managed by the ShardManager. func (sm *ShardManager) TryWrite(kvIdx uint64, b []byte, commit common.Hash) (bool, error) { @@ -65,7 +91,7 @@ func (sm *ShardManager) TryWrite(kvIdx uint64, b []byte, commit common.Hash) (bo } } -// Read the encoded KV data from storage file and decode it. +// TryRead Read the encoded KV data from storage file and decode it. // Return error if the read IO fails. // Return false if the data is not managed by the ShardManager. func (sm *ShardManager) TryRead(kvIdx uint64, readLen int, commit common.Hash) ([]byte, bool, error) { @@ -78,12 +104,12 @@ func (sm *ShardManager) TryRead(kvIdx uint64, readLen int, commit common.Hash) ( } } -// Decode the encoded KV data. +// DecodeKV Decode the encoded KV data. func (sm *ShardManager) DecodeKV(kvIdx uint64, b []byte, hash common.Hash) ([]byte, bool, error) { return sm.DecodeOrEncodeKV(kvIdx, b, hash, false) } -// Encode the raw KV data. +// EncodeKV Encode the raw KV data. func (sm *ShardManager) EncodeKV(kvIdx uint64, b []byte, hash common.Hash) ([]byte, bool, error) { return sm.DecodeOrEncodeKV(kvIdx, b, hash, true) } @@ -120,7 +146,7 @@ func (sm *ShardManager) DecodeOrEncodeKV(kvIdx uint64, b []byte, hash common.Has return nil, false, nil } -// Read the encoded KV data from storage file and return it. +// TryReadEncoded Read the encoded KV data from storage file and return it. // Return error if the read IO fails. // Return false if the data is not managed by the ShardManager. func (sm *ShardManager) TryReadEncoded(kvIdx uint64, readLen int) ([]byte, bool, error) { @@ -133,6 +159,36 @@ func (sm *ShardManager) TryReadEncoded(kvIdx uint64, readLen int) ([]byte, bool, } } +// TryReadChunk Read the encoded KV data using chunkIdx from storage file and decode it. +// Return error if the read IO fails. +// Return false if the data is not managed by the ShardManager. +func (sm *ShardManager) TryReadChunk(chunkIdx uint64, commit common.Hash) ([]byte, bool, error) { + kvIdx := chunkIdx / sm.chunksPerKv + cIdx := chunkIdx % sm.chunksPerKv + shardIdx := kvIdx / sm.kvEntries + if ds, ok := sm.shardMap[shardIdx]; ok { + b, err := ds.ReadChunk(kvIdx, cIdx, commit) // read all the data + return b, true, err + } else { + return nil, false, nil + } +} + +// TryReadChunkEncoded Read the encoded KV data using chunkIdx from storage file and return it. +// Return error if the read IO fails. +// Return false if the data is not managed by the ShardManager. +func (sm *ShardManager) TryReadChunkEncoded(chunkIdx uint64) ([]byte, bool, error) { + kvIdx := chunkIdx / sm.chunksPerKv + cIdx := chunkIdx % sm.chunksPerKv + shardIdx := kvIdx / sm.kvEntries + if ds, ok := sm.shardMap[shardIdx]; ok { + b, err := ds.ReadChunkEncoded(kvIdx, cIdx) // read all the data + return b, true, err + } else { + return nil, false, nil + } +} + func (sm *ShardManager) IsComplete() error { for _, ds := range sm.shardMap { if !ds.IsComplete() { diff --git a/sstorminer/merklelib.go b/sstorminer/merklelib.go new file mode 100644 index 000000000000..179f410ca7d8 --- /dev/null +++ b/sstorminer/merklelib.go @@ -0,0 +1,90 @@ +package sstorminer + +import ( + "bytes" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func expectedDiff(lastMineTime uint64, difficulty *big.Int, minedTime uint64, targetIntervalSec, cutoff, diffAdjDivisor, minDiff *big.Int) *big.Int { + interval := new(big.Int).SetUint64(minedTime - lastMineTime) + diff := difficulty + if interval.Cmp(targetIntervalSec) < 0 { + // diff = diff + (diff-interval*diff/cutoff)/diffAdjDivisor + diff = new(big.Int).Add(diff, new(big.Int).Div( + new(big.Int).Sub(diff, new(big.Int).Div(new(big.Int).Mul(interval, diff), cutoff)), diffAdjDivisor)) + if diff.Cmp(minDiff) < 0 { + diff = minDiff + } + } else { + // dec := (interval*diff/cutoff - diff) / diffAdjDivisor + dec := new(big.Int).Div(new(big.Int).Div(new(big.Int).Mul(interval, diff), cutoff), diffAdjDivisor) + if new(big.Int).Add(dec, minDiff).Cmp(diff) > 0 { + diff = minDiff + } else { + diff = new(big.Int).Sub(diff, dec) + } + } + + return diff +} + +func getProof(data []byte, chunkSize, nChunkBits, chunkIdx uint64) ([]common.Hash, error) { + nChunks := uint64(1) << nChunkBits + if chunkIdx >= nChunks { + return []common.Hash{}, fmt.Errorf("index out of scope") + } + nodes := make([]common.Hash, nChunks) + for i := uint64(0); i < nChunks; i++ { + off := i * chunkSize + if off > uint64(len(data)) { + break + } + l := uint64(len(data)) - off + if l >= chunkSize { + l = chunkSize + } + nodes[i] = crypto.Keccak256Hash(data[off : off+l]) + } + n, proofIdx := nChunks, uint64(0) + proofs := make([]common.Hash, nChunkBits) + for n != 1 { + proofs[proofIdx] = nodes[(chunkIdx/2)*2+1-chunkIdx%2] + for i := uint64(0); i < n/2; i++ { + nodes[i] = crypto.Keccak256Hash(nodes[i*2].Bytes(), nodes[i*2+1].Bytes()) + } + n = n / 2 + chunkIdx = chunkIdx / 2 + proofIdx = proofIdx + 1 + } + return proofs, nil +} + +func calculateRootWithProof(dataHash common.Hash, chunkIdx uint64, proofs []common.Hash) (common.Hash, error) { + hash := dataHash + nChunkBits := uint64(len(proofs)) + if chunkIdx >= uint64(1)< params.MaximumExtraDataSize { - return fmt.Errorf("extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize) - } - miner.worker.setExtra(extra) - return nil -} - -func (miner *Miner) SetEtherbase(addr common.Address) { - miner.coinbase = addr - miner.worker.setEtherbase(addr) +// SetRecommitInterval sets the interval for sealing work resubmitting. +func (miner *Miner) SetRecommitInterval(interval time.Duration) { + miner.worker.setRecommitInterval(interval) } diff --git a/sstorminer/miner_test.go b/sstorminer/miner_test.go index ac3795444a72..306fc21fe403 100644 --- a/sstorminer/miner_test.go +++ b/sstorminer/miner_test.go @@ -47,7 +47,7 @@ func NewMockBackend(bc *core.BlockChain, txPool *core.TxPool) *mockBackend { } } -func (m *mockBackend) BlockChain() *core.BlockChain { +func (m *mockBackend) BlockChain() BlockChain { return m.bc } @@ -86,7 +86,7 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) func TestMiner(t *testing.T) { miner, mux, cleanup := createMiner(t) defer cleanup(false) - miner.Start(common.HexToAddress("0x12345")) + miner.Start() waitForMiningState(t, miner, true) // Start the downloader mux.Post(downloader.StartEvent{}) @@ -114,7 +114,7 @@ func TestMiner(t *testing.T) { func TestMinerDownloaderFirstFails(t *testing.T) { miner, mux, cleanup := createMiner(t) defer cleanup(false) - miner.Start(common.HexToAddress("0x12345")) + miner.Start() waitForMiningState(t, miner, true) // Start the downloader mux.Post(downloader.StartEvent{}) @@ -146,7 +146,7 @@ func TestMinerDownloaderFirstFails(t *testing.T) { func TestMinerStartStopAfterDownloaderEvents(t *testing.T) { miner, mux, cleanup := createMiner(t) defer cleanup(false) - miner.Start(common.HexToAddress("0x12345")) + miner.Start() waitForMiningState(t, miner, true) // Start the downloader mux.Post(downloader.StartEvent{}) @@ -159,7 +159,7 @@ func TestMinerStartStopAfterDownloaderEvents(t *testing.T) { miner.Stop() waitForMiningState(t, miner, false) - miner.Start(common.HexToAddress("0x678910")) + miner.Start() waitForMiningState(t, miner, true) miner.Stop() @@ -170,13 +170,13 @@ func TestStartWhileDownload(t *testing.T) { miner, mux, cleanup := createMiner(t) defer cleanup(false) waitForMiningState(t, miner, false) - miner.Start(common.HexToAddress("0x12345")) + miner.Start() waitForMiningState(t, miner, true) // Stop the downloader and wait for the update loop to run mux.Post(downloader.StartEvent{}) waitForMiningState(t, miner, false) // Starting the miner after the downloader should not work - miner.Start(common.HexToAddress("0x12345")) + miner.Start() waitForMiningState(t, miner, false) } @@ -184,7 +184,7 @@ func TestStartStopMiner(t *testing.T) { miner, _, cleanup := createMiner(t) defer cleanup(false) waitForMiningState(t, miner, false) - miner.Start(common.HexToAddress("0x12345")) + miner.Start() waitForMiningState(t, miner, true) miner.Stop() waitForMiningState(t, miner, false) @@ -195,36 +195,13 @@ func TestCloseMiner(t *testing.T) { miner, _, cleanup := createMiner(t) defer cleanup(true) waitForMiningState(t, miner, false) - miner.Start(common.HexToAddress("0x12345")) + miner.Start() waitForMiningState(t, miner, true) // Terminate the miner and wait for the update loop to run miner.Close() waitForMiningState(t, miner, false) } -// TestMinerSetEtherbase checks that etherbase becomes set even if mining isn't -// possible at the moment -func TestMinerSetEtherbase(t *testing.T) { - miner, mux, cleanup := createMiner(t) - defer cleanup(false) - // Start with a 'bad' mining address - miner.Start(common.HexToAddress("0xdead")) - waitForMiningState(t, miner, true) - // Start the downloader - mux.Post(downloader.StartEvent{}) - waitForMiningState(t, miner, false) - // Now user tries to configure proper mining address - miner.Start(common.HexToAddress("0x1337")) - // Stop the downloader and wait for the update loop to run - mux.Post(downloader.DoneEvent{}) - - waitForMiningState(t, miner, true) - // The miner should now be using the good address - if got, exp := miner.coinbase, common.HexToAddress("0x1337"); got != exp { - t.Fatalf("Wrong coinbase, got %x expected %x", got, exp) - } -} - // waitForMiningState waits until either // * the desired mining state was reached // * a timeout was reached which fails the test @@ -243,9 +220,7 @@ func waitForMiningState(t *testing.T, m *Miner, mining bool) { func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) { // Create Ethash config - config := Config{ - Etherbase: common.HexToAddress("123456789"), - } + config := Config{} // Create chainConfig memdb := memorydb.New() chainDB := rawdb.NewDatabase(memdb) @@ -269,7 +244,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) { // Create event Mux mux := new(event.TypeMux) // Create Miner - miner := New(backend, &config, chainConfig, mux, engine, nil) + miner := New(backend, &config, chainConfig, mux, nil) cleanup := func(skipMiner bool) { bc.Stop() engine.Close() diff --git a/sstorminer/unconfirmed.go b/sstorminer/unconfirmed.go deleted file mode 100644 index d7c371d816c9..000000000000 --- a/sstorminer/unconfirmed.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package sstorminer - -import ( - "container/ring" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" -) - -// chainRetriever is used by the unconfirmed block set to verify whether a previously -// mined block is part of the canonical chain or not. -type chainRetriever interface { - // GetHeaderByNumber retrieves the canonical header associated with a block number. - GetHeaderByNumber(number uint64) *types.Header - - // GetBlockByNumber retrieves the canonical block associated with a block number. - GetBlockByNumber(number uint64) *types.Block -} - -// unconfirmedBlock is a small collection of metadata about a locally mined block -// that is placed into a unconfirmed set for canonical chain inclusion tracking. -type unconfirmedBlock struct { - index uint64 - hash common.Hash -} - -// unconfirmedBlocks implements a data structure to maintain locally mined blocks -// have not yet reached enough maturity to guarantee chain inclusion. It is -// used by the miner to provide logs to the user when a previously mined block -// has a high enough guarantee to not be reorged out of the canonical chain. -type unconfirmedBlocks struct { - chain chainRetriever // Blockchain to verify canonical status through - depth uint // Depth after which to discard previous blocks - blocks *ring.Ring // Block infos to allow canonical chain cross checks - lock sync.Mutex // Protects the fields from concurrent access -} - -// newUnconfirmedBlocks returns new data structure to track currently unconfirmed blocks. -func newUnconfirmedBlocks(chain chainRetriever, depth uint) *unconfirmedBlocks { - return &unconfirmedBlocks{ - chain: chain, - depth: depth, - } -} - -// Insert adds a new block to the set of unconfirmed ones. -func (set *unconfirmedBlocks) Insert(index uint64, hash common.Hash) { - // If a new block was mined locally, shift out any old enough blocks - set.Shift(index) - - // Create the new item as its own ring - item := ring.New(1) - item.Value = &unconfirmedBlock{ - index: index, - hash: hash, - } - // Set as the initial ring or append to the end - set.lock.Lock() - defer set.lock.Unlock() - - if set.blocks == nil { - set.blocks = item - } else { - set.blocks.Move(-1).Link(item) - } - // Display a log for the user to notify of a new mined block unconfirmed - log.Info("🔨 mined potential block", "number", index, "hash", hash) -} - -// Shift drops all unconfirmed blocks from the set which exceed the unconfirmed sets depth -// allowance, checking them against the canonical chain for inclusion or staleness -// report. -func (set *unconfirmedBlocks) Shift(height uint64) { - set.lock.Lock() - defer set.lock.Unlock() - - for set.blocks != nil { - // Retrieve the next unconfirmed block and abort if too fresh - next := set.blocks.Value.(*unconfirmedBlock) - if next.index+uint64(set.depth) > height { - break - } - // Block seems to exceed depth allowance, check for canonical status - header := set.chain.GetHeaderByNumber(next.index) - switch { - case header == nil: - log.Warn("Failed to retrieve header of mined block", "number", next.index, "hash", next.hash) - case header.Hash() == next.hash: - log.Info("🔗 block reached canonical chain", "number", next.index, "hash", next.hash) - default: - // Block is not canonical, check whether we have an uncle or a lost block - included := false - for number := next.index; !included && number < next.index+uint64(set.depth) && number <= height; number++ { - if block := set.chain.GetBlockByNumber(number); block != nil { - for _, uncle := range block.Uncles() { - if uncle.Hash() == next.hash { - included = true - break - } - } - } - } - if included { - log.Info("â‘‚ block became an uncle", "number", next.index, "hash", next.hash) - } else { - log.Info("😱 block lost", "number", next.index, "hash", next.hash) - } - } - // Drop the block out of the ring - if set.blocks.Value == set.blocks.Next().Value { - set.blocks = nil - } else { - set.blocks = set.blocks.Move(-1) - set.blocks.Unlink(1) - set.blocks = set.blocks.Move(1) - } - } -} diff --git a/sstorminer/unconfirmed_test.go b/sstorminer/unconfirmed_test.go deleted file mode 100644 index 6126c377784a..000000000000 --- a/sstorminer/unconfirmed_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package sstorminer - -import ( - "testing" - - "github.com/ethereum/go-ethereum/core/types" -) - -// noopChainRetriever is an implementation of headerRetriever that always -// returns nil for any requested headers. -type noopChainRetriever struct{} - -func (r *noopChainRetriever) GetHeaderByNumber(number uint64) *types.Header { - return nil -} -func (r *noopChainRetriever) GetBlockByNumber(number uint64) *types.Block { - return nil -} - -// Tests that inserting blocks into the unconfirmed set accumulates them until -// the desired depth is reached, after which they begin to be dropped. -func TestUnconfirmedInsertBounds(t *testing.T) { - limit := uint(10) - - pool := newUnconfirmedBlocks(new(noopChainRetriever), limit) - for depth := uint64(0); depth < 2*uint64(limit); depth++ { - // Insert multiple blocks for the same level just to stress it - for i := 0; i < int(depth); i++ { - pool.Insert(depth, [32]byte{byte(depth), byte(i)}) - } - // Validate that no blocks below the depth allowance are left in - pool.blocks.Do(func(block interface{}) { - if block := block.(*unconfirmedBlock); block.index+uint64(limit) <= depth { - t.Errorf("depth %d: block %x not dropped", depth, block.hash) - } - }) - } -} - -// Tests that shifting blocks out of the unconfirmed set works both for normal -// cases as well as for corner cases such as empty sets, empty shifts or full -// shifts. -func TestUnconfirmedShifts(t *testing.T) { - // Create a pool with a few blocks on various depths - limit, start := uint(10), uint64(25) - - pool := newUnconfirmedBlocks(new(noopChainRetriever), limit) - for depth := start; depth < start+uint64(limit); depth++ { - pool.Insert(depth, [32]byte{byte(depth)}) - } - // Try to shift below the limit and ensure no blocks are dropped - pool.Shift(start + uint64(limit) - 1) - if n := pool.blocks.Len(); n != int(limit) { - t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit) - } - // Try to shift half the blocks out and verify remainder - pool.Shift(start + uint64(limit) - 1 + uint64(limit/2)) - if n := pool.blocks.Len(); n != int(limit)/2 { - t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit/2) - } - // Try to shift all the remaining blocks out and verify emptyness - pool.Shift(start + 2*uint64(limit)) - if n := pool.blocks.Len(); n != 0 { - t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) - } - // Try to shift out from the empty set and make sure it doesn't break - pool.Shift(start + 3*uint64(limit)) - if n := pool.blocks.Len(); n != 0 { - t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) - } -} diff --git a/sstorminer/worker.go b/sstorminer/worker.go index 8be27f625c0d..fab54870295b 100644 --- a/sstorminer/worker.go +++ b/sstorminer/worker.go @@ -17,153 +17,219 @@ package sstorminer import ( - "errors" + "crypto/ecdsa" "fmt" + "github.com/ethereum/go-ethereum/core/state" "math/big" + "math/rand" + "sort" + "strings" "sync" "sync/atomic" "time" - mapset "github.com/deckarep/golang-set" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/misc" - "github.com/ethereum/go-ethereum/consensus/tendermint" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/trie" + sstor "github.com/ethereum/go-ethereum/sstorage" +) + +const ( + ABI = "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startShardId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"shardLenBits\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"miner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"minedTs\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"maskedData\",\"type\":\"bytes[]\"}],\"name\":\"mine\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + MineFunc = "mine" + gas = uint64(1000000) ) const ( // resultQueueSize is the size of channel listening to sealing result. resultQueueSize = 10 - // txChanSize is the size of channel listening to NewTxsEvent. - // The number is referenced from the size of tx pool. - txChanSize = 4096 - // chainHeadChanSize is the size of channel listening to ChainHeadEvent. chainHeadChanSize = 10 - // chainSideChanSize is the size of channel listening to ChainSideEvent. - chainSideChanSize = 10 - - // resubmitAdjustChanSize is the size of resubmitting interval adjustment channel. - resubmitAdjustChanSize = 10 - - // sealingLogAtDepth is the number of confirmations before logging successful sealing. - sealingLogAtDepth = 7 - // minRecommitInterval is the minimal time interval to recreate the sealing block with // any newly arrived transactions. minRecommitInterval = 1 * time.Second - // maxRecommitInterval is the maximum time interval to recreate the sealing block with - // any newly arrived transactions. - maxRecommitInterval = 15 * time.Second - - // intervalAdjustRatio is the impact a single interval adjustment has on sealing work - // resubmitting interval. - intervalAdjustRatio = 0.1 + mineTimeOut = uint64(10) +) - // intervalAdjustBias is applied during the new resubmit interval calculation in favor of - // increasing upper limit or decreasing lower limit so that the limit can be reachable. - intervalAdjustBias = 200 * 1000.0 * 1000.0 +var ( + maxUint256 = new(big.Int).Sub(new(big.Int).Exp(new(big.Int).SetUint64(2), + new(big.Int).SetUint64(256), nil), new(big.Int).SetUint64(1)) + vABI, _ = abi.JSON(strings.NewReader(ABI)) +) - // staleThreshold is the maximum depth of the acceptable stale block. - staleThreshold = 7 +const ( + TaskStateNoStart = iota + TaskStateMining + TaskStateMined ) -// environment is the worker's current environment and holds all -// information of the sealing block generation. -type environment struct { - signer types.Signer - - state *state.StateDB // apply state changes here - ancestors mapset.Set // ancestor set (used for checking uncle parent validity) - family mapset.Set // family set (used for checking uncle invalidity) - tcount int // tx count in cycle - totalCalldata int // total calldata in cycle - gasPool *core.GasPool // available gas used to pack transactions - coinbase common.Address - - header *types.Header - txs []*types.Transaction - receipts []*types.Receipt -} +type BlockChain interface { + CurrentBlock() *types.Block -// copy creates a deep copy of environment. -func (env *environment) copy() *environment { - cpy := &environment{ - signer: env.signer, - state: env.state.Copy(), - ancestors: env.ancestors.Clone(), - family: env.family.Clone(), - tcount: env.tcount, - totalCalldata: env.totalCalldata, - coinbase: env.coinbase, - header: types.CopyHeader(env.header), - } - if env.gasPool != nil { - gasPool := *env.gasPool - cpy.gasPool = &gasPool - } - // The content of txs and uncles are immutable, unnecessary - // to do the expensive deep copy for them. - cpy.txs = make([]*types.Transaction, len(env.txs)) - copy(cpy.txs, env.txs) - for hash, uncle := range env.uncles { - cpy.uncles[hash] = uncle - } - return cpy -} + InsertChain(chain types.Blocks) (int, error) -// discard terminates the background prefetcher go-routine. It should -// always be called for all created environment instances otherwise -// the go-routine leak can happen. -func (env *environment) discard() { - if env.state == nil { - return - } - env.state.StopPrefetcher() + GetSstorageMiningInfo(root common.Hash, contract common.Address, shardId uint64) (*core.MiningInfo, error) + + ReadKVsByIndexList(contract common.Address, indexes []uint64, useMaxKVsize bool) ([]*core.KV, error) + + SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription + + State() (*state.StateDB, error) } // task contains all information for consensus engine sealing and result submitting. type task struct { - receipts []*types.Receipt - state *state.StateDB - block *types.Block - createdAt time.Time + contract common.Address + shardIdx uint64 + kvSizeBits uint64 + chunkSizeBits uint64 + kvEntriesBits uint64 + miner common.Address + running int32 + info *core.MiningInfo + shardManager *sstor.ShardManager + startMiningTime uint64 + state uint64 + mu sync.RWMutex // The lock used to protect the state } -const ( - commitInterruptNone int32 = iota - commitInterruptNewHead - commitInterruptResubmit -) +func (t *task) getState() uint64 { + t.mu.Lock() + defer t.mu.Unlock() + return t.state +} -// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier. -type newWorkReq struct { - interrupt *int32 - noempty bool - timestamp int64 +func (t *task) setState(state uint64) { + t.mu.Lock() + defer t.mu.Unlock() + t.state = state +} + +// start sets the running status as 1 and triggers new work submitting. +func (t *task) start() { + t.mu.Lock() + defer t.mu.Unlock() + atomic.StoreInt32(&t.running, 1) +} + +// stop sets the running status as 0. +func (t *task) stop() { + t.mu.Lock() + defer t.mu.Unlock() + atomic.StoreInt32(&t.running, 0) +} + +// isRunning returns an indicator whether worker is running or not. +func (t *task) isRunning() bool { + return atomic.LoadInt32(&t.running) == 1 +} + +type tasks []*task + +func (t tasks) Len() int { return len(t) } +func (t tasks) Less(i, j int) bool { return t[i].info.LastMineTime < t[j].info.LastMineTime } +func (t tasks) Swap(i, j int) { t[i], t[j] = t[j], t[i] } + +type result struct { + task *task + startShardId uint64 + shardLenBits uint64 + miner common.Address + minedTs uint64 + nonce uint64 + kvIdxs []uint64 + chunkIdxs []uint64 + encodedData [][]byte + proofs [][]common.Hash +} + +type txSorter struct { + txs []*types.Transaction + baseFee *big.Int +} + +func newSorter(txs []*types.Transaction, baseFee *big.Int) *txSorter { + return &txSorter{ + txs: txs, + baseFee: baseFee, + } +} + +func (s *txSorter) Len() int { return len(s.txs) } +func (s *txSorter) Swap(i, j int) { + s.txs[i], s.txs[j] = s.txs[j], s.txs[i] +} +func (s *txSorter) Less(i, j int) bool { + // It's okay to discard the error because a tx would never be + // accepted into a block with an invalid effective tip. + tip1, _ := s.txs[i].EffectiveGasTip(s.baseFee) + tip2, _ := s.txs[j].EffectiveGasTip(s.baseFee) + return tip1.Cmp(tip2) < 0 } -// getWorkReq represents a request for getting a new sealing work with provided parameters. -type getWorkReq struct { - params *generateParams - err error - result chan *types.Block +type priceOracle struct { + chainConfig *params.ChainConfig + baseFee *big.Int + suggestGasTip *big.Int + blockNumber uint64 + ignoreUnder *big.Int + limit int } -// intervalAdjust represents a resubmitting interval adjustment. -type intervalAdjust struct { - ratio float64 - inc bool +// The TipGap provided in the SuggestGasTipCap method (provide by EthAPIBackend) will extract the smallest 3 TipGaps +// from each of the latest n blocks, sort them, and take the median according to the set percentile. +// +// # To make it simple, if the transaction volume is small, suggestGasTip set to 0, otherwise the third smallest GasTip is used. +// +// updatePriceOracle update suggestGasTip according to block. +func (o *priceOracle) updatePriceOracle(block *types.Block) { + if block.NumberU64() < o.blockNumber { + return + } + // if block GasUsed smaller than GasLimit / 2, only baseFee is needed, + // so suggestGasTip can be set to 0. + if block.GasUsed() < block.GasLimit()/2 { + o.suggestGasTip = new(big.Int).SetUint64(0) + return + } + + // otherwise, the third smallest GasTip is used. + signer := types.MakeSigner(o.chainConfig, block.Number()) + sorter := newSorter(block.Transactions(), block.BaseFee()) + sort.Sort(sorter) + + var prices []*big.Int + for _, tx := range sorter.txs { + tip, _ := tx.EffectiveGasTip(block.BaseFee()) + if o.ignoreUnder != nil && tip.Cmp(o.ignoreUnder) == -1 { + continue + } + sender, err := types.Sender(signer, tx) + if err == nil && sender != block.Coinbase() { + prices = append(prices, tip) + if len(prices) >= o.limit { + break + } + } + } + if len(prices) > 0 { + o.suggestGasTip = prices[len(prices)-1] + } +} + +// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier. +type newWorkReq struct { + timestamp int64 } // worker is the main object which takes care of submitting new work to consensus engine @@ -173,82 +239,85 @@ type worker struct { chainConfig *params.ChainConfig engine consensus.Engine eth Backend - chain *core.BlockChain + chain BlockChain + priceOracle *priceOracle // Subscriptions mux *event.TypeMux chainHeadCh chan core.ChainHeadEvent chainHeadSub event.Subscription + client *ethclient.Client + signer types.Signer + privKey *ecdsa.PrivateKey // Channels newWorkCh chan *newWorkReq - getWorkCh chan *getWorkReq taskCh chan *task - resultCh chan *types.Block + resultCh chan *result startCh chan struct{} + taskStartCh chan struct{} exitCh chan struct{} + taskDoneCh chan struct{} resubmitIntervalCh chan time.Duration - resubmitAdjustCh chan *intervalAdjust wg sync.WaitGroup + mu sync.RWMutex // The lock used to protect the coinbase and extra fields - current *environment // An environment for current running cycle. - unconfirmed *unconfirmedBlocks // A set of locally mined blocks pending canonicalness confirmations. - - mu sync.RWMutex // The lock used to protect the coinbase and extra fields - coinbase common.Address - extra []byte - - pendingMu sync.RWMutex - pendingTasks map[common.Hash]*task - - snapshotMu sync.RWMutex // The lock used to protect the snapshots below - snapshotBlock *types.Block - snapshotReceipts types.Receipts - snapshotState *state.StateDB + tasks tasks + running int32 - // atomic status counters - running int32 // The indicator whether the consensus engine is running or not. - newTxs int32 // New arrival transaction count since last sealing work submitting. - - // noempty is the flag used to control whether the feature of pre-seal empty - // block is enabled. The default value is false(pre-seal is enabled by default). - // But in some special scenario the consensus engine will seal blocks instantaneously, - // in this case this feature will add all empty blocks into canonical chain - // non-stop and no real transaction will be included. - noempty uint32 - - // External functions - isLocalBlock func(header *types.Header) bool // Function used to determine whether the specified block is mined by local miner. - - // Test hooks - newTaskHook func(*task) // Method to call upon receiving a new sealing task. - skipSealHook func(*task) bool // Method to decide whether skipping the sealing. - fullTaskHook func() // Method to call before pushing the full sealing task. - resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval. + newTaskHook func(*task) + newResultHook func(*result) } -func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(header *types.Header) bool, init bool) *worker { +func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, mux *event.TypeMux, privKey *ecdsa.PrivateKey, init bool) *worker { worker := &worker{ config: config, chainConfig: chainConfig, - engine: engine, eth: eth, mux: mux, chain: eth.BlockChain(), - isLocalBlock: isLocalBlock, - unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), sealingLogAtDepth), - pendingTasks: make(map[common.Hash]*task), + tasks: make([]*task, 0), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), newWorkCh: make(chan *newWorkReq), - getWorkCh: make(chan *getWorkReq), - taskCh: make(chan *task), - resultCh: make(chan *types.Block, resultQueueSize), + taskCh: make(chan *task, len(sstor.Shards())), + resultCh: make(chan *result, resultQueueSize), exitCh: make(chan struct{}), startCh: make(chan struct{}, 1), resubmitIntervalCh: make(chan time.Duration), - resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), + taskDoneCh: make(chan struct{}), + taskStartCh: make(chan struct{}), + signer: types.NewEIP2930Signer(chainConfig.ChainID), + privKey: privKey, + } + for addr, sm := range sstor.ContractToShardManager { + for idx, shard := range sm.ShardMap() { + // info, _ := worker.chain.GetSstorageMiningInfo(worker.chain.CurrentBlock().Root(), addr, idx) + task := task{ + contract: addr, + shardIdx: idx, + kvSizeBits: sm.MaxKvSizeBits(), + chunkSizeBits: sm.ChunksPerKvBits(), + kvEntriesBits: sm.KvEntriesBits(), + miner: shard.Miner(), + shardManager: sm, + running: 1, + info: nil, + } + worker.tasks = append(worker.tasks, &task) + } + } + + curBlock := eth.BlockChain().CurrentBlock() + worker.priceOracle = &priceOracle{ + chainConfig: chainConfig, + baseFee: curBlock.BaseFee(), + blockNumber: curBlock.NumberU64(), + ignoreUnder: new(big.Int).SetUint64(2 * params.GWei), + limit: 3, } + worker.priceOracle.updatePriceOracle(curBlock) + // Subscribe events for blockchain worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) @@ -263,7 +332,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus go worker.mainLoop() go worker.newWorkLoop(recommit) go worker.resultLoop() - go worker.taskLoop() + go worker.taskLoop() // can change to multi threads to run task // Submit first work to initialize pending state. if init { @@ -272,45 +341,11 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus return worker } -// setEtherbase sets the etherbase used to initialize the block coinbase field. -func (w *worker) setEtherbase(addr common.Address) { - w.mu.Lock() - defer w.mu.Unlock() - w.coinbase = addr -} - -func (w *worker) setGasCeil(ceil uint64) { - w.mu.Lock() - defer w.mu.Unlock() - w.config.GasCeil = ceil -} - -// setExtra sets the content used to initialize the block extra field. -func (w *worker) setExtra(extra []byte) { - w.mu.Lock() - defer w.mu.Unlock() - w.extra = extra -} - -// disablePreseal disables pre-sealing feature -func (w *worker) disablePreseal() { - atomic.StoreUint32(&w.noempty, 1) -} - -// enablePreseal enables pre-sealing feature -func (w *worker) enablePreseal() { - atomic.StoreUint32(&w.noempty, 0) -} - -func (w *worker) init() { - tm, isTm := w.engine.(*tendermint.Tendermint) - if isTm { - err := tm.Init(w.chain, func(parent common.Hash, coinbase common.Address, timestamp uint64) (*types.Block, error) { - return w.getSealingBlock(parent, timestamp, coinbase, common.Hash{}) - }, w.mux) - if err != nil { - log.Crit("tm.Init", "err", err) - } +// setRecommitInterval updates the interval for miner sealing work recommitting. +func (w *worker) setRecommitInterval(interval time.Duration) { + select { + case w.resubmitIntervalCh <- interval: + case <-w.exitCh: } } @@ -330,6 +365,25 @@ func (w *worker) isRunning() bool { return atomic.LoadInt32(&w.running) == 1 } +// start sets the running status as 1 for a task and triggers task start chan. +func (w *worker) startTask(contract common.Address, shardIdx uint64) { + for _, task := range w.tasks { + if task.contract == contract && task.shardIdx == shardIdx && !task.isRunning() { + task.start() + w.taskStartCh <- struct{}{} + } + } +} + +// stop sets the running status as 0 for a task. +func (w *worker) stopTask(contract common.Address, shardIdx uint64) { + for _, task := range w.tasks { + if task.contract == contract && task.shardIdx == shardIdx && task.isRunning() { + task.stop() + } + } +} + // close terminates all background threads maintained by the worker. // Note the worker does not support being closed multiple times. func (w *worker) close() { @@ -338,90 +392,36 @@ func (w *worker) close() { w.wg.Wait() } -// recalcRecommit recalculates the resubmitting interval upon feedback. -func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) time.Duration { - var ( - prevF = float64(prev.Nanoseconds()) - next float64 - ) - if inc { - next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target+intervalAdjustBias) - max := float64(maxRecommitInterval.Nanoseconds()) - if next > max { - next = max - } - } else { - next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target-intervalAdjustBias) - min := float64(minRecommit.Nanoseconds()) - if next < min { - next = min - } - } - return time.Duration(int64(next)) -} - // newWorkLoop is a standalone goroutine to submit new sealing work upon received events. func (w *worker) newWorkLoop(recommit time.Duration) { defer w.wg.Done() var ( - interrupt *int32 minRecommit = recommit // minimal resubmit interval specified by user. - timestamp int64 // timestamp for each round of sealing. ) timer := time.NewTimer(0) defer timer.Stop() <-timer.C // discard the initial tick - // commit aborts in-flight transaction execution with given signal and resubmits a new one. - commit := func(noempty bool, s int32) { - if interrupt != nil { - atomic.StoreInt32(interrupt, s) - } - interrupt = new(int32) - select { - case w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp}: - case <-w.exitCh: - return - } - timer.Reset(recommit) - atomic.StoreInt32(&w.newTxs, 0) - } - // clearPending cleans the stale pending tasks. - clearPending := func(number uint64) { - w.pendingMu.Lock() - for h, t := range w.pendingTasks { - if t.block.NumberU64()+staleThreshold <= number { - delete(w.pendingTasks, h) - } - } - w.pendingMu.Unlock() - } - for { select { case <-w.startCh: - clearPending(w.chain.CurrentBlock().NumberU64()) - - timestamp = time.Now().Unix() - commit(false, commitInterruptNewHead) + fmt.Println("start") + w.updateTaskInfo(w.chain.CurrentBlock().Root(), time.Now().Unix()) + timer.Reset(recommit) case head := <-w.chainHeadCh: - clearPending(head.Block.NumberU64()) - - timestamp = time.Now().Unix() - commit(false, commitInterruptNewHead) + fmt.Println("new chain head") + w.updateTaskInfo(head.Block.Root(), time.Now().Unix()) + timer.Reset(recommit) case <-timer.C: // If sealing is running resubmit a new work cycle periodically to pull in // higher priced transactions. Disable this overhead for pending blocks. - if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) { - // Short circuit if no new transaction arrives. - if atomic.LoadInt32(&w.newTxs) == 0 { - timer.Reset(recommit) - continue - } - commit(true, commitInterruptResubmit) + if w.isRunning() { + fmt.Println("time out") + timer.Reset(recommit) + w.updateTaskInfo(w.chain.CurrentBlock().Root(), time.Now().Unix()) } case interval := <-w.resubmitIntervalCh: @@ -431,28 +431,7 @@ func (w *worker) newWorkLoop(recommit time.Duration) { interval = minRecommitInterval } log.Info("Miner recommit interval update", "from", minRecommit, "to", interval) - minRecommit, recommit = interval, interval - - if w.resubmitHook != nil { - w.resubmitHook(minRecommit, recommit) - } - - case adjust := <-w.resubmitAdjustCh: - // Adjust resubmit interval by feedback. - if adjust.inc { - before := recommit - target := float64(recommit.Nanoseconds()) / adjust.ratio - recommit = recalcRecommit(minRecommit, recommit, target, true) - log.Trace("Increase miner recommit interval", "from", before, "to", recommit) - } else { - before := recommit - recommit = recalcRecommit(minRecommit, recommit, float64(minRecommit.Nanoseconds()), false) - log.Trace("Decrease miner recommit interval", "from", before, "to", recommit) - } - - if w.resubmitHook != nil { - w.resubmitHook(minRecommit, recommit) - } + recommit = interval case <-w.exitCh: return @@ -466,43 +445,38 @@ func (w *worker) newWorkLoop(recommit time.Duration) { func (w *worker) mainLoop() { defer w.wg.Done() defer w.chainHeadSub.Unsubscribe() - defer func() { - if w.current != nil { - w.current.discard() - } - }() - cleanTicker := time.NewTicker(time.Second * 10) - defer cleanTicker.Stop() + var stopCh chan struct{} + interrupt := func() { + if stopCh != nil { + close(stopCh) + stopCh = nil + } + } for { select { - case req := <-w.newWorkCh: - w.commitWork(req.interrupt, req.noempty, req.timestamp) + case <-w.newWorkCh: + fmt.Println("get new work") + interrupt() + stopCh = make(chan struct{}) + w.commitWork(stopCh) + fmt.Println("new work") - case req := <-w.getWorkCh: - block, err := w.generateWork(req.params) - if err != nil { - req.err = err - req.result <- nil - } else { - req.result <- block - } + case <-w.taskDoneCh: + fmt.Println("get task done") + interrupt() + stopCh = make(chan struct{}) + w.commitWork(stopCh) + fmt.Println("task done") - case <-cleanTicker.C: - chainHead := w.chain.CurrentBlock() - for hash, uncle := range w.localUncles { - if uncle.NumberU64()+staleThreshold <= chainHead.NumberU64() { - delete(w.localUncles, hash) - } - } - for hash, uncle := range w.remoteUncles { - if uncle.NumberU64()+staleThreshold <= chainHead.NumberU64() { - delete(w.remoteUncles, hash) - } - } + case <-w.taskStartCh: + fmt.Println("get task start") + interrupt() + stopCh = make(chan struct{}) + w.commitWork(stopCh) + fmt.Println("task start") - // System stopped case <-w.exitCh: return case <-w.chainHeadSub.Err(): @@ -515,48 +489,21 @@ func (w *worker) mainLoop() { // push them to consensus engine. func (w *worker) taskLoop() { defer w.wg.Done() - var ( - stopCh chan struct{} - prev common.Hash - ) - // interrupt aborts the in-flight sealing task. - interrupt := func() { - if stopCh != nil { - close(stopCh) - stopCh = nil - } - } for { select { case task := <-w.taskCh: + fmt.Println("get task") if w.newTaskHook != nil { w.newTaskHook(task) } - // Reject duplicate sealing work due to resubmitting. - sealHash := w.engine.SealHash(task.block.Header()) - if sealHash == prev { - continue + _, err := w.mineTask(task) + if err != nil { + log.Warn("mine task fail", "err", err.Error()) } - // Interrupt previous sealing operation - interrupt() - stopCh, prev = make(chan struct{}), sealHash + w.taskDoneCh <- struct{}{} - if w.skipSealHook != nil && w.skipSealHook(task) { - continue - } - w.pendingMu.Lock() - w.pendingTasks[sealHash] = task - w.pendingMu.Unlock() - - if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil { - log.Warn("Block sealing failed", "err", err) - w.pendingMu.Lock() - delete(w.pendingTasks, sealHash) - w.pendingMu.Unlock() - } case <-w.exitCh: - interrupt() return } } @@ -568,66 +515,13 @@ func (w *worker) resultLoop() { defer w.wg.Done() for { select { - case block := <-w.resultCh: - // Short circuit when receiving empty result. - if block == nil { - continue - } - // Short circuit when receiving duplicate result caused by resubmitting. - if w.chain.HasBlock(block.Hash(), block.NumberU64()) { - continue - } - var ( - sealhash = w.engine.SealHash(block.Header()) - hash = block.Hash() - ) - w.pendingMu.RLock() - task, exist := w.pendingTasks[sealhash] - w.pendingMu.RUnlock() - if !exist { - log.Error("Block found but no relative pending task", "number", block.Number(), "sealhash", sealhash, "hash", hash) - continue - } - // Different block could share same sealhash, deep copy here to prevent write-write conflict. - var ( - receipts = make([]*types.Receipt, len(task.receipts)) - logs []*types.Log - ) - for i, taskReceipt := range task.receipts { - receipt := new(types.Receipt) - receipts[i] = receipt - *receipt = *taskReceipt - - // add block location fields - receipt.BlockHash = hash - receipt.BlockNumber = block.Number() - receipt.TransactionIndex = uint(i) - - // Update the block hash in all logs since it is now available and not when the - // receipt/log of individual transactions were created. - receipt.Logs = make([]*types.Log, len(taskReceipt.Logs)) - for i, taskLog := range taskReceipt.Logs { - log := new(types.Log) - receipt.Logs[i] = log - *log = *taskLog - log.BlockHash = hash - } - logs = append(logs, receipt.Logs...) + case result := <-w.resultCh: + fmt.Println("get result") + if w.newResultHook != nil { + w.newResultHook(result) } - // Commit block and state to database. - _, err := w.chain.WriteBlockAndSetHead(block, receipts, logs, task.state, true) - if err != nil { - log.Error("Failed writing block to chain", "err", err) - continue - } - log.Info("Successfully sealed new block", "number", block.Number(), "sealhash", sealhash, "hash", hash, - "elapsed", common.PrettyDuration(time.Since(task.createdAt))) - - // Broadcast the block and announce chain insertion event - w.mux.Post(core.NewMinedBlockEvent{Block: block}) - // Insert the block into the set of pending ones to resultLoop for confirmations - w.unconfirmed.Insert(block.NumberU64(), block.Hash()) + w.submitMinedResult(result) case <-w.exitCh: return @@ -635,447 +529,208 @@ func (w *worker) resultLoop() { } } -// makeEnv creates a new environment for the sealing block. -func (w *worker) makeEnv(parent *types.Block, header *types.Header, coinbase common.Address) (*environment, error) { - // Retrieve the parent state to execute on top and start a prefetcher for - // the miner to speed block sealing up a bit. - state, err := w.chain.StateAt(parent.Root()) - if err != nil { - // Note since the sealing block can be created upon the arbitrary parent - // block, but the state of parent block may already be pruned, so the necessary - // state recovery is needed here in the future. - // - // The maximum acceptable reorg depth can be limited by the finalised block - // somehow. TODO(rjl493456442) fix the hard-coded number here later. - state, err = w.eth.StateAtBlock(parent, 1024, nil, false, false) - log.Warn("Recovered mining state", "root", parent.Root(), "err", err) - } - if err != nil { - return nil, err - } - state.StartPrefetcher("miner") - - // Note the passed coinbase may be different with header.Coinbase. - env := &environment{ - signer: types.MakeSigner(w.chainConfig, header.Number), - state: state, - coinbase: coinbase, - ancestors: mapset.NewSet(), - family: mapset.NewSet(), - header: header, - uncles: make(map[common.Hash]*types.Header), - } - // when 08 is processed ancestors contain 07 (quick block) - for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { - for _, uncle := range ancestor.Uncles() { - env.family.Add(uncle.Hash()) +// updateTaskInfo aborts in-flight transaction execution with given signal and resubmits a new one. +func (w *worker) updateTaskInfo(root common.Hash, timestamp int64) { + w.mu.Lock() + defer w.mu.Unlock() + + updated := false + for _, task := range w.tasks { + info, err := w.chain.GetSstorageMiningInfo(root, task.contract, task.shardIdx) + if err != nil { + log.Warn("failed to get sstorage mining info", "error", err.Error()) + } + if task.info == nil || !info.Equal(task.info) { + task.info = info + task.setState(TaskStateNoStart) + updated = true + log.Info("update task info", "shard idx", task.shardIdx, "MiningHash", + info.MiningHash.Hex(), "LastMineTime", task.info.LastMineTime, "Difficulty", + info.Difficulty, "BlockMined", info.BlockMined) } - env.family.Add(ancestor.Hash()) - env.ancestors.Add(ancestor.Hash()) } - // Keep track of transactions which return errors so they can be removed - env.tcount = 0 - env.totalCalldata = 0 - return env, nil -} - -// updateSnapshot updates pending snapshot block, receipts and state. -func (w *worker) updateSnapshot(env *environment) { - w.snapshotMu.Lock() - defer w.snapshotMu.Unlock() - - w.snapshotBlock = types.NewBlock( - env.header, - env.txs, - env.unclelist(), - env.receipts, - trie.NewStackTrie(nil), - ) - w.snapshotReceipts = copyReceipts(env.receipts) - w.snapshotState = env.state.Copy() -} -func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) { - snap := env.state.Snapshot() - - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig()) - if err != nil { - env.state.RevertToSnapshot(snap) - return nil, err + if updated { + select { + case w.newWorkCh <- &newWorkReq{timestamp: timestamp}: + case <-w.exitCh: + return + } } - env.txs = append(env.txs, tx) - env.receipts = append(env.receipts, receipt) - - return receipt.Logs, nil } -func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32) bool { - gasLimit := env.header.GasLimit - if env.gasPool == nil { - env.gasPool = new(core.GasPool).AddGas(gasLimit) +// commitWork generates several new sealing tasks based on the parent block +// and submit them to the sealer. +func (w *worker) commitWork(stopCh chan struct{}) { + if !w.isRunning() { + return } - var coalescedLogs []*types.Log - - for { - // In the following three cases, we will interrupt the execution of the transaction. - // (1) new head block event arrival, the interrupt signal is 1 - // (2) worker start or restart, the interrupt signal is 1 - // (3) worker recreate the sealing block with any newly arrived transactions, the interrupt signal is 2. - // For the first two cases, the semi-finished work will be discarded. - // For the third case, the semi-finished work will be submitted to the consensus engine. - if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone { - // Notify resubmit loop to increase resubmitting interval due to too frequent commits. - if atomic.LoadInt32(interrupt) == commitInterruptResubmit { - ratio := float64(gasLimit-env.gasPool.Gas()) / float64(gasLimit) - if ratio < 0.1 { - ratio = 0.1 - } - w.resubmitAdjustCh <- &intervalAdjust{ - ratio: ratio, - inc: true, + // sort and find the oldest task to mine + sort.Sort(w.tasks) + go func() { + for _, t := range w.tasks { + if t.isRunning() && t.getState() < TaskStateMined { + select { + case w.taskCh <- t: + log.Info("add task", "shard idx", t.shardIdx) + case <-w.exitCh: + log.Info("Worker has exited") + case <-stopCh: + log.Info("cancel commitWork") } + break } - return atomic.LoadInt32(interrupt) == commitInterruptNewHead - } - // If we don't have enough gas for any further transactions then we're done - if env.gasPool.Gas() < params.TxGas { - log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) - break - } - - // Retrieve the next transaction and abort if all done - tx := txs.Peek() - if tx == nil { - break - } - - if w.chainConfig.IsPisa(env.header.Number) && env.totalCalldata+len(tx.Data()) > core.MaxCalldataEIP4488(env.tcount+1) { - log.Trace("Total transaction calldata exceeded, ignoring transaction", "hash", tx.Hash(), "eip4488", w.chainConfig.PisaBlock) - - break } + }() +} - // Error may be ignored here. The error has already been checked - // during transaction acceptance is the transaction pool. - // - // We use the eip155 signer regardless of the current hf. - from, _ := types.Sender(env.signer, tx) - // Check whether the tx is replay protected. If we're not in the EIP155 hf - // phase, start ignoring the sender until we do. - if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { - log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) - - txs.Pop() - continue +func (w *worker) calculateDiffAndInitHash(info *core.MiningInfo, startShardId, shardLen, minedTs uint64) (diff *big.Int, diffs []*big.Int, hash0 common.Hash, err error) { + diffs = make([]*big.Int, shardLen) + diff = new(big.Int).SetUint64(0) + hash0 = common.Hash{} + for i := uint64(0); i < shardLen; i++ { + shardId := startShardId + i + if minedTs < info.LastMineTime { + err = fmt.Errorf("minedTs too small") } - // Start executing the transaction - env.state.Prepare(tx.Hash(), env.tcount) - - logs, err := w.commitTransaction(env, tx) - switch { - case errors.Is(err, core.ErrGasLimitReached): - // Pop the current out-of-gas transaction without shifting in the next from the account - log.Trace("Gas limit exceeded for current block", "sender", from) - txs.Pop() - - case errors.Is(err, core.ErrNonceTooLow): - // New head notification data race between the transaction pool and miner, shift - log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) - txs.Shift() - - case errors.Is(err, core.ErrNonceTooHigh): - // Reorg notification data race between the transaction pool and miner, skip account = - log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) - txs.Pop() - - case errors.Is(err, nil): - // Everything ok, collect the logs and shift in the next transaction from the same account - coalescedLogs = append(coalescedLogs, logs...) - env.tcount++ - env.totalCalldata += len(tx.Data()) - txs.Shift() - - case errors.Is(err, core.ErrTxTypeNotSupported): - // Pop the unsupported transaction without shifting in the next from the account - log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) - txs.Pop() - - default: - // Strange error, discard the transaction and get the next in line (note, the - // nonce-too-high clause will prevent us from executing in vain). - log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) - txs.Shift() - } - } - - if !w.isRunning() && len(coalescedLogs) > 0 { - // We don't push the pendingLogsEvent while we are sealing. The reason is that - // when we are sealing, the worker will regenerate a sealing block every 3 seconds. - // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. - - // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined - // logs by filling in the block hash when the block was mined by the local miner. This can - // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. - cpy := make([]*types.Log, len(coalescedLogs)) - for i, l := range coalescedLogs { - cpy[i] = new(types.Log) - *cpy[i] = *l + diffs[i] = expectedDiff(info.LastMineTime, info.Difficulty, minedTs, + w.config.TargetIntervalSec, w.config.Cutoff, w.config.DiffAdjDivisor, w.config.MinimumDiff) + diff = new(big.Int).Add(diff, diffs[i]) + hash0 = crypto.Keccak256Hash(hash0.Bytes(), uint64ToByte32(shardId), info.MiningHash.Bytes()) + } + + return diff, diffs, hash0, nil +} + +func (w *worker) hashimoto(t *task, startShardId, shardLenBits uint64, hash0 common.Hash) (common.Hash, [][]byte, []uint64, []uint64, error) { + hash0Bytes := hash0.Bytes() + dataSet := make([][]byte, w.config.RandomChecks) + kvIdxs, chunkIdxs := make([]uint64, w.config.RandomChecks), make([]uint64, w.config.RandomChecks) + rowBits := t.kvEntriesBits + t.chunkSizeBits + shardLenBits + for i := 0; i < w.config.RandomChecks; i++ { + chunkIdx := new(big.Int).SetBytes(hash0Bytes).Uint64()%(uint64(1)<> t.chunkSizeBits + chunkIdxs[i] = chunkIdx % (1 << t.chunkSizeBits) + hash0Bytes = crypto.Keccak256Hash(hash0Bytes, data).Bytes() + } else { + if !exist { + err = fmt.Errorf("chunk not support: chunkIdxs %d", chunkIdx) + } + return hash0, dataSet, kvIdxs, chunkIdxs, err } - w.pendingLogsFeed.Send(cpy) - } - // Notify resubmit loop to decrease resubmitting interval if current interval is larger - // than the user-specified one. - if interrupt != nil { - w.resubmitAdjustCh <- &intervalAdjust{inc: false} } - return false -} -// generateParams wraps various of settings for generating sealing task. -type generateParams struct { - timestamp uint64 // The timstamp for sealing task - forceTime bool // Flag whether the given timestamp is immutable or not - parentHash common.Hash // Parent block hash, empty means the latest chain head - coinbase common.Address // The fee recipient address for including transaction - random common.Hash // The randomness generated by beacon chain, empty before the merge - noUncle bool // Flag whether the uncle block inclusion is allowed - noExtra bool // Flag whether the extra field assignment is allowed + return hash0, dataSet, kvIdxs, chunkIdxs, nil } -// prepareWork constructs the sealing task according to the given parameters, -// either based on the last chain head or specified parent. In this function -// the pending transactions are not filled yet, only the empty task returned. -func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { - w.mu.RLock() - defer w.mu.RUnlock() - - // Find the parent block for sealing task - parent := w.chain.CurrentBlock() - if genParams.parentHash != (common.Hash{}) { - parent = w.chain.GetBlockByHash(genParams.parentHash) - } - if parent == nil { - return nil, fmt.Errorf("missing parent") - } - // Sanity check the timestamp correctness, recap the timestamp - // to parent+1 if the mutation is allowed. - timestamp := genParams.timestamp - if parent.Time() >= timestamp { - if genParams.forceTime { - return nil, fmt.Errorf("invalid timestamp, parent %d given %d", parent.Time(), timestamp) - } - timestamp = parent.Time() + 1 - } - // Construct the sealing block header, set the extra field if it's allowed - num := parent.Number() - header := &types.Header{ - ParentHash: parent.Hash(), - Number: num.Add(num, common.Big1), - GasLimit: core.CalcGasLimit(parent.GasLimit(), w.config.GasCeil), - Time: timestamp, - Coinbase: genParams.coinbase, - } - if !genParams.noExtra && len(w.extra) != 0 { - header.Extra = w.extra +func (w *worker) mineTask(t *task) (bool, error) { + if t.getState() == TaskStateMined { + return true, nil } - // Set the randomness field from the beacon chain if it's available. - if genParams.random != (common.Hash{}) { - header.MixDigest = genParams.random - } - // Set baseFee and GasLimit if we are on an EIP-1559 chain - if w.chainConfig.IsLondon(header.Number) { - header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header()) - if !w.chainConfig.IsLondon(parent.Number()) { - parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier - header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil) - } - } - // Run the consensus preparation with the default or customized consensus engine. - if err := w.engine.Prepare(w.chain, header); err != nil { - log.Error("Failed to prepare header for sealing", "err", err) - return nil, err - } - // Could potentially happen if starting to mine in an odd state. - // Note genParams.coinbase can be different with header.Coinbase - // since clique algorithm can modify the coinbase field in header. - env, err := w.makeEnv(parent, header, genParams.coinbase) + minedTs := uint64(time.Now().Unix()) + // using random nonce, so we can run multi mine with threads + rand.Seed(int64(minedTs)) + nonce := rand.Uint64() + var ( + dataSet [][]byte + kvIdxs []uint64 + chunkIdxs []uint64 + shardLenBits uint64 = 0 + ) + + // todo shard len can be not 1 later + diff, _, hash0, err := w.calculateDiffAndInitHash(t.info, t.shardIdx, uint64(1)< uint64(time.Now().Unix()) { + hash0 = crypto.Keccak256Hash(hash0.Bytes(), addressToByte32(t.miner), uint64ToByte32(minedTs), uint64ToByte32(nonce)) + hash0, dataSet, kvIdxs, chunkIdxs, err = w.hashimoto(t, t.shardIdx, shardLenBits, hash0) + + // Check if the data matches the hash in metadata. + if requiredDiff.Cmp(new(big.Int).SetBytes(hash0.Bytes())) < 0 { + proofs := make([][]common.Hash, 0) + kvs, err := w.chain.ReadKVsByIndexList(t.contract, kvIdxs, true) + if err != nil { + return false, err + } + if len(kvs) != len(kvIdxs) { + return false, fmt.Errorf("fail to get all the kvs %v", kvIdxs) + } + + for i := 0; i < len(dataSet); i++ { + if kvs[i].Idx != kvIdxs[i] { + ps, err := getProof(kvs[i].Data, sstor.CHUNK_SIZE, t.chunkSizeBits, chunkIdxs[i]) + if err != nil { + return false, err + } + proofs[i] = ps } } - } - // Prefer to locally generated uncle - commitUncles(w.localUncles) - commitUncles(w.remoteUncles) - } - return env, nil -} + t.setState(TaskStateMined) + w.resultCh <- &result{ + task: t, + startShardId: t.shardIdx, + shardLenBits: 0, + miner: t.miner, + minedTs: minedTs, + nonce: nonce, + kvIdxs: kvIdxs, + chunkIdxs: chunkIdxs, + encodedData: dataSet, + proofs: proofs, + } -// fillTransactions retrieves the pending transactions from the txpool and fills them -// into the given sealing block. The transaction selection and ordering strategy can -// be customized with the plugin in the future. -func (w *worker) fillTransactions(interrupt *int32, env *environment) { - // Split the pending transactions into locals and remotes - // Fill the block with all available pending transactions. - pending := w.eth.TxPool().Pending(true) - localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending - for _, account := range w.eth.TxPool().Locals() { - if txs := remoteTxs[account]; len(txs) > 0 { - delete(remoteTxs, account) - localTxs[account] = txs - } - } - if len(localTxs) > 0 { - txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee) - if w.commitTransactions(env, txs, interrupt) { - return - } - } - if len(remoteTxs) > 0 { - txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee) - if w.commitTransactions(env, txs, interrupt) { - return + return true, nil } + nonce++ } + + return false, nil } -// generateWork generates a sealing block based on the given parameters. -func (w *worker) generateWork(params *generateParams) (*types.Block, error) { - work, err := w.prepareWork(params) +func (w *worker) submitMinedResult(result *result) error { + data, err := vABI.Pack(MineFunc, result.task.shardIdx, 0, result.task.miner, result.minedTs, + result.nonce, result.proofs, result.encodedData) if err != nil { - return nil, err + return err } - defer work.discard() - w.fillTransactions(nil, work) - return w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts) -} + gasFeeCap := w.chain.CurrentBlock().BaseFee() + nonce := w.eth.TxPool().Nonce(result.task.miner) -// commitWork generates several new sealing tasks based on the parent block -// and submit them to the sealer. -func (w *worker) commitWork(interrupt *int32, noempty bool, timestamp int64) { - start := time.Now() - - // Set the coinbase if the worker is running or it's required - var coinbase common.Address - if w.isRunning() { - if w.coinbase == (common.Address{}) { - log.Error("Refusing to mine without etherbase") - return - } - coinbase = w.coinbase // Use the preset address as the fee recipient - } - work, err := w.prepareWork(&generateParams{ - timestamp: uint64(timestamp), - coinbase: coinbase, - }) - if err != nil { - return + baseTx := &types.DynamicFeeTx{ + ChainID: w.chainConfig.ChainID, + To: &result.task.contract, + Nonce: nonce, + GasTipCap: w.priceOracle.suggestGasTip, + GasFeeCap: gasFeeCap, + Gas: gas, + Value: new(big.Int).SetInt64(0), + Data: data, } - // Create an empty block based on temporary copied state for - // sealing in advance without waiting block execution finished. - if !noempty && atomic.LoadUint32(&w.noempty) == 0 { - w.commit(work.copy(), nil, false, start) - } - // Fill pending transactions from the txpool - w.fillTransactions(interrupt, work) - w.commit(work.copy(), w.fullTaskHook, true, start) - - // Swap out the old work with the new one, terminating any leftover - // prefetcher processes in the mean time and starting a new one. - if w.current != nil { - w.current.discard() - } - w.current = work -} -// commit runs any post-transaction state modifications, assembles the final block -// and commits new work if consensus engine is running. -// Note the assumption is held that the mutation is allowed to the passed env, do -// the deep copy first. -func (w *worker) commit(env *environment, interval func(), update bool, start time.Time) error { - _, isTm := w.engine.(*tendermint.Tendermint) - if w.isRunning() && !isTm { - if interval != nil { - interval() - } - // Create a local environment copy, avoid the data race with snapshot state. - // https://github.com/ethereum/go-ethereum/issues/24299 - env := env.copy() - block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts) - if err != nil { - return err - } - // If we're post merge, just ignore - if !w.isTTDReached(block.Header()) { - select { - case w.taskCh <- &task{receipts: env.receipts, state: env.state, block: block, createdAt: time.Now()}: - w.unconfirmed.Shift(block.NumberU64() - 1) - log.Info("Commit new sealing work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()), - "uncles", len(env.uncles), "txs", env.tcount, "totalCalldata", env.totalCalldata, - "gas", block.GasUsed(), "fees", totalFees(block, env.receipts), - "elapsed", common.PrettyDuration(time.Since(start))) - - case <-w.exitCh: - log.Info("Worker has exited") - } - } + signedTx, err := types.SignTx(types.NewTx(baseTx), w.signer, w.privKey) + if err != nil { + return err } - return nil -} -// getSealingBlock generates the sealing block based on the given parameters. -func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash) (*types.Block, error) { - req := &getWorkReq{ - params: &generateParams{ - timestamp: timestamp, - forceTime: true, - parentHash: parent, - coinbase: coinbase, - random: random, - noUncle: true, - noExtra: true, - }, - result: make(chan *types.Block, 1), - } - select { - case w.getWorkCh <- req: - block := <-req.result - if block == nil { - return nil, req.err - } - return block, nil - case <-w.exitCh: - return nil, errors.New("miner closed") - } + return w.eth.TxPool().AddLocal(signedTx) } -// isTTDReached returns the indicator if the given block has reached the total -// terminal difficulty for The Merge transition. -func (w *worker) isTTDReached(header *types.Header) bool { - td, ttd := w.chain.GetTd(header.ParentHash, header.Number.Uint64()-1), w.chain.Config().TerminalTotalDifficulty - return td != nil && ttd != nil && td.Cmp(ttd) >= 0 +func uint64ToByte32(u uint64) []byte { + return common.BigToHash(new(big.Int).SetUint64(u)).Bytes() } -// totalFees computes total consumed miner fees in ETH. Block transactions and receipts have to have the same order. -func totalFees(block *types.Block, receipts []*types.Receipt) *big.Float { - feesWei := new(big.Int) - for i, tx := range block.Transactions() { - minerFee, _ := tx.EffectiveGasTip(block.BaseFee()) - feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), minerFee)) - } - return new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether))) +func addressToByte32(addr common.Address) []byte { + return common.BytesToHash(addr.Bytes()).Bytes() } diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go index 255a2675158d..6b859aeec35b 100644 --- a/sstorminer/worker_test.go +++ b/sstorminer/worker_test.go @@ -17,17 +17,17 @@ package sstorminer import ( + "encoding/binary" "errors" + "fmt" "math/big" "math/rand" - "sync/atomic" + "os" "testing" "time" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -37,7 +37,11 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/sstorage" + "github.com/holiman/uint256" + "golang.org/x/crypto/sha3" ) const ( @@ -53,7 +57,6 @@ var ( // Test chain configurations testTxPoolConfig core.TxPoolConfig ethashChainConfig *params.ChainConfig - cliqueChainConfig *params.ChainConfig // Test accounts testBankKey, _ = crypto.GenerateKey() @@ -67,10 +70,22 @@ var ( pendingTxs []*types.Transaction newTxs []*types.Transaction - testConfig = &Config{ - Recommit: time.Second, - GasCeil: params.GenesisGasLimit, + contract = common.HexToAddress("0x0000000000000000000000000000000003330001") + kvEntriesBits = uint64(9) + kvEntries = uint64(1) << 9 + blocks = 5 + + defaultConfig = &Config{ + RandomChecks: 16, + MinimumDiff: new(big.Int).SetUint64(1), + TargetIntervalSec: new(big.Int).SetUint64(3), + Cutoff: new(big.Int).SetUint64(40), + DiffAdjDivisor: new(big.Int).SetUint64(1024), + Recommit: 1 * time.Second, } + + diff = new(big.Int).SetUint64(1024) + blockMined = new(big.Int).SetUint64(1) ) func init() { @@ -78,12 +93,6 @@ func init() { testTxPoolConfig.Journal = "" ethashChainConfig = new(params.ChainConfig) *ethashChainConfig = *params.TestChainConfig - cliqueChainConfig = new(params.ChainConfig) - *cliqueChainConfig = *params.TestChainConfig - cliqueChainConfig.Clique = ¶ms.CliqueConfig{ - Period: 10, - Epoch: 30000, - } signer := types.LatestSigner(params.TestChainConfig) tx1 := types.MustSignNewTx(testBankKey, signer, &types.AccessListTx{ @@ -108,33 +117,67 @@ func init() { rand.Seed(time.Now().UnixNano()) } +type wrapBlockChain struct { + *core.BlockChain + stateDB *state.StateDB +} + +func (bc *wrapBlockChain) GetSstorageMiningInfo(root common.Hash, contract common.Address, shardId uint64) (*core.MiningInfo, error) { + return bc.GetSstorageMiningInfoWithStateDB(bc.stateDB, contract, shardId) +} + +func (bc *wrapBlockChain) State() (*state.StateDB, error) { + return bc.stateDB, nil +} + +func (bc *wrapBlockChain) ReadKVsByIndexList(contract common.Address, indexes []uint64, useMaxKVsize bool) ([]*core.KV, error) { + return bc.BlockChain.ReadKVsByIndexListWithState(bc.stateDB, contract, indexes, useMaxKVsize) +} + +func hashAdd(hash common.Hash, i uint64) common.Hash { + return common.BytesToHash(new(big.Int).Add(hash.Big(), new(big.Int).SetUint64(i)).Bytes()) +} + +func (bc *wrapBlockChain) saveMiningInfo(shardId uint64, info *core.MiningInfo) { + position := getSlotHash(0, uint256.NewInt(shardId).Bytes32()) + // fmt.Println(position.Hex()) + bc.stateDB.SetState(contract, position, info.MiningHash) + bc.stateDB.SetState(contract, hashAdd(position, 1), common.BigToHash(new(big.Int).SetUint64(info.LastMineTime))) + bc.stateDB.SetState(contract, hashAdd(position, 2), common.BigToHash(info.Difficulty)) + bc.stateDB.SetState(contract, hashAdd(position, 3), common.BigToHash(info.BlockMined)) +} + +func (bc *wrapBlockChain) initMiningInfos(shardIdxList []uint64, diff *big.Int, blockMined *big.Int) map[uint64]*core.MiningInfo { + infos := make(map[uint64]*core.MiningInfo) + for _, idx := range shardIdxList { + info := new(core.MiningInfo) + info.MiningHash = crypto.Keccak256Hash() + info.LastMineTime = uint64(time.Now().Unix()) + info.Difficulty = diff + info.BlockMined = blockMined + bc.saveMiningInfo(idx, info) + infos[idx] = info + } + return infos +} + // testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing. type testWorkerBackend struct { - db ethdb.Database - txPool *core.TxPool - chain *core.BlockChain - testTxFeed event.Feed - genesis *core.Genesis - uncleBlock *types.Block + db ethdb.Database + txPool *core.TxPool + chain BlockChain + testTxFeed event.Feed + genesis *core.Genesis + miningInfos map[uint64]*core.MiningInfo } -func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend { +func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, shardIdxList []uint64, + db ethdb.Database, n int, shardIsFull bool) (*testWorkerBackend, map[uint64]*core.MiningInfo) { var gspec = core.Genesis{ Config: chainConfig, Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, } - switch e := engine.(type) { - case *clique.Clique: - gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength) - copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes()) - e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) { - return crypto.Sign(crypto.Keccak256(data), testBankKey) - }) - case *ethash.Ethash: - default: - t.Fatalf("unexpected consensus engine type: %T", engine) - } genesis := gspec.MustCommit(db) chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil) @@ -149,521 +192,495 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine t.Fatalf("failed to insert origin chain: %v", err) } } - parent := genesis - if n > 0 { - parent = chain.GetBlockByHash(chain.CurrentBlock().ParentHash()) + + stateDB, _ := chain.State() + wchain := wrapBlockChain{ + BlockChain: chain, + stateDB: stateDB, } - blocks, _ := core.GenerateChain(chainConfig, parent, engine, db, 1, func(i int, gen *core.BlockGen) { - gen.SetCoinbase(testUserAddress) - }) + infos := wchain.initMiningInfos(shardIdxList, diff, blockMined) + makeKVStorage(stateDB, contract, shardIdxList, 1< Date: Wed, 22 Mar 2023 17:49:59 +0800 Subject: [PATCH 04/23] remove test case --- sstorminer/worker_test.go | 73 ++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go index 6b859aeec35b..86293c4cb762 100644 --- a/sstorminer/worker_test.go +++ b/sstorminer/worker_test.go @@ -534,47 +534,48 @@ func TestWork_TriggerByNewBlock(test *testing.T) { } } -func TestWork_ShardIsNotFull(test *testing.T) { - var ( - taskMined int - resultGet int - resultCh = make(chan *result, 2) - shardIdxList = []uint64{0} - engine = ethash.NewFaker() - db = rawdb.NewMemoryDatabase() - ) - - w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, db, shardIdxList, 0, false) +/* + func TestWork_ShardIsNotFull(test *testing.T) { + var ( + taskMined int + resultGet int + resultCh = make(chan *result, 2) + shardIdxList = []uint64{0} + engine = ethash.NewFaker() + db = rawdb.NewMemoryDatabase() + ) + + w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, db, shardIdxList, 0, false) + + defer w.close() + defer engine.Close() + defer func(files []string) { + for _, file := range files { + os.Remove(file) + } + }(files) - defer w.close() - defer engine.Close() - defer func(files []string) { - for _, file := range files { - os.Remove(file) + w.newTaskHook = func(task *task) { + taskMined += 1 + } + w.newResultHook = func(result *result) { + resultGet += 1 + resultCh <- result } - }(files) - - w.newTaskHook = func(task *task) { - taskMined += 1 - } - w.newResultHook = func(result *result) { - resultGet += 1 - resultCh <- result - } - w.start() // Start mining! - select { - case r := <-resultCh: - fmt.Println("getresult") - stateDB, _ := w.chain.State() - if err := verifyTaskResult(stateDB, w.chain, r); err != nil { - test.Error("verify mined result failed", err.Error()) + w.start() // Start mining! + select { + case r := <-resultCh: + fmt.Println("getresult") + stateDB, _ := w.chain.State() + if err := verifyTaskResult(stateDB, w.chain, r); err != nil { + test.Error("verify mined result failed", err.Error()) + } + case <-time.NewTimer(3 * time.Second).C: + test.Error("new task timeout") } - case <-time.NewTimer(3 * time.Second).C: - test.Error("new task timeout") } -} - +*/ func TestWork_StartAndStopTask(test *testing.T) { var ( shardIdxList = []uint64{0, 1, 2} From bbb3a9e96ec32e3742833fe664324cd4643935b9 Mon Sep 17 00:00:00 2001 From: pingke Date: Thu, 23 Mar 2023 11:04:47 +0800 Subject: [PATCH 05/23] fix bug and add storage miner contract param --- cmd/geth/main.go | 1 + cmd/utils/flags.go | 7 ++++ core/blockchain.go | 2 +- eth/backend.go | 6 +++- eth/ethconfig/config.go | 9 ++--- eth/protocols/sstorage/sync_test.go | 10 +++--- sstorminer/miner.go | 23 +++++-------- sstorminer/miner_test.go | 10 ++---- sstorminer/worker.go | 38 +++++++++++---------- sstorminer/worker_test.go | 51 +++++++++++++---------------- 10 files changed, 77 insertions(+), 80 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 20aadaef3e76..5e56de1452be 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -135,6 +135,7 @@ var ( utils.SstorageFileFlag, utils.SstorageMineFlag, utils.SstorageNodeKeyFlag, + utils.SstorageMinerContractFlag, utils.NATFlag, utils.NoDiscoverFlag, utils.DiscoveryV5Flag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 01d9ae3fca80..35edeb0090bb 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -574,6 +574,10 @@ var ( Name: "sstorage.nodekey", Usage: "Sstorage miner node key file", } + SstorageMinerContractFlag = cli.StringFlag{ + Name: "sstorage.minercontract", + Usage: "Sstorage miner contract", + } // Logging and debug settings EthStatsURLFlag = cli.StringFlag{ Name: "ethstats", @@ -1139,6 +1143,9 @@ func setSstorage(ctx *cli.Context, cfg *ethconfig.Config) { if ctx.GlobalIsSet(SstorageNodeKeyFlag.Name) { cfg.SstorageNodeKey = ctx.GlobalString(SstorageNodeKeyFlag.Name) } + if ctx.GlobalIsSet(SstorageMinerContractFlag.Name) { + cfg.SstorageMinerContract = ctx.GlobalString(SstorageMinerContractFlag.Name) + } sstorage.InitializeConfig() for _, s := range cfg.SstorageShards { diff --git a/core/blockchain.go b/core/blockchain.go index 58e0eb48816f..20b89f922121 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2518,7 +2518,7 @@ func (bc *BlockChain) VerifyAndWriteKV(contract common.Address, data map[uint64] continue } - success, err := sm.TryWrite(vkv.Idx, vkv.Data, vkv.MetaHash) + success, err := sm.TryWrite(vkv.Idx, vkv.Data, common.BytesToHash(meta.HashInMeta)) if err != nil { log.Warn("write kv fail", "error", err) } diff --git a/eth/backend.go b/eth/backend.go index 699158bde1b9..3d011db613e3 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -284,7 +284,11 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if len(sstorage.Shards()) == 0 { return nil, fmt.Errorf("no shards is exist") } - eth.sstorMiner = sstorminer.New(eth, &config.SStorMiner, chainConfig, eth.EventMux(), privKey) + if config.SstorageMinerContract == "" { + return nil, fmt.Errorf("miner contract is needed when the sstorage mine is enabled.") + } + minerContract := common.HexToAddress(config.SstorageMinerContract) + eth.sstorMiner = sstorminer.New(eth, &config.SStorMiner, chainConfig, eth.EventMux(), privKey, minerContract) } eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 4d21b9107e92..f3fbed179013 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -232,10 +232,11 @@ type Config struct { ValidatorChangeEpochId uint64 // Sstorage config - SstorageFiles []string `toml:",omitempty"` - SstorageShards []string `toml:",omitempty"` - SstorageMine bool - SstorageNodeKey string + SstorageFiles []string `toml:",omitempty"` + SstorageShards []string `toml:",omitempty"` + SstorageMine bool + SstorageNodeKey string + SstorageMinerContract string } // CreateConsensusEngine creates a consensus engine for the given chain configuration. diff --git a/eth/protocols/sstorage/sync_test.go b/eth/protocols/sstorage/sync_test.go index 57e841871608..d5fe541e5fa5 100644 --- a/eth/protocols/sstorage/sync_test.go +++ b/eth/protocols/sstorage/sync_test.go @@ -78,7 +78,7 @@ func (c *blockChain) VerifyAndWriteKV(contract common.Address, data map[uint64][ synced++ syncedBytes += uint64(len(val)) - metaHash, meta, err := core.GetSstorageMetadata(c.stateDB, contract, idx) + _, meta, err := core.GetSstorageMetadata(c.stateDB, contract, idx) if err != nil || meta == nil { log.Warn("processKVResponse: get vkv MetaHash for verification fail", "error", err) continue @@ -90,7 +90,7 @@ func (c *blockChain) VerifyAndWriteKV(contract common.Address, data map[uint64][ continue } - success, err := sm.TryWrite(idx, rawData, metaHash) + success, err := sm.TryWrite(idx, rawData, common.BytesToHash(meta.HashInMeta)) if err != nil { log.Warn("write kv fail", "error", err) continue @@ -461,9 +461,7 @@ func verifyKVs(stateDB *state.StateDB, data map[common.Address]map[uint64][]byte } for idx, val := range shards { _, meta, err := core.GetSstorageMetadata(stateDB, contract, idx) - if _, ok := destroyedList[idx]; ok { - val = make([]byte, shardData.MaxKvSize()) - } + if err != nil { t.Fatalf("get MetaHash data fail with err: %s.", err.Error()) } @@ -475,7 +473,7 @@ func verifyKVs(stateDB *state.StateDB, data map[common.Address]map[uint64][]byte t.Fatalf("TryRead sstroage Data fail. err: %s", "shard Idx not support") } - if !bytes.Equal(val, sval) { + if _, ok := destroyedList[idx]; !ok && !bytes.Equal(val, sval) { t.Fatalf("verify KV failed; index: %d; val: %s; sval: %s", idx, common.Bytes2Hex(val), common.Bytes2Hex(sval)) } diff --git a/sstorminer/miner.go b/sstorminer/miner.go index 05401606b4ad..6555fe92f244 100644 --- a/sstorminer/miner.go +++ b/sstorminer/miner.go @@ -21,8 +21,6 @@ import ( "crypto/ecdsa" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -35,9 +33,8 @@ import ( // Backend wraps all methods required for mining. Only full node is capable // to offer all the functions here. type Backend interface { - BlockChain() BlockChain + BlockChain() *core.BlockChain TxPool() *core.TxPool - StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) } // Config is the configuration parameters of mining. @@ -52,25 +49,23 @@ type Config struct { // Miner creates blocks and searches for proof-of-work values. type Miner struct { - mux *event.TypeMux - worker *worker - coinbase common.Address - eth Backend - exitCh chan struct{} - startCh chan struct{} - stopCh chan struct{} + mux *event.TypeMux + worker *worker + eth Backend + exitCh chan struct{} + startCh chan struct{} + stopCh chan struct{} wg sync.WaitGroup } -func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, privKey *ecdsa.PrivateKey) *Miner { +func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, privKey *ecdsa.PrivateKey, minerContract common.Address) *Miner { miner := &Miner{ - eth: eth, mux: mux, exitCh: make(chan struct{}), startCh: make(chan struct{}), stopCh: make(chan struct{}), - worker: newWorker(config, chainConfig, eth, mux, privKey, true), + worker: newWorker(config, chainConfig, eth, eth.BlockChain(), mux, privKey, minerContract, true), } miner.wg.Add(1) go miner.update() diff --git a/sstorminer/miner_test.go b/sstorminer/miner_test.go index 306fc21fe403..dca8128aff3c 100644 --- a/sstorminer/miner_test.go +++ b/sstorminer/miner_test.go @@ -18,7 +18,6 @@ package sstorminer import ( - "errors" "testing" "time" @@ -47,7 +46,7 @@ func NewMockBackend(bc *core.BlockChain, txPool *core.TxPool) *mockBackend { } } -func (m *mockBackend) BlockChain() BlockChain { +func (m *mockBackend) BlockChain() *core.BlockChain { return m.bc } @@ -55,10 +54,6 @@ func (m *mockBackend) TxPool() *core.TxPool { return m.txPool } -func (m *mockBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) { - return nil, errors.New("not supported") -} - type testBlockChain struct { statedb *state.StateDB gasLimit uint64 @@ -188,7 +183,6 @@ func TestStartStopMiner(t *testing.T) { waitForMiningState(t, miner, true) miner.Stop() waitForMiningState(t, miner, false) - } func TestCloseMiner(t *testing.T) { @@ -244,7 +238,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) { // Create event Mux mux := new(event.TypeMux) // Create Miner - miner := New(backend, &config, chainConfig, mux, nil) + miner := New(backend, &config, chainConfig, mux, nil, common.Address{}) cleanup := func(skipMiner bool) { bc.Stop() engine.Close() diff --git a/sstorminer/worker.go b/sstorminer/worker.go index fab54870295b..abdae86a2a94 100644 --- a/sstorminer/worker.go +++ b/sstorminer/worker.go @@ -89,7 +89,8 @@ type BlockChain interface { // task contains all information for consensus engine sealing and result submitting. type task struct { - contract common.Address + storageContract common.Address + minerContract common.Address shardIdx uint64 kvSizeBits uint64 chunkSizeBits uint64 @@ -270,13 +271,14 @@ type worker struct { newResultHook func(*result) } -func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, mux *event.TypeMux, privKey *ecdsa.PrivateKey, init bool) *worker { +func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, chain BlockChain, mux *event.TypeMux, privKey *ecdsa.PrivateKey, + minerContract common.Address, init bool) *worker { worker := &worker{ config: config, chainConfig: chainConfig, eth: eth, mux: mux, - chain: eth.BlockChain(), + chain: chain, tasks: make([]*task, 0), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), newWorkCh: make(chan *newWorkReq), @@ -292,17 +294,17 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, mux } for addr, sm := range sstor.ContractToShardManager { for idx, shard := range sm.ShardMap() { - // info, _ := worker.chain.GetSstorageMiningInfo(worker.chain.CurrentBlock().Root(), addr, idx) task := task{ - contract: addr, - shardIdx: idx, - kvSizeBits: sm.MaxKvSizeBits(), - chunkSizeBits: sm.ChunksPerKvBits(), - kvEntriesBits: sm.KvEntriesBits(), - miner: shard.Miner(), - shardManager: sm, - running: 1, - info: nil, + storageContract: addr, + minerContract: minerContract, + shardIdx: idx, + kvSizeBits: sm.MaxKvSizeBits(), + chunkSizeBits: sm.ChunksPerKvBits(), + kvEntriesBits: sm.KvEntriesBits(), + miner: shard.Miner(), + shardManager: sm, + running: 1, + info: nil, } worker.tasks = append(worker.tasks, &task) } @@ -368,7 +370,7 @@ func (w *worker) isRunning() bool { // start sets the running status as 1 for a task and triggers task start chan. func (w *worker) startTask(contract common.Address, shardIdx uint64) { for _, task := range w.tasks { - if task.contract == contract && task.shardIdx == shardIdx && !task.isRunning() { + if task.storageContract == contract && task.shardIdx == shardIdx && !task.isRunning() { task.start() w.taskStartCh <- struct{}{} } @@ -378,7 +380,7 @@ func (w *worker) startTask(contract common.Address, shardIdx uint64) { // stop sets the running status as 0 for a task. func (w *worker) stopTask(contract common.Address, shardIdx uint64) { for _, task := range w.tasks { - if task.contract == contract && task.shardIdx == shardIdx && task.isRunning() { + if task.storageContract == contract && task.shardIdx == shardIdx && task.isRunning() { task.stop() } } @@ -536,7 +538,7 @@ func (w *worker) updateTaskInfo(root common.Hash, timestamp int64) { updated := false for _, task := range w.tasks { - info, err := w.chain.GetSstorageMiningInfo(root, task.contract, task.shardIdx) + info, err := w.chain.GetSstorageMiningInfo(root, task.minerContract, task.shardIdx) if err != nil { log.Warn("failed to get sstorage mining info", "error", err.Error()) } @@ -659,7 +661,7 @@ func (w *worker) mineTask(t *task) (bool, error) { // Check if the data matches the hash in metadata. if requiredDiff.Cmp(new(big.Int).SetBytes(hash0.Bytes())) < 0 { proofs := make([][]common.Hash, 0) - kvs, err := w.chain.ReadKVsByIndexList(t.contract, kvIdxs, true) + kvs, err := w.chain.ReadKVsByIndexList(t.storageContract, kvIdxs, true) if err != nil { return false, err } @@ -710,7 +712,7 @@ func (w *worker) submitMinedResult(result *result) error { baseTx := &types.DynamicFeeTx{ ChainID: w.chainConfig.ChainID, - To: &result.task.contract, + To: &result.task.minerContract, Nonce: nonce, GasTipCap: w.priceOracle.suggestGasTip, GasFeeCap: gasFeeCap, diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go index 86293c4cb762..992d882566b4 100644 --- a/sstorminer/worker_test.go +++ b/sstorminer/worker_test.go @@ -70,6 +70,7 @@ var ( pendingTxs []*types.Transaction newTxs []*types.Transaction + minerContract = common.HexToAddress("0x0000000000000000000000000000000000000001") contract = common.HexToAddress("0x0000000000000000000000000000000003330001") kvEntriesBits = uint64(9) kvEntries = uint64(1) << 9 @@ -141,10 +142,10 @@ func hashAdd(hash common.Hash, i uint64) common.Hash { func (bc *wrapBlockChain) saveMiningInfo(shardId uint64, info *core.MiningInfo) { position := getSlotHash(0, uint256.NewInt(shardId).Bytes32()) // fmt.Println(position.Hex()) - bc.stateDB.SetState(contract, position, info.MiningHash) - bc.stateDB.SetState(contract, hashAdd(position, 1), common.BigToHash(new(big.Int).SetUint64(info.LastMineTime))) - bc.stateDB.SetState(contract, hashAdd(position, 2), common.BigToHash(info.Difficulty)) - bc.stateDB.SetState(contract, hashAdd(position, 3), common.BigToHash(info.BlockMined)) + bc.stateDB.SetState(minerContract, position, info.MiningHash) + bc.stateDB.SetState(minerContract, hashAdd(position, 1), common.BigToHash(new(big.Int).SetUint64(info.LastMineTime))) + bc.stateDB.SetState(minerContract, hashAdd(position, 2), common.BigToHash(info.Difficulty)) + bc.stateDB.SetState(minerContract, hashAdd(position, 3), common.BigToHash(info.BlockMined)) } func (bc *wrapBlockChain) initMiningInfos(shardIdxList []uint64, diff *big.Int, blockMined *big.Int) map[uint64]*core.MiningInfo { @@ -165,14 +166,14 @@ func (bc *wrapBlockChain) initMiningInfos(shardIdxList []uint64, diff *big.Int, type testWorkerBackend struct { db ethdb.Database txPool *core.TxPool - chain BlockChain + chain *core.BlockChain testTxFeed event.Feed genesis *core.Genesis miningInfos map[uint64]*core.MiningInfo } func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, shardIdxList []uint64, - db ethdb.Database, n int, shardIsFull bool) (*testWorkerBackend, map[uint64]*core.MiningInfo) { + db ethdb.Database, n int, shardIsFull bool) *testWorkerBackend { var gspec = core.Genesis{ Config: chainConfig, Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, @@ -193,24 +194,16 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine } } - stateDB, _ := chain.State() - wchain := wrapBlockChain{ - BlockChain: chain, - stateDB: stateDB, - } - infos := wchain.initMiningInfos(shardIdxList, diff, blockMined) - makeKVStorage(stateDB, contract, shardIdxList, 1< Date: Thu, 23 Mar 2023 17:00:34 +0800 Subject: [PATCH 06/23] update comments --- eth/backend.go | 10 +++++----- eth/ethconfig/config.go | 2 +- sstorminer/worker.go | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/eth/backend.go b/eth/backend.go index 3d011db613e3..04371ef51119 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -20,8 +20,6 @@ package eth import ( "errors" "fmt" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/sstorminer" "math/big" "runtime" "sync" @@ -41,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/core/state/pruner" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/filters" @@ -62,6 +61,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/sstorage" + "github.com/ethereum/go-ethereum/sstorminer" ) // Config contains the configuration options of the ETH protocol. @@ -500,17 +500,17 @@ func (s *Ethereum) SetEtherbase(etherbase common.Address) { // is already running, this method just return. func (s *Ethereum) StartSstorMining() { // If the miner was not running, initialize it - if !s.IsMining() { + if !s.IsSstorMining() { go s.sstorMiner.Start() } } // StopSstorMining terminates the sstorage miner. func (s *Ethereum) StopSstorMining() { - s.miner.Stop() + s.sstorMiner.Stop() } -func (s *Ethereum) IsSstorMining() bool { return s.miner.Mining() } +func (s *Ethereum) IsSstorMining() bool { return s.sstorMiner.Mining() } func (s *Ethereum) SstorMiner() *sstorminer.Miner { return s.sstorMiner } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index f3fbed179013..ec4278f923fa 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -18,7 +18,6 @@ package ethconfig import ( - "github.com/ethereum/go-ethereum/sstorminer" "math/big" "os" "os/user" @@ -40,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/sstorminer" ) // FullNodeGPO contains default gasprice oracle settings for full node. diff --git a/sstorminer/worker.go b/sstorminer/worker.go index abdae86a2a94..a6a1655c3a85 100644 --- a/sstorminer/worker.go +++ b/sstorminer/worker.go @@ -187,10 +187,11 @@ type priceOracle struct { limit int } -// The TipGap provided in the SuggestGasTipCap method (provide by EthAPIBackend) will extract the smallest 3 TipGaps +// The TipGap provided in the SuggestGasTipCap method (provided by EthAPIBackend) will extract the smallest 3 TipGaps // from each of the latest n blocks, sort them, and take the median according to the set percentile. // -// # To make it simple, if the transaction volume is small, suggestGasTip set to 0, otherwise the third smallest GasTip is used. +// # To make it simple, if the transaction volume is small, suggestGasTip set to 0, otherwise the third smallest GasTip +// in the latest block is used. // // updatePriceOracle update suggestGasTip according to block. func (o *priceOracle) updatePriceOracle(block *types.Block) { From 18d57ca53d47c81969d3f426dbe9373768f7addf Mon Sep 17 00:00:00 2001 From: pingke Date: Mon, 27 Mar 2023 13:30:05 +0800 Subject: [PATCH 07/23] fix bug and resolve comments --- core/blockchain.go | 25 ++-- eth/downloader/downloader.go | 2 +- eth/protocols/sstorage/sync.go | 17 ++- eth/protocols/sstorage/sync_test.go | 77 ++++++------ go.mod | 8 +- go.sum | 12 +- sstorage/data_shard.go | 20 +-- sstorage/shard_manager.go | 12 +- sstorminer/merklelib.go | 36 ++++++ sstorminer/worker.go | 19 +-- sstorminer/worker_test.go | 186 +++++++++++++++++++--------- 11 files changed, 261 insertions(+), 153 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 20b89f922121..bbd5cd130565 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2353,8 +2353,7 @@ func (bc *BlockChain) PreExecuteBlock(block *types.Block) (err error) { } var ( - emptyHash = common.Hash{} - defaultHashBytes = crypto.Keccak256Hash().Bytes() + emptyHash = common.Hash{} ) type SstorageMetadata struct { @@ -2419,10 +2418,9 @@ func GetDefaultMetadata(index uint64) (common.Hash, *SstorageMetadata) { meta := &SstorageMetadata{ KVIdx: index, KVSize: uint64(0), - HashInMeta: defaultHashBytes[:24], + HashInMeta: make([]byte, 24), } hashBytes := make([]byte, 32) - copy(hashBytes[:24], meta.HashInMeta) binary.BigEndian.PutUint32(hashBytes[27:], uint32(index)) return common.BytesToHash(hashBytes), meta } @@ -2530,16 +2528,16 @@ func (bc *BlockChain) VerifyAndWriteKV(contract common.Address, data map[uint64] } // ReadKVsByIndexList Read the KVs by a list of KV index. -func (bc *BlockChain) ReadKVsByIndexList(contract common.Address, indexes []uint64, useMaxKVsize bool) ([]*KV, error) { +func (bc *BlockChain) ReadKVsByIndexList(contract common.Address, indexes []uint64, returnEmpty bool) ([]*KV, error) { stateDB, err := bc.StateAt(bc.CurrentBlock().Root()) if err != nil { return nil, err } - return bc.ReadKVsByIndexListWithState(stateDB, contract, indexes, useMaxKVsize) + return bc.ReadKVsByIndexListWithState(stateDB, contract, indexes, returnEmpty) } -func (bc *BlockChain) ReadKVsByIndexListWithState(stateDB *state.StateDB, contract common.Address, indexes []uint64, useMaxKVsize bool) ([]*KV, error) { +func (bc *BlockChain) ReadKVsByIndexListWithState(stateDB *state.StateDB, contract common.Address, indexes []uint64, returnEmpty bool) ([]*KV, error) { sm := sstorage.ContractToShardManager[contract] if sm == nil { return nil, fmt.Errorf("shard manager for contract %s is not support", contract.Hex()) @@ -2548,14 +2546,15 @@ func (bc *BlockChain) ReadKVsByIndexListWithState(stateDB *state.StateDB, contra res := make([]*KV, 0) for _, idx := range indexes { _, meta, err := GetSstorageMetadata(stateDB, contract, idx) - if err != nil { + if returnEmpty && meta.KVSize == 0 { + kv := KV{idx, make([]byte, 0)} + res = append(res, &kv) continue } - l := int(meta.KVSize) - if useMaxKVsize { - l = int(sm.MaxKvSize()) + if err != nil { + continue } - data, ok, err := sm.TryRead(idx, l, common.BytesToHash(meta.HashInMeta)) + data, ok, err := sm.TryRead(idx, int(meta.KVSize), common.BytesToHash(meta.HashInMeta)) if ok && err == nil { kv := KV{idx, data} res = append(res, &kv) @@ -2683,7 +2682,7 @@ func (bc *BlockChain) GetSstorageMiningInfo(root common.Hash, contract common.Ad func (bc *BlockChain) GetSstorageMiningInfoWithStateDB(stateDB *state.StateDB, contract common.Address, shardId uint64) (*MiningInfo, error) { info := new(MiningInfo) - position := getSlotHash(0, uint256.NewInt(shardId).Bytes32()) + position := getSlotHash(3, uint256.NewInt(shardId).Bytes32()) info.MiningHash = stateDB.GetState(contract, position) if info.MiningHash == emptyHash { return nil, fmt.Errorf("fail to get mining info for shard %d", shardId) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index df24b59b07ed..c319f5a91fd8 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -224,7 +224,7 @@ type BlockChain interface { } // New creates a new downloader to fetch hashes and blocks from remote peers. -func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn) *Downloader { +func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain *core.BlockChain, lightchain LightChain, dropPeer peerDropFn) *Downloader { if lightchain == nil { lightchain = chain } diff --git a/eth/protocols/sstorage/sync.go b/eth/protocols/sstorage/sync.go index f97b6a98368c..273b62c015a8 100644 --- a/eth/protocols/sstorage/sync.go +++ b/eth/protocols/sstorage/sync.go @@ -25,10 +25,9 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" - - "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/ethdb" @@ -53,6 +52,7 @@ const ( var ErrCancelled = errors.New("sync cancelled") var ( + empty = make([]byte, 0) requestTimeoutInMillisecond = 1000 * time.Millisecond // Millisecond ) @@ -492,6 +492,19 @@ func (s *Syncer) loadSyncStatus() { lastKvIndex = 0 } for _, sid := range shards { + // if the shard is not full, fill in the data shard with []byte{}, + if lastKvIndex < sm.KvEntries()*(sid+1)-1 { + go func() { + lastIdx, err := s.chain.GetSstorageLastKvIdx(contract) + if err != nil { + log.Info("loadSyncStatus failed when update: get lastKvIdx") + lastIdx = 0 + } + for i := lastIdx; i < sm.KvEntries()*(sid+1)-1; i++ { + sm.TryWrite(i, empty, common.Hash{}) + } + }() + } exist := false for _, task := range progress.Tasks { if task.Contract == contract && task.ShardId == sid { diff --git a/eth/protocols/sstorage/sync_test.go b/eth/protocols/sstorage/sync_test.go index d5fe541e5fa5..536ba3b2cc16 100644 --- a/eth/protocols/sstorage/sync_test.go +++ b/eth/protocols/sstorage/sync_test.go @@ -285,7 +285,6 @@ func createKVRequestResponse(t *testPeer, id uint64, stateDB *state.StateDB, con } else { values = append(values, &core.KV{Idx: idx, Data: bs}) } - } } @@ -375,13 +374,7 @@ func getSlotHash(slotIdx uint64, key common.Hash) common.Hash { slotdata := slot[:] data := append(keydata, slotdata...) - hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) - hasher.Write(data) - - hashRes := common.Hash{} - hasher.Read(hashRes[:]) - - return hashRes + return crypto.Keccak256Hash(data) } // makeKVStorage generate a range of storage Data and its metadata @@ -445,6 +438,9 @@ func createSstorage(contract common.Address, shardIdxList []uint64, kvSizeBits, } sm.AddDataFile(df) } + for i := shardIdx * sm.KvEntries(); i < (shardIdx+1)*sm.KvEntries(); i++ { + sm.TryWrite(i, empty, common.Hash{}) + } } shards := make(map[common.Address][]uint64) @@ -461,19 +457,30 @@ func verifyKVs(stateDB *state.StateDB, data map[common.Address]map[uint64][]byte } for idx, val := range shards { _, meta, err := core.GetSstorageMetadata(stateDB, contract, idx) - if err != nil { t.Fatalf("get MetaHash data fail with err: %s.", err.Error()) } + sval, ok, err := shardData.TryRead(idx, len(val), common.BytesToHash(meta.HashInMeta)) if err != nil { t.Fatalf("TryRead sstorage Data fail. err: %s", err.Error()) } if !ok { - t.Fatalf("TryRead sstroage Data fail. err: %s", "shard Idx not support") + t.Fatalf("TryRead sstroage Data fail. err: %s, index %d", "shard Idx not support", idx) } - if _, ok := destroyedList[idx]; !ok && !bytes.Equal(val, sval) { + if _, ok := destroyedList[idx]; ok { + val = make([]byte, sstorage.CHUNK_SIZE) + _, defaultMeta := core.GetDefaultMetadata(idx) + sval, ok, err = shardData.TryReadChunk(idx, common.BytesToHash(defaultMeta.HashInMeta)) + if err != nil { + t.Fatalf("TryReadChunk fail. err: %s", err.Error()) + } + if !ok { + t.Fatalf("TryReadChunk fail. err: %s", "shard Idx not support") + } + } + if !bytes.Equal(val, sval) { t.Fatalf("verify KV failed; index: %d; val: %s; sval: %s", idx, common.Bytes2Hex(val), common.Bytes2Hex(sval)) } @@ -827,9 +834,9 @@ func TestSyncWithEmptyResponse(t *testing.T) { return source } - data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntriesBits) - syncer := setupSyncer(shards, stateDB, kvEntriesBits, mkSource("source", shards, data)) - for i := uint64(0); i < kvEntriesBits; i++ { + data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntries) + syncer := setupSyncer(shards, stateDB, kvEntries, mkSource("source", shards, data)) + for i := uint64(0); i < kvEntries; i++ { destroyedList[i] = struct{}{} } done := checkStall(t, term) @@ -874,9 +881,9 @@ func TestSyncWithNoResponse(t *testing.T) { return source } - data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntriesBits) - syncer := setupSyncer(shards, stateDB, kvEntriesBits, mkSource("source", shards, data)) - for i := uint64(0); i < kvEntriesBits; i++ { + data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntries) + syncer := setupSyncer(shards, stateDB, kvEntries, mkSource("source", shards, data)) + for i := uint64(0); i < kvEntries; i++ { destroyedList[i] = struct{}{} } done := checkStall(t, term) @@ -921,8 +928,8 @@ func TestSyncWithFewerResult(t *testing.T) { return source } - data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntriesBits-reduce) - syncer := setupSyncer(shards, stateDB, kvEntriesBits-reduce, mkSource("source", shards, data)) + data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntries-reduce) + syncer := setupSyncer(shards, stateDB, kvEntries-reduce, mkSource("source", shards, data)) done := checkStall(t, term) if err := syncer.Sync(cancel); err != nil { t.Fatalf("sync failed: %v", err) @@ -963,9 +970,9 @@ func TestSyncMismatchWithMeta(t *testing.T) { return source } - data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntriesBits) - destroyedList := destoryData(data, make(map[uint64]struct{}), 0, kvEntriesBits, 8) - syncer := setupSyncer(shards, stateDB, kvEntriesBits, mkSource("source", shards, data)) + data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntries) + destroyedList := destoryData(data, make(map[uint64]struct{}), 0, kvEntries, 8) + syncer := setupSyncer(shards, stateDB, kvEntries, mkSource("source", shards, data)) done := checkStall(t, term) if err := syncer.Sync(cancel); err != ErrCancelled { t.Fatalf("sync cancelled error is expected: %v", err) @@ -1051,15 +1058,15 @@ func TestMultiSyncWithDataOverlayWithDestroyed(t *testing.T) { return source } - expectedData, localShards := makeKVStorage(stateDB, contract, []uint64{0, 1, 2}, kvEntriesBits) - data, shards := makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntriesBits) - list := destoryData(data, make(map[uint64]struct{}), 0, kvEntriesBits*3, 8) + expectedData, localShards := makeKVStorage(stateDB, contract, []uint64{0, 1, 2}, kvEntries) + data, shards := makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntries) + list := destoryData(data, make(map[uint64]struct{}), 0, kvEntries*3, 8) peer0 := mkSource("source_0", shards, data) - data, shards = makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntriesBits) - _ = destoryData(data, list, 0, kvEntriesBits*3, 8) + data, shards = makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntries) + _ = destoryData(data, list, 0, kvEntries*3, 8) peer1 := mkSource("source_1", shards, data) - syncer := setupSyncer(localShards, stateDB, kvEntriesBits*3, peer0, peer1) + syncer := setupSyncer(localShards, stateDB, kvEntries*3, peer0, peer1) done := checkStall(t, term) if err := syncer.Sync(cancel); err != nil { t.Fatalf("sync failed: %v", err) @@ -1098,15 +1105,15 @@ func TestAddPeerDuringSyncing(t *testing.T) { return source } - expectedData, localShards := makeKVStorage(stateDB, contract, []uint64{0, 1, 2}, kvEntriesBits) - data, shards := makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntriesBits) - list := destoryData(data, make(map[uint64]struct{}), 0, kvEntriesBits*3, 8) + expectedData, localShards := makeKVStorage(stateDB, contract, []uint64{0, 1, 2}, kvEntries) + data, shards := makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntries) + list := destoryData(data, make(map[uint64]struct{}), 0, kvEntries*3, 8) peer0 := mkSource("source_0", shards, data) - data, shards = makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntriesBits) - _ = destoryData(data, list, 0, kvEntriesBits*3, 8) + data, shards = makeKVStorage(nil, contract, []uint64{0, 1, 2}, kvEntries) + _ = destoryData(data, list, 0, kvEntries*3, 8) peer1 := mkSource("source_1", shards, data) - syncer := setupSyncer(localShards, stateDB, kvEntriesBits*3, peer0) + syncer := setupSyncer(localShards, stateDB, kvEntries*3, peer0) done := checkStall(t, term) go func() { time.Sleep(100 * time.Millisecond) @@ -1140,7 +1147,7 @@ func TestSaveAndLoadSyncStatus(t *testing.T) { } }(files) - syncer := setupSyncer(shards, stateDB, kvEntriesBits) + syncer := setupSyncer(shards, stateDB, kvEntries) task0 := createKvTask(contract, entries, 0, lastKvIndex, 20) task0.KvSubTasks = make([]*kvSubTask, 0) task1 := createKvTask(contract, entries, 1, lastKvIndex, 4) diff --git a/go.mod b/go.mod index 88a83df3a439..cff95169586d 100644 --- a/go.mod +++ b/go.mod @@ -154,11 +154,11 @@ require ( github.com/libp2p/go-tcp-transport v0.2.4 // indirect github.com/libp2p/go-ws-transport v0.4.0 // indirect github.com/libp2p/go-yamux/v2 v2.2.0 // indirect - github.com/lucas-clemente/quic-go v0.21.2 // indirect + github.com/lucas-clemente/quic-go v0.26.0 // indirect github.com/magiconair/properties v1.8.0 // indirect - github.com/marten-seemann/qtls-go1-15 v0.1.5 // indirect - github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.0-rc.1 // indirect + github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect + github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect + github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d // indirect github.com/mattn/go-runewidth v0.0.9 // indirect diff --git a/go.sum b/go.sum index 218c2d3e2b61..5b01f14b6a32 100644 --- a/go.sum +++ b/go.sum @@ -819,8 +819,9 @@ github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZj github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= -github.com/lucas-clemente/quic-go v0.21.2 h1:8LqqL7nBQFDUINadW0fHV/xSaCQJgmJC0Gv+qUnjd78= github.com/lucas-clemente/quic-go v0.21.2/go.mod h1:vF5M1XqhBAHgbjKcJOXY3JZz3GP0T3FQhz/uyOUS38Q= +github.com/lucas-clemente/quic-go v0.26.0 h1:ALBQXr9UJ8A1LyzvceX4jd9QFsHvlI0RR6BkV16o00A= +github.com/lucas-clemente/quic-go v0.26.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= @@ -833,12 +834,15 @@ github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2o github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= -github.com/marten-seemann/qtls-go1-15 v0.1.5 h1:Ci4EIUN6Rlb+D6GmLdej/bCQ4nPYNtVXQB+xjiXE1nk= github.com/marten-seemann/qtls-go1-15 v0.1.5/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= -github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco= github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.0-rc.1 h1:/rpmWuGvceLwwWuaKPdjpR4JJEUH0tq64/I3hvzaNLM= +github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= +github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= github.com/marten-seemann/qtls-go1-17 v0.1.0-rc.1/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8= +github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc= +github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= +github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y= +github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= diff --git a/sstorage/data_shard.go b/sstorage/data_shard.go index 61011a2598ec..bfdaf6a19de9 100644 --- a/sstorage/data_shard.go +++ b/sstorage/data_shard.go @@ -126,7 +126,7 @@ func (ds *DataShard) readChunkWith(kvIdx uint64, chunkIdx uint64, decoder func([ if !ds.Contains(kvIdx) { return nil, fmt.Errorf("kv not found") } - if chunkIdx > ds.chunksPerKv { + if chunkIdx >= ds.chunksPerKv { return nil, fmt.Errorf("chunkIdx out of range, chunkIdx: %d vs chunksPerKv %d", chunkIdx, ds.chunksPerKv) } idx := kvIdx*ds.chunksPerKv + chunkIdx @@ -134,7 +134,7 @@ func (ds *DataShard) readChunkWith(kvIdx uint64, chunkIdx uint64, decoder func([ if err != nil { return nil, err } - data = decoder(data, chunkIdx) + data = decoder(data, idx) return data, nil } @@ -238,7 +238,7 @@ func decodeChunk(bs []byte, encodeType uint64, encodeKey common.Hash) []byte { } else if encodeType == NO_ENCODE { return bs } else if encodeType == ENCODE_ETHASH { - return MaskDataInPlace(pora.GetMaskData(0, encodeKey, len(bs), nil), bs) + return UnmaskDataInPlace(pora.GetMaskData(0, encodeKey, len(bs), nil), bs) } else { panic("unsupported encode type") } @@ -253,20 +253,12 @@ func (ds *DataShard) Write(kvIdx uint64, b []byte, commit common.Hash) error { if uint64(len(b)) > ds.kvSize { return fmt.Errorf("write data too large") } - + cb := make([]byte, ds.kvSize) + copy(cb, b) for i := uint64(0); i < ds.chunksPerKv; i++ { - off := int(i * CHUNK_SIZE) - if off >= len(b) { - break - } - writeLen := len(b) - off - if writeLen > int(CHUNK_SIZE) { - writeLen = int(CHUNK_SIZE) - } - chunkIdx := kvIdx*ds.chunksPerKv + i encodeKey := calcEncodeKey(commit, chunkIdx, ds.Miner()) - encodedChunk := encodeChunk(b[off:off+writeLen], ds.EncodeType(), encodeKey) + encodedChunk := encodeChunk(cb[int(i*CHUNK_SIZE):int((i+1)*CHUNK_SIZE)], ds.EncodeType(), encodeKey) err := ds.writeChunk(chunkIdx, encodedChunk) if err != nil { diff --git a/sstorage/shard_manager.go b/sstorage/shard_manager.go index 4749b2e16172..93c83fbfb377 100644 --- a/sstorage/shard_manager.go +++ b/sstorage/shard_manager.go @@ -9,24 +9,24 @@ import ( type ShardManager struct { shardMap map[uint64]*DataShard contractAddress common.Address - kvSize uint64 - chunksPerKv uint64 - kvEntries uint64 kvSizeBits uint64 + kvSize uint64 chunksPerKvBits uint64 + chunksPerKv uint64 kvEntriesBits uint64 + kvEntries uint64 } func NewShardManager(contractAddress common.Address, kvSizeBits uint64, kvEntriesBits uint64) *ShardManager { return &ShardManager{ shardMap: make(map[uint64]*DataShard), contractAddress: contractAddress, + kvSizeBits: kvSizeBits, kvSize: 1 << kvSizeBits, - chunksPerKv: (1 << kvSizeBits) / CHUNK_SIZE, + kvEntriesBits: kvEntriesBits, kvEntries: 1 << kvEntriesBits, - kvSizeBits: kvSizeBits, chunksPerKvBits: kvSizeBits - CHUNK_SIZE_BITS, - kvEntriesBits: kvEntriesBits, + chunksPerKv: (1 << kvSizeBits) / CHUNK_SIZE, } } diff --git a/sstorminer/merklelib.go b/sstorminer/merklelib.go index 179f410ca7d8..6930c04a37e1 100644 --- a/sstorminer/merklelib.go +++ b/sstorminer/merklelib.go @@ -33,6 +33,9 @@ func expectedDiff(lastMineTime uint64, difficulty *big.Int, minedTime uint64, ta } func getProof(data []byte, chunkSize, nChunkBits, chunkIdx uint64) ([]common.Hash, error) { + if len(data) == 0 { + return nil, nil + } nChunks := uint64(1) << nChunkBits if chunkIdx >= nChunks { return []common.Hash{}, fmt.Errorf("index out of scope") @@ -64,6 +67,9 @@ func getProof(data []byte, chunkSize, nChunkBits, chunkIdx uint64) ([]common.Has } func calculateRootWithProof(dataHash common.Hash, chunkIdx uint64, proofs []common.Hash) (common.Hash, error) { + if len(proofs) == 0 { + return dataHash, nil + } hash := dataHash nChunkBits := uint64(len(proofs)) if chunkIdx >= uint64(1)<= l { + // empty mean the leaf is zero + break + } + size := l - off + if size >= chunkSize { + size = chunkSize + } + hash := crypto.Keccak256Hash(data[off : off+size]) + nodes[i] = hash + } + n := chunkPerKV + for n != 1 { + for i := uint64(0); i < n/2; i++ { + nodes[i] = crypto.Keccak256Hash(nodes[i*2].Bytes(), nodes[i*2+1].Bytes()) + } + + n = n / 2 + } + return nodes[0] +} diff --git a/sstorminer/worker.go b/sstorminer/worker.go index a6a1655c3a85..2ab92dbacd23 100644 --- a/sstorminer/worker.go +++ b/sstorminer/worker.go @@ -190,8 +190,8 @@ type priceOracle struct { // The TipGap provided in the SuggestGasTipCap method (provided by EthAPIBackend) will extract the smallest 3 TipGaps // from each of the latest n blocks, sort them, and take the median according to the set percentile. // -// # To make it simple, if the transaction volume is small, suggestGasTip set to 0, otherwise the third smallest GasTip -// in the latest block is used. +// To make it simple, if the transaction volume is small, suggestGasTip set to 0, otherwise the third smallest GasTip +// in the latest block is used. // // updatePriceOracle update suggestGasTip according to block. func (o *priceOracle) updatePriceOracle(block *types.Block) { @@ -409,12 +409,10 @@ func (w *worker) newWorkLoop(recommit time.Duration) { for { select { case <-w.startCh: - fmt.Println("start") w.updateTaskInfo(w.chain.CurrentBlock().Root(), time.Now().Unix()) timer.Reset(recommit) case head := <-w.chainHeadCh: - fmt.Println("new chain head") w.updateTaskInfo(head.Block.Root(), time.Now().Unix()) timer.Reset(recommit) @@ -422,7 +420,6 @@ func (w *worker) newWorkLoop(recommit time.Duration) { // If sealing is running resubmit a new work cycle periodically to pull in // higher priced transactions. Disable this overhead for pending blocks. if w.isRunning() { - fmt.Println("time out") timer.Reset(recommit) w.updateTaskInfo(w.chain.CurrentBlock().Root(), time.Now().Unix()) } @@ -460,25 +457,19 @@ func (w *worker) mainLoop() { for { select { case <-w.newWorkCh: - fmt.Println("get new work") interrupt() stopCh = make(chan struct{}) w.commitWork(stopCh) - fmt.Println("new work") case <-w.taskDoneCh: - fmt.Println("get task done") interrupt() stopCh = make(chan struct{}) w.commitWork(stopCh) - fmt.Println("task done") case <-w.taskStartCh: - fmt.Println("get task start") interrupt() stopCh = make(chan struct{}) w.commitWork(stopCh) - fmt.Println("task start") case <-w.exitCh: return @@ -496,7 +487,6 @@ func (w *worker) taskLoop() { for { select { case task := <-w.taskCh: - fmt.Println("get task") if w.newTaskHook != nil { w.newTaskHook(task) } @@ -519,7 +509,6 @@ func (w *worker) resultLoop() { for { select { case result := <-w.resultCh: - fmt.Println("get result") if w.newResultHook != nil { w.newResultHook(result) } @@ -661,7 +650,7 @@ func (w *worker) mineTask(t *task) (bool, error) { // Check if the data matches the hash in metadata. if requiredDiff.Cmp(new(big.Int).SetBytes(hash0.Bytes())) < 0 { - proofs := make([][]common.Hash, 0) + proofs := make([][]common.Hash, len(kvIdxs)) kvs, err := w.chain.ReadKVsByIndexList(t.storageContract, kvIdxs, true) if err != nil { return false, err @@ -671,7 +660,7 @@ func (w *worker) mineTask(t *task) (bool, error) { } for i := 0; i < len(dataSet); i++ { - if kvs[i].Idx != kvIdxs[i] { + if kvs[i].Idx == kvIdxs[i] { ps, err := getProof(kvs[i].Data, sstor.CHUNK_SIZE, t.chunkSizeBits, chunkIdxs[i]) if err != nil { return false, err diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go index 992d882566b4..a162aa2d07a9 100644 --- a/sstorminer/worker_test.go +++ b/sstorminer/worker_test.go @@ -141,7 +141,6 @@ func hashAdd(hash common.Hash, i uint64) common.Hash { func (bc *wrapBlockChain) saveMiningInfo(shardId uint64, info *core.MiningInfo) { position := getSlotHash(0, uint256.NewInt(shardId).Bytes32()) - // fmt.Println(position.Hex()) bc.stateDB.SetState(minerContract, position, info.MiningHash) bc.stateDB.SetState(minerContract, hashAdd(position, 1), common.BigToHash(new(big.Int).SetUint64(info.LastMineTime))) bc.stateDB.SetState(minerContract, hashAdd(position, 2), common.BigToHash(info.Difficulty)) @@ -318,8 +317,8 @@ func makeKVStorage(stateDB *state.StateDB, contract common.Address, shards []uin last = sidx*kvCount + rand.Uint64()%kvCount } for i := sidx * kvCount; i < (sidx+1)*kvCount; i++ { - var val []byte - metaHash := crypto.Keccak256Hash(val) + val := make([]byte, 0) + metaHash := common.Hash{} if i < last { val = make([]byte, 8) binary.BigEndian.PutUint64(val, i) @@ -328,7 +327,7 @@ func makeKVStorage(stateDB *state.StateDB, contract common.Address, shards []uin key := getSlotHash(2, uint256.NewInt(i).Bytes32()) stateDB.SetState(contract, key, skey) - metaHash = crypto.Keccak256Hash(val) + metaHash = merkleRootWithMinTree(val, sm.ChunksPerKv(), sstorage.CHUNK_SIZE) meta := generateMetadata(i, uint64(len(val)), metaHash) key = getSlotHash(1, skey) stateDB.SetState(contract, key, meta) @@ -342,7 +341,6 @@ func makeKVStorage(stateDB *state.StateDB, contract common.Address, shards []uin func updateMiningInfoAndInsertNewBlock(pinfo *core.MiningInfo, chain *wrapBlockChain, engine *ethash.Ethash, db ethdb.Database) error { info := new(core.MiningInfo) info.MiningHash = crypto.Keccak256Hash(pinfo.MiningHash.Bytes()) - fmt.Println(info.MiningHash.Hex()) info.LastMineTime = uint64(time.Now().Unix()) info.Difficulty = diff info.BlockMined = blockMined @@ -358,18 +356,25 @@ func updateMiningInfoAndInsertNewBlock(pinfo *core.MiningInfo, chain *wrapBlockC func verifyTaskResult(stateDB *state.StateDB, chain BlockChain, r *result) error { for i, proofs := range r.proofs { - _, meta, err := core.GetSstorageMetadata(stateDB, contract, r.kvIdxs[i]) - if err != nil { - return err - } - data, got, err := r.task.shardManager.TryReadChunk(r.kvIdxs[i]*r.task.shardManager.ChunksPerKv()+r.chunkIdxs[i], common.BytesToHash(meta.HashInMeta)) - if err != nil { - return err - } - if !got { - return fmt.Errorf("fail to get data for storageContract %s vkidx %d", contract.Hex(), r.kvIdxs[i]) + _, meta, _ := core.GetSstorageMetadata(stateDB, contract, r.kvIdxs[i]) + hash := common.Hash{} + off := sstorage.CHUNK_SIZE * r.chunkIdxs[i] + if meta.KVSize > off { + data, got, err := r.task.shardManager.TryReadChunk(r.kvIdxs[i]*r.task.shardManager.ChunksPerKv()+r.chunkIdxs[i], common.BytesToHash(meta.HashInMeta)) + if err != nil { + return err + } + if !got { + return fmt.Errorf("fail to get data for storageContract %s vkidx %d", contract.Hex(), r.kvIdxs[i]) + } + + if meta.KVSize < off+sstorage.CHUNK_SIZE { + data = data[:meta.KVSize-off] + } + hash = crypto.Keccak256Hash(data) } - vr := verify(meta.HashInMeta, crypto.Keccak256Hash(data), r.chunkIdxs[i], proofs) + + vr := verify(meta.HashInMeta, hash, r.chunkIdxs[i], proofs) if !vr { return fmt.Errorf("verify proofs fail for index %d fail", r.kvIdxs[i]) } @@ -427,10 +432,9 @@ func TestWork_SingleShard(test *testing.T) { test.Error("verify task fail") } case r := <-resultCh: - fmt.Println("getresult") stateDB, _ := w.chain.State() if err := verifyTaskResult(stateDB, w.chain, r); err != nil { - test.Error("verify mined result failed", err.Error()) + test.Error("verify mined result failed:", err.Error()) } return case <-time.NewTimer(3 * time.Second).C: @@ -472,7 +476,6 @@ func TestWork_MultiShards(test *testing.T) { for i := 0; i < 2; i += 1 { select { case r := <-resultCh: - fmt.Println("getresult") stateDB, _ := w.chain.State() if err := verifyTaskResult(stateDB, w.chain, r); err != nil { test.Error("verify mined result failed", err.Error()) @@ -520,7 +523,6 @@ func TestWork_TriggerByNewBlock(test *testing.T) { for i := 0; i < 2; i += 1 { select { case r := <-resultCh: - fmt.Println("getresult") stateDB, _ := w.chain.State() if err := verifyTaskResult(stateDB, w.chain, r); err != nil { test.Error("verify mined result failed", err.Error()) @@ -535,48 +537,46 @@ func TestWork_TriggerByNewBlock(test *testing.T) { } } -/* - func TestWork_ShardIsNotFull(test *testing.T) { - var ( - taskMined int - resultGet int - resultCh = make(chan *result, 2) - shardIdxList = []uint64{0} - engine = ethash.NewFaker() - db = rawdb.NewMemoryDatabase() - ) - - w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, db, shardIdxList, 0, false) - - defer w.close() - defer engine.Close() - defer func(files []string) { - for _, file := range files { - os.Remove(file) - } - }(files) +func TestWork_ShardIsNotFull(test *testing.T) { + var ( + taskMined int + resultGet int + resultCh = make(chan *result, 2) + shardIdxList = []uint64{0} + engine = ethash.NewFaker() + db = rawdb.NewMemoryDatabase() + ) - w.newTaskHook = func(task *task) { - taskMined += 1 - } - w.newResultHook = func(result *result) { - resultGet += 1 - resultCh <- result + w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, db, shardIdxList, 0, false) + + defer w.close() + defer engine.Close() + defer func(files []string) { + for _, file := range files { + os.Remove(file) } + }(files) - w.start() // Start mining! - select { - case r := <-resultCh: - fmt.Println("getresult") - stateDB, _ := w.chain.State() - if err := verifyTaskResult(stateDB, w.chain, r); err != nil { - test.Error("verify mined result failed", err.Error()) - } - case <-time.NewTimer(3 * time.Second).C: - test.Error("new task timeout") + w.newTaskHook = func(task *task) { + taskMined += 1 + } + w.newResultHook = func(result *result) { + resultGet += 1 + resultCh <- result + } + + w.start() // Start mining! + select { + case r := <-resultCh: + stateDB, _ := w.chain.State() + if err := verifyTaskResult(stateDB, w.chain, r); err != nil { + test.Error("verify mined result failed", err.Error()) } + case <-time.NewTimer(3 * time.Second).C: + test.Error("new task timeout") } -*/ +} + func TestWork_StartAndStopTask(test *testing.T) { var ( shardIdxList = []uint64{0, 1, 2} @@ -611,7 +611,6 @@ func TestWork_StartAndStopTask(test *testing.T) { for i := 0; i < 3; i += 1 { select { case r := <-resultCh: - // fmt.Println("getresult") stateDB, _ := w.chain.State() if err := verifyTaskResult(stateDB, w.chain, r); err != nil { test.Error("verify mined result failed", err.Error()) @@ -674,9 +673,78 @@ func TestWork_LargeKV(test *testing.T) { case r := <-resultCh: stateDB, _ := w.chain.State() if err := verifyTaskResult(stateDB, w.chain, r); err != nil { - test.Error("verify mined result failed", err.Error()) + test.Error("verify mined result failed:", err.Error()) } case <-time.NewTimer(3 * time.Second).C: test.Error("new task timeout") } } + +func TestWork_ProofsCreateAndVerify(test *testing.T) { + type testCase struct { + chunkPerKVBits uint64 + val []byte + } + + testCases := []testCase{ + { + chunkPerKVBits: uint64(1), + val: contract.Bytes(), + }, + { + chunkPerKVBits: uint64(1), + val: make([]byte, 0), + }, + { + chunkPerKVBits: uint64(4), + val: make([]byte, 0), + }, + { + chunkPerKVBits: uint64(4), + val: contract.Bytes(), + }, + { + chunkPerKVBits: uint64(4), + val: append(make([]byte, sstorage.CHUNK_SIZE), contract.Bytes()...), + }, + { + chunkPerKVBits: uint64(4), + val: append(make([]byte, sstorage.CHUNK_SIZE*2), contract.Bytes()...), + }, + { + chunkPerKVBits: uint64(4), + val: append(make([]byte, sstorage.CHUNK_SIZE*3), contract.Bytes()...), + }, + } + + for _, tc := range testCases { + testWork_ProofsCreateAndVerify(test, tc.val, tc.chunkPerKVBits) + } +} + +func testWork_ProofsCreateAndVerify(test *testing.T, val []byte, chunkPerKVBits uint64) { + chunkPerKV := uint64(1) << chunkPerKVBits + datalen := uint64(len(val)) + + root := merkleRootWithMinTree(val, chunkPerKV, sstorage.CHUNK_SIZE) + for i := uint64(0); i < chunkPerKV; i++ { + hash := common.Hash{} + off := i * sstorage.CHUNK_SIZE + + ps, err := getProof(val, sstorage.CHUNK_SIZE, chunkPerKVBits, i) + if err != nil { + test.Error("error:", err.Error()) + } + if off < datalen { + size := datalen - off + if size > sstorage.CHUNK_SIZE { + size = sstorage.CHUNK_SIZE + } + hash = crypto.Keccak256Hash(val[off : off+size]) + } + vr := verify(root.Bytes()[:24], hash, i, ps) + if !vr { + test.Error("verify proof fail, chunk ", i) + } + } +} From 268d56d881e9658c238e7e90ad387511dd8d5012 Mon Sep 17 00:00:00 2001 From: pingke Date: Mon, 27 Mar 2023 15:05:16 +0800 Subject: [PATCH 08/23] fix test --- eth/downloader/downloader.go | 6 +++--- eth/downloader/downloader_test.go | 2 +- eth/protocols/sstorage/sync.go | 4 ++-- eth/protocols/sstorage/sync_test.go | 23 +++++++++++++++-------- sstorminer/worker_test.go | 2 +- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index c319f5a91fd8..ac2505184652 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -213,18 +213,18 @@ type BlockChain interface { VerifyAndWriteKV(contract common.Address, data map[uint64][]byte, provderAddr common.Address) (uint64, uint64, []uint64, error) // ReadEncodedKVsByIndexList Read the encoded KVs by a list of KV index. - ReadEncodedKVsByIndexList(contract common.Address, indexes []uint64) ([]*core.KV, error) + ReadEncodedKVsByIndexList(contract common.Address, shardId uint64, indexes []uint64) (common.Address, []*core.KV, error) // ReadEncodedKVsByIndexRange Read encoded KVs sequentially starting from origin until the index exceeds the limit or // the amount of data read is greater than the bytes. - ReadEncodedKVsByIndexRange(contract common.Address, origin uint64, limit uint64, bytes uint64) ([]*core.KV, error) + ReadEncodedKVsByIndexRange(contract common.Address, shardId uint64, origin uint64, limit uint64, bytes uint64) (common.Address, []*core.KV, error) // GetSstorageLastKvIdx get LastKvIdx from a sstorage contract with latest stateDB. GetSstorageLastKvIdx(contract common.Address) (uint64, error) } // New creates a new downloader to fetch hashes and blocks from remote peers. -func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain *core.BlockChain, lightchain LightChain, dropPeer peerDropFn) *Downloader { +func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn) *Downloader { if lightchain == nil { lightchain = chain } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 70c6a51215b5..3526c088dc9b 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -711,7 +711,7 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) { // Create peers of every type tester.newPeer("peer 66", eth.ETH66, chain.blocks[1:]) - //tester.newPeer("peer 65", eth.ETH67, chain.blocks[1:) + // tester.newPeer("peer 65", eth.ETH67, chain.blocks[1:) // Synchronise with the requested peer and make sure all blocks were retrieved if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil { diff --git a/eth/protocols/sstorage/sync.go b/eth/protocols/sstorage/sync.go index 273b62c015a8..a7c7000ede60 100644 --- a/eth/protocols/sstorage/sync.go +++ b/eth/protocols/sstorage/sync.go @@ -247,11 +247,11 @@ type BlockChain interface { VerifyAndWriteKV(contract common.Address, data map[uint64][]byte, providerAddress common.Address) (uint64, uint64, []uint64, error) // ReadEncodedKVsByIndexList Read the masked KVs by a list of KV index. - ReadEncodedKVsByIndexList(contract common.Address, indexes []uint64) ([]*core.KV, error) + ReadEncodedKVsByIndexList(contract common.Address, shardId uint64, indexes []uint64) (common.Address, []*core.KV, error) // ReadEncodedKVsByIndexRange Read masked KVs sequentially starting from origin until the index exceeds the limit or // the amount of data read is greater than the bytes. - ReadEncodedKVsByIndexRange(contract common.Address, origin uint64, limit uint64, bytes uint64) ([]*core.KV, error) + ReadEncodedKVsByIndexRange(contract common.Address, shardId uint64, origin uint64, limit uint64, bytes uint64) (common.Address, []*core.KV, error) // GetSstorageLastKvIdx get LastKvIdx from a sstorage contract with latest stateDB. GetSstorageLastKvIdx(contract common.Address) (uint64, error) diff --git a/eth/protocols/sstorage/sync_test.go b/eth/protocols/sstorage/sync_test.go index 536ba3b2cc16..5b9bc3057fe1 100644 --- a/eth/protocols/sstorage/sync_test.go +++ b/eth/protocols/sstorage/sync_test.go @@ -102,12 +102,15 @@ func (c *blockChain) VerifyAndWriteKV(contract common.Address, data map[uint64][ return synced, syncedBytes, inserted, nil } -func (c *blockChain) ReadEncodedKVsByIndexList(contract common.Address, indexes []uint64) ([]*core.KV, error) { +func (c *blockChain) ReadEncodedKVsByIndexList(contract common.Address, shardId uint64, indexes []uint64) (common.Address, []*core.KV, error) { sm := sstorage.ContractToShardManager[contract] if sm == nil { - return nil, fmt.Errorf("shard manager for contract %s is not support", contract.Hex()) + return common.Address{}, nil, fmt.Errorf("shard manager for contract %s is not support", contract.Hex()) + } + miner, ok := sm.GetShardMiner(shardId) + if !ok { + return common.Address{}, nil, fmt.Errorf("shard %d do not support for contract %s", shardId, contract.Hex()) } - res := make([]*core.KV, 0) for _, idx := range indexes { _, meta, err := core.GetSstorageMetadata(c.stateDB, contract, idx) @@ -121,15 +124,19 @@ func (c *blockChain) ReadEncodedKVsByIndexList(contract common.Address, indexes } } - return res, nil + return miner, res, nil } -func (c *blockChain) ReadEncodedKVsByIndexRange(contract common.Address, origin uint64, limit uint64, bytes uint64) ([]*core.KV, error) { +func (c *blockChain) ReadEncodedKVsByIndexRange(contract common.Address, shardId uint64, origin uint64, limit uint64, + bytes uint64) (common.Address, []*core.KV, error) { sm := sstorage.ContractToShardManager[contract] if sm == nil { - return nil, fmt.Errorf("shard manager for contract %s is not support", contract.Hex()) + return common.Address{}, nil, fmt.Errorf("shard manager for contract %s is not support", contract.Hex()) + } + miner, ok := sm.GetShardMiner(shardId) + if !ok { + return common.Address{}, nil, fmt.Errorf("shard %d do not support for contract %s", shardId, contract.Hex()) } - res := make([]*core.KV, 0) read := uint64(0) for idx := origin; idx <= limit; idx++ { @@ -148,7 +155,7 @@ func (c *blockChain) ReadEncodedKVsByIndexRange(contract common.Address, origin } } - return res, nil + return miner, res, nil } func (c *blockChain) GetSstorageLastKvIdx(contract common.Address) (uint64, error) { diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go index a162aa2d07a9..6440e611ed56 100644 --- a/sstorminer/worker_test.go +++ b/sstorminer/worker_test.go @@ -140,7 +140,7 @@ func hashAdd(hash common.Hash, i uint64) common.Hash { } func (bc *wrapBlockChain) saveMiningInfo(shardId uint64, info *core.MiningInfo) { - position := getSlotHash(0, uint256.NewInt(shardId).Bytes32()) + position := getSlotHash(3, uint256.NewInt(shardId).Bytes32()) bc.stateDB.SetState(minerContract, position, info.MiningHash) bc.stateDB.SetState(minerContract, hashAdd(position, 1), common.BigToHash(new(big.Int).SetUint64(info.LastMineTime))) bc.stateDB.SetState(minerContract, hashAdd(position, 2), common.BigToHash(info.Difficulty)) From f0485b23d114a19c5e580d7d72325b4d8dbcc315 Mon Sep 17 00:00:00 2001 From: pingke Date: Thu, 30 Mar 2023 10:23:00 +0800 Subject: [PATCH 09/23] add task to sstorage sync to fill up empty KV and change create/verify proof to use MinTree --- core/blockchain.go | 48 +++++++-- eth/downloader/downloader.go | 4 + eth/protocols/sstorage/sync.go | 152 ++++++++++++++++++++++------ eth/protocols/sstorage/sync_test.go | 32 +++++- sstorage/data_shard.go | 24 ++--- sstorminer/merklelib.go | 53 +++++++++- sstorminer/worker.go | 2 +- sstorminer/worker_test.go | 112 ++++++++++++++------ 8 files changed, 338 insertions(+), 89 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index bbd5cd130565..2ae97df593b3 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2394,17 +2394,16 @@ func GetSstorageMetadata(s *state.StateDB, contract common.Address, index uint64 // uint24 kvSize; // bytes24 hash; // } - defaultHash, defaultMeta := GetDefaultMetadata(index) position := getSlotHash(2, uint256.NewInt(index).Bytes32()) skey := s.GetState(contract, position) if skey == emptyHash { - return defaultHash, defaultMeta, fmt.Errorf("fail to get skey for index %d", index) + return emptyHash, nil, fmt.Errorf("fail to get skey for index %d", index) } position = getSlotHash(1, skey) meta := s.GetState(contract, position) if meta == emptyHash { - return defaultHash, defaultMeta, fmt.Errorf("fail to get SstorageMetadata for skey %s", skey.Hex()) + return emptyHash, nil, fmt.Errorf("fail to get SstorageMetadata for skey %s", skey.Hex()) } return meta, &SstorageMetadata{ @@ -2456,6 +2455,34 @@ func VerifyKV(sm *sstorage.ShardManager, idx uint64, val []byte, meta *SstorageM return data, nil } +func (bc *BlockChain) FillSstorWithEmptyKV(contract common.Address, start, limit uint64) (uint64, error) { + sm := sstorage.ContractToShardManager[contract] + if sm == nil { + return start, fmt.Errorf("kv verify fail: contract not support, contract: %s", contract.Hex()) + } + + bc.chainmu.TryLock() + defer bc.chainmu.Unlock() + + empty := make([]byte, 0) + lastKvIdx, err := bc.GetSstorageLastKvIdx(contract) + if err != nil { + return start, fmt.Errorf("get lastKvIdx for FillEmptyKV fail, err: %s", err.Error()) + } + for idx := start; idx <= limit; idx++ { + if lastKvIdx > idx { + continue + } + _, err = sm.TryWrite(idx, empty, common.Hash{}) + if err != nil { + err = fmt.Errorf("write empty to kv file fail, index: %d; error: %s", idx, err.Error()) + return idx, err + } + } + + return limit + 1, nil +} + // VerifyAndWriteKV verify a list of raw KV data using the metadata saved in the local level DB and write successfully verified // KVs to the sstorage file. And return the inserted KV index list. func (bc *BlockChain) VerifyAndWriteKV(contract common.Address, data map[uint64][]byte, providerAddr common.Address) (uint64, uint64, []uint64, error) { @@ -2543,16 +2570,21 @@ func (bc *BlockChain) ReadKVsByIndexListWithState(stateDB *state.StateDB, contra return nil, fmt.Errorf("shard manager for contract %s is not support", contract.Hex()) } + val := stateDB.GetState(contract, uint256.NewInt(0).Bytes32()) + lastIndex := new(big.Int).SetBytes(val.Bytes()).Uint64() + res := make([]*KV, 0) for _, idx := range indexes { - _, meta, err := GetSstorageMetadata(stateDB, contract, idx) - if returnEmpty && meta.KVSize == 0 { - kv := KV{idx, make([]byte, 0)} - res = append(res, &kv) + if idx >= lastIndex { + if returnEmpty { + kv := KV{idx, make([]byte, 0)} + res = append(res, &kv) + } continue } + _, meta, err := GetSstorageMetadata(stateDB, contract, idx) if err != nil { - continue + return nil, fmt.Errorf("get storage metadata fail, err: ", err.Error()) } data, ok, err := sm.TryRead(idx, int(meta.KVSize), common.BytesToHash(meta.HashInMeta)) if ok && err == nil { diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index ac2505184652..e9a18231bf61 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -212,6 +212,10 @@ type BlockChain interface { // KVs to the sstorage file. And return the inserted KV index list. VerifyAndWriteKV(contract common.Address, data map[uint64][]byte, provderAddr common.Address) (uint64, uint64, []uint64, error) + // FillSstorWithEmptyKV get the lastKVIndex and if the kv index need to fill is larger than or equal to lastKVIndex + // fill up the kv with empty ([]byte{}), so the data in the file will be filled with encode empty data + FillSstorWithEmptyKV(contract common.Address, start, limit uint64) (uint64, error) + // ReadEncodedKVsByIndexList Read the encoded KVs by a list of KV index. ReadEncodedKVsByIndexList(contract common.Address, shardId uint64, indexes []uint64) (common.Address, []*core.KV, error) diff --git a/eth/protocols/sstorage/sync.go b/eth/protocols/sstorage/sync.go index a7c7000ede60..5c6499fa77d6 100644 --- a/eth/protocols/sstorage/sync.go +++ b/eth/protocols/sstorage/sync.go @@ -134,16 +134,29 @@ type kvHealResponse struct { // kvTask represents the sync task for a sstorage shard. type kvTask struct { // These fields get serialized to leveldb on shutdown - Contract common.Address // Contract address - ShardId uint64 // ShardId - KvSubTasks []*kvSubTask - HealTask *kvHealTask + Contract common.Address // Contract address + ShardId uint64 // ShardId + KvSubTasks []*kvSubTask + HealTask *kvHealTask + KvSubEmptyTasks []*kvSubEmptyTask statelessPeers map[string]struct{} // Peers that failed to deliver kv Data done bool // Flag whether the task can be removed } +// task which is used to write empty to sstorage file, so the files will fill up with encode data +type kvSubEmptyTask struct { + kvTask *kvTask + + next uint64 + First uint64 + Last uint64 + + isRunning bool + done bool // Flag whether the task can be removed +} + type kvSubTask struct { kvTask *kvTask @@ -246,6 +259,10 @@ type BlockChain interface { // KVs to the sstorage file. And return the inserted KV index list. VerifyAndWriteKV(contract common.Address, data map[uint64][]byte, providerAddress common.Address) (uint64, uint64, []uint64, error) + // FillSstorWithEmptyKV get the lastKVIndex and if the kv index need to fill is larger than or equal to lastKVIndex + // fill up the kv with empty ([]byte{}), so the data in the file will be filled with encode empty data + FillSstorWithEmptyKV(contract common.Address, start, limit uint64) (uint64, error) + // ReadEncodedKVsByIndexList Read the masked KVs by a list of KV index. ReadEncodedKVsByIndexList(contract common.Address, shardId uint64, indexes []uint64) (common.Address, []*core.KV, error) @@ -287,6 +304,8 @@ type Syncer struct { kvRangeReqs map[uint64]*kvRangeRequest // KV requests currently running kvHealReqs map[uint64]*kvHealRequest // KV heal requests currently running + runningEmptyTaskTreads int // Number of working threads for processing empty task + kvSynced uint64 // Number of kvs downloaded kvBytes common.StorageSize // Number of kv bytes downloaded kvSyncing uint64 // Number of kvs downloading @@ -314,10 +333,11 @@ func NewSyncer(db ethdb.KeyValueStore, chain BlockChain, sstorageInfo map[common rates: msgrate.NewTrackers(log.New("proto", "sstorage")), update: make(chan struct{}, 1), - kvRangeIdlers: make(map[string]struct{}), - kvHealIdlers: make(map[string]struct{}), - kvRangeReqs: make(map[uint64]*kvRangeRequest), - kvHealReqs: make(map[uint64]*kvHealRequest), + kvRangeIdlers: make(map[string]struct{}), + kvHealIdlers: make(map[string]struct{}), + runningEmptyTaskTreads: 0, + kvRangeReqs: make(map[uint64]*kvRangeRequest), + kvHealReqs: make(map[uint64]*kvHealRequest), } } @@ -430,6 +450,8 @@ func (s *Syncer) Sync(cancel chan struct{}) error { // Assign all the Data retrieval tasks to any free peers s.assignKVHealTasks(kvHealResps, kvHealReqFails, cancel) + s.assignKVEmptyTasks() + // Wait for something to happen select { case <-time.After(requestTimeoutInMillisecond): @@ -478,6 +500,10 @@ func (s *Syncer) loadSyncStatus() { kvSubTask.kvTask = task kvSubTask.next = kvSubTask.First } + for _, kvSubEmptyTask := range task.KvSubEmptyTasks { + kvSubEmptyTask.kvTask = task + kvSubEmptyTask.next = kvSubEmptyTask.First + } } s.kvSynced, s.kvBytes = progress.KVSynced, progress.KVBytes } @@ -492,19 +518,6 @@ func (s *Syncer) loadSyncStatus() { lastKvIndex = 0 } for _, sid := range shards { - // if the shard is not full, fill in the data shard with []byte{}, - if lastKvIndex < sm.KvEntries()*(sid+1)-1 { - go func() { - lastIdx, err := s.chain.GetSstorageLastKvIdx(contract) - if err != nil { - log.Info("loadSyncStatus failed when update: get lastKvIdx") - lastIdx = 0 - } - for i := lastIdx; i < sm.KvEntries()*(sid+1)-1; i++ { - sm.TryWrite(i, empty, common.Hash{}) - } - }() - } exist := false for _, task := range progress.Tasks { if task.Contract == contract && task.ShardId == sid { @@ -516,13 +529,7 @@ func (s *Syncer) loadSyncStatus() { if exist { continue } - first, limit := sm.KvEntries()*sid, sm.KvEntries()*(sid+1)-1 - if lastKvIndex > 0 && first >= lastKvIndex { - continue - } - if lastKvIndex > 0 && limit >= lastKvIndex { - limit = lastKvIndex - 1 - } + task := kvTask{ Contract: contract, ShardId: sid, @@ -534,6 +541,18 @@ func (s *Syncer) loadSyncStatus() { kvTask: &task, Indexes: make(map[uint64]int64), } + + first, limit := sm.KvEntries()*sid, sm.KvEntries()*(sid+1)-1 + firstEmpty, limitForEmpty := uint64(0), uint64(0) + if lastKvIndex > 0 && first >= lastKvIndex { + firstEmpty, limitForEmpty = first, limit + limit = first - 1 + } + if lastKvIndex > 0 && limit >= lastKvIndex { + firstEmpty, limitForEmpty = lastKvIndex, limit + limit = lastKvIndex - 1 + } + subTasks := make([]*kvSubTask, 0) // split task for a shard to 16 subtasks and if one batch is too small // set to minSubTaskSize @@ -542,7 +561,7 @@ func (s *Syncer) loadSyncStatus() { maxTaskSize = minSubTaskSize } - for first < limit { + for first <= limit { last := first + maxTaskSize if last > limit { last = limit @@ -559,14 +578,39 @@ func (s *Syncer) loadSyncStatus() { first = last + 1 } - task.HealTask, task.KvSubTasks = &healTask, subTasks + subEmptyTasks := make([]*kvSubEmptyTask, 0) + if limitForEmpty > 0 { + maxEmptyTaskSize := (limitForEmpty - firstEmpty + maxConcurrency) / maxConcurrency + if maxEmptyTaskSize < minSubTaskSize { + maxEmptyTaskSize = minSubTaskSize + } + + for firstEmpty <= limitForEmpty { + last := firstEmpty + maxEmptyTaskSize + if last > limitForEmpty { + last = limitForEmpty + } + subTask := kvSubEmptyTask{ + kvTask: &task, + next: firstEmpty, + First: firstEmpty, + Last: last, + done: false, + } + + subEmptyTasks = append(subEmptyTasks, &subTask) + firstEmpty = last + 1 + } + } + + task.HealTask, task.KvSubTasks, task.KvSubEmptyTasks = &healTask, subTasks, subEmptyTasks s.tasks = append(s.tasks, &task) } } allDone := true for _, task := range s.tasks { - if len(task.KvSubTasks) > 0 || len(task.HealTask.Indexes) > 0 { + if len(task.KvSubTasks) > 0 || len(task.HealTask.Indexes) > 0 || len(task.KvSubEmptyTasks) > 0 { allDone = false break } @@ -618,7 +662,13 @@ func (s *Syncer) cleanKVTasks() { i-- } } - if len(task.KvSubTasks) > 0 { + for i := 0; i < len(task.KvSubEmptyTasks); i++ { + if task.KvSubEmptyTasks[i].done { + task.KvSubEmptyTasks = append(task.KvSubEmptyTasks[:i], task.KvSubEmptyTasks[i+1:]...) + i-- + } + } + if len(task.KvSubTasks) > 0 || len(task.KvSubEmptyTasks) > 0 { allDone = false } } @@ -840,6 +890,44 @@ func (s *Syncer) assignKVHealTasks(success chan *kvHealResponse, fail chan *kvHe } } +// assignKVEmptyTasks attempts to match idle peers to heal kv requests to retrieval missing kv from the kv range request. +func (s *Syncer) assignKVEmptyTasks() { + s.lock.Lock() + defer s.lock.Unlock() + + if s.runningEmptyTaskTreads >= maxConcurrency { + return + } + s.runningEmptyTaskTreads++ + + // Iterate over all the tasks and try to find a pending one + for _, task := range s.tasks { + for _, subEmptyTask := range task.KvSubEmptyTasks { + if subEmptyTask.isRunning { + continue + } + subTask := subEmptyTask + subTask.isRunning = true + start, limit := subTask.next, subTask.Last + if limit >= start+minSubTaskSize { + limit = start + minSubTaskSize + } + go func(eTask *kvSubEmptyTask, contract common.Address, start, limit uint64) { + next, err := s.chain.FillSstorWithEmptyKV(contract, start, limit) + if err != nil { + log.Warn("fill in empty fail", "err", err.Error()) + } + eTask.next = next + if eTask.next > eTask.Last { + eTask.done = true + } + eTask.isRunning = false + s.runningEmptyTaskTreads-- + }(subTask, task.Contract, start, limit) + } + } +} + // revertRequests locates all the currently pending reuqests from a particular // peer and reverts them, rescheduling for others to fulfill. func (s *Syncer) revertRequests(peer string) { diff --git a/eth/protocols/sstorage/sync_test.go b/eth/protocols/sstorage/sync_test.go index 5b9bc3057fe1..fdfb1e5ee6dd 100644 --- a/eth/protocols/sstorage/sync_test.go +++ b/eth/protocols/sstorage/sync_test.go @@ -63,6 +63,31 @@ func (c *blockChain) CurrentBlock() *types.Block { return c.block } +func (bc *blockChain) FillSstorWithEmptyKV(contract common.Address, start, limit uint64) (uint64, error) { + sm := sstorage.ContractToShardManager[contract] + if sm == nil { + return start, fmt.Errorf("kv verify fail: contract not support, contract: %s", contract.Hex()) + } + + empty := make([]byte, 0) + lastKvIdx, err := bc.GetSstorageLastKvIdx(contract) + if err != nil { + return start, fmt.Errorf("get lastKvIdx for FillEmptyKV fail, err: %s", err.Error()) + } + for idx := start; idx <= limit; idx++ { + if lastKvIdx > idx { + continue + } + _, err = sm.TryWrite(idx, empty, common.Hash{}) + if err != nil { + err = fmt.Errorf("write empty to kv file fail, index: %d; error: %s", idx, err.Error()) + return idx, err + } + } + + return limit + 1, nil +} + func (c *blockChain) VerifyAndWriteKV(contract common.Address, data map[uint64][]byte, providerAddr common.Address) (uint64, uint64, []uint64, error) { var ( synced uint64 @@ -478,8 +503,7 @@ func verifyKVs(stateDB *state.StateDB, data map[common.Address]map[uint64][]byte if _, ok := destroyedList[idx]; ok { val = make([]byte, sstorage.CHUNK_SIZE) - _, defaultMeta := core.GetDefaultMetadata(idx) - sval, ok, err = shardData.TryReadChunk(idx, common.BytesToHash(defaultMeta.HashInMeta)) + sval, ok, err = shardData.TryReadChunk(idx, common.BytesToHash(make([]byte, 24))) if err != nil { t.Fatalf("TryReadChunk fail. err: %s", err.Error()) } @@ -699,8 +723,8 @@ func TestSync(t *testing.T) { return source } - data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntriesBits) - syncer := setupSyncer(shards, stateDB, kvEntriesBits, mkSource("source", shards, data)) + data, _ := makeKVStorage(stateDB, contract, []uint64{0}, kvEntries) + syncer := setupSyncer(shards, stateDB, kvEntries, mkSource("source", shards, data)) done := checkStall(t, term) if err := syncer.Sync(cancel); err != nil { t.Fatalf("sync failed: %v", err) diff --git a/sstorage/data_shard.go b/sstorage/data_shard.go index bfdaf6a19de9..568e5de0bb03 100644 --- a/sstorage/data_shard.go +++ b/sstorage/data_shard.go @@ -99,21 +99,14 @@ func (ds *DataShard) GetStorageFile(chunkIdx uint64) *DataFile { return nil } -// Read the encoded data from storage and return it. -func (ds *DataShard) ReadEncoded(kvIdx uint64, readLen int) ([]byte, error) { - return ds.readWith(kvIdx, readLen, func(cdata []byte, chunkIdx uint64) []byte { - return cdata - }) -} - -// Read the encoded data from storage and return it. +// ReadChunkEncoded read the encoded data from storage and return it. func (ds *DataShard) ReadChunkEncoded(kvIdx uint64, chunkIdx uint64) ([]byte, error) { return ds.readChunkWith(kvIdx, chunkIdx, func(cdata []byte, chunkIdx uint64) []byte { return cdata }) } -// Read the encoded data from storage and decode it. +// ReadChunk read the encoded data from storage and decode it. func (ds *DataShard) ReadChunk(kvIdx uint64, chunkIdx uint64, commit common.Hash) ([]byte, error) { return ds.readChunkWith(kvIdx, chunkIdx, func(cdata []byte, chunkIdx uint64) []byte { encodeKey := calcEncodeKey(commit, chunkIdx, ds.dataFiles[0].miner) @@ -121,7 +114,7 @@ func (ds *DataShard) ReadChunk(kvIdx uint64, chunkIdx uint64, commit common.Hash }) } -// Read the encoded data from storage with a decoder. +// readChunkWith read the encoded chunk from storage with a decoder. func (ds *DataShard) readChunkWith(kvIdx uint64, chunkIdx uint64, decoder func([]byte, uint64) []byte) ([]byte, error) { if !ds.Contains(kvIdx) { return nil, fmt.Errorf("kv not found") @@ -138,6 +131,13 @@ func (ds *DataShard) readChunkWith(kvIdx uint64, chunkIdx uint64, decoder func([ return data, nil } +// ReadEncoded read the encoded data from storage and return it. +func (ds *DataShard) ReadEncoded(kvIdx uint64, readLen int) ([]byte, error) { + return ds.readWith(kvIdx, readLen, func(cdata []byte, chunkIdx uint64) []byte { + return cdata + }) +} + // Read the encoded data from storage and decode it. func (ds *DataShard) Read(kvIdx uint64, readLen int, commit common.Hash) ([]byte, error) { return ds.readWith(kvIdx, readLen, func(cdata []byte, chunkIdx uint64) []byte { @@ -146,7 +146,7 @@ func (ds *DataShard) Read(kvIdx uint64, readLen int, commit common.Hash) ([]byte }) } -// Read the encoded data from storage with a decoder. +// readWith read the encoded data from storage with a decoder. func (ds *DataShard) readWith(kvIdx uint64, readLen int, decoder func([]byte, uint64) []byte) ([]byte, error) { if !ds.Contains(kvIdx) { return nil, fmt.Errorf("kv not found") @@ -262,7 +262,7 @@ func (ds *DataShard) Write(kvIdx uint64, b []byte, commit common.Hash) error { err := ds.writeChunk(chunkIdx, encodedChunk) if err != nil { - return nil + return err } } return nil diff --git a/sstorminer/merklelib.go b/sstorminer/merklelib.go index 6930c04a37e1..07771d5eb7e4 100644 --- a/sstorminer/merklelib.go +++ b/sstorminer/merklelib.go @@ -95,7 +95,7 @@ func verify(root []byte, dataHash common.Hash, chunkIdx uint64, proofs []common. return bytes.Compare(root[:24], r.Bytes()[:24]) == 0 } -func merkleRootWithMinTree(data []byte, chunkPerKV uint64, chunkSize uint64) common.Hash { +func merkleRoot(data []byte, chunkPerKV uint64, chunkSize uint64) common.Hash { l := uint64(len(data)) if l == 0 { return common.Hash{} @@ -124,3 +124,54 @@ func merkleRootWithMinTree(data []byte, chunkPerKV uint64, chunkSize uint64) com } return nodes[0] } + +func findNChunk(dataLen, chunkSize uint64) (uint64, uint64) { + if dataLen == 0 { + return 0, 0 + } + n := (dataLen+chunkSize-1)/chunkSize - 1 + nChunkBits := uint64(0) + for n != 0 { + nChunkBits++ + n = n >> 1 + } + + return uint64(1) << nChunkBits, nChunkBits +} + +func getProofWithMinTree(data []byte, chunkSize, nChunkBits, chunkIdx uint64) ([]common.Hash, error) { + if len(data) == 0 { + return []common.Hash{}, nil + } + nChunks := uint64(1) << nChunkBits + if chunkIdx >= nChunks { + return []common.Hash{}, fmt.Errorf("index out of scope") + } + nMinChunks, nMinChunkBits := findNChunk(uint64(len(data)), chunkSize) + if chunkIdx >= nMinChunks { + return []common.Hash{}, nil + } + return getProof(data, chunkSize, nMinChunkBits, chunkIdx) +} + +func verifyWithMinTree(root []byte, dataHash common.Hash, chunkIdx uint64, proofs []common.Hash) bool { + nMinChunkBits := uint64(len(proofs)) + if chunkIdx >= uint64(1)< lastKvIdx { + lastKvIdx = last + } } + stateDB.SetState(contract, uint256.NewInt(0).Bytes32(), uint256.NewInt(lastKvIdx).Bytes32()) } func updateMiningInfoAndInsertNewBlock(pinfo *core.MiningInfo, chain *wrapBlockChain, engine *ethash.Ethash, db ethdb.Database) error { @@ -355,26 +352,33 @@ func updateMiningInfoAndInsertNewBlock(pinfo *core.MiningInfo, chain *wrapBlockC } func verifyTaskResult(stateDB *state.StateDB, chain BlockChain, r *result) error { + val := stateDB.GetState(contract, uint256.NewInt(0).Bytes32()) + lastKvIdx := new(big.Int).SetBytes(val.Bytes()).Uint64() for i, proofs := range r.proofs { - _, meta, _ := core.GetSstorageMetadata(stateDB, contract, r.kvIdxs[i]) hash := common.Hash{} - off := sstorage.CHUNK_SIZE * r.chunkIdxs[i] - if meta.KVSize > off { - data, got, err := r.task.shardManager.TryReadChunk(r.kvIdxs[i]*r.task.shardManager.ChunksPerKv()+r.chunkIdxs[i], common.BytesToHash(meta.HashInMeta)) - if err != nil { - return err - } - if !got { - return fmt.Errorf("fail to get data for storageContract %s vkidx %d", contract.Hex(), r.kvIdxs[i]) + root := make([]byte, 24) + if lastKvIdx > r.kvIdxs[i] { + off := sstorage.CHUNK_SIZE * r.chunkIdxs[i] + _, meta, _ := core.GetSstorageMetadata(stateDB, contract, r.kvIdxs[i]) + + if meta.KVSize > off { + data, got, err := r.task.shardManager.TryReadChunk(r.kvIdxs[i]*r.task.shardManager.ChunksPerKv()+r.chunkIdxs[i], common.BytesToHash(meta.HashInMeta)) + if err != nil { + return err + } + if !got { + return fmt.Errorf("fail to get data for storageContract %s vkidx %d", contract.Hex(), r.kvIdxs[i]) + } + if meta.KVSize < off+sstorage.CHUNK_SIZE { + data = data[:meta.KVSize-off] + } + hash = crypto.Keccak256Hash(data) } - if meta.KVSize < off+sstorage.CHUNK_SIZE { - data = data[:meta.KVSize-off] - } - hash = crypto.Keccak256Hash(data) + root = meta.HashInMeta } - vr := verify(meta.HashInMeta, hash, r.chunkIdxs[i], proofs) + vr := verifyWithMinTree(root, hash, r.chunkIdxs[i], proofs) if !vr { return fmt.Errorf("verify proofs fail for index %d fail", r.kvIdxs[i]) } @@ -687,6 +691,14 @@ func TestWork_ProofsCreateAndVerify(test *testing.T) { } testCases := []testCase{ + { + chunkPerKVBits: uint64(0), + val: contract.Bytes(), + }, + { + chunkPerKVBits: uint64(0), + val: make([]byte, 0), + }, { chunkPerKVBits: uint64(1), val: contract.Bytes(), @@ -715,18 +727,29 @@ func TestWork_ProofsCreateAndVerify(test *testing.T) { chunkPerKVBits: uint64(4), val: append(make([]byte, sstorage.CHUNK_SIZE*3), contract.Bytes()...), }, + { + chunkPerKVBits: uint64(7), + val: contract.Bytes(), + }, + { + chunkPerKVBits: uint64(7), + val: append(make([]byte, sstorage.CHUNK_SIZE), contract.Bytes()...), + }, } for _, tc := range testCases { testWork_ProofsCreateAndVerify(test, tc.val, tc.chunkPerKVBits) } + for _, tc := range testCases { + testWork_ProofsCreateAndVerifyWithMinTree(test, tc.val, tc.chunkPerKVBits) + } } func testWork_ProofsCreateAndVerify(test *testing.T, val []byte, chunkPerKVBits uint64) { chunkPerKV := uint64(1) << chunkPerKVBits - datalen := uint64(len(val)) + dataLen := uint64(len(val)) - root := merkleRootWithMinTree(val, chunkPerKV, sstorage.CHUNK_SIZE) + root := merkleRoot(val, chunkPerKV, sstorage.CHUNK_SIZE) for i := uint64(0); i < chunkPerKV; i++ { hash := common.Hash{} off := i * sstorage.CHUNK_SIZE @@ -735,8 +758,8 @@ func testWork_ProofsCreateAndVerify(test *testing.T, val []byte, chunkPerKVBits if err != nil { test.Error("error:", err.Error()) } - if off < datalen { - size := datalen - off + if off < dataLen { + size := dataLen - off if size > sstorage.CHUNK_SIZE { size = sstorage.CHUNK_SIZE } @@ -748,3 +771,30 @@ func testWork_ProofsCreateAndVerify(test *testing.T, val []byte, chunkPerKVBits } } } + +func testWork_ProofsCreateAndVerifyWithMinTree(test *testing.T, val []byte, chunkPerKVBits uint64) { + chunkPerKV := uint64(1) << chunkPerKVBits + dataLen := uint64(len(val)) + + root := merkleRootWithMinTree(val, sstorage.CHUNK_SIZE) + for i := uint64(0); i < chunkPerKV; i++ { + hash := common.Hash{} + off := i * sstorage.CHUNK_SIZE + + ps, err := getProofWithMinTree(val, sstorage.CHUNK_SIZE, chunkPerKVBits, i) + if err != nil { + test.Error("error:", err.Error()) + } + if off < dataLen { + size := dataLen - off + if size > sstorage.CHUNK_SIZE { + size = sstorage.CHUNK_SIZE + } + hash = crypto.Keccak256Hash(val[off : off+size]) + } + vr := verifyWithMinTree(root.Bytes()[:24], hash, i, ps) + if !vr { + test.Error("verify proof fail, chunk ", i) + } + } +} From d1010edf2c5f501c87fab2c2436e23c474db646c Mon Sep 17 00:00:00 2001 From: pingke Date: Fri, 31 Mar 2023 00:37:32 +0800 Subject: [PATCH 10/23] resolve comment: using keystore instead of using nodekey file --- cmd/geth/main.go | 2 +- cmd/utils/flags.go | 10 +++++----- eth/backend.go | 17 +++++++++++------ eth/ethconfig/config.go | 2 +- sstorminer/miner.go | 5 ++--- sstorminer/worker.go | 23 ++++++++++++++--------- sstorminer/worker_test.go | 9 ++++++++- 7 files changed, 42 insertions(+), 26 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 5e56de1452be..1e2416046d49 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -134,7 +134,7 @@ var ( utils.SstorageShardFlag, utils.SstorageFileFlag, utils.SstorageMineFlag, - utils.SstorageNodeKeyFlag, + utils.SstorageTXSignerFlag, utils.SstorageMinerContractFlag, utils.NATFlag, utils.NoDiscoverFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 35edeb0090bb..6efaea755547 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -570,9 +570,9 @@ var ( Name: "sstorage.mine", Usage: "Enable sstorage mining", } - SstorageNodeKeyFlag = cli.StringFlag{ - Name: "sstorage.nodekey", - Usage: "Sstorage miner node key file", + SstorageTXSignerFlag = cli.StringFlag{ + Name: "sstorage.txsigner", + Usage: "Account used to sign tx submit to sstorage miner contract", } SstorageMinerContractFlag = cli.StringFlag{ Name: "sstorage.minercontract", @@ -1140,8 +1140,8 @@ func setSstorage(ctx *cli.Context, cfg *ethconfig.Config) { if ctx.GlobalIsSet(SstorageMineFlag.Name) { cfg.SstorageMine = ctx.GlobalBool(SstorageMineFlag.Name) } - if ctx.GlobalIsSet(SstorageNodeKeyFlag.Name) { - cfg.SstorageNodeKey = ctx.GlobalString(SstorageNodeKeyFlag.Name) + if ctx.GlobalIsSet(SstorageTXSignerFlag.Name) { + cfg.SstorageTXSigner = ctx.GlobalString(SstorageTXSignerFlag.Name) } if ctx.GlobalIsSet(SstorageMinerContractFlag.Name) { cfg.SstorageMinerContract = ctx.GlobalString(SstorageMinerContractFlag.Name) diff --git a/eth/backend.go b/eth/backend.go index 04371ef51119..e1d1b3c72c10 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -39,7 +39,6 @@ import ( "github.com/ethereum/go-ethereum/core/state/pruner" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/filters" @@ -277,10 +276,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) if config.SstorageMine { - privKey, err := crypto.LoadECDSA(config.SstorageNodeKey) - if err != nil { - return nil, err - } if len(sstorage.Shards()) == 0 { return nil, fmt.Errorf("no shards is exist") } @@ -288,7 +283,17 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { return nil, fmt.Errorf("miner contract is needed when the sstorage mine is enabled.") } minerContract := common.HexToAddress(config.SstorageMinerContract) - eth.sstorMiner = sstorminer.New(eth, &config.SStorMiner, chainConfig, eth.EventMux(), privKey, minerContract) + if config.SstorageTXSigner == "" { + return nil, fmt.Errorf("TX signer is needed when the sstorage mine is enabled.") + } + signer := accounts.Account{Address: common.HexToAddress(config.SstorageTXSigner)} + wallet, err := eth.accountManager.Find(signer) + if wallet == nil || err != nil { + log.Error("sstorage tx signer account unavailable locally", "err", err) + return nil, fmt.Errorf("signer missing: %v", err) + } + + eth.sstorMiner = sstorminer.New(eth, &config.SStorMiner, chainConfig, eth.EventMux(), &sstorminer.TXSigner{signer, wallet.SignTx}, minerContract) } eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index ec4278f923fa..79c5e71eac5c 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -235,7 +235,7 @@ type Config struct { SstorageFiles []string `toml:",omitempty"` SstorageShards []string `toml:",omitempty"` SstorageMine bool - SstorageNodeKey string + SstorageTXSigner string SstorageMinerContract string } diff --git a/sstorminer/miner.go b/sstorminer/miner.go index 6555fe92f244..976c4d346bb4 100644 --- a/sstorminer/miner.go +++ b/sstorminer/miner.go @@ -18,7 +18,6 @@ package sstorminer import ( - "crypto/ecdsa" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/downloader" @@ -59,13 +58,13 @@ type Miner struct { wg sync.WaitGroup } -func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, privKey *ecdsa.PrivateKey, minerContract common.Address) *Miner { +func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, txSigner *TXSigner, minerContract common.Address) *Miner { miner := &Miner{ mux: mux, exitCh: make(chan struct{}), startCh: make(chan struct{}), stopCh: make(chan struct{}), - worker: newWorker(config, chainConfig, eth, eth.BlockChain(), mux, privKey, minerContract, true), + worker: newWorker(config, chainConfig, eth, eth.BlockChain(), mux, txSigner, minerContract, true), } miner.wg.Add(1) go miner.update() diff --git a/sstorminer/worker.go b/sstorminer/worker.go index 864e3221d586..401bd643d759 100644 --- a/sstorminer/worker.go +++ b/sstorminer/worker.go @@ -17,9 +17,7 @@ package sstorminer import ( - "crypto/ecdsa" "fmt" - "github.com/ethereum/go-ethereum/core/state" "math/big" "math/rand" "sort" @@ -28,10 +26,12 @@ import ( "sync/atomic" "time" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" @@ -87,6 +87,13 @@ type BlockChain interface { State() (*state.StateDB, error) } +type SignTxFn func(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) + +type TXSigner struct { + Account accounts.Account // Ethereum address of the signing key + SignFn SignTxFn // Signer function to sign tx +} + // task contains all information for consensus engine sealing and result submitting. type task struct { storageContract common.Address @@ -249,8 +256,7 @@ type worker struct { chainHeadCh chan core.ChainHeadEvent chainHeadSub event.Subscription client *ethclient.Client - signer types.Signer - privKey *ecdsa.PrivateKey + signer *TXSigner // Channels newWorkCh chan *newWorkReq @@ -272,7 +278,7 @@ type worker struct { newResultHook func(*result) } -func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, chain BlockChain, mux *event.TypeMux, privKey *ecdsa.PrivateKey, +func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, chain BlockChain, mux *event.TypeMux, txSigner *TXSigner, minerContract common.Address, init bool) *worker { worker := &worker{ config: config, @@ -283,15 +289,14 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, cha tasks: make([]*task, 0), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), newWorkCh: make(chan *newWorkReq), - taskCh: make(chan *task, len(sstor.Shards())), + taskCh: make(chan *task), resultCh: make(chan *result, resultQueueSize), exitCh: make(chan struct{}), startCh: make(chan struct{}, 1), resubmitIntervalCh: make(chan time.Duration), taskDoneCh: make(chan struct{}), taskStartCh: make(chan struct{}), - signer: types.NewEIP2930Signer(chainConfig.ChainID), - privKey: privKey, + signer: txSigner, } for addr, sm := range sstor.ContractToShardManager { for idx, shard := range sm.ShardMap() { @@ -711,7 +716,7 @@ func (w *worker) submitMinedResult(result *result) error { Data: data, } - signedTx, err := types.SignTx(types.NewTx(baseTx), w.signer, w.privKey) + signedTx, err := w.signer.SignFn(w.signer.Account, types.NewTx(baseTx), w.chainConfig.ChainID) if err != nil { return err } diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go index dbafbae9caa5..3d619f9e4dfd 100644 --- a/sstorminer/worker_test.go +++ b/sstorminer/worker_test.go @@ -26,6 +26,7 @@ import ( "testing" "time" + "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" @@ -224,7 +225,13 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens infos := wchain.initMiningInfos(shardIdxList, diff, blockMined) makeKVStorage(stateDB, contract, shardIdxList, 1< Date: Fri, 31 Mar 2023 17:22:31 +0800 Subject: [PATCH 11/23] resolve build --- eth/protocols/sstorage/sync.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/eth/protocols/sstorage/sync.go b/eth/protocols/sstorage/sync.go index 995d3505f483..5c6499fa77d6 100644 --- a/eth/protocols/sstorage/sync.go +++ b/eth/protocols/sstorage/sync.go @@ -268,12 +268,7 @@ type BlockChain interface { // ReadEncodedKVsByIndexRange Read masked KVs sequentially starting from origin until the index exceeds the limit or // the amount of data read is greater than the bytes. -<<<<<<< HEAD ReadEncodedKVsByIndexRange(contract common.Address, shardId uint64, origin uint64, limit uint64, bytes uint64) (common.Address, []*core.KV, error) -======= - ReadEncodedKVsByIndexRange(contract common.Address, shardId uint64, origin uint64, - limit uint64, bytes uint64) (common.Address, []*core.KV, error) ->>>>>>> 7db82a8f2cb14d1cfade453b6d07d00ce9d9aaba // GetSstorageLastKvIdx get LastKvIdx from a sstorage contract with latest stateDB. GetSstorageLastKvIdx(contract common.Address) (uint64, error) From 80c2bda2fa100eabf6a7f483a39a7bb8d51386eb Mon Sep 17 00:00:00 2001 From: pingke Date: Mon, 3 Apr 2023 19:06:09 +0800 Subject: [PATCH 12/23] resolve --- sstorage/shard_config.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sstorage/shard_config.go b/sstorage/shard_config.go index 69480d0c5a2f..5a9d7e2de6eb 100644 --- a/sstorage/shard_config.go +++ b/sstorage/shard_config.go @@ -12,19 +12,19 @@ import ( var ContractToShardManager = make(map[common.Address]*ShardManager) type ShardInfo struct { - Contract common.Address - KVSize uint64 - KVEntries uint64 + Contract common.Address + KVSizeBits uint64 + KVEntrieBits uint64 } // TODO: move to chain specific config? var ShardInfos = []*ShardInfo{ - {common.HexToAddress("0x0000000000000000000000000000000003330001"), 4 * 1024, 256 * 1024}, + {common.HexToAddress("0x0000000000000000000000000000000003330001"), 12, 18}, } func InitializeConfig() { for _, sinfo := range ShardInfos { - ContractToShardManager[sinfo.Contract] = NewShardManager(sinfo.Contract, sinfo.KVSize, sinfo.KVEntries) + ContractToShardManager[sinfo.Contract] = NewShardManager(sinfo.Contract, sinfo.KVSizeBits, sinfo.KVEntrieBits) } } From 78f4e971334ab6329bdbf83559645b0bd75d2d72 Mon Sep 17 00:00:00 2001 From: pingke Date: Wed, 5 Apr 2023 08:54:08 +0800 Subject: [PATCH 13/23] update kv size to 128k --- sstorage/shard_config.go | 2 +- sstorminer/worker_test.go | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sstorage/shard_config.go b/sstorage/shard_config.go index 5a9d7e2de6eb..825196f50723 100644 --- a/sstorage/shard_config.go +++ b/sstorage/shard_config.go @@ -19,7 +19,7 @@ type ShardInfo struct { // TODO: move to chain specific config? var ShardInfos = []*ShardInfo{ - {common.HexToAddress("0x0000000000000000000000000000000003330001"), 12, 18}, + {common.HexToAddress("0x0000000000000000000000000000000003330001"), 17, 18}, } func InitializeConfig() { diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go index 3d619f9e4dfd..5d4930a4c17c 100644 --- a/sstorminer/worker_test.go +++ b/sstorminer/worker_test.go @@ -406,7 +406,7 @@ func TestWork_SingleShard(test *testing.T) { engine = ethash.NewFaker() ) - w, infos, files, _ := newTestWorker(test, ethashChainConfig, engine, rawdb.NewMemoryDatabase(), shardIdxList, 0, true) + w, infos, files, _ := newTestWorker(test, ethashChainConfig, engine, rawdb.NewMemoryDatabase(), shardIdxList, 5, true) defer w.close() defer engine.Close() @@ -460,7 +460,7 @@ func TestWork_MultiShards(test *testing.T) { engine = ethash.NewFaker() ) - w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, rawdb.NewMemoryDatabase(), shardIdxList, 0, true) + w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, rawdb.NewMemoryDatabase(), shardIdxList, 5, true) defer w.close() defer engine.Close() @@ -507,7 +507,7 @@ func TestWork_TriggerByNewBlock(test *testing.T) { db = rawdb.NewMemoryDatabase() ) - w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, db, shardIdxList, 0, true) + w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, db, shardIdxList, 5, true) defer w.close() defer engine.Close() @@ -558,7 +558,7 @@ func TestWork_ShardIsNotFull(test *testing.T) { db = rawdb.NewMemoryDatabase() ) - w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, db, shardIdxList, 0, false) + w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, db, shardIdxList, 5, false) defer w.close() defer engine.Close() @@ -594,7 +594,7 @@ func TestWork_StartAndStopTask(test *testing.T) { engine = ethash.NewFaker() ) - w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, rawdb.NewMemoryDatabase(), shardIdxList, 0, true) + w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, rawdb.NewMemoryDatabase(), shardIdxList, 5, true) defer w.close() defer engine.Close() @@ -649,14 +649,14 @@ func TestWork_StartAndStopTask(test *testing.T) { } } -func TestWork_LargeKV(test *testing.T) { +func TestWork_SmallKV(test *testing.T) { var ( shardIdxList = []uint64{0} engine = ethash.NewFaker() db = rawdb.NewMemoryDatabase() ) - w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, db, shardIdxList, 2, true) + w, _, files, _ := newTestWorker(test, ethashChainConfig, engine, db, shardIdxList, 0, true) defer w.close() defer engine.Close() From 6d3f9a7a99695af9c5ba11176fe356d0c78be41e Mon Sep 17 00:00:00 2001 From: pingke Date: Mon, 10 Apr 2023 18:26:10 +0800 Subject: [PATCH 14/23] resolve comments and fix bugs --- consensus/tendermint/gov/gov.go | 8 +-- core/genesis.go | 2 +- eth/backend.go | 1 + eth/downloader/downloader.go | 2 +- eth/protocols/sstorage/sync.go | 28 +++++++--- eth/protocols/sstorage/sync_test.go | 3 +- params/config.go | 2 +- sstorage/shard_config.go | 16 ++---- sstorminer/miner.go | 32 ++++-------- sstorminer/miner_test.go | 80 +++++------------------------ sstorminer/worker.go | 38 +++++++++----- 11 files changed, 81 insertions(+), 131 deletions(-) diff --git a/consensus/tendermint/gov/gov.go b/consensus/tendermint/gov/gov.go index bf796d8f6bf6..5017b8d21e09 100644 --- a/consensus/tendermint/gov/gov.go +++ b/consensus/tendermint/gov/gov.go @@ -101,7 +101,7 @@ func (g *Governance) NextValidatorsAndPowersForProposal() ([]common.Address, []u return nil, nil, 0, common.Hash{}, err } - validators, powers, err := g.getValidatorsAndPowersFromContract(header.Hash()) + validators, powers, err := g.getValidatorsAndPowersFromContract(header.Number()) if err != nil { return nil, nil, 0, common.Hash{}, err } @@ -131,7 +131,7 @@ func (g *Governance) NextValidatorsAndPowersAt(remoteChainNumber uint64, hash co fmt.Errorf("block hash mismatch", "remoteChainNumber hash", header.Hash(), "hash", hash) } - validators, powers, err := g.getValidatorsAndPowersFromContract(hash) + validators, powers, err := g.getValidatorsAndPowersFromContract(header.Number()) if err != nil { return nil, nil, err } @@ -141,7 +141,7 @@ func (g *Governance) NextValidatorsAndPowersAt(remoteChainNumber uint64, hash co } // getValidatorsAndPowersFromContract get next validators from contract -func (g *Governance) getValidatorsAndPowersFromContract(blockHash common.Hash) ([]common.Address, []uint64, error) { +func (g *Governance) getValidatorsAndPowersFromContract(blockNumber *big.Int) ([]common.Address, []uint64, error) { data, err := g.validatorSetABI.Pack(contractFunc_GetValidator) if err != nil { return nil, nil, err @@ -154,7 +154,7 @@ func (g *Governance) getValidatorsAndPowersFromContract(blockHash common.Hash) ( Gas: gas, Data: msgData, } - result, err := g.client.CallContractAtHash(g.ctx, msg, blockHash) + result, err := g.client.CallContract(g.ctx, msg, blockNumber) if err != nil { return nil, nil, err } diff --git a/core/genesis.go b/core/genesis.go index 8b1893a1766a..69d48ec4e2a8 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -446,7 +446,7 @@ func DefaultWeb3QTestnetGenesisBlock() *Genesis { common.HexToAddress("0x977cfc676bb06daed7ddfa7711bcfe8d50c93081"), common.HexToAddress("0xcd21538af6e33ff6fcf1e2ca20f771413004cfd3"), }, - NextValidatorPowers: []uint64{1, 1, 1, 1}, + NextValidatorPowers: []uint64{11, 11, 11, 11}, } } diff --git a/eth/backend.go b/eth/backend.go index 926a412e7975..110e856dd94c 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -314,6 +314,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } eth.sstorMiner = sstorminer.New(eth, &config.SStorMiner, chainConfig, eth.EventMux(), &sstorminer.TXSigner{signer, wallet.SignTx}, minerContract) + eth.sstorMiner.Start() } eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index e9a18231bf61..6b33a1f16481 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -244,7 +244,7 @@ func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain Bl headerProcCh: make(chan *headerTask, 1), quitCh: make(chan struct{}), SnapSyncer: snap.NewSyncer(stateDb), - SstorSyncer: sstorage.NewSyncer(stateDb, chain, sstor.Shards()), + SstorSyncer: sstorage.NewSyncer(stateDb, chain, mux, sstor.Shards()), stateSyncStart: make(chan *stateSync), sstorSyncStart: make(chan *sstorSync), } diff --git a/eth/protocols/sstorage/sync.go b/eth/protocols/sstorage/sync.go index 5c6499fa77d6..044dc9b240cd 100644 --- a/eth/protocols/sstorage/sync.go +++ b/eth/protocols/sstorage/sync.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "math/rand" + "runtime" "sort" "sync" "time" @@ -52,6 +53,7 @@ const ( var ErrCancelled = errors.New("sync cancelled") var ( + maxEmptyTaskTreads int empty = make([]byte, 0) requestTimeoutInMillisecond = 1000 * time.Millisecond // Millisecond ) @@ -286,6 +288,7 @@ type BlockChain interface { type Syncer struct { db ethdb.KeyValueStore // Database to store the sync state chain BlockChain + mux *event.TypeMux // Event multiplexer to announce sync operation events tasks []*kvTask sstorageInfo map[common.Address][]uint64 // Map for Contract address to support shardIds @@ -319,10 +322,14 @@ type Syncer struct { } // NewSyncer creates a new sstorage syncer to download the sharded storage content over the sstorage protocol. -func NewSyncer(db ethdb.KeyValueStore, chain BlockChain, sstorageInfo map[common.Address][]uint64) *Syncer { +func NewSyncer(db ethdb.KeyValueStore, chain BlockChain, mux *event.TypeMux, sstorageInfo map[common.Address][]uint64) *Syncer { + maxEmptyTaskTreads = runtime.NumCPU() - 2 + if maxEmptyTaskTreads < 1 { + maxEmptyTaskTreads = 1 + } return &Syncer{ - db: db, - + db: db, + mux: mux, tasks: make([]*kvTask, 0), sstorageInfo: sstorageInfo, chain: chain, @@ -580,7 +587,7 @@ func (s *Syncer) loadSyncStatus() { subEmptyTasks := make([]*kvSubEmptyTask, 0) if limitForEmpty > 0 { - maxEmptyTaskSize := (limitForEmpty - firstEmpty + maxConcurrency) / maxConcurrency + maxEmptyTaskSize := (limitForEmpty - firstEmpty + uint64(maxEmptyTaskTreads)) / uint64(maxEmptyTaskTreads) if maxEmptyTaskSize < minSubTaskSize { maxEmptyTaskSize = minSubTaskSize } @@ -616,10 +623,17 @@ func (s *Syncer) loadSyncStatus() { } } if allDone { - s.syncDone = true + s.setSyncDone() } } +type SstorSyncDone struct{} + +func (s *Syncer) setSyncDone() { + s.syncDone = true + s.mux.Post(SstorSyncDone{}) +} + // saveSyncStatus marshals the remaining sync tasks into leveldb. func (s *Syncer) saveSyncStatus() { // Store the actual progress markers @@ -676,7 +690,7 @@ func (s *Syncer) cleanKVTasks() { // If everything was just finalized, generate the account trie and start heal if allDone { s.lock.Lock() - s.syncDone = true + s.setSyncDone() s.lock.Unlock() log.Info("Sstorage sync done", "task count", len(s.tasks)) @@ -895,7 +909,7 @@ func (s *Syncer) assignKVEmptyTasks() { s.lock.Lock() defer s.lock.Unlock() - if s.runningEmptyTaskTreads >= maxConcurrency { + if s.runningEmptyTaskTreads >= maxEmptyTaskTreads { return } s.runningEmptyTaskTreads++ diff --git a/eth/protocols/sstorage/sync_test.go b/eth/protocols/sstorage/sync_test.go index 7f789f70dc46..664b218e9cb0 100644 --- a/eth/protocols/sstorage/sync_test.go +++ b/eth/protocols/sstorage/sync_test.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/binary" "fmt" + "github.com/ethereum/go-ethereum/event" "math/rand" "os" "sync" @@ -342,7 +343,7 @@ func setupSyncer(shards map[common.Address][]uint64, stateDB *state.StateDB, las rlp.DecodeBytes(blockEnc, &block) chain := blockChain{block: &block, stateDB: stateDB} chain.lastKvIdx = lastKvIdx - syncer := NewSyncer(db, &chain, shards) + syncer := NewSyncer(db, &chain, new(event.TypeMux), shards) for _, peer := range peers { syncer.Register(peer) peer.remote = syncer diff --git a/params/config.go b/params/config.go index 73f88c9e3142..083470f6e9cb 100644 --- a/params/config.go +++ b/params/config.go @@ -34,7 +34,7 @@ var ( SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9") RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177") GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a") - Web3QTestnetGenesisHash = common.HexToHash("0xe1b551a47236ea806a1a9f6a9082ab989cffd999a44caa6015edc27136e0aab5") + Web3QTestnetGenesisHash = common.HexToHash("0x5b6c05509246d26abb013df13638125a0767e26b6cb28ca1fb19387999a537fa") Web3QGalileoGenesisHash = common.HexToHash("0xa576a985390f3a643e2acdeaed074cc9866c99f6bdf3ca8c49ec959054703745") ) diff --git a/sstorage/shard_config.go b/sstorage/shard_config.go index 825196f50723..ee3e9b9e4df1 100644 --- a/sstorage/shard_config.go +++ b/sstorage/shard_config.go @@ -80,26 +80,16 @@ func AddDataShardFromConfig(cfg string) error { } func AddDataFileFromConfig(cfg string) error { - // Format is kvSize,dataFile - ss := strings.Split(cfg, ",") - if len(ss) != 2 || len(ss[0]) == 0 || len(ss[1]) == 0 { - return fmt.Errorf("incorrect data shard cfg") - } - - kvSize, err := parseKvSize(ss[0]) + df, err := OpenDataFile(cfg) if err != nil { return err } - sm := findShardManaager(kvSize) + sm := findShardManaager(df.maxKvSize) if sm == nil { - return fmt.Errorf("shard with kv size %d not found", kvSize) + return fmt.Errorf("shard with kv size %d not found", df.maxKvSize) } - df, err := OpenDataFile(ss[1]) - if err != nil { - return err - } return sm.AddDataFile(df) } diff --git a/sstorminer/miner.go b/sstorminer/miner.go index 976c4d346bb4..5ba53b3d1c16 100644 --- a/sstorminer/miner.go +++ b/sstorminer/miner.go @@ -18,15 +18,15 @@ package sstorminer import ( + "math/big" + "sync" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/protocols/sstorage" "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "math/big" - "sync" - "time" ) // Backend wraps all methods required for mining. Only full node is capable @@ -78,8 +78,8 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even func (miner *Miner) update() { defer miner.wg.Done() - // todo Add sstorage evnet - events := miner.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) + // Subscribe sstorage SstorSyncDone evnet + events := miner.mux.Subscribe(sstorage.SstorSyncDone{}) defer func() { if !events.Closed() { events.Unsubscribe() @@ -87,7 +87,7 @@ func (miner *Miner) update() { }() shouldStart := false - canStart := true + canStart := false dlEventCh := events.Chan() for { select { @@ -98,21 +98,7 @@ func (miner *Miner) update() { continue } switch ev.Data.(type) { - case downloader.StartEvent: - wasMining := miner.Mining() - miner.worker.stop() - canStart = false - if wasMining { - // Resume mining after sync was finished - shouldStart = true - log.Info("Mining aborted due to sync") - } - case downloader.FailedEvent: - canStart = true - if shouldStart { - miner.worker.start() - } - case downloader.DoneEvent: + case sstorage.SstorSyncDone: canStart = true if shouldStart { miner.worker.start() diff --git a/sstorminer/miner_test.go b/sstorminer/miner_test.go index dca8128aff3c..166efb661296 100644 --- a/sstorminer/miner_test.go +++ b/sstorminer/miner_test.go @@ -28,7 +28,7 @@ import ( "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/eth/downloader" + "github.com/ethereum/go-ethereum/eth/protocols/sstorage" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/trie" @@ -81,74 +81,22 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) func TestMiner(t *testing.T) { miner, mux, cleanup := createMiner(t) defer cleanup(false) - miner.Start() - waitForMiningState(t, miner, true) - // Start the downloader - mux.Post(downloader.StartEvent{}) waitForMiningState(t, miner, false) - // Stop the downloader and wait for the update loop to run - mux.Post(downloader.DoneEvent{}) - waitForMiningState(t, miner, true) - - // Subsequent downloader events after a successful DoneEvent should not cause the - // miner to start or stop. This prevents a security vulnerability - // that would allow entities to present fake high blocks that would - // stop mining operations by causing a downloader sync - // until it was discovered they were invalid, whereon mining would resume. - mux.Post(downloader.StartEvent{}) - waitForMiningState(t, miner, true) - - mux.Post(downloader.FailedEvent{}) - waitForMiningState(t, miner, true) -} - -// TestMinerDownloaderFirstFails tests that mining is only -// permitted to run indefinitely once the downloader sees a DoneEvent (success). -// An initial FailedEvent should allow mining to stop on a subsequent -// downloader StartEvent. -func TestMinerDownloaderFirstFails(t *testing.T) { - miner, mux, cleanup := createMiner(t) - defer cleanup(false) + // start miner, but the miner.Start() - waitForMiningState(t, miner, true) - // Start the downloader - mux.Post(downloader.StartEvent{}) - waitForMiningState(t, miner, false) - - // Stop the downloader and wait for the update loop to run - mux.Post(downloader.FailedEvent{}) - waitForMiningState(t, miner, true) - - // Since the downloader hasn't yet emitted a successful DoneEvent, - // we expect the miner to stop on next StartEvent. - mux.Post(downloader.StartEvent{}) waitForMiningState(t, miner, false) - - // Downloader finally succeeds. - mux.Post(downloader.DoneEvent{}) - waitForMiningState(t, miner, true) - - // Downloader starts again. - // Since it has achieved a DoneEvent once, we expect miner - // state to be unchanged. - mux.Post(downloader.StartEvent{}) - waitForMiningState(t, miner, true) - - mux.Post(downloader.FailedEvent{}) + // Start the downloader + mux.Post(sstorage.SstorSyncDone{}) waitForMiningState(t, miner, true) } -func TestMinerStartStopAfterDownloaderEvents(t *testing.T) { +func TestMinerStartStopAfterSyncDoneEvents(t *testing.T) { miner, mux, cleanup := createMiner(t) defer cleanup(false) miner.Start() - waitForMiningState(t, miner, true) - // Start the downloader - mux.Post(downloader.StartEvent{}) waitForMiningState(t, miner, false) - - // Downloader finally succeeds. - mux.Post(downloader.DoneEvent{}) + // Start the downloader + mux.Post(sstorage.SstorSyncDone{}) waitForMiningState(t, miner, true) miner.Stop() @@ -161,24 +109,23 @@ func TestMinerStartStopAfterDownloaderEvents(t *testing.T) { waitForMiningState(t, miner, false) } -func TestStartWhileDownload(t *testing.T) { +func TestStartAfterDownload(t *testing.T) { miner, mux, cleanup := createMiner(t) defer cleanup(false) waitForMiningState(t, miner, false) - miner.Start() - waitForMiningState(t, miner, true) // Stop the downloader and wait for the update loop to run - mux.Post(downloader.StartEvent{}) + mux.Post(sstorage.SstorSyncDone{}) waitForMiningState(t, miner, false) // Starting the miner after the downloader should not work miner.Start() - waitForMiningState(t, miner, false) + waitForMiningState(t, miner, true) } func TestStartStopMiner(t *testing.T) { - miner, _, cleanup := createMiner(t) + miner, mux, cleanup := createMiner(t) defer cleanup(false) waitForMiningState(t, miner, false) + mux.Post(sstorage.SstorSyncDone{}) miner.Start() waitForMiningState(t, miner, true) miner.Stop() @@ -186,9 +133,10 @@ func TestStartStopMiner(t *testing.T) { } func TestCloseMiner(t *testing.T) { - miner, _, cleanup := createMiner(t) + miner, mux, cleanup := createMiner(t) defer cleanup(true) waitForMiningState(t, miner, false) + mux.Post(sstorage.SstorSyncDone{}) miner.Start() waitForMiningState(t, miner, true) // Terminate the miner and wait for the update loop to run diff --git a/sstorminer/worker.go b/sstorminer/worker.go index 401bd643d759..779be362c427 100644 --- a/sstorminer/worker.go +++ b/sstorminer/worker.go @@ -96,6 +96,7 @@ type TXSigner struct { // task contains all information for consensus engine sealing and result submitting. type task struct { + worker *worker storageContract common.Address minerContract common.Address shardIdx uint64 @@ -111,6 +112,11 @@ type task struct { mu sync.RWMutex // The lock used to protect the state } +func (t *task) expectedDiff(minedTime uint64) *big.Int { + return expectedDiff(t.info.LastMineTime, t.info.Difficulty, minedTime, t.worker.config.TargetIntervalSec, + t.worker.config.Cutoff, t.worker.config.DiffAdjDivisor, t.worker.config.MinimumDiff) +} + func (t *task) getState() uint64 { t.mu.Lock() defer t.mu.Unlock() @@ -144,9 +150,12 @@ func (t *task) isRunning() bool { type tasks []*task -func (t tasks) Len() int { return len(t) } -func (t tasks) Less(i, j int) bool { return t[i].info.LastMineTime < t[j].info.LastMineTime } -func (t tasks) Swap(i, j int) { t[i], t[j] = t[j], t[i] } +func (t tasks) Len() int { return len(t) } +func (t tasks) Less(i, j int) bool { + minedTs := uint64(time.Now().Unix()) + return t[i].expectedDiff(minedTs).Cmp(t[j].expectedDiff(minedTs)) < 0 +} +func (t tasks) Swap(i, j int) { t[i], t[j] = t[j], t[i] } type result struct { task *task @@ -301,6 +310,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, cha for addr, sm := range sstor.ContractToShardManager { for idx, shard := range sm.ShardMap() { task := task{ + worker: worker, storageContract: addr, minerContract: minerContract, shardIdx: idx, @@ -518,6 +528,7 @@ func (w *worker) resultLoop() { w.newResultHook(result) } + // todo refer to the current layer 2 to process submissions w.submitMinedResult(result) case <-w.exitCh: @@ -562,7 +573,7 @@ func (w *worker) commitWork(stopCh chan struct{}) { if !w.isRunning() { return } - // sort and find the oldest task to mine + // sort and find the task with smallest diff to mine sort.Sort(w.tasks) go func() { for _, t := range w.tasks { @@ -581,31 +592,30 @@ func (w *worker) commitWork(stopCh chan struct{}) { }() } -func (w *worker) calculateDiffAndInitHash(info *core.MiningInfo, startShardId, shardLen, minedTs uint64) (diff *big.Int, diffs []*big.Int, hash0 common.Hash, err error) { +func (w *worker) calculateDiffAndInitHash(t *task, shardLen, minedTs uint64) (diff *big.Int, diffs []*big.Int, hash0 common.Hash, err error) { diffs = make([]*big.Int, shardLen) diff = new(big.Int).SetUint64(0) hash0 = common.Hash{} for i := uint64(0); i < shardLen; i++ { - shardId := startShardId + i - if minedTs < info.LastMineTime { + shardId := t.shardIdx + i + if minedTs < t.info.LastMineTime { err = fmt.Errorf("minedTs too small") } - diffs[i] = expectedDiff(info.LastMineTime, info.Difficulty, minedTs, - w.config.TargetIntervalSec, w.config.Cutoff, w.config.DiffAdjDivisor, w.config.MinimumDiff) + diffs[i] = t.expectedDiff(minedTs) diff = new(big.Int).Add(diff, diffs[i]) - hash0 = crypto.Keccak256Hash(hash0.Bytes(), uint64ToByte32(shardId), info.MiningHash.Bytes()) + hash0 = crypto.Keccak256Hash(hash0.Bytes(), uint64ToByte32(shardId), t.info.MiningHash.Bytes()) } return diff, diffs, hash0, nil } -func (w *worker) hashimoto(t *task, startShardId, shardLenBits uint64, hash0 common.Hash) (common.Hash, [][]byte, []uint64, []uint64, error) { +func (w *worker) hashimoto(t *task, shardLenBits uint64, hash0 common.Hash) (common.Hash, [][]byte, []uint64, []uint64, error) { hash0Bytes := hash0.Bytes() dataSet := make([][]byte, w.config.RandomChecks) kvIdxs, chunkIdxs := make([]uint64, w.config.RandomChecks), make([]uint64, w.config.RandomChecks) rowBits := t.kvEntriesBits + t.chunkSizeBits + shardLenBits for i := 0; i < w.config.RandomChecks; i++ { - chunkIdx := new(big.Int).SetBytes(hash0Bytes).Uint64()%(uint64(1)< uint64(time.Now().Unix()) { hash0 = crypto.Keccak256Hash(hash0.Bytes(), addressToByte32(t.miner), uint64ToByte32(minedTs), uint64ToByte32(nonce)) - hash0, dataSet, kvIdxs, chunkIdxs, err = w.hashimoto(t, t.shardIdx, shardLenBits, hash0) + hash0, dataSet, kvIdxs, chunkIdxs, err = w.hashimoto(t, shardLenBits, hash0) // Check if the data matches the hash in metadata. if requiredDiff.Cmp(new(big.Int).SetBytes(hash0.Bytes())) < 0 { From bf3b5b0a90f62fa13782389327a0b9ec5a063611 Mon Sep 17 00:00:00 2001 From: pingke Date: Tue, 11 Apr 2023 17:39:26 +0800 Subject: [PATCH 15/23] fix bugs --- eth/protocols/sstorage/sync.go | 52 +++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/eth/protocols/sstorage/sync.go b/eth/protocols/sstorage/sync.go index 044dc9b240cd..5e28909d2dce 100644 --- a/eth/protocols/sstorage/sync.go +++ b/eth/protocols/sstorage/sync.go @@ -45,7 +45,7 @@ const ( maxConcurrency = 16 - minSubTaskSize = 512 + minSubTaskSize = 16 ) // ErrCancelled is returned from sstorage syncing if the operation was prematurely @@ -187,7 +187,7 @@ type kvHealTask struct { func (h *kvHealTask) hasIndexInRange(first, last uint64) (bool, uint64) { min, exist := last, false for idx, _ := range h.Indexes { - if idx <= last && idx >= first { + if idx < last && idx >= first { exist = true if min > idx { min = idx @@ -549,15 +549,14 @@ func (s *Syncer) loadSyncStatus() { Indexes: make(map[uint64]int64), } - first, limit := sm.KvEntries()*sid, sm.KvEntries()*(sid+1)-1 + first, limit := sm.KvEntries()*sid, sm.KvEntries()*(sid+1) firstEmpty, limitForEmpty := uint64(0), uint64(0) - if lastKvIndex > 0 && first >= lastKvIndex { + if first >= lastKvIndex { firstEmpty, limitForEmpty = first, limit - limit = first - 1 - } - if lastKvIndex > 0 && limit >= lastKvIndex { + limit = first + } else if limit >= lastKvIndex { firstEmpty, limitForEmpty = lastKvIndex, limit - limit = lastKvIndex - 1 + limit = lastKvIndex } subTasks := make([]*kvSubTask, 0) @@ -568,7 +567,7 @@ func (s *Syncer) loadSyncStatus() { maxTaskSize = minSubTaskSize } - for first <= limit { + for first < limit { last := first + maxTaskSize if last > limit { last = limit @@ -582,7 +581,7 @@ func (s *Syncer) loadSyncStatus() { } subTasks = append(subTasks, &subTask) - first = last + 1 + first = last } subEmptyTasks := make([]*kvSubEmptyTask, 0) @@ -592,7 +591,7 @@ func (s *Syncer) loadSyncStatus() { maxEmptyTaskSize = minSubTaskSize } - for firstEmpty <= limitForEmpty { + for firstEmpty < limitForEmpty { last := firstEmpty + maxEmptyTaskSize if last > limitForEmpty { last = limitForEmpty @@ -606,7 +605,7 @@ func (s *Syncer) loadSyncStatus() { } subEmptyTasks = append(subEmptyTasks, &subTask) - firstEmpty = last + 1 + firstEmpty = last } } @@ -766,7 +765,7 @@ func (s *Syncer) assignKVRangeTasks(success chan *kvRangeResponse, fail chan *kv contract: task.Contract, shardId: task.ShardId, origin: subTask.next, - limit: subTask.Last, + limit: subTask.Last - 1, time: time.Now(), deliver: success, revert: fail, @@ -909,35 +908,36 @@ func (s *Syncer) assignKVEmptyTasks() { s.lock.Lock() defer s.lock.Unlock() - if s.runningEmptyTaskTreads >= maxEmptyTaskTreads { - return - } - s.runningEmptyTaskTreads++ - // Iterate over all the tasks and try to find a pending one for _, task := range s.tasks { for _, subEmptyTask := range task.KvSubEmptyTasks { + if s.runningEmptyTaskTreads >= maxEmptyTaskTreads { + return + } + s.runningEmptyTaskTreads++ if subEmptyTask.isRunning { continue } subTask := subEmptyTask subTask.isRunning = true - start, limit := subTask.next, subTask.Last - if limit >= start+minSubTaskSize { - limit = start + minSubTaskSize + start, last := subTask.next, subTask.Last + if last > start+minSubTaskSize { + last = start + minSubTaskSize } go func(eTask *kvSubEmptyTask, contract common.Address, start, limit uint64) { + t := time.Now() next, err := s.chain.FillSstorWithEmptyKV(contract, start, limit) if err != nil { log.Warn("fill in empty fail", "err", err.Error()) } + log.Warn("FillSstorWithEmptyKV", "time", time.Now().Sub(t).Seconds()) eTask.next = next - if eTask.next > eTask.Last { + if eTask.next >= eTask.Last { eTask.done = true } eTask.isRunning = false s.runningEmptyTaskTreads-- - }(subTask, task.Contract, start, limit) + }(subTask, task.Contract, start, last-1) } } } @@ -1095,7 +1095,7 @@ func (s *Syncer) processKVRangeResponse(res *kvRangeResponse) { res.task.kvTask.HealTask.Indexes[n] = 0 } } - if max == res.task.Last { + if max == res.task.Last-1 { res.task.done = true } else { res.task.next = max + 1 @@ -1193,7 +1193,7 @@ func (s *Syncer) OnKVs(peer SyncPeer, id uint64, providerAddr common.Address, kv // get id range and check range sm := sstorage.ContractToShardManager[req.contract] if sm == nil { - logger.Debug("Peer rejected kv request") + logger.Debug("Peer rejected kv request", "len", len(req.task.kvTask.HealTask.Indexes)) req.task.kvTask.statelessPeers[peer.ID()] = struct{}{} s.lock.Unlock() @@ -1288,7 +1288,7 @@ func (s *Syncer) OnKVRange(peer SyncPeer, id uint64, providerAddr common.Address // get id range and check range sm := sstorage.ContractToShardManager[req.contract] if sm == nil { - logger.Debug("Peer rejected kv request") + logger.Debug("Peer rejected kv request", "origin", req.origin, "limit", req.limit) req.task.kvTask.statelessPeers[peer.ID()] = struct{}{} s.lock.Unlock() From ed2d30bfd01e3e643803375578688547055d463f Mon Sep 17 00:00:00 2001 From: pingke Date: Sun, 16 Apr 2023 23:13:52 +0800 Subject: [PATCH 16/23] bug fix --- core/blockchain.go | 20 +--- eth/backend.go | 3 +- eth/ethconfig/config.go | 6 +- eth/protocols/sstorage/sync.go | 37 +++++-- eth/sync.go | 2 +- sstorage/merklelib.go | 129 ++++++++++++++++++++++++ sstorage/shard_manager.go | 4 +- sstorminer/merklelib.go | 177 --------------------------------- sstorminer/miner.go | 2 +- sstorminer/worker.go | 55 ++++++++-- sstorminer/worker_test.go | 37 +++++-- 11 files changed, 244 insertions(+), 228 deletions(-) create mode 100644 sstorage/merklelib.go delete mode 100644 sstorminer/merklelib.go diff --git a/core/blockchain.go b/core/blockchain.go index 53d5ca0bc2ec..8dd4a54af06c 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -20,7 +20,6 @@ package core import ( "bytes" "context" - "encoding/binary" "errors" "fmt" "io" @@ -2444,17 +2443,6 @@ func GetSstorageMetadata(s *state.StateDB, contract common.Address, index uint64 nil } -func GetDefaultMetadata(index uint64) (common.Hash, *SstorageMetadata) { - meta := &SstorageMetadata{ - KVIdx: index, - KVSize: uint64(0), - HashInMeta: make([]byte, 24), - } - hashBytes := make([]byte, 32) - binary.BigEndian.PutUint32(hashBytes[27:], uint32(index)) - return common.BytesToHash(hashBytes), meta -} - // VerifyKV verify kv using SstorageMetadata func VerifyKV(sm *sstorage.ShardManager, idx uint64, val []byte, meta *SstorageMetadata, isEncoded bool, providerAddr common.Address) ([]byte, error) { if idx != meta.KVIdx { @@ -2477,10 +2465,10 @@ func VerifyKV(sm *sstorage.ShardManager, idx uint64, val []byte, meta *SstorageM data = d } - hash := crypto.Keccak256Hash(data) - if !bytes.Equal(hash[:24], meta.HashInMeta) { - return nil, fmt.Errorf("verifyKV fail: size error; Data hash: %s; MetaHash hash (24): %s", - common.Bytes2Hex(hash[:24]), common.Bytes2Hex(meta.HashInMeta)) + root := sstorage.MerkleRootWithMinTree(data) + if !bytes.Equal(root[:24], meta.HashInMeta) { + return nil, fmt.Errorf("verifyKV fail: Data hash: %s; MetaHash hash (24): %s", + common.Bytes2Hex(root[:24]), common.Bytes2Hex(meta.HashInMeta)) } return data, nil diff --git a/eth/backend.go b/eth/backend.go index 110e856dd94c..a63de5bf450c 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -191,10 +191,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if config.IsMiner && chainConfig.MindReading.EnableBlockNumber.Cmp(big.NewInt(0)) >= 0 && chainConfig.MindReading.CallRpc == "" { return nil, fmt.Errorf("Validator must enable MindReading with valid MindReadingCallRpc ") } + log.Info("Initialised mindReading configuration", "mindReading conf", *chainConfig.MindReading) } - log.Info("Initialised mindReading configuration", "mindReading conf", *chainConfig.MindReading) - if err = chainDb.StartFreeze(chainDb, chainConfig); err != nil { log.Crit("Failed to StartFreeze", "error", err) } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 7ad3b69b8eae..e6e7d3174155 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -92,8 +92,8 @@ var Defaults = Config{ }, SStorMiner: sstorminer.Config{ RandomChecks: 16, - MinimumDiff: new(big.Int).SetUint64(1), - TargetIntervalSec: new(big.Int).SetUint64(60), + MinimumDiff: new(big.Int).SetUint64(100), + TargetIntervalSec: new(big.Int).SetUint64(300), Cutoff: new(big.Int).SetUint64(40), DiffAdjDivisor: new(big.Int).SetUint64(1024), Recommit: 15 * time.Second, @@ -231,7 +231,7 @@ type Config struct { ValChainId uint64 ValidatorChangeEpochId uint64 - //MindReading Config + // MindReading Config IsMiner bool MindReadingEnableBlockNumber *big.Int MindReadingSupportChainId uint64 diff --git a/eth/protocols/sstorage/sync.go b/eth/protocols/sstorage/sync.go index 5e28909d2dce..6a8554292a24 100644 --- a/eth/protocols/sstorage/sync.go +++ b/eth/protocols/sstorage/sync.go @@ -222,8 +222,10 @@ type SyncProgress struct { Tasks []*kvTask // The suspended kv tasks // Status report during syncing phase - KVSynced uint64 // Number of kvs downloaded - KVBytes common.StorageSize // Number of kv bytes downloaded + KVSynced uint64 // Number of kvs downloaded + KVBytes common.StorageSize // Number of kv bytes downloaded + EmptyKVToFill uint64 + EmptyKVFilled uint64 } // SyncPeer abstracts out the methods required for a peer to be synced against @@ -313,6 +315,9 @@ type Syncer struct { kvBytes common.StorageSize // Number of kv bytes downloaded kvSyncing uint64 // Number of kvs downloading + emptyKVToFill uint64 + emptyKVFilled uint64 + startTime time.Time // Time instance when sstorage sync started logTime time.Time // Time instance when status was Last reported @@ -493,6 +498,7 @@ func (s *Syncer) Sync(cancel chan struct{}) error { func (s *Syncer) loadSyncStatus() { // Start a fresh sync for retrieval. s.kvSynced, s.kvBytes = 0, 0 + s.emptyKVToFill, s.emptyKVFilled = 0, 0 var progress SyncProgress if status := rawdb.ReadSstorageSyncStatus(s.db); status != nil { @@ -510,9 +516,11 @@ func (s *Syncer) loadSyncStatus() { for _, kvSubEmptyTask := range task.KvSubEmptyTasks { kvSubEmptyTask.kvTask = task kvSubEmptyTask.next = kvSubEmptyTask.First + s.emptyKVToFill += (kvSubEmptyTask.Last - kvSubEmptyTask.First) } } s.kvSynced, s.kvBytes = progress.KVSynced, progress.KVBytes + s.emptyKVFilled = progress.EmptyKVFilled } } @@ -586,6 +594,7 @@ func (s *Syncer) loadSyncStatus() { subEmptyTasks := make([]*kvSubEmptyTask, 0) if limitForEmpty > 0 { + s.emptyKVToFill += limitForEmpty - firstEmpty maxEmptyTaskSize := (limitForEmpty - firstEmpty + uint64(maxEmptyTaskTreads)) / uint64(maxEmptyTaskTreads) if maxEmptyTaskSize < minSubTaskSize { maxEmptyTaskSize = minSubTaskSize @@ -637,9 +646,11 @@ func (s *Syncer) setSyncDone() { func (s *Syncer) saveSyncStatus() { // Store the actual progress markers progress := &SyncProgress{ - Tasks: s.tasks, - KVSynced: s.kvSynced, - KVBytes: s.kvBytes, + Tasks: s.tasks, + KVSynced: s.kvSynced, + KVBytes: s.kvBytes, + EmptyKVToFill: s.emptyKVToFill, + EmptyKVFilled: s.emptyKVFilled, } status, err := json.Marshal(progress) if err != nil { @@ -654,8 +665,10 @@ func (s *Syncer) Progress() (*SyncProgress, uint64) { defer s.lock.Unlock() progress := &SyncProgress{ - KVSynced: s.kvSynced, - KVBytes: s.kvBytes, + KVSynced: s.kvSynced, + KVBytes: s.kvBytes, + EmptyKVFilled: s.emptyKVFilled, + EmptyKVToFill: s.emptyKVToFill, } return progress, s.kvSyncing } @@ -932,6 +945,11 @@ func (s *Syncer) assignKVEmptyTasks() { } log.Warn("FillSstorWithEmptyKV", "time", time.Now().Sub(t).Seconds()) eTask.next = next + filled := next - start + s.emptyKVFilled += filled + if s.emptyKVToFill > filled { + s.emptyKVToFill -= filled + } if eTask.next >= eTask.Last { eTask.done = true } @@ -1364,6 +1382,7 @@ func (s *Syncer) report(force bool) { progress = fmt.Sprintf("%.2f%%", float64(synced)*100/float64(kvsToSync+synced)) kv = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(s.kvSynced), s.kvBytes.TerminalString()) ) - log.Info("State sync in progress", "synced", progress, "state", synced, "kvsToSync", kvsToSync, - "sub task remain", subTaskRemain, "kv", kv, "eta", common.PrettyDuration(estTime-elapsed)) + log.Info("Sstorage sync in progress", "synced", progress, "state", synced, "kvsToSync", kvsToSync, + "sub task remain", subTaskRemain, "kv", kv, "eta", common.PrettyDuration(estTime-elapsed), + "empty KV filled", s.emptyKVFilled, "empty KV to fill", s.emptyKVToFill) } diff --git a/eth/sync.go b/eth/sync.go index b8ac67d3b2d1..d9e014985e85 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -31,7 +31,7 @@ import ( const ( forceSyncCycle = 10 * time.Second // Time interval to force syncs, even if few peers are available - defaultMinSyncPeers = 5 // Amount of peers desired to start syncing + defaultMinSyncPeers = 2 // Amount of peers desired to start syncing ) // syncTransactions starts sending all currently pending transactions to the given peer. diff --git a/sstorage/merklelib.go b/sstorage/merklelib.go new file mode 100644 index 000000000000..f3d2c4982c34 --- /dev/null +++ b/sstorage/merklelib.go @@ -0,0 +1,129 @@ +package sstorage + +import ( + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func GetProof(data []byte, nChunkBits, chunkIdx uint64) ([]common.Hash, error) { + if len(data) == 0 { + return nil, nil + } + nChunks := uint64(1) << nChunkBits + if chunkIdx >= nChunks { + return []common.Hash{}, fmt.Errorf("index out of scope") + } + nodes := make([]common.Hash, nChunks) + for i := uint64(0); i < nChunks; i++ { + off := i * CHUNK_SIZE + if off > uint64(len(data)) { + break + } + l := uint64(len(data)) - off + if l >= CHUNK_SIZE { + l = CHUNK_SIZE + } + nodes[i] = crypto.Keccak256Hash(data[off : off+l]) + } + n, proofIdx := nChunks, uint64(0) + proofs := make([]common.Hash, nChunkBits) + for n != 1 { + proofs[proofIdx] = nodes[(chunkIdx/2)*2+1-chunkIdx%2] + for i := uint64(0); i < n/2; i++ { + nodes[i] = crypto.Keccak256Hash(nodes[i*2].Bytes(), nodes[i*2+1].Bytes()) + } + n = n / 2 + chunkIdx = chunkIdx / 2 + proofIdx = proofIdx + 1 + } + return proofs, nil +} + +func CalculateRootWithProof(dataHash common.Hash, chunkIdx uint64, proofs []common.Hash) (common.Hash, error) { + if len(proofs) == 0 { + return dataHash, nil + } + hash := dataHash + nChunkBits := uint64(len(proofs)) + if chunkIdx >= uint64(1)<= l { + // empty mean the leaf is zero + break + } + size := l - off + if size >= CHUNK_SIZE { + size = CHUNK_SIZE + } + hash := crypto.Keccak256Hash(data[off : off+size]) + nodes[i] = hash + } + n := chunkPerKV + for n != 1 { + for i := uint64(0); i < n/2; i++ { + nodes[i] = crypto.Keccak256Hash(nodes[i*2].Bytes(), nodes[i*2+1].Bytes()) + } + + n = n / 2 + } + return nodes[0] +} + +func findNChunk(dataLen uint64) (uint64, uint64) { + if dataLen == 0 { + return 0, 0 + } + n := (dataLen+CHUNK_SIZE-1)/CHUNK_SIZE - 1 + nChunkBits := uint64(0) + for n != 0 { + nChunkBits++ + n = n >> 1 + } + + return uint64(1) << nChunkBits, nChunkBits +} + +func GetProofWithMinTree(data []byte, nChunkBits, chunkIdx uint64) ([]common.Hash, error) { + if len(data) == 0 { + return []common.Hash{}, nil + } + nChunks := uint64(1) << nChunkBits + if chunkIdx >= nChunks { + return []common.Hash{}, fmt.Errorf("index out of scope") + } + nMinChunks, nMinChunkBits := findNChunk(uint64(len(data))) + if chunkIdx >= nMinChunks { + return []common.Hash{}, nil + } + return GetProof(data, nMinChunkBits, chunkIdx) +} + +func MerkleRootWithMinTree(data []byte) common.Hash { + l := uint64(len(data)) + if l == 0 { + return common.Hash{} + } + nChunk, _ := findNChunk(uint64(len(data))) + return MerkleRoot(data, nChunk) +} diff --git a/sstorage/shard_manager.go b/sstorage/shard_manager.go index 93c83fbfb377..a8fd47e34676 100644 --- a/sstorage/shard_manager.go +++ b/sstorage/shard_manager.go @@ -111,12 +111,12 @@ func (sm *ShardManager) GetShardMiner(shardIdx uint64) (common.Address, bool) { return common.Address{}, false } -// Decode the encoded KV data. +// DecodeKV Decode the encoded KV data. func (sm *ShardManager) DecodeKV(kvIdx uint64, b []byte, hash common.Hash, providerAddr common.Address) ([]byte, bool, error) { return sm.DecodeOrEncodeKV(kvIdx, b, hash, providerAddr, false) } -// Encode the raw KV data. +// EncodeKV Encode the raw KV data. func (sm *ShardManager) EncodeKV(kvIdx uint64, b []byte, hash common.Hash, providerAddr common.Address) ([]byte, bool, error) { return sm.DecodeOrEncodeKV(kvIdx, b, hash, providerAddr, true) } diff --git a/sstorminer/merklelib.go b/sstorminer/merklelib.go deleted file mode 100644 index 07771d5eb7e4..000000000000 --- a/sstorminer/merklelib.go +++ /dev/null @@ -1,177 +0,0 @@ -package sstorminer - -import ( - "bytes" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -func expectedDiff(lastMineTime uint64, difficulty *big.Int, minedTime uint64, targetIntervalSec, cutoff, diffAdjDivisor, minDiff *big.Int) *big.Int { - interval := new(big.Int).SetUint64(minedTime - lastMineTime) - diff := difficulty - if interval.Cmp(targetIntervalSec) < 0 { - // diff = diff + (diff-interval*diff/cutoff)/diffAdjDivisor - diff = new(big.Int).Add(diff, new(big.Int).Div( - new(big.Int).Sub(diff, new(big.Int).Div(new(big.Int).Mul(interval, diff), cutoff)), diffAdjDivisor)) - if diff.Cmp(minDiff) < 0 { - diff = minDiff - } - } else { - // dec := (interval*diff/cutoff - diff) / diffAdjDivisor - dec := new(big.Int).Div(new(big.Int).Div(new(big.Int).Mul(interval, diff), cutoff), diffAdjDivisor) - if new(big.Int).Add(dec, minDiff).Cmp(diff) > 0 { - diff = minDiff - } else { - diff = new(big.Int).Sub(diff, dec) - } - } - - return diff -} - -func getProof(data []byte, chunkSize, nChunkBits, chunkIdx uint64) ([]common.Hash, error) { - if len(data) == 0 { - return nil, nil - } - nChunks := uint64(1) << nChunkBits - if chunkIdx >= nChunks { - return []common.Hash{}, fmt.Errorf("index out of scope") - } - nodes := make([]common.Hash, nChunks) - for i := uint64(0); i < nChunks; i++ { - off := i * chunkSize - if off > uint64(len(data)) { - break - } - l := uint64(len(data)) - off - if l >= chunkSize { - l = chunkSize - } - nodes[i] = crypto.Keccak256Hash(data[off : off+l]) - } - n, proofIdx := nChunks, uint64(0) - proofs := make([]common.Hash, nChunkBits) - for n != 1 { - proofs[proofIdx] = nodes[(chunkIdx/2)*2+1-chunkIdx%2] - for i := uint64(0); i < n/2; i++ { - nodes[i] = crypto.Keccak256Hash(nodes[i*2].Bytes(), nodes[i*2+1].Bytes()) - } - n = n / 2 - chunkIdx = chunkIdx / 2 - proofIdx = proofIdx + 1 - } - return proofs, nil -} - -func calculateRootWithProof(dataHash common.Hash, chunkIdx uint64, proofs []common.Hash) (common.Hash, error) { - if len(proofs) == 0 { - return dataHash, nil - } - hash := dataHash - nChunkBits := uint64(len(proofs)) - if chunkIdx >= uint64(1)<= l { - // empty mean the leaf is zero - break - } - size := l - off - if size >= chunkSize { - size = chunkSize - } - hash := crypto.Keccak256Hash(data[off : off+size]) - nodes[i] = hash - } - n := chunkPerKV - for n != 1 { - for i := uint64(0); i < n/2; i++ { - nodes[i] = crypto.Keccak256Hash(nodes[i*2].Bytes(), nodes[i*2+1].Bytes()) - } - - n = n / 2 - } - return nodes[0] -} - -func findNChunk(dataLen, chunkSize uint64) (uint64, uint64) { - if dataLen == 0 { - return 0, 0 - } - n := (dataLen+chunkSize-1)/chunkSize - 1 - nChunkBits := uint64(0) - for n != 0 { - nChunkBits++ - n = n >> 1 - } - - return uint64(1) << nChunkBits, nChunkBits -} - -func getProofWithMinTree(data []byte, chunkSize, nChunkBits, chunkIdx uint64) ([]common.Hash, error) { - if len(data) == 0 { - return []common.Hash{}, nil - } - nChunks := uint64(1) << nChunkBits - if chunkIdx >= nChunks { - return []common.Hash{}, fmt.Errorf("index out of scope") - } - nMinChunks, nMinChunkBits := findNChunk(uint64(len(data)), chunkSize) - if chunkIdx >= nMinChunks { - return []common.Hash{}, nil - } - return getProof(data, chunkSize, nMinChunkBits, chunkIdx) -} - -func verifyWithMinTree(root []byte, dataHash common.Hash, chunkIdx uint64, proofs []common.Hash) bool { - nMinChunkBits := uint64(len(proofs)) - if chunkIdx >= uint64(1)< 0 { + diff = minDiff + } else { + diff = new(big.Int).Sub(diff, dec) + } + } + + return diff +} + func (t *task) expectedDiff(minedTime uint64) *big.Int { return expectedDiff(t.info.LastMineTime, t.info.Difficulty, minedTime, t.worker.config.TargetIntervalSec, t.worker.config.Cutoff, t.worker.config.DiffAdjDivisor, t.worker.config.MinimumDiff) @@ -542,11 +565,15 @@ func (w *worker) updateTaskInfo(root common.Hash, timestamp int64) { w.mu.Lock() defer w.mu.Unlock() + if !w.isRunning() { + return + } updated := false for _, task := range w.tasks { info, err := w.chain.GetSstorageMiningInfo(root, task.minerContract, task.shardIdx) if err != nil { log.Warn("failed to get sstorage mining info", "error", err.Error()) + continue } if task.info == nil || !info.Equal(task.info) { task.info = info @@ -615,7 +642,7 @@ func (w *worker) hashimoto(t *task, shardLenBits uint64, hash0 common.Hash) (com kvIdxs, chunkIdxs := make([]uint64, w.config.RandomChecks), make([]uint64, w.config.RandomChecks) rowBits := t.kvEntriesBits + t.chunkSizeBits + shardLenBits for i := 0; i < w.config.RandomChecks; i++ { - chunkIdx := new(big.Int).SetBytes(hash0Bytes).Uint64()%(uint64(1)< uint64(time.Now().Unix()) { - hash0 = crypto.Keccak256Hash(hash0.Bytes(), addressToByte32(t.miner), uint64ToByte32(minedTs), uint64ToByte32(nonce)) - hash0, dataSet, kvIdxs, chunkIdxs, err = w.hashimoto(t, shardLenBits, hash0) + hash1 := crypto.Keccak256Hash(hash0.Bytes(), addressToByte32(t.miner), uint64ToByte32(minedTs), uint64ToByte32(nonce)) + hash1, dataSet, kvIdxs, chunkIdxs, err = w.hashimoto(t, shardLenBits, hash1) - // Check if the data matches the hash in metadata. - if requiredDiff.Cmp(new(big.Int).SetBytes(hash0.Bytes())) < 0 { + if requiredDiff.Cmp(new(big.Int).SetBytes(hash1.Bytes())) >= 0 { + log.Warn("calculateDiffAndInitHash", "diff", diff, "hash1", hash1.Hex(), "minedTs", minedTs, + "MiningHash", t.info.MiningHash, "LastMineTime", t.info.LastMineTime, "nonce", nonce, "miner", t.miner.Hex()) proofs := make([][]common.Hash, len(kvIdxs)) kvs, err := w.chain.ReadKVsByIndexList(t.storageContract, kvIdxs, true) if err != nil { @@ -676,7 +706,7 @@ func (w *worker) mineTask(t *task) (bool, error) { for i := 0; i < len(dataSet); i++ { if kvs[i].Idx == kvIdxs[i] { - ps, err := getProofWithMinTree(kvs[i].Data, sstor.CHUNK_SIZE, t.chunkSizeBits, chunkIdxs[i]) + ps, err := sstor.GetProofWithMinTree(kvs[i].Data, t.chunkSizeBits, chunkIdxs[i]) if err != nil { return false, err } @@ -706,8 +736,8 @@ func (w *worker) mineTask(t *task) (bool, error) { } func (w *worker) submitMinedResult(result *result) error { - data, err := vABI.Pack(MineFunc, result.task.shardIdx, 0, result.task.miner, result.minedTs, - result.nonce, result.proofs, result.encodedData) + data, err := vABI.Pack(MineFunc, new(big.Int).SetUint64(result.task.shardIdx), new(big.Int).SetUint64(0), result.task.miner, + new(big.Int).SetUint64(result.minedTs), new(big.Int).SetUint64(result.nonce), result.proofs, result.encodedData) if err != nil { return err } @@ -728,9 +758,12 @@ func (w *worker) submitMinedResult(result *result) error { signedTx, err := w.signer.SignFn(w.signer.Account, types.NewTx(baseTx), w.chainConfig.ChainID) if err != nil { + w.resultCh <- result return err } + log.Warn("submitMinedResult", "shard idx", result.task.shardIdx, "tx hash", signedTx.Hash(), + "kv idx list", result.kvIdxs, "chunk idx list", result.chunkIdxs) return w.eth.TxPool().AddLocal(signedTx) } diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go index 5d4930a4c17c..82dc21c69d50 100644 --- a/sstorminer/worker_test.go +++ b/sstorminer/worker_test.go @@ -17,6 +17,7 @@ package sstorminer import ( + "bytes" "encoding/binary" "errors" "fmt" @@ -327,7 +328,7 @@ func makeKVStorage(stateDB *state.StateDB, contract common.Address, shards []uin key := getSlotHash(2, uint256.NewInt(i).Bytes32()) stateDB.SetState(contract, key, skey) - metaHash = merkleRootWithMinTree(val, sstorage.CHUNK_SIZE) + metaHash = sstorage.MerkleRootWithMinTree(val) meta := generateMetadata(i, uint64(len(val)), metaHash) key = getSlotHash(1, skey) stateDB.SetState(contract, key, meta) @@ -358,6 +359,28 @@ func updateMiningInfoAndInsertNewBlock(pinfo *core.MiningInfo, chain *wrapBlockC return nil } +func verify(root []byte, dataHash common.Hash, chunkIdx uint64, proofs []common.Hash) bool { + r, err := sstorage.CalculateRootWithProof(dataHash, chunkIdx, proofs) + if err != nil { + return false + } + + return bytes.Compare(root[:24], r.Bytes()[:24]) == 0 +} + +func verifyWithMinTree(root []byte, dataHash common.Hash, chunkIdx uint64, proofs []common.Hash) bool { + nMinChunkBits := uint64(len(proofs)) + if chunkIdx >= uint64(1)< Date: Mon, 17 Apr 2023 01:14:49 +0800 Subject: [PATCH 17/23] fix test --- eth/protocols/sstorage/sync_test.go | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/eth/protocols/sstorage/sync_test.go b/eth/protocols/sstorage/sync_test.go index 664b218e9cb0..397f462f39f6 100644 --- a/eth/protocols/sstorage/sync_test.go +++ b/eth/protocols/sstorage/sync_test.go @@ -37,7 +37,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/sstorage" "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) var ( @@ -359,17 +358,7 @@ func getSKey(contract common.Address, idx uint64) common.Hash { slotdata := slot[:] data := append(keydata, slotdata...) - return hash(data) -} - -func hash(data []byte) common.Hash { - hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) - hasher.Write(data) - - hashRes := common.Hash{} - hasher.Read(hashRes[:]) - - return hashRes + return crypto.Keccak256Hash(data) } func checkStall(t *testing.T, term func()) chan struct{} { @@ -432,7 +421,7 @@ func makeKVStorage(stateDB *state.StateDB, contract common.Address, shards []uin key := getSlotHash(2, uint256.NewInt(i).Bytes32()) stateDB.SetState(contract, key, skey) - meta := generateMetadata(i, uint64(len(val)), hash(val)) + meta := generateMetadata(i, uint64(len(val)), sstorage.MerkleRootWithMinTree(val)) key = getSlotHash(1, skey) stateDB.SetState(contract, key, meta) } From 53aaf000ca3925db0ddc1d818bd327ea7b7e1529 Mon Sep 17 00:00:00 2001 From: pingke Date: Tue, 18 Apr 2023 00:13:28 +0800 Subject: [PATCH 18/23] bug fix --- trie/database.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie/database.go b/trie/database.go index 6ac9e060c83b..20fc056d61ea 100644 --- a/trie/database.go +++ b/trie/database.go @@ -792,7 +792,7 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H for addr, m := range db.shardedStorage { sm := db.contractToShardManager[addr] for kvIdx, b := range m { - _, err := sm.TryWrite(kvIdx, b, common.BytesToHash(b[:KvHashLen])) + _, err := sm.TryWrite(kvIdx, b[KvHashLen:], common.BytesToHash(b[:KvHashLen])) if err != nil { log.Error("Failed to write sstorage", "kvIdx", kvIdx, "err", err) } From 5bdbcff93d80eebf7db41d06a6305d215fa652bd Mon Sep 17 00:00:00 2001 From: pingke Date: Mon, 24 Apr 2023 19:11:40 +0800 Subject: [PATCH 19/23] fix bug and resolve comments --- cmd/sstorage/main.go | 8 ++-- core/blockchain.go | 2 +- core/genesis.go | 10 ++--- eth/backend.go | 10 ++--- eth/protocols/sstorage/sync.go | 1 + eth/protocols/sstorage/sync_test.go | 7 ++-- params/config.go | 2 +- sstorage/data_file.go | 12 ++++-- sstorminer/miner.go | 12 +++++- sstorminer/miner_test.go | 2 +- sstorminer/worker.go | 61 +++++++++++++++++++---------- sstorminer/worker_test.go | 27 ++++++++++--- 12 files changed, 102 insertions(+), 52 deletions(-) diff --git a/cmd/sstorage/main.go b/cmd/sstorage/main.go index 998d00004a80..dd662da417bf 100644 --- a/cmd/sstorage/main.go +++ b/cmd/sstorage/main.go @@ -15,7 +15,7 @@ import ( ) var ( - chunkLen *uint64 + kvLen *uint64 miner *string filenames *[]string @@ -63,7 +63,7 @@ var ShardWriteCmd = &cobra.Command{ } func init() { - chunkLen = CreateCmd.Flags().Uint64("len", 0, "Chunk idx len to create") + kvLen = CreateCmd.Flags().Uint64("kv_len", 0, "kv idx len to create") filenames = rootCmd.PersistentFlags().StringArray("filename", []string{}, "Data filename") miner = rootCmd.PersistentFlags().String("miner", "", "miner address") @@ -110,9 +110,9 @@ func runCreate(cmd *cobra.Command, args []string) { } minerAddr := common.HexToAddress(*miner) - log.Info("Creating data file", "chunkIdx", *chunkIdx, "chunkLen", *chunkLen, "miner", minerAddr, "encodeType", *encodeType) + log.Info("Creating data file", "kvIdx", *kvIdx, "kvLen", *kvLen, "miner", minerAddr, "encodeType", *encodeType) - _, err := sstorage.Create((*filenames)[0], *chunkIdx, *chunkLen, 0, *kvSize, *encodeType, minerAddr) + _, err := sstorage.Create((*filenames)[0], *kvIdx, *kvLen, 0, *kvSize, *encodeType, minerAddr) if err != nil { log.Crit("create failed", "error", err) } diff --git a/core/blockchain.go b/core/blockchain.go index 8dd4a54af06c..e95f2d3180ca 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2564,7 +2564,7 @@ func (bc *BlockChain) VerifyAndWriteKV(contract common.Address, data map[uint64] success, err := sm.TryWrite(vkv.Idx, vkv.Data, common.BytesToHash(meta.HashInMeta)) if err != nil { - log.Warn("write kv fail", "error", err) + log.Warn("write kv fail", "kvIdx", vkv.Idx, "kvHash", common.Bytes2Hex(meta.HashInMeta), "error", err) } if success { inserted = append(inserted, vkv.Idx) diff --git a/core/genesis.go b/core/genesis.go index 69d48ec4e2a8..2def810e5988 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -441,12 +441,12 @@ func DefaultWeb3QTestnetGenesisBlock() *Genesis { common.HexToAddress("0x5C935469C5592Aeeac3372e922d9bCEabDF8830d"): {Balance: new(big.Int).Mul(big.NewInt(1000000000000000000), big.NewInt(1000000000))}, // 1e9 Ether }, NextValidators: []common.Address{ - common.HexToAddress("0x2cff0b8e36522eba76f6f5c328d58581243882e4"), - common.HexToAddress("0x959994471dee37411f579dd2820a8743cba20f46"), - common.HexToAddress("0x977cfc676bb06daed7ddfa7711bcfe8d50c93081"), - common.HexToAddress("0xcd21538af6e33ff6fcf1e2ca20f771413004cfd3"), + common.HexToAddress("0xf3025bac5d2e9a179f78e0295a0dd0cd74003e16"), + common.HexToAddress("0x9b30603c22474755c0917254b3e86e78646c87de"), + common.HexToAddress("0x6562837cbadff8ccdfad90a5e40d44bdab561dad"), + common.HexToAddress("0x46a1a4832a046cf7a6d9fc862c155b2c90196dde"), }, - NextValidatorPowers: []uint64{11, 11, 11, 11}, + NextValidatorPowers: []uint64{1, 1, 1, 1}, } } diff --git a/eth/backend.go b/eth/backend.go index a63de5bf450c..bbe9ed1d281e 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -294,6 +294,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) + eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} + if eth.APIBackend.allowUnprotectedTxs { + log.Info("Unprotected transactions allowed") + } if config.SstorageMine { if len(sstorage.Shards()) == 0 { return nil, fmt.Errorf("no shards is exist") @@ -312,14 +316,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { return nil, fmt.Errorf("signer missing: %v", err) } - eth.sstorMiner = sstorminer.New(eth, &config.SStorMiner, chainConfig, eth.EventMux(), &sstorminer.TXSigner{signer, wallet.SignTx}, minerContract) + eth.sstorMiner = sstorminer.New(eth, eth.APIBackend, &config.SStorMiner, chainConfig, eth.EventMux(), &sstorminer.TXSigner{signer, wallet.SignTx}, minerContract) eth.sstorMiner.Start() } - eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} - if eth.APIBackend.allowUnprotectedTxs { - log.Info("Unprotected transactions allowed") - } gpoParams := config.GPO if gpoParams.Default == nil { gpoParams.Default = config.Miner.GasPrice diff --git a/eth/protocols/sstorage/sync.go b/eth/protocols/sstorage/sync.go index 6a8554292a24..750e46a2a9e7 100644 --- a/eth/protocols/sstorage/sync.go +++ b/eth/protocols/sstorage/sync.go @@ -509,6 +509,7 @@ func (s *Syncer) loadSyncStatus() { log.Debug("Scheduled sstorage sync task", "Contract", task.Contract.Hex(), "shard", task.ShardId, "count", len(task.KvSubTasks)) task.HealTask.kvTask = task + task.statelessPeers = make(map[string]struct{}) for _, kvSubTask := range task.KvSubTasks { kvSubTask.kvTask = task kvSubTask.next = kvSubTask.First diff --git a/eth/protocols/sstorage/sync_test.go b/eth/protocols/sstorage/sync_test.go index 397f462f39f6..85ca67c33c5b 100644 --- a/eth/protocols/sstorage/sync_test.go +++ b/eth/protocols/sstorage/sync_test.go @@ -447,10 +447,9 @@ func createSstorage(contract common.Address, shardIdxList []uint64, kvSizeBits, fileId := shardIdx*filePerShard + i fileName := fmt.Sprintf(".\\ss%d.dat", fileId) files = append(files, fileName) - chunkPerfile := kvEntries * kvSize / sstorage.CHUNK_SIZE / filePerShard - startChunkId := fileId * chunkPerfile - endChunkId := (fileId + 1) * chunkPerfile - _, err := sstorage.Create(fileName, startChunkId, endChunkId, 0, kvSize, sstorage.ENCODE_KECCAK_256, miner) + kvPerfile := kvEntries / filePerShard + startKVId := fileId * kvPerfile + _, err := sstorage.Create(fileName, startKVId, kvPerfile, 0, kvSize, sstorage.ENCODE_KECCAK_256, miner) if err != nil { log.Crit("open failed", "error", err) } diff --git a/params/config.go b/params/config.go index 083470f6e9cb..fa424d0ab773 100644 --- a/params/config.go +++ b/params/config.go @@ -34,7 +34,7 @@ var ( SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9") RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177") GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a") - Web3QTestnetGenesisHash = common.HexToHash("0x5b6c05509246d26abb013df13638125a0767e26b6cb28ca1fb19387999a537fa") + Web3QTestnetGenesisHash = common.HexToHash("0xaba50efdf3572a2d25dab20e08bb5d9f30aea360c74aff451e702cf749094d70") Web3QGalileoGenesisHash = common.HexToHash("0xa576a985390f3a643e2acdeaed074cc9866c99f6bdf3ca8c49ec959054703745") ) diff --git a/sstorage/data_file.go b/sstorage/data_file.go index 597f58087ce2..2144c97362a7 100644 --- a/sstorage/data_file.go +++ b/sstorage/data_file.go @@ -68,21 +68,25 @@ func UnmaskDataInPlace(userData []byte, maskData []byte) []byte { return userData } -func Create(filename string, chunkIdxStart uint64, chunkIdxLen uint64, epoch, maxKvSize uint64, encodeType uint64, miner common.Address) (*DataFile, error) { +func Create(filename string, kvIdxStart uint64, kvIdxLen uint64, epoch, maxKvSize uint64, encodeType uint64, miner common.Address) (*DataFile, error) { log.Info("Creating file", "filename", filename) file, err := os.Create(filename) if err != nil { return nil, err } + if maxKvSize%CHUNK_SIZE != 0 { + return nil, fmt.Errorf("max kv size %% CHUNK_SIZE should be 0") + } + chunkPerKV := maxKvSize / CHUNK_SIZE // actual initialization is done when synchronize - err = fallocate.Fallocate(file, int64(CHUNK_SIZE*chunkIdxLen), int64(CHUNK_SIZE)) + err = fallocate.Fallocate(file, int64(CHUNK_SIZE), int64(maxKvSize*kvIdxLen)) if err != nil { return nil, err } dataFile := &DataFile{ file: file, - chunkIdxStart: chunkIdxStart, - chunkIdxLen: chunkIdxLen, + chunkIdxStart: kvIdxStart * chunkPerKV, + chunkIdxLen: kvIdxLen * chunkPerKV, encodeType: encodeType, maxKvSize: maxKvSize, miner: miner, diff --git a/sstorminer/miner.go b/sstorminer/miner.go index 8bea701bd537..320f80957514 100644 --- a/sstorminer/miner.go +++ b/sstorminer/miner.go @@ -18,12 +18,14 @@ package sstorminer import ( + "context" "math/big" "sync" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/protocols/sstorage" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" @@ -36,6 +38,12 @@ type Backend interface { TxPool() *core.TxPool } +type apiBackend interface { + GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) + SendTx(ctx context.Context, signedTx *types.Transaction) error + SuggestGasTipCap(ctx context.Context) (*big.Int, error) +} + // Config is the configuration parameters of mining. type Config struct { RandomChecks int @@ -58,13 +66,13 @@ type Miner struct { wg sync.WaitGroup } -func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, txSigner *TXSigner, minerContract common.Address) *Miner { +func New(eth Backend, api apiBackend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, txSigner *TXSigner, minerContract common.Address) *Miner { miner := &Miner{ mux: mux, exitCh: make(chan struct{}), startCh: make(chan struct{}), stopCh: make(chan struct{}), - worker: newWorker(config, chainConfig, eth, eth.BlockChain(), mux, txSigner, minerContract, false), + worker: newWorker(config, chainConfig, eth, api, eth.BlockChain(), mux, txSigner, minerContract, false), } miner.wg.Add(1) go miner.update() diff --git a/sstorminer/miner_test.go b/sstorminer/miner_test.go index 166efb661296..0c4957de94b0 100644 --- a/sstorminer/miner_test.go +++ b/sstorminer/miner_test.go @@ -186,7 +186,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) { // Create event Mux mux := new(event.TypeMux) // Create Miner - miner := New(backend, &config, chainConfig, mux, nil, common.Address{}) + miner := New(backend, nil, &config, chainConfig, mux, nil, common.Address{}) cleanup := func(skipMiner bool) { bc.Stop() engine.Close() diff --git a/sstorminer/worker.go b/sstorminer/worker.go index 6956dcaf0ae0..e5d8df45ac6d 100644 --- a/sstorminer/worker.go +++ b/sstorminer/worker.go @@ -17,6 +17,7 @@ package sstorminer import ( + "context" "fmt" "math/big" "math/rand" @@ -280,6 +281,7 @@ type worker struct { chainConfig *params.ChainConfig engine consensus.Engine eth Backend + apiBackend apiBackend chain BlockChain priceOracle *priceOracle @@ -296,6 +298,7 @@ type worker struct { resultCh chan *result startCh chan struct{} taskStartCh chan struct{} + resultSubmitFailCh chan struct{} exitCh chan struct{} taskDoneCh chan struct{} resubmitIntervalCh chan time.Duration @@ -310,13 +313,14 @@ type worker struct { newResultHook func(*result) } -func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, chain BlockChain, mux *event.TypeMux, txSigner *TXSigner, +func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, api apiBackend, chain BlockChain, mux *event.TypeMux, txSigner *TXSigner, minerContract common.Address, init bool) *worker { worker := &worker{ config: config, chainConfig: chainConfig, eth: eth, mux: mux, + apiBackend: api, chain: chain, tasks: make([]*task, 0), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), @@ -328,6 +332,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, cha resubmitIntervalCh: make(chan time.Duration), taskDoneCh: make(chan struct{}), taskStartCh: make(chan struct{}), + resultSubmitFailCh: make(chan struct{}), signer: txSigner, } for addr, sm := range sstor.ContractToShardManager { @@ -509,6 +514,11 @@ func (w *worker) mainLoop() { stopCh = make(chan struct{}) w.commitWork(stopCh) + case <-w.resultSubmitFailCh: + interrupt() + stopCh = make(chan struct{}) + w.commitWork(stopCh) + case <-w.exitCh: return case <-w.chainHeadSub.Err(): @@ -552,7 +562,13 @@ func (w *worker) resultLoop() { } // todo refer to the current layer 2 to process submissions - w.submitMinedResult(result) + err := w.submitMinedResult(result) + if err != nil { + result.task.setState(TaskStateNoStart) + w.resultSubmitFailCh <- struct{}{} + log.Warn("w.submitMinedResult", "MiningHash", result.task.info.MiningHash.Hex(), + "LastMineTime", result.task.info.LastMineTime, "miner", result.miner, "error", err.Error()) + } case <-w.exitCh: return @@ -637,18 +653,17 @@ func (w *worker) calculateDiffAndInitHash(t *task, shardLen, minedTs uint64) (di } func (w *worker) hashimoto(t *task, shardLenBits uint64, hash0 common.Hash) (common.Hash, [][]byte, []uint64, []uint64, error) { - hash0Bytes := hash0.Bytes() dataSet := make([][]byte, w.config.RandomChecks) kvIdxs, chunkIdxs := make([]uint64, w.config.RandomChecks), make([]uint64, w.config.RandomChecks) rowBits := t.kvEntriesBits + t.chunkSizeBits + shardLenBits for i := 0; i < w.config.RandomChecks; i++ { - chunkIdx := new(big.Int).SetBytes(hash0Bytes).Uint64()%(uint64(1)<> t.chunkSizeBits chunkIdxs[i] = chunkIdx % (1 << t.chunkSizeBits) - hash0Bytes = crypto.Keccak256Hash(hash0Bytes, data).Bytes() + hash0 = crypto.Keccak256Hash(hash0.Bytes(), data) } else { if !exist { err = fmt.Errorf("chunk not support: chunkIdxs %d", chunkIdx) @@ -736,35 +751,41 @@ func (w *worker) mineTask(t *task) (bool, error) { } func (w *worker) submitMinedResult(result *result) error { + ctx := context.Background() data, err := vABI.Pack(MineFunc, new(big.Int).SetUint64(result.task.shardIdx), new(big.Int).SetUint64(0), result.task.miner, new(big.Int).SetUint64(result.minedTs), new(big.Int).SetUint64(result.nonce), result.proofs, result.encodedData) if err != nil { return err } - gasFeeCap := w.chain.CurrentBlock().BaseFee() - nonce := w.eth.TxPool().Nonce(result.task.miner) + nonce, _ := w.apiBackend.GetPoolNonce(ctx, w.signer.Account.Address) + gasPrice, err := w.apiBackend.SuggestGasTipCap(ctx) + gasPrice = new(big.Int).Add(gasPrice, w.chain.CurrentBlock().BaseFee()) + if err != nil { + return err + } - baseTx := &types.DynamicFeeTx{ - ChainID: w.chainConfig.ChainID, - To: &result.task.minerContract, - Nonce: nonce, - GasTipCap: w.priceOracle.suggestGasTip, - GasFeeCap: gasFeeCap, - Gas: gas, - Value: new(big.Int).SetInt64(0), - Data: data, + baseTx := &types.LegacyTx{ + To: &result.task.minerContract, + Nonce: nonce, + GasPrice: gasPrice, + Gas: gas, + Value: new(big.Int).SetInt64(0), + Data: data, } signedTx, err := w.signer.SignFn(w.signer.Account, types.NewTx(baseTx), w.chainConfig.ChainID) if err != nil { - w.resultCh <- result + log.Warn("worker::submitMinedResult() >>>>>> sign tx error <<<<<<", "err", err) return err } - log.Warn("submitMinedResult", "shard idx", result.task.shardIdx, "tx hash", signedTx.Hash(), - "kv idx list", result.kvIdxs, "chunk idx list", result.chunkIdxs) - return w.eth.TxPool().AddLocal(signedTx) + err = w.apiBackend.SendTx(ctx, signedTx) + if err != nil { + return fmt.Errorf("SendTransaction hash %s, ERROR %s ", signedTx.Hash().Hex(), err.Error()) + } + log.Warn("Submit mining tx", "hash", signedTx.Hash().Hex()) + return nil } func uint64ToByte32(u uint64) []byte { diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go index 82dc21c69d50..f7c94ab1aaad 100644 --- a/sstorminer/worker_test.go +++ b/sstorminer/worker_test.go @@ -18,6 +18,7 @@ package sstorminer import ( "bytes" + "context" "encoding/binary" "errors" "fmt" @@ -162,6 +163,23 @@ func (bc *wrapBlockChain) initMiningInfos(shardIdxList []uint64, diff *big.Int, return infos } +type mockApiBackend struct { + nonce uint64 +} + +func (api *mockApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { + api.nonce++ + return api.nonce, nil +} + +func (api *mockApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { + return nil +} + +func (api *mockApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + return new(big.Int).SetUint64(6000000), nil +} + // testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing. type testWorkerBackend struct { db ethdb.Database @@ -232,7 +250,7 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens return types.SignTx(tx, types.NewEIP2930Signer(chainConfig.ChainID), testBankKey) }, } - w := newWorker(defaultConfig, chainConfig, backend, &wchain, new(event.TypeMux), signer, minerContract, false) + w := newWorker(defaultConfig, chainConfig, backend, &mockApiBackend{nonce: 0}, &wchain, new(event.TypeMux), signer, minerContract, false) return w, infos, files, backend } @@ -250,10 +268,9 @@ func createSstorage(contract common.Address, shardIdxList []uint64, kvSizeBits, fileId := shardIdx*filePerShard + i fileName := fmt.Sprintf(".\\ss%d.dat", fileId) files = append(files, fileName) - chunkPerfile := kvEntries * kvSize / sstorage.CHUNK_SIZE / filePerShard - startChunkId := fileId * chunkPerfile - endChunkId := (fileId + 1) * chunkPerfile - _, err := sstorage.Create(fileName, startChunkId, endChunkId, 0, kvSize, sstorage.ENCODE_KECCAK_256, miner) + kvPerfile := kvEntries / filePerShard + startKVId := fileId * kvPerfile + _, err := sstorage.Create(fileName, startKVId, kvPerfile, 0, kvSize, sstorage.ENCODE_KECCAK_256, miner) if err != nil { log.Crit("open failed", "error", err) } From 43e02092a0cff1933825c63793fb257f4d1ad103 Mon Sep 17 00:00:00 2001 From: pingke Date: Tue, 25 Apr 2023 01:02:12 +0800 Subject: [PATCH 20/23] update contract bytecode --- core/vm/contracts.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index d7a34788df68..13ea6f6da286 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -678,7 +678,7 @@ var ( systemContracts = map[common.Address][]byte{ // Get the url of PrecompileManager: https://github.com/ethstorage/storage-contracts/blob/developing/contracts/DecentralizedKVDaggerHashimoto.sol // contract at 0x0000000000000000000000000000000003330001 is complied DecentralizedKVDaggerHashimoto() + 0.8.16 solc (enable optimized) - common.HexToAddress("0x0000000000000000000000000000000003330001"): common.Hex2Bytes("6080604052600436106102675760003560e01c806378e9792511610144578063b2aebe88116100b6578063d32897131161007a578063d328971314610962578063d4044b3314610996578063dca00511146109ca578063dd7e57d4146109ed578063df80ca5514610a04578063e7a84c4614610a1957600080fd5b8063b2aebe881461087f578063bf8b887c1461089f578063c4a942cb146108da578063c5d3490c1461090e578063ca2af6231461094257600080fd5b8063919c6eae11610108578063919c6eae1461074157806395bc267314610775578063a097365f14610795578063a4a8435e146107c9578063afd5644d146107fd578063b1e1a3441461085f57600080fd5b806378e9792514610675578063812d2e72146106a95780638612af34146106dd5780638891ce9c1461070a578063896b49911461072a57600080fd5b806349bdd6f5116101dd5780636d951bc5116101a15780636d951bc5146105295780636da6d51e1461055d57806372f2b9dc1461058c578063739b482f146105a657806373e8b3d4146105da578063749cf2821461064857600080fd5b806349bdd6f51461044d5780634e86235e1461046d57806354b02ba4146104a15780636620dfc5146104d55780636cece5f81461050957600080fd5b806327c845dc1161022f57806327c845dc146102d357806328de3c9b1461033a578063390df0b61461039e5780633cb2fecc146103d2578063429dd7ad1461040657806344e77d991461043a57600080fd5b80630fce307b1461026c57806315853983146102b35780631aff59e2146102d55780631ccbc6da146102f5578063258ae5821461030a575b600080fd5b34801561027857600080fd5b506102a07fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e681565b6040519081526020015b60405180910390f35b3480156102bf57600080fd5b506102d36102ce3660046129c1565b610a30565b005b3480156102e157600080fd5b506102d36102f0366004612acf565b610a49565b34801561030157600080fd5b506102a0610b3f565b34801561031657600080fd5b5061032a610325366004612af1565b610b4f565b60405190151581526020016102aa565b34801561034657600080fd5b5061037e610355366004612b37565b600360208190526000918252604090912080546001820154600283015492909301549092919084565b6040805194855260208501939093529183015260608201526080016102aa565b3480156103aa57600080fd5b506102a07fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e681565b3480156103de57600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000081565b34801561041257600080fd5b506000546104249064ffffffffff1681565b60405164ffffffffff90911681526020016102aa565b6102d3610448366004612af1565b610c7b565b34801561045957600080fd5b506102d3610468366004612b50565b610eab565b34801561047957600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000c81565b3480156104ad57600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000002881565b3480156104e157600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000081565b34801561051557600080fd5b506102d3610524366004612b95565b6110d9565b34801561053557600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000006481565b34801561056957600080fd5b506105746203330281565b6040516001600160a01b0390911681526020016102aa565b34801561059857600080fd5b5060045461032a9060ff1681565b3480156105b257600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000040081565b3480156105e657600080fd5b5061032a6105f5366004612b37565b60408051336020808301919091528183019390935281518082038301815260609091018252805190830120600090815260019092529081902054600160401b9004901b67ffffffffffffffff1916151590565b34801561065457600080fd5b50610668610663366004612beb565b6111a3565b6040516102aa9190612c67565b34801561068157600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000181565b3480156106b557600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000012c81565b3480156106e957600080fd5b506106f2600281565b6040516001600160401b0390911681526020016102aa565b34801561071657600080fd5b50610668610725366004612c7a565b6112b4565b34801561073657600080fd5b506105746203330581565b34801561074d57600080fd5b506102a07f00000000000000000000000000000000000000000000000000000000000003e881565b34801561078157600080fd5b506102d3610790366004612b37565b6113ad565b3480156107a157600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000100081565b3480156107d557600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000081565b34801561080957600080fd5b506102a0610818366004612b37565b6040805133602080830191909152818301939093528151808203830181526060909101825280519083012060009081526001909252902054600160281b900462ffffff1690565b34801561086b57600080fd5b5061032a61087a366004612cb3565b6113ba565b34801561088b57600080fd5b506102d361089a366004612acf565b6115dc565b3480156108ab57600080fd5b506108cc6108ba366004612af1565b80519181526020820181209181529091565b6040516102aa929190612d86565b3480156108e657600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000100081565b34801561091a57600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000681565b34801561094e57600080fd5b5061066861095d366004612d9f565b6116a3565b34801561096e57600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000001081565b3480156109a257600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000001281565b3480156109d657600080fd5b506106686109e5366004612af1565b606092915050565b3480156109f957600080fd5b506105746203330381565b348015610a1057600080fd5b506102a0611789565b348015610a2557600080fd5b506105746203330481565b610a4042888888888888886117cd565b50505050505050565b60045460ff1615610a965760405162461bcd60e51b8152602060048201526012602482015271185b1c9958591e481a5b9a5d081cda185c9960721b60448201526064015b60405180910390fd5b6004805460ff1916600190811790915560036020527f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92f008390557f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff8290556000527fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054d919091557fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c55565b6000610b4a426118ea565b905090565b60408051336020820152908101839052600090819060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff81168652600160281b810462ffffff1693860193909352600160401b909204831b67ffffffffffffffff1916928401839052935003610c085760405162461bcd60e51b815260206004820152600c60248201526b1add881b9bdd08195e1a5cdd60a21b6044820152606401610a8d565b8351816020015162ffffff1614610c2457600092505050610c73565b6000610c50857f000000000000000000000000000000000000000000000000000000000000100061193f565b9050806001600160401b03191682604001516001600160401b0319161493505050505b92915050565b565b7f000000000000000000000000000000000000000000000000000000000000100081511115610cdd5760405162461bcd60e51b815260206004820152600e60248201526d6461746120746f6f206c6172676560901b6044820152606401610a8d565b610ce5611b70565b6040805133602082015290810183905260009060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff81168652600160281b810462ffffff1693860193909352600160401b909204831b67ffffffffffffffff1916928401839052935003610dfa57610d6d610b3f565b341015610db15760405162461bcd60e51b81526020600482015260126024820152711b9bdd08195b9bdd59da081c185e5b595b9d60721b6044820152606401610a8d565b6000805464ffffffffff90811680845282526002602052604082208490559054610ddd91166001612e16565b6000805464ffffffffff191664ffffffffff929092169190911790555b825162ffffff166020820152610e30837f000000000000000000000000000000000000000000000000000000000000100061193f565b67ffffffffffffffff19908116604080840191825260008581526001602090815290829020855181549287015194519384901c600160401b0262ffffff909516600160281b029290951664ffffffffff909516948517919091176001600160401b031692909217909155610ea59190856110d9565b50505050565b6040805133602082015290810183905260009060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff8116808752600160281b820462ffffff1694870194909452600160401b9004841b67ffffffffffffffff191693850184905290945090919003610f665760405162461bcd60e51b815260206004820152600c60248201526b1add881b9bdd08195e1a5cdd60a21b6044820152606401610a8d565b6040805160608101825260008082526020808301828152838501838152888452600192839052858420945185549251915190961c600160401b0262ffffff91909116600160281b0267ffffffffffffffff199290921664ffffffffff96871617919091176001600160401b031617909255805490926002928492610fea9216612e3b565b64ffffffffff908116825260208083019390935260409182016000908120548683168083526002808752858420839055828452600196879052948320805464ffffffffff191690911790558154909550909384926110489216612e3b565b64ffffffffff9081168252602082019290925260400160009081209290925590546110769160019116612e3b565b6000805464ffffffffff191664ffffffffff92831690811790915561109c9184166115dc565b846001600160a01b03166108fc6110b1610b3f565b6040518115909202916000818181858888f19350505050158015610a40573d6000803e3d6000fd5b604080519083901c9060009062033302906110fc90879085908790602001612e59565b60408051601f198184030181529082905261111691612e81565b6000604051808303816000865af19150503d8060008114611153576040519150601f19603f3d011682016040523d82523d6000602084013e611158565b606091505b505090508061119c5760405162461bcd60e51b815260206004820152601060248201526f6661696c656420746f2070757452617760801b6044820152606401610a8d565b5050505050565b6060816000036111c257506040805160008152602081019091526112ad565b6040805133602082015290810185905260009060600160408051808303601f1901815282825280516020918201206000818152600183528390206060850184525464ffffffffff81168552600160281b810462ffffff16928501839052600160401b9004831b67ffffffffffffffff191692840192909252909250851061125b57505060408051600081526020810190915290506112ad565b602081015162ffffff1661126f8686612e9d565b111561128d5784816020015162ffffff1661128a9190612eb0565b93505b6112a88160400151826000015164ffffffffff1687876112b4565b925050505b9392505050565b6040805185821c6020820181905291810185905260608082018590526080820184905291906000908190620333039060a00160408051601f198184030181529082905261130091612e81565b600060405180830381855afa9150503d806000811461133b576040519150601f19603f3d011682016040523d82523d6000602084013e611340565b606091505b50915091508161138b5760405162461bcd60e51b81526020600482015260166024820152756661696c656420746f2073797374656d47657452617760501b6044820152606401610a8d565b8080602001905181019061139f9190612ec3565b93505050505b949350505050565b6113b78133610eab565b50565b600060017f00000000000000000000000000000000000000000000000000000000000000001b816113eb8288612f46565b90506000866020015162ffffff167f0000000000000000000000000000000000000000000000000000000000001000836114259190612f5a565b1061145e577fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e685805190602001201493505050506113a5565b817f0000000000000000000000000000000000000000000000000000000000001000600189602001516114919190612f79565b62ffffff166114a09190612f95565b036115a35760006114d17f000000000000000000000000000000000000000000000000000000000000100084612f5a565b886020015162ffffff166114e59190612eb0565b6020870181902092509050600061151c827f0000000000000000000000000000000000000000000000000000000000001000612eb0565b9050801561159c576000816001600160401b0381111561153e5761153e6127ce565b6040519080825280601f01601f191660200182016040528015611568576020820181803683370190505b5090506000808360208401209150838560208c0101209050808214611598576000985050505050505050506113a5565b5050505b50506115ac565b50835160208501205b60006115b9828489611b79565b604089015167ffffffffffffffff19918216911614945050505050949350505050565b6040805160208101849052908101829052600090620333059060600160408051601f198184030181529082905261161291612e81565b6000604051808303816000865af19150503d806000811461164f576040519150601f19603f3d011682016040523d82523d6000602084013e611654565b606091505b505090508061169e5760405162461bcd60e51b81526020600482015260166024820152756661696c656420746f2073797374656d50757452617760501b6044820152606401610a8d565b505050565b6040805160609185901c90600090819062033304906116cf906002908b9087908b908b90602001612fa9565b60408051601f19818403018152908290526116e991612e81565b600060405180830381855afa9150503d8060008114611724576040519150601f19603f3d011682016040523d82523d6000602084013e611729565b606091505b50915091508161138b5760405162461bcd60e51b815260206004820152602560248201527f6661696c656420746f2073797374656d556e6d61736b4368756e6b57697468456044820152640e8d0c2e6d60db1b6064820152608401610a8d565b600080546117c19064ffffffffff167f00000000000000000000000000000000000000000000000000000000000000061c6001612e16565b64ffffffffff16905090565b878411156118115760405162461bcd60e51b81526020600482015260116024820152706d696e6564547320746f6f206c6172676560781b6044820152606401610a8d565b6001861b600080806118248b858a611cad565b604080516020808201939093526001600160a01b038e1681830152606081018d905260808082018d90528251808303909101815260a090910190915280519101209194509250905061187a8b8b838c8a8a611e97565b9050600061188a84600019612f95565b9050808211156118cd5760405162461bcd60e51b815260206004820152600e60248201526d0c8d2cccc40dcdee840dac2e8c6d60931b6044820152606401610a8d565b506118dc8b858b8b8686612280565b505050505050505050505050565b6000610c737f000000000000000000000000000000000000000000000000000000000000000061193a7f000000000000000000000000000000000000000000000000000000000000000185612eb0565b612422565b6000825160000361195257506000610c73565b60008260018486516119649190612e9d565b61196e9190612eb0565b6119789190612f95565b9050600060018211156119935761198e82612463565b611996565b60015b90506000816001600160401b038111156119b2576119b26127ce565b6040519080825280602002602001820160405280156119db578160200160208202803683370190505b50905060005b82811015611a61576000806119f68884612f5a565b905088518110611a07575050611a61565b6000818a51611a169190612eb0565b9050888110611a225750875b808260208c010120925082858581518110611a3f57611a3f612ff6565b6020026020010181815250505050508080611a599061300c565b9150506119e1565b508192505b82600114611b4a5760005b611a7c600285612f95565b811015611b375781611a8f826002612f5a565b81518110611a9f57611a9f612ff6565b602002602001015182826002611ab59190612f5a565b611ac0906001612e9d565b81518110611ad057611ad0612ff6565b6020026020010151604051602001611af2929190918252602082015260400190565b60405160208183030381529060405280519060200120828281518110611b1a57611b1a612ff6565b602090810291909101015280611b2f8161300c565b915050611a71565b50611b43600284612f95565b9250611a66565b80600081518110611b5d57611b5d612ff6565b6020026020010151935050505092915050565b610c79426124a0565b805160009084906001811b8510611bc65760405162461bcd60e51b81526020600482015260116024820152706368756e6b4964206f766572666c6f777360781b6044820152606401610a8d565b60005b81811015611ca257611bdc600287612f46565b600003611c355782858281518110611bf657611bf6612ff6565b6020026020010151604051602001611c18929190918252602082015260400190565b604051602081830303815290604052805190602001209250611c83565b848181518110611c4757611c47612ff6565b602002602001015183604051602001611c6a929190918252602082015260400190565b6040516020818303038152906040528051906020012092505b611c8e600287612f95565b955080611c9a8161300c565b915050611bc9565b509095945050505050565b600060606000846001600160401b03811115611ccb57611ccb6127ce565b604051908082528060200260200182016040528015611cf4578160200160208202803683370190505b50600093509150829050805b85811015611e8d576000611d148289612e9d565b6000818152600360205260409020600181015491925090871015611d6e5760405162461bcd60e51b81526020600482015260116024820152701b5a5b9959151cc81d1bdbc81cdb585b1b607a1b6044820152606401610a8d565b611dfc81887f000000000000000000000000000000000000000000000000000000000000012c7f00000000000000000000000000000000000000000000000000000000000000287f00000000000000000000000000000000000000000000000000000000000004007f0000000000000000000000000000000000000000000000000000000000000064612593565b858481518110611e0e57611e0e612ff6565b602002602001018181525050848381518110611e2c57611e2c612ff6565b602002602001015186611e3f9190612e9d565b81546040805160208101889052908101859052606081019190915290965060800160405160208183030381529060405280519060200120935050508080611e859061300c565b915050611d00565b5093509350939050565b60007f0000000000000000000000000000000000000000000000000000000000000010825114611f095760405162461bcd60e51b815260206004820152601f60248201527f6461746120767320636865636b733a206c656e677468206d69736d61746368006044820152606401610a8d565b7f0000000000000000000000000000000000000000000000000000000000000010835114611f835760405162461bcd60e51b815260206004820152602160248201527f70726f6f667320767320636865636b733a206c656e677468206d69736d6174636044820152600d60fb1b6064820152608401610a8d565b60007f0000000000000000000000000000000000000000000000000000000000000000611fd0887f0000000000000000000000000000000000000000000000000000000000000006612e9d565b611fda9190612e9d565b6001901b905060005b7f0000000000000000000000000000000000000000000000000000000000000010811015612273577f000000000000000000000000000000000000000000000000000000000000100084828151811061203e5761203e612ff6565b602002602001015151146120895760405162461bcd60e51b8152602060048201526012602482015271696e76616c69642070726f6f662073697a6560701b6044820152606401610a8d565b60006120958389612f46565b905060006120e37f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000006612e9d565b6120f0908c901b83612e9d565b7f000000000000000000000000000000000000000000000000000000000000000081901c6000818152600260209081526040808320548352600182528083208151606081018352905464ffffffffff81168252600160281b810462ffffff1693820193909352600160401b909204811b67ffffffffffffffff19169082018190528a51949550929390926121a2918691908e908d908b90811061219557612195612ff6565b60200260200101516116a3565b90506121c984838c89815181106121bb576121bb612ff6565b6020026020010151846113ba565b61220c5760405162461bcd60e51b815260206004820152601460248201527334b73b30b634b21030b1b1b2b9b990383937b7b360611b6044820152606401610a8d565b600089878151811061222057612220612ff6565b6020908102919091018101519d8e527f00000000000000000000000000000000000000000000000000000000000010009081018e209d525085945061226b935084925061300c915050565b915050611fe3565b5094979650505050505050565b60008061228b611789565b905060005b878110156123665760006122a4828b612e9d565b9050828111612353576000818152600360205260409020600181015461230e907f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000061b908a612661565b6123189086612e9d565b9450612351600360008481526020019081526020016000208989868151811061234357612343612ff6565b6020026020010151896126c0565b505b508061235e8161300c565b915050612290565b5060006127106123967f00000000000000000000000000000000000000000000000000000000000003e885612f5a565b6123a09190612f95565b905060006123ae8285612eb0565b604051909150419083156108fc029084906000818181858888f193505050501580156123de573d6000803e3d6000fd5b506040516001600160a01b0389169082156108fc029083906000818181858888f19350505050158015612415573d6000803e3d6000fd5b5050505050505050505050565b600060806124507f0000000000000000000000000000000000000000000000000000000000000000846126e4565b61245a9085612f5a565b901c9392505050565b6000612470600183612eb0565b91505b61247e600183612eb0565b82161561249957612490600183612eb0565b82169150612473565b5060011b90565b60005460017f000000000000000000000000000000000000000000000000000000000000000681901b916124dd9164ffffffffff90911690612e16565b64ffffffffff166124ee9190612f46565b6000036113b757600080547f00000000000000000000000000000000000000000000000000000000000000069061252d9064ffffffffff166001612e16565b64ffffffffff16901c60016125429190612e16565b64ffffffffff166000818152600360208190526040822060019081018690559293509161256f9084612eb0565b81526020808201929092526040908101600090812054938152600390925290205550565b6000808760010154876125a69190612eb0565b6002890154909150868210156125fd5784816125c28885612f95565b6125cd906001612eb0565b6125d79190612f5a565b6125e19190612f95565b6125eb9082612e9d565b9050838110156125f85750825b612655565b60008582600161260d8a87612f95565b6126179190612eb0565b6126219190612f5a565b61262b9190612f95565b9050816126388683612e9d565b111561264657849150612653565b6126508183612eb0565b91505b505b98975050505050505050565b60006113a5846126917f000000000000000000000000000000000000000000000000000000000000000186612eb0565b6126bb7f000000000000000000000000000000000000000000000000000000000000000186612eb0565b6126f0565b60038401546126d0906001612e9d565b600385015583556002830155600190910155565b60006112ad8383612766565b6000608061271e7f0000000000000000000000000000000000000000000000000000000000000000846126e4565b6127487f0000000000000000000000000000000000000000000000000000000000000000866126e4565b6127529190612eb0565b61275c9086612f5a565b901c949350505050565b6000600160801b5b82156112ad578260011660010361279057608061278b8583612f5a565b901c90505b608061279c8580612f5a565b901c93506127ab600284612f95565b925061276e565b80356001600160a01b03811681146127c957600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715612806576128066127ce565b60405290565b604051601f8201601f191681016001600160401b0381118282101715612834576128346127ce565b604052919050565b60006001600160401b03821115612855576128556127ce565b5060051b60200190565b600082601f83011261287057600080fd5b813560206128856128808361283c565b61280c565b82815260059290921b840181019181810190868411156128a457600080fd5b8286015b848110156128bf57803583529183019183016128a8565b509695505050505050565b60006001600160401b038211156128e3576128e36127ce565b50601f01601f191660200190565b600082601f83011261290257600080fd5b8135612910612880826128ca565b81815284602083860101111561292557600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f83011261295357600080fd5b813560206129636128808361283c565b82815260059290921b8401810191818101908684111561298257600080fd5b8286015b848110156128bf5780356001600160401b038111156129a55760008081fd5b6129b38986838b01016128f1565b845250918301918301612986565b600080600080600080600060e0888a0312156129dc57600080fd5b87359650602088013595506129f3604089016127b2565b9450606088013593506080880135925060a08801356001600160401b0380821115612a1d57600080fd5b818a0191508a601f830112612a3157600080fd5b8135612a3f6128808261283c565b8082825260208201915060208360051b86010192508d831115612a6157600080fd5b602085015b83811015612a9a578481351115612a7c57600080fd5b612a8c8f6020833589010161285f565b835260209283019201612a66565b509550505060c08a0135915080821115612ab357600080fd5b50612ac08a828b01612942565b91505092959891949750929550565b60008060408385031215612ae257600080fd5b50508035926020909101359150565b60008060408385031215612b0457600080fd5b8235915060208301356001600160401b03811115612b2157600080fd5b612b2d858286016128f1565b9150509250929050565b600060208284031215612b4957600080fd5b5035919050565b60008060408385031215612b6357600080fd5b82359150612b73602084016127b2565b90509250929050565b803567ffffffffffffffff19811681146127c957600080fd5b600080600060608486031215612baa57600080fd5b83359250612bba60208501612b7c565b915060408401356001600160401b03811115612bd557600080fd5b612be1868287016128f1565b9150509250925092565b600080600060608486031215612c0057600080fd5b505081359360208301359350604090920135919050565b60005b83811015612c32578181015183820152602001612c1a565b50506000910152565b60008151808452612c53816020860160208601612c17565b601f01601f19169290920160200192915050565b6020815260006112ad6020830184612c3b565b60008060008060808587031215612c9057600080fd5b612c9985612b7c565b966020860135965060408601359560600135945092505050565b60008060008084860360c0811215612cca57600080fd5b853594506060601f1982011215612ce057600080fd5b50612ce96127e4565b602086013564ffffffffff81168114612d0157600080fd5b8152604086013562ffffff81168114612d1957600080fd5b6020820152612d2a60608701612b7c565b6040820152925060808501356001600160401b0380821115612d4b57600080fd5b612d578883890161285f565b935060a0870135915080821115612d6d57600080fd5b50612d7a878288016128f1565b91505092959194509250565b8281526040602082015260006113a56040830184612c3b565b60008060008060808587031215612db557600080fd5b84356001600160401b038082168214612dcd57600080fd5b819550612ddc60208801612b7c565b9450612dea604088016127b2565b93506060870135915080821115612d6d57600080fd5b634e487b7160e01b600052601160045260246000fd5b64ffffffffff818116838216019080821115612e3457612e34612e00565b5092915050565b64ffffffffff828116828216039080821115612e3457612e34612e00565b838152826020820152606060408201526000612e786060830184612c3b565b95945050505050565b60008251612e93818460208701612c17565b9190910192915050565b80820180821115610c7357610c73612e00565b81810381811115610c7357610c73612e00565b600060208284031215612ed557600080fd5b81516001600160401b03811115612eeb57600080fd5b8201601f81018413612efc57600080fd5b8051612f0a612880826128ca565b818152856020838501011115612f1f57600080fd5b612e78826020830160208601612c17565b634e487b7160e01b600052601260045260246000fd5b600082612f5557612f55612f30565b500690565b6000816000190483118215151615612f7457612f74612e00565b500290565b62ffffff828116828216039080821115612e3457612e34612e00565b600082612fa457612fa4612f30565b500490565b6001600160401b03868116825285166020820152604081018490526001600160a01b038316606082015260a060808201819052600090612feb90830184612c3b565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161301e5761301e612e00565b506001019056fea2646970667358221220818e32029f91fbabb52182f57c50b99971de6a63ca4b85bf469137b3d7523f7264736f6c63430008100033"), + common.HexToAddress("0x0000000000000000000000000000000003330001"): common.Hex2Bytes("60806040526004361061025c5760003560e01c8063749cf28211610144578063b1e1a344116100b6578063d32897131161007a578063d32897131461091c578063d4044b3314610950578063dca0051114610984578063dd7e57d4146109a7578063df80ca55146109be578063e7a84c46146109d357600080fd5b8063b1e1a34414610854578063b2aebe8814610874578063c4a942cb14610894578063c5d3490c146108c8578063ca2af623146108fc57600080fd5b8063896b499111610108578063896b49911461071f578063919c6eae1461073657806395bc26731461076a578063a097365f1461078a578063a4a8435e146107be578063afd5644d146107f257600080fd5b8063749cf2821461063d57806378e979251461066a578063812d2e721461069e5780638612af34146106d25780638891ce9c146106ff57600080fd5b8063429dd7ad116101dd5780636620dfc5116101a15780636620dfc5146104e45780636cece5f8146105185780636d951bc5146105385780636da6d51e1461056c578063739b482f1461059b57806373e8b3d4146105cf57600080fd5b8063429dd7ad1461041557806344e77d991461044957806349bdd6f51461045c5780634e86235e1461047c57806354b02ba4146104b057600080fd5b8063258ae58211610224578063258ae5821461032957806327c845dc146102f257806328de3c9b14610349578063390df0b6146103ad5780633cb2fecc146103e157600080fd5b806304cbaa51146102615780630fce307b1461029057806315853983146102d25780631aff59e2146102f45780631ccbc6da14610314575b600080fd5b34801561026d57600080fd5b5060045461027b9060ff1681565b60405190151581526020015b60405180910390f35b34801561029c57600080fd5b506102c47f6387d10d3fe6d4fcb51c9f9caf0c34f88526afc3d0c6a2b80adfceeea2b4a70181565b604051908152602001610287565b3480156102de57600080fd5b506102f26102ed366004612b7d565b6109ea565b005b34801561030057600080fd5b506102f261030f366004612c8b565b610a03565b34801561032057600080fd5b506102c4610b04565b34801561033557600080fd5b5061027b610344366004612cad565b610b14565b34801561035557600080fd5b5061038d610364366004612cf3565b600360208190526000918252604090912080546001820154600283015492909301549092919084565b604080519485526020850193909352918301526060820152608001610287565b3480156103b957600080fd5b506102c47fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e681565b3480156103ed57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000081565b34801561042157600080fd5b506000546104339064ffffffffff1681565b60405164ffffffffff9091168152602001610287565b6102f2610457366004612cad565b610c40565b34801561046857600080fd5b506102f2610477366004612d0c565b610e70565b34801561048857600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000001181565b3480156104bc57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000002881565b3480156104f057600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000581565b34801561052457600080fd5b506102f2610533366004612d51565b61109e565b34801561054457600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000006481565b34801561057857600080fd5b506105836203330281565b6040516001600160a01b039091168152602001610287565b3480156105a757600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000040081565b3480156105db57600080fd5b5061027b6105ea366004612cf3565b60408051336020808301919091528183019390935281518082038301815260609091018252805190830120600090815260019092529081902054600160401b9004901b67ffffffffffffffff1916151590565b34801561064957600080fd5b5061065d610658366004612da7565b611168565b6040516102879190612e23565b34801561067657600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000181565b3480156106aa57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000012c81565b3480156106de57600080fd5b506106e7600281565b6040516001600160401b039091168152602001610287565b34801561070b57600080fd5b5061065d61071a366004612e36565b611279565b34801561072b57600080fd5b506105836203330581565b34801561074257600080fd5b506102c47f00000000000000000000000000000000000000000000000000000000000003e881565b34801561077657600080fd5b506102f2610785366004612cf3565b611372565b34801561079657600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000002000081565b3480156107ca57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000081565b3480156107fe57600080fd5b506102c461080d366004612cf3565b6040805133602080830191909152818301939093528151808203830181526060909101825280519083012060009081526001909252902054600160281b900462ffffff1690565b34801561086057600080fd5b5061027b61086f366004612e6f565b61137f565b34801561088057600080fd5b506102f261088f366004612c8b565b6115a1565b3480156108a057600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000100081565b3480156108d457600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000a81565b34801561090857600080fd5b5061065d610917366004612f42565b61166f565b34801561092857600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000001081565b34801561095c57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000001b81565b34801561099057600080fd5b5061065d61099f366004612cad565b606092915050565b3480156109b357600080fd5b506105836203330381565b3480156109ca57600080fd5b506102c4611755565b3480156109df57600080fd5b506105836203330481565b6109fa4288888888888888611799565b50505050505050565b60045460ff1615610a5b5760405162461bcd60e51b815260206004820152601960248201527f616c726561647920696e697469616c697a65642073686172640000000000000060448201526064015b60405180910390fd5b6004805460ff1916600190811790915560036020527f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92f008390557f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff8290556000527fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054d919091557fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c55565b6000610b0f426118d0565b905090565b60408051336020820152908101839052600090819060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff81168652600160281b810462ffffff1693860193909352600160401b909204831b67ffffffffffffffff1916928401839052935003610bcd5760405162461bcd60e51b815260206004820152600c60248201526b1add881b9bdd08195e1a5cdd60a21b6044820152606401610a52565b8351816020015162ffffff1614610be957600092505050610c38565b6000610c15857f0000000000000000000000000000000000000000000000000000000000001000611925565b9050806001600160401b03191682604001516001600160401b0319161493505050505b92915050565b565b7f000000000000000000000000000000000000000000000000000000000002000081511115610ca25760405162461bcd60e51b815260206004820152600e60248201526d6461746120746f6f206c6172676560901b6044820152606401610a52565b610caa611b56565b6040805133602082015290810183905260009060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff81168652600160281b810462ffffff1693860193909352600160401b909204831b67ffffffffffffffff1916928401839052935003610dbf57610d32610b04565b341015610d765760405162461bcd60e51b81526020600482015260126024820152711b9bdd08195b9bdd59da081c185e5b595b9d60721b6044820152606401610a52565b6000805464ffffffffff90811680845282526002602052604082208490559054610da291166001612fb9565b6000805464ffffffffff191664ffffffffff929092169190911790555b825162ffffff166020820152610df5837f0000000000000000000000000000000000000000000000000000000000001000611925565b67ffffffffffffffff19908116604080840191825260008581526001602090815290829020855181549287015194519384901c600160401b0262ffffff909516600160281b029290951664ffffffffff909516948517919091176001600160401b031692909217909155610e6a91908561109e565b50505050565b6040805133602082015290810183905260009060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff8116808752600160281b820462ffffff1694870194909452600160401b9004841b67ffffffffffffffff191693850184905290945090919003610f2b5760405162461bcd60e51b815260206004820152600c60248201526b1add881b9bdd08195e1a5cdd60a21b6044820152606401610a52565b6040805160608101825260008082526020808301828152838501838152888452600192839052858420945185549251915190961c600160401b0262ffffff91909116600160281b0267ffffffffffffffff199290921664ffffffffff96871617919091176001600160401b031617909255805490926002928492610faf9216612fde565b64ffffffffff908116825260208083019390935260409182016000908120548683168083526002808752858420839055828452600196879052948320805464ffffffffff1916909117905581549095509093849261100d9216612fde565b64ffffffffff90811682526020820192909252604001600090812092909255905461103b9160019116612fde565b6000805464ffffffffff191664ffffffffff9283169081179091556110619184166115a1565b846001600160a01b03166108fc611076610b04565b6040518115909202916000818181858888f193505050501580156109fa573d6000803e3d6000fd5b604080519083901c9060009062033302906110c190879085908790602001612ffc565b60408051601f19818403018152908290526110db91613024565b6000604051808303816000865af19150503d8060008114611118576040519150601f19603f3d011682016040523d82523d6000602084013e61111d565b606091505b50509050806111615760405162461bcd60e51b815260206004820152601060248201526f6661696c656420746f2070757452617760801b6044820152606401610a52565b5050505050565b6060816000036111875750604080516000815260208101909152611272565b6040805133602082015290810185905260009060600160408051808303601f1901815282825280516020918201206000818152600183528390206060850184525464ffffffffff81168552600160281b810462ffffff16928501839052600160401b9004831b67ffffffffffffffff19169284019290925290925085106112205750506040805160008152602081019091529050611272565b602081015162ffffff166112348686613040565b11156112525784816020015162ffffff1661124f9190613053565b93505b61126d8160400151826000015164ffffffffff168787611279565b925050505b9392505050565b6040805185821c6020820181905291810185905260608082018590526080820184905291906000908190620333039060a00160408051601f19818403018152908290526112c591613024565b600060405180830381855afa9150503d8060008114611300576040519150601f19603f3d011682016040523d82523d6000602084013e611305565b606091505b5091509150816113505760405162461bcd60e51b81526020600482015260166024820152756661696c656420746f2073797374656d47657452617760501b6044820152606401610a52565b808060200190518101906113649190613066565b93505050505b949350505050565b61137c8133610e70565b50565b600060017f00000000000000000000000000000000000000000000000000000000000000051b816113b082886130e9565b90506000866020015162ffffff167f0000000000000000000000000000000000000000000000000000000000001000836113ea91906130fd565b10611423577fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e6858051906020012014935050505061136a565b817f000000000000000000000000000000000000000000000000000000000000100060018960200151611456919061311c565b62ffffff166114659190613138565b036115685760006114967f0000000000000000000000000000000000000000000000000000000000001000846130fd565b886020015162ffffff166114aa9190613053565b602087018190209250905060006114e1827f0000000000000000000000000000000000000000000000000000000000001000613053565b90508015611561576000816001600160401b038111156115035761150361298a565b6040519080825280601f01601f19166020018201604052801561152d576020820181803683370190505b5090506000808360208401209150838560208c010120905080821461155d5760009850505050505050505061136a565b5050505b5050611571565b50835160208501205b600061157e828489611b5f565b604089015167ffffffffffffffff19918216911614945050505050949350505050565b6040805160208101849052908101829052600090620333059060600160408051601f19818403018152908290526115d791613024565b6000604051808303816000865af19150503d8060008114611614576040519150601f19603f3d011682016040523d82523d6000602084013e611619565b606091505b505090508061166a5760405162461bcd60e51b815260206004820152601960248201527f6661696c656420746f2073797374656d52656d6f7665526177000000000000006044820152606401610a52565b505050565b6040805160609185901c906000908190620333049061169b906002908b9087908b908b9060200161314c565b60408051601f19818403018152908290526116b591613024565b600060405180830381855afa9150503d80600081146116f0576040519150601f19603f3d011682016040523d82523d6000602084013e6116f5565b606091505b5091509150816113505760405162461bcd60e51b815260206004820152602560248201527f6661696c656420746f2073797374656d556e6d61736b4368756e6b57697468456044820152640e8d0c2e6d60db1b6064820152608401610a52565b6000805461178d9064ffffffffff167f000000000000000000000000000000000000000000000000000000000000000a1c6001612fb9565b64ffffffffff16905090565b878411156117dd5760405162461bcd60e51b81526020600482015260116024820152706d696e6564547320746f6f206c6172676560781b6044820152606401610a52565b6001861b600080806117f08b858a611c93565b604080516020808201939093526001600160a01b038e1681830152606081018d905260808082018d90528251808303909101815260a09091019091528051910120919450925090506118468b8b838c8a8a611e7d565b9050600061185684600019613138565b905060006118638361224a565b61186c8361224a565b60405160200161187d929190613199565b60408051601f19818403018152919052905080828411156118b15760405162461bcd60e51b8152600401610a529190612e23565b5050506118c28b858b8b86866122a1565b505050505050505050505050565b6000610c387f00000000000000000000000000000000000000000000000000000000000000006119207f000000000000000000000000000000000000000000000000000000000000000185613053565b612443565b6000825160000361193857506000610c38565b600082600184865161194a9190613040565b6119549190613053565b61195e9190613138565b9050600060018211156119795761197482612484565b61197c565b60015b90506000816001600160401b038111156119985761199861298a565b6040519080825280602002602001820160405280156119c1578160200160208202803683370190505b50905060005b82811015611a47576000806119dc88846130fd565b9050885181106119ed575050611a47565b6000818a516119fc9190613053565b9050888110611a085750875b808260208c010120925082858581518110611a2557611a25613205565b6020026020010181815250505050508080611a3f9061321b565b9150506119c7565b508192505b82600114611b305760005b611a62600285613138565b811015611b1d5781611a758260026130fd565b81518110611a8557611a85613205565b602002602001015182826002611a9b91906130fd565b611aa6906001613040565b81518110611ab657611ab6613205565b6020026020010151604051602001611ad8929190918252602082015260400190565b60405160208183030381529060405280519060200120828281518110611b0057611b00613205565b602090810291909101015280611b158161321b565b915050611a57565b50611b29600284613138565b9250611a4c565b80600081518110611b4357611b43613205565b6020026020010151935050505092915050565b610c3e426124c1565b805160009084906001811b8510611bac5760405162461bcd60e51b81526020600482015260116024820152706368756e6b4964206f766572666c6f777360781b6044820152606401610a52565b60005b81811015611c8857611bc26002876130e9565b600003611c1b5782858281518110611bdc57611bdc613205565b6020026020010151604051602001611bfe929190918252602082015260400190565b604051602081830303815290604052805190602001209250611c69565b848181518110611c2d57611c2d613205565b602002602001015183604051602001611c50929190918252602082015260400190565b6040516020818303038152906040528051906020012092505b611c74600287613138565b955080611c808161321b565b915050611baf565b509095945050505050565b600060606000846001600160401b03811115611cb157611cb161298a565b604051908082528060200260200182016040528015611cda578160200160208202803683370190505b50600093509150829050805b85811015611e73576000611cfa8289613040565b6000818152600360205260409020600181015491925090871015611d545760405162461bcd60e51b81526020600482015260116024820152701b5a5b9959151cc81d1bdbc81cdb585b1b607a1b6044820152606401610a52565b611de281887f000000000000000000000000000000000000000000000000000000000000012c7f00000000000000000000000000000000000000000000000000000000000000287f00000000000000000000000000000000000000000000000000000000000004007f00000000000000000000000000000000000000000000000000000000000000646125b4565b858481518110611df457611df4613205565b602002602001018181525050848381518110611e1257611e12613205565b602002602001015186611e259190613040565b81546040805160208101889052908101859052606081019190915290965060800160405160208183030381529060405280519060200120935050508080611e6b9061321b565b915050611ce6565b5093509350939050565b60007f0000000000000000000000000000000000000000000000000000000000000010825114611eef5760405162461bcd60e51b815260206004820152601f60248201527f6461746120767320636865636b733a206c656e677468206d69736d61746368006044820152606401610a52565b7f0000000000000000000000000000000000000000000000000000000000000010835114611f695760405162461bcd60e51b815260206004820152602160248201527f70726f6f667320767320636865636b733a206c656e677468206d69736d6174636044820152600d60fb1b6064820152608401610a52565b60007f0000000000000000000000000000000000000000000000000000000000000005611fb6887f000000000000000000000000000000000000000000000000000000000000000a613040565b611fc09190613040565b6001901b905060005b7f000000000000000000000000000000000000000000000000000000000000001081101561223d5760007f000000000000000000000000000000000000000000000000000000000000100090508085838151811061202957612029613205565b602002602001015151146120745760405162461bcd60e51b8152602060048201526012602482015271696e76616c69642070726f6f662073697a6560701b6044820152606401610a52565b6000612080848a6130e9565b905060006120ce7f00000000000000000000000000000000000000000000000000000000000000057f000000000000000000000000000000000000000000000000000000000000000a613040565b6120db908d901b83613040565b7f000000000000000000000000000000000000000000000000000000000000000581901c6000818152600260209081526040808320548352600182528083208151606081018352905464ffffffffff81168252600160281b810462ffffff1693820193909352600160401b909204811b67ffffffffffffffff19169082018190528b519495509293909261218d918691908f908e908c90811061218057612180613205565b602002602001015161166f565b90506121b484838d8a815181106121a6576121a6613205565b60200260200101518461137f565b6121f75760405162461bcd60e51b815260206004820152601460248201527334b73b30b634b21030b1b1b2b9b990383937b7b360611b6044820152606401610a52565b60008a888151811061220b5761220b613205565b602002602001015190508d81526020870181209d508681525050505050505080806122359061321b565b915050611fc9565b5094979650505050505050565b6060816000036122745750506040805180820190915260048152630307830360e41b602082015290565b8160005b811561229757806122888161321b565b915050600882901c9150612278565b61136a8482612682565b6000806122ac611755565b905060005b878110156123875760006122c5828b613040565b9050828111612374576000818152600360205260409020600181015461232f907f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000a1b908a61281d565b6123399086613040565b9450612372600360008481526020019081526020016000208989868151811061236457612364613205565b60200260200101518961287c565b505b508061237f8161321b565b9150506122b1565b5060006127106123b77f00000000000000000000000000000000000000000000000000000000000003e8856130fd565b6123c19190613138565b905060006123cf8285613053565b604051909150419083156108fc029084906000818181858888f193505050501580156123ff573d6000803e3d6000fd5b506040516001600160a01b0389169082156108fc029083906000818181858888f19350505050158015612436573d6000803e3d6000fd5b5050505050505050505050565b600060806124717f0000000000000000000000000000000000000000000000000000000000000000846128a0565b61247b90856130fd565b901c9392505050565b6000612491600183613053565b91505b61249f600183613053565b8216156124ba576124b1600183613053565b82169150612494565b5060011b90565b60005460017f000000000000000000000000000000000000000000000000000000000000000a81901b916124fe9164ffffffffff90911690612fb9565b64ffffffffff1661250f91906130e9565b60000361137c57600080547f000000000000000000000000000000000000000000000000000000000000000a9061254e9064ffffffffff166001612fb9565b64ffffffffff16901c60016125639190612fb9565b64ffffffffff16600081815260036020819052604082206001908101869055929350916125909084613053565b81526020808201929092526040908101600090812054938152600390925290205550565b6000808760010154876125c79190613053565b60028901549091508682101561261e5784816125e38885613138565b6125ee906001613053565b6125f891906130fd565b6126029190613138565b61260c9082613040565b9050838110156126195750825b612676565b60008582600161262e8a87613138565b6126389190613053565b61264291906130fd565b61264c9190613138565b9050816126598683613040565b111561266757849150612674565b6126718183613053565b91505b505b98975050505050505050565b606060006126918360026130fd565b61269c906002613040565b6001600160401b038111156126b3576126b361298a565b6040519080825280601f01601f1916602001820160405280156126dd576020820181803683370190505b509050600360fc1b816000815181106126f8576126f8613205565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061272757612727613205565b60200101906001600160f81b031916908160001a905350600061274b8460026130fd565b612756906001613040565b90505b60018111156127ce576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061278a5761278a613205565b1a60f81b8282815181106127a0576127a0613205565b60200101906001600160f81b031916908160001a90535060049490941c936127c781613234565b9050612759565b5083156112725760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610a52565b600061136a8461284d7f000000000000000000000000000000000000000000000000000000000000000186613053565b6128777f000000000000000000000000000000000000000000000000000000000000000186613053565b6128ac565b600384015461288c906001613040565b600385015583556002830155600190910155565b60006112728383612922565b600060806128da7f0000000000000000000000000000000000000000000000000000000000000000846128a0565b6129047f0000000000000000000000000000000000000000000000000000000000000000866128a0565b61290e9190613053565b61291890866130fd565b901c949350505050565b6000600160801b5b8215611272578260011660010361294c57608061294785836130fd565b901c90505b608061295885806130fd565b901c9350612967600284613138565b925061292a565b80356001600160a01b038116811461298557600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b03811182821017156129c2576129c261298a565b60405290565b604051601f8201601f191681016001600160401b03811182821017156129f0576129f061298a565b604052919050565b60006001600160401b03821115612a1157612a1161298a565b5060051b60200190565b600082601f830112612a2c57600080fd5b81356020612a41612a3c836129f8565b6129c8565b82815260059290921b84018101918181019086841115612a6057600080fd5b8286015b84811015612a7b5780358352918301918301612a64565b509695505050505050565b60006001600160401b03821115612a9f57612a9f61298a565b50601f01601f191660200190565b600082601f830112612abe57600080fd5b8135612acc612a3c82612a86565b818152846020838601011115612ae157600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112612b0f57600080fd5b81356020612b1f612a3c836129f8565b82815260059290921b84018101918181019086841115612b3e57600080fd5b8286015b84811015612a7b5780356001600160401b03811115612b615760008081fd5b612b6f8986838b0101612aad565b845250918301918301612b42565b600080600080600080600060e0888a031215612b9857600080fd5b8735965060208801359550612baf6040890161296e565b9450606088013593506080880135925060a08801356001600160401b0380821115612bd957600080fd5b818a0191508a601f830112612bed57600080fd5b8135612bfb612a3c826129f8565b8082825260208201915060208360051b86010192508d831115612c1d57600080fd5b602085015b83811015612c56578481351115612c3857600080fd5b612c488f60208335890101612a1b565b835260209283019201612c22565b509550505060c08a0135915080821115612c6f57600080fd5b50612c7c8a828b01612afe565b91505092959891949750929550565b60008060408385031215612c9e57600080fd5b50508035926020909101359150565b60008060408385031215612cc057600080fd5b8235915060208301356001600160401b03811115612cdd57600080fd5b612ce985828601612aad565b9150509250929050565b600060208284031215612d0557600080fd5b5035919050565b60008060408385031215612d1f57600080fd5b82359150612d2f6020840161296e565b90509250929050565b803567ffffffffffffffff198116811461298557600080fd5b600080600060608486031215612d6657600080fd5b83359250612d7660208501612d38565b915060408401356001600160401b03811115612d9157600080fd5b612d9d86828701612aad565b9150509250925092565b600080600060608486031215612dbc57600080fd5b505081359360208301359350604090920135919050565b60005b83811015612dee578181015183820152602001612dd6565b50506000910152565b60008151808452612e0f816020860160208601612dd3565b601f01601f19169290920160200192915050565b6020815260006112726020830184612df7565b60008060008060808587031215612e4c57600080fd5b612e5585612d38565b966020860135965060408601359560600135945092505050565b60008060008084860360c0811215612e8657600080fd5b853594506060601f1982011215612e9c57600080fd5b50612ea56129a0565b602086013564ffffffffff81168114612ebd57600080fd5b8152604086013562ffffff81168114612ed557600080fd5b6020820152612ee660608701612d38565b6040820152925060808501356001600160401b0380821115612f0757600080fd5b612f1388838901612a1b565b935060a0870135915080821115612f2957600080fd5b50612f3687828801612aad565b91505092959194509250565b60008060008060808587031215612f5857600080fd5b84356001600160401b038082168214612f7057600080fd5b819550612f7f60208801612d38565b9450612f8d6040880161296e565b93506060870135915080821115612f2957600080fd5b634e487b7160e01b600052601160045260246000fd5b64ffffffffff818116838216019080821115612fd757612fd7612fa3565b5092915050565b64ffffffffff828116828216039080821115612fd757612fd7612fa3565b83815282602082015260606040820152600061301b6060830184612df7565b95945050505050565b60008251613036818460208701612dd3565b9190910192915050565b80820180821115610c3857610c38612fa3565b81810381811115610c3857610c38612fa3565b60006020828403121561307857600080fd5b81516001600160401b0381111561308e57600080fd5b8201601f8101841361309f57600080fd5b80516130ad612a3c82612a86565b8181528560208385010111156130c257600080fd5b61301b826020830160208601612dd3565b634e487b7160e01b600052601260045260246000fd5b6000826130f8576130f86130d3565b500690565b600081600019048311821515161561311757613117612fa3565b500290565b62ffffff828116828216039080821115612fd757612fd7612fa3565b600082613147576131476130d3565b500490565b6001600160401b03868116825285166020820152604081018490526001600160a01b038316606082015260a06080820181905260009061318e90830184612df7565b979650505050505050565b753234b333103737ba1036b0ba31b41d903430b9b4181d60511b8152600083516131ca816016850160208801612dd3565b6e10103932b8bab4b932b22234b3331d60891b60169184019182015283516131f9816025840160208801612dd3565b01602501949350505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161322d5761322d612fa3565b5060010190565b60008161324357613243612fa3565b50600019019056fea26469706673582212204fca2ca323dc60ce2f3d4311cf00f0f33ac3b656154fcd642a749218f334557164736f6c63430008100033"), // Get the url of Web3qBridge: https://github.com/QuarkChain/staking-contracts/blob/cross_chain_event/contracts/token/Web3qBridge.sol // contract at 0x0000000000000000000000000000000003330002 is complied Web3qBridge 0.8.9 solc (enable optimized) tokenManager: common.Hex2Bytes("60806040526004361061009c5760003560e01c8063885b012e11610064578063885b012e146101455780638929268814610184578063cde5f63b1461019b578063dbc11758146101a3578063ee271935146101d2578063ee9277b1146101f257600080fd5b80632362e53a146100a15780632996f972146100de5780632f6af8cc146100f55780633ffe450814610118578063474d6dea1461012f575b600080fd5b3480156100ad57600080fd5b506000546100c1906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ea57600080fd5b506100c16203332381565b34801561010157600080fd5b5061010a600a81565b6040519081526020016100d5565b34801561012457600080fd5b506100c16203332181565b34801561013b57600080fd5b5061010a60025481565b34801561015157600080fd5b506101826101603660046106b8565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b005b34801561019057600080fd5b506100c16203332281565b61018261023d565b3480156101af57600080fd5b506101c36101be3660046106dc565b610297565b6040516100d593929190610773565b3480156101de57600080fd5b506101826101ed3660046107dc565b61031f565b3480156101fe57600080fd5b5061022d61020d3660046107dc565b600160209081526000928352604080842090915290825290205460ff1681565b60405190151581526020016100d5565b61024733346104b3565b60028054906000610257836107fe565b90915550506002546040513481523391907fc838383de55ec352dbaa3387ea63cfc867d4bddf19389bce783dbde403459c769060200160405180910390a3565b60006060806000806102ac8a8a8a8a8a61055a565b91509150816102f0576000818060200190518101906102cb91906108bd565b90508060405162461bcd60e51b81526004016102e7919061090e565b60405180910390fd5b6000806000838060200190518101906103099190610941565b919f909e50909c509a5050505050505050505050565b600082815260016020908152604080832084845290915290205460ff16156103895760405162461bcd60e51b815260206004820152601a60248201527f746865206275726e206c6f6720686173206265656e207573656400000000000060448201526064016102e7565b600082815260016020818152604080842085855282528320805460ff1916909217909155819081906103c39060049087908790600a610297565b60005492955090935091506001600160a01b038085169116146104215760405162461bcd60e51b81526020600482015260166024820152750c6dedce8e4c2c6e840c2c8c8e440dcde40dac2e8c6d60531b60448201526064016102e7565b60008260018151811061043657610436610a20565b602002602001015160001c90506000828060200190518101906104599190610a36565b905061046582826105fe565b816001600160a01b031686887fea683109724089070580fcd9f3e5f4a7e585bd0eb900a9cdc15903d6e82445ec846040516104a291815260200190565b60405180910390a450505050505050565b604080516001600160a01b0384166020820152908101829052600090620333239060600160408051601f19818403018152908290526104f191610a4f565b6000604051808303816000865af19150503d806000811461052e576040519150601f19603f3d011682016040523d82523d6000602084013e610533565b606091505b50509050806105555760405163ac5ca12160e01b815260040160405180910390fd5b505050565b604080516020810187905290810185905260608181018590526080820184905260a08201839052600091829060c00160408051601f1981840301815290829052915062033321906105ac908390610a4f565b6000604051808303816000865af19150503d80600081146105e9576040519150601f19603f3d011682016040523d82523d6000602084013e6105ee565b606091505b5092509250509550959350505050565b604080516001600160a01b0384166020820152908101829052600090620333229060600160408051601f198184030181529082905261063c91610a4f565b6000604051808303816000865af19150503d8060008114610679576040519150601f19603f3d011682016040523d82523d6000602084013e61067e565b606091505b505090508061055557604051635caede6760e11b815260040160405180910390fd5b6001600160a01b03811681146106b557600080fd5b50565b6000602082840312156106ca57600080fd5b81356106d5816106a0565b9392505050565b600080600080600060a086880312156106f457600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b60005b8381101561073257818101518382015260200161071a565b83811115610741576000848401525b50505050565b6000815180845261075f816020860160208601610717565b601f01601f19169290920160200192915050565b6001600160a01b038416815260606020808301829052845191830182905260009185820191906080850190845b818110156107bc578451835293830193918301916001016107a0565b505084810360408601526107d08187610747565b98975050505050505050565b600080604083850312156107ef57600080fd5b50508035926020909101359150565b600060001982141561082057634e487b7160e01b600052601160045260246000fd5b5060010190565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561086657610866610827565b604052919050565b600067ffffffffffffffff83111561088857610888610827565b61089b601f8401601f191660200161083d565b90508281528383830111156108af57600080fd5b6106d5836020830184610717565b6000602082840312156108cf57600080fd5b815167ffffffffffffffff8111156108e657600080fd5b8201601f810184136108f757600080fd5b6109068482516020840161086e565b949350505050565b6020815260006106d56020830184610747565b600082601f83011261093257600080fd5b6106d58383516020850161086e565b60008060006060848603121561095657600080fd5b8351610961816106a0565b8093505060208085015167ffffffffffffffff8082111561098157600080fd5b818701915087601f83011261099557600080fd5b8151818111156109a7576109a7610827565b8060051b6109b685820161083d565b918252838101850191858101908b8411156109d057600080fd5b948601945b838610156109ee578551825294860194908601906109d5565b60408b0151909850955050505080831115610a0857600080fd5b5050610a1686828701610921565b9150509250925092565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610a4857600080fd5b5051919050565b60008251610a61818460208701610717565b919091019291505056fea2646970667358221220b1003dd9a162c1405348aa2ed55f52d945e62f6e81f49565728124e95cce688564736f6c63430008090033"), @@ -1318,7 +1318,7 @@ func (c *tokenIssuer) RequiredGas(input []byte) uint64 { // who uses the chain for the first time and load the account leaf at cache until completing the `AddBalance` operation, // then commit the account-leaf to disk. // At the same time, if the user is not a new account, this previously charged gas can cover the gas cost for - //`AddBalance` operation and is similar to the gas cost of Transfer(21000) for users + // `AddBalance` operation and is similar to the gas cost of Transfer(21000) for users return params.CallNewAccountGas } @@ -1689,8 +1689,8 @@ func VerifyCrossChainCall(client MindReadingClient, externalCallInput string) ([ MinimumConfirms: 10, } evm := NewEVMWithMRC(BlockContext{BlockNumber: big.NewInt(0)}, TxContext{}, mrctx, nil, chainCfg, evmConfig) - //evmInterpreter := NewEVMInterpreter(evm, evm.Config) - //evm.interpreter = evmInterpreter + // evmInterpreter := NewEVMInterpreter(evm, evm.Config) + // evm.interpreter = evmInterpreter if res, _, err := RunPrecompiledContract(&PrecompiledContractCallEnv{evm: evm}, p, common.FromHex(externalCallInput), gas); err != nil { return nil, err From 5a6ede9e65510f8ab036d039d97a269e4c6655f4 Mon Sep 17 00:00:00 2001 From: pingke Date: Tue, 2 May 2023 08:44:13 +0800 Subject: [PATCH 21/23] add check fill empty func to sstorage console --- cmd/sstorage/main.go | 42 ++++++++++++++++++++++++++++++++++++++++++ sstorage/data_file.go | 16 ++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/cmd/sstorage/main.go b/cmd/sstorage/main.go index dd662da417bf..0943a633f302 100644 --- a/cmd/sstorage/main.go +++ b/cmd/sstorage/main.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "bytes" "fmt" "io" "os" @@ -62,6 +63,12 @@ var ShardWriteCmd = &cobra.Command{ Run: runShardWrite, } +var CheckEmtpyKVsCmd = &cobra.Command{ + Use: "check_empty_kvs", + Short: "check empty Kvs have been filled", + Run: runCheckEmtpyKVs, +} + func init() { kvLen = CreateCmd.Flags().Uint64("kv_len", 0, "kv idx len to create") @@ -231,6 +238,40 @@ func runShardWrite(cmd *cobra.Command, args []string) { log.Info("Write value", "kvIdx", *kvIdx, "bytes", len(bs)) } +func runCheckEmtpyKVs(cmd *cobra.Command, args []string) { + setupLogger() + + if len(*filenames) != 1 { + log.Crit("must provide a filename") + } + + var err error + var df *sstorage.DataFile + df, err = sstorage.OpenDataFile((*filenames)[0]) + if err != nil { + log.Crit("open failed", "error", err) + } + + commit := common.Hash{} + chunkPerKv := df.KVSize() / sstorage.CHUNK_SIZE + startChunkIdx := (*kvIdx) * chunkPerKv + log.Info("start to verify", "kvidx", *kvIdx, "startChunkIdx", startChunkIdx, "EndChunkIdx", df.EndChunkIdx()) + for chunkIdx := startChunkIdx; startChunkIdx < df.EndChunkIdx(); chunkIdx++ { + maskedChunkData, err := df.Read(chunkIdx, int(sstorage.CHUNK_SIZE)) + if err != nil { + log.Warn("read sstorage file failed", "chunkidx", chunkIdx, "error", err) + } + encodeKey := sstorage.CalcEncodeKey(commit, chunkIdx, df.Miner()) + unmaskedChunk := sstorage.DecodeChunk(maskedChunkData, 2, encodeKey) + if bytes.Compare(unmaskedChunk, make([]byte, sstorage.CHUNK_SIZE)) != 0 { + log.Warn("verify empty chunk", "chunkidx", chunkIdx) + } + if chunkIdx%(chunkPerKv*100) == 0 { + log.Info("verify verify state", "chunkidx", chunkIdx) + } + } +} + // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "sstorage", @@ -243,6 +284,7 @@ func init() { rootCmd.AddCommand(ChunkWriteCmd) rootCmd.AddCommand(ShardReadCmd) rootCmd.AddCommand(ShardWriteCmd) + rootCmd.AddCommand(CheckEmtpyKVsCmd) } func main() { diff --git a/sstorage/data_file.go b/sstorage/data_file.go index 2144c97362a7..b6f10e5c3cb5 100644 --- a/sstorage/data_file.go +++ b/sstorage/data_file.go @@ -255,3 +255,19 @@ func (df *DataFile) readHeader() error { return nil } + +func (df *DataFile) Miner() common.Address { + return df.miner +} + +func (df *DataFile) KVSize() uint64 { + return df.maxKvSize +} + +func (df *DataFile) EndChunkIdx() uint64 { + return df.chunkIdxStart + df.chunkIdxLen - 1 +} + +func (df *DataFile) StartChunkIdx() uint64 { + return df.chunkIdxStart +} From 5262933e517e627bd9d72d9cfba3707ab2da02c2 Mon Sep 17 00:00:00 2001 From: pingke Date: Tue, 2 May 2023 11:01:43 +0800 Subject: [PATCH 22/23] bug fix --- cmd/sstorage/main.go | 2 +- eth/ethconfig/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/sstorage/main.go b/cmd/sstorage/main.go index 0943a633f302..58e8652f8f1a 100644 --- a/cmd/sstorage/main.go +++ b/cmd/sstorage/main.go @@ -256,7 +256,7 @@ func runCheckEmtpyKVs(cmd *cobra.Command, args []string) { chunkPerKv := df.KVSize() / sstorage.CHUNK_SIZE startChunkIdx := (*kvIdx) * chunkPerKv log.Info("start to verify", "kvidx", *kvIdx, "startChunkIdx", startChunkIdx, "EndChunkIdx", df.EndChunkIdx()) - for chunkIdx := startChunkIdx; startChunkIdx < df.EndChunkIdx(); chunkIdx++ { + for chunkIdx := startChunkIdx; chunkIdx < df.EndChunkIdx(); chunkIdx++ { maskedChunkData, err := df.Read(chunkIdx, int(sstorage.CHUNK_SIZE)) if err != nil { log.Warn("read sstorage file failed", "chunkidx", chunkIdx, "error", err) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index e6e7d3174155..fdc2efe71563 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -92,7 +92,7 @@ var Defaults = Config{ }, SStorMiner: sstorminer.Config{ RandomChecks: 16, - MinimumDiff: new(big.Int).SetUint64(100), + MinimumDiff: new(big.Int).SetUint64(10000), TargetIntervalSec: new(big.Int).SetUint64(300), Cutoff: new(big.Int).SetUint64(40), DiffAdjDivisor: new(big.Int).SetUint64(1024), From 29c335b9050525b819c6a3b6686dc1a3ba030c6a Mon Sep 17 00:00:00 2001 From: pingke Date: Thu, 4 May 2023 00:21:34 +0800 Subject: [PATCH 23/23] bug fix --- core/blockchain.go | 11 ++- core/vm/contracts.go | 14 +++- core/vm/contracts_test.go | 6 +- eth/ethconfig/config.go | 11 ++- sstorminer/miner.go | 13 ++-- sstorminer/worker.go | 139 +++++++++++++++----------------------- sstorminer/worker_test.go | 60 ++++++++-------- 7 files changed, 115 insertions(+), 139 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index e95f2d3180ca..fd9251b24631 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2467,8 +2467,8 @@ func VerifyKV(sm *sstorage.ShardManager, idx uint64, val []byte, meta *SstorageM root := sstorage.MerkleRootWithMinTree(data) if !bytes.Equal(root[:24], meta.HashInMeta) { - return nil, fmt.Errorf("verifyKV fail: Data hash: %s; MetaHash hash (24): %s", - common.Bytes2Hex(root[:24]), common.Bytes2Hex(meta.HashInMeta)) + return nil, fmt.Errorf("verifyKV fail: Data hash: %s; MetaHash hash (24): %s, providerAddr %s, data %s", + common.Bytes2Hex(root[:24]), common.Bytes2Hex(meta.HashInMeta), providerAddr.Hex(), common.Bytes2Hex(data)) } return data, nil @@ -2480,8 +2480,8 @@ func (bc *BlockChain) FillSstorWithEmptyKV(contract common.Address, start, limit return start, fmt.Errorf("kv verify fail: contract not support, contract: %s", contract.Hex()) } - bc.chainmu.TryLock() - defer bc.chainmu.Unlock() + // bc.chainmu.TryLock() + // defer bc.chainmu.Unlock() empty := make([]byte, 0) lastKvIdx, err := bc.GetSstorageLastKvIdx(contract) @@ -2558,7 +2558,7 @@ func (bc *BlockChain) VerifyAndWriteKV(contract common.Address, data map[uint64] if metaHash != vkv.MetaHash { // TODO: verify the storage data again before returning error - log.Warn("verify vkv fail", "error", err) + log.Warn("verify vkv fail", "kvIdx", vkv.Idx, "kvHash", common.Bytes2Hex(meta.HashInMeta), "error", err) continue } @@ -2692,7 +2692,6 @@ func (bc *BlockChain) GetSstorageLastKvIdx(contract common.Address) (uint64, err } val := stateDB.GetState(contract, uint256.NewInt(0).Bytes32()) - log.Warn("GetSstorageLastKvIdx", "val", common.Bytes2Hex(val.Bytes())) return new(big.Int).SetBytes(val.Bytes()).Uint64(), nil } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 13ea6f6da286..fa261f7825aa 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -17,6 +17,7 @@ package vm import ( + "bytes" "context" "crypto/sha256" "encoding/binary" @@ -678,7 +679,7 @@ var ( systemContracts = map[common.Address][]byte{ // Get the url of PrecompileManager: https://github.com/ethstorage/storage-contracts/blob/developing/contracts/DecentralizedKVDaggerHashimoto.sol // contract at 0x0000000000000000000000000000000003330001 is complied DecentralizedKVDaggerHashimoto() + 0.8.16 solc (enable optimized) - common.HexToAddress("0x0000000000000000000000000000000003330001"): common.Hex2Bytes("60806040526004361061025c5760003560e01c8063749cf28211610144578063b1e1a344116100b6578063d32897131161007a578063d32897131461091c578063d4044b3314610950578063dca0051114610984578063dd7e57d4146109a7578063df80ca55146109be578063e7a84c46146109d357600080fd5b8063b1e1a34414610854578063b2aebe8814610874578063c4a942cb14610894578063c5d3490c146108c8578063ca2af623146108fc57600080fd5b8063896b499111610108578063896b49911461071f578063919c6eae1461073657806395bc26731461076a578063a097365f1461078a578063a4a8435e146107be578063afd5644d146107f257600080fd5b8063749cf2821461063d57806378e979251461066a578063812d2e721461069e5780638612af34146106d25780638891ce9c146106ff57600080fd5b8063429dd7ad116101dd5780636620dfc5116101a15780636620dfc5146104e45780636cece5f8146105185780636d951bc5146105385780636da6d51e1461056c578063739b482f1461059b57806373e8b3d4146105cf57600080fd5b8063429dd7ad1461041557806344e77d991461044957806349bdd6f51461045c5780634e86235e1461047c57806354b02ba4146104b057600080fd5b8063258ae58211610224578063258ae5821461032957806327c845dc146102f257806328de3c9b14610349578063390df0b6146103ad5780633cb2fecc146103e157600080fd5b806304cbaa51146102615780630fce307b1461029057806315853983146102d25780631aff59e2146102f45780631ccbc6da14610314575b600080fd5b34801561026d57600080fd5b5060045461027b9060ff1681565b60405190151581526020015b60405180910390f35b34801561029c57600080fd5b506102c47f6387d10d3fe6d4fcb51c9f9caf0c34f88526afc3d0c6a2b80adfceeea2b4a70181565b604051908152602001610287565b3480156102de57600080fd5b506102f26102ed366004612b7d565b6109ea565b005b34801561030057600080fd5b506102f261030f366004612c8b565b610a03565b34801561032057600080fd5b506102c4610b04565b34801561033557600080fd5b5061027b610344366004612cad565b610b14565b34801561035557600080fd5b5061038d610364366004612cf3565b600360208190526000918252604090912080546001820154600283015492909301549092919084565b604080519485526020850193909352918301526060820152608001610287565b3480156103b957600080fd5b506102c47fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e681565b3480156103ed57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000081565b34801561042157600080fd5b506000546104339064ffffffffff1681565b60405164ffffffffff9091168152602001610287565b6102f2610457366004612cad565b610c40565b34801561046857600080fd5b506102f2610477366004612d0c565b610e70565b34801561048857600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000001181565b3480156104bc57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000002881565b3480156104f057600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000581565b34801561052457600080fd5b506102f2610533366004612d51565b61109e565b34801561054457600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000006481565b34801561057857600080fd5b506105836203330281565b6040516001600160a01b039091168152602001610287565b3480156105a757600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000040081565b3480156105db57600080fd5b5061027b6105ea366004612cf3565b60408051336020808301919091528183019390935281518082038301815260609091018252805190830120600090815260019092529081902054600160401b9004901b67ffffffffffffffff1916151590565b34801561064957600080fd5b5061065d610658366004612da7565b611168565b6040516102879190612e23565b34801561067657600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000181565b3480156106aa57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000012c81565b3480156106de57600080fd5b506106e7600281565b6040516001600160401b039091168152602001610287565b34801561070b57600080fd5b5061065d61071a366004612e36565b611279565b34801561072b57600080fd5b506105836203330581565b34801561074257600080fd5b506102c47f00000000000000000000000000000000000000000000000000000000000003e881565b34801561077657600080fd5b506102f2610785366004612cf3565b611372565b34801561079657600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000002000081565b3480156107ca57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000081565b3480156107fe57600080fd5b506102c461080d366004612cf3565b6040805133602080830191909152818301939093528151808203830181526060909101825280519083012060009081526001909252902054600160281b900462ffffff1690565b34801561086057600080fd5b5061027b61086f366004612e6f565b61137f565b34801561088057600080fd5b506102f261088f366004612c8b565b6115a1565b3480156108a057600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000100081565b3480156108d457600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000a81565b34801561090857600080fd5b5061065d610917366004612f42565b61166f565b34801561092857600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000001081565b34801561095c57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000001b81565b34801561099057600080fd5b5061065d61099f366004612cad565b606092915050565b3480156109b357600080fd5b506105836203330381565b3480156109ca57600080fd5b506102c4611755565b3480156109df57600080fd5b506105836203330481565b6109fa4288888888888888611799565b50505050505050565b60045460ff1615610a5b5760405162461bcd60e51b815260206004820152601960248201527f616c726561647920696e697469616c697a65642073686172640000000000000060448201526064015b60405180910390fd5b6004805460ff1916600190811790915560036020527f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92f008390557f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff8290556000527fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054d919091557fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c55565b6000610b0f426118d0565b905090565b60408051336020820152908101839052600090819060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff81168652600160281b810462ffffff1693860193909352600160401b909204831b67ffffffffffffffff1916928401839052935003610bcd5760405162461bcd60e51b815260206004820152600c60248201526b1add881b9bdd08195e1a5cdd60a21b6044820152606401610a52565b8351816020015162ffffff1614610be957600092505050610c38565b6000610c15857f0000000000000000000000000000000000000000000000000000000000001000611925565b9050806001600160401b03191682604001516001600160401b0319161493505050505b92915050565b565b7f000000000000000000000000000000000000000000000000000000000002000081511115610ca25760405162461bcd60e51b815260206004820152600e60248201526d6461746120746f6f206c6172676560901b6044820152606401610a52565b610caa611b56565b6040805133602082015290810183905260009060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff81168652600160281b810462ffffff1693860193909352600160401b909204831b67ffffffffffffffff1916928401839052935003610dbf57610d32610b04565b341015610d765760405162461bcd60e51b81526020600482015260126024820152711b9bdd08195b9bdd59da081c185e5b595b9d60721b6044820152606401610a52565b6000805464ffffffffff90811680845282526002602052604082208490559054610da291166001612fb9565b6000805464ffffffffff191664ffffffffff929092169190911790555b825162ffffff166020820152610df5837f0000000000000000000000000000000000000000000000000000000000001000611925565b67ffffffffffffffff19908116604080840191825260008581526001602090815290829020855181549287015194519384901c600160401b0262ffffff909516600160281b029290951664ffffffffff909516948517919091176001600160401b031692909217909155610e6a91908561109e565b50505050565b6040805133602082015290810183905260009060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff8116808752600160281b820462ffffff1694870194909452600160401b9004841b67ffffffffffffffff191693850184905290945090919003610f2b5760405162461bcd60e51b815260206004820152600c60248201526b1add881b9bdd08195e1a5cdd60a21b6044820152606401610a52565b6040805160608101825260008082526020808301828152838501838152888452600192839052858420945185549251915190961c600160401b0262ffffff91909116600160281b0267ffffffffffffffff199290921664ffffffffff96871617919091176001600160401b031617909255805490926002928492610faf9216612fde565b64ffffffffff908116825260208083019390935260409182016000908120548683168083526002808752858420839055828452600196879052948320805464ffffffffff1916909117905581549095509093849261100d9216612fde565b64ffffffffff90811682526020820192909252604001600090812092909255905461103b9160019116612fde565b6000805464ffffffffff191664ffffffffff9283169081179091556110619184166115a1565b846001600160a01b03166108fc611076610b04565b6040518115909202916000818181858888f193505050501580156109fa573d6000803e3d6000fd5b604080519083901c9060009062033302906110c190879085908790602001612ffc565b60408051601f19818403018152908290526110db91613024565b6000604051808303816000865af19150503d8060008114611118576040519150601f19603f3d011682016040523d82523d6000602084013e61111d565b606091505b50509050806111615760405162461bcd60e51b815260206004820152601060248201526f6661696c656420746f2070757452617760801b6044820152606401610a52565b5050505050565b6060816000036111875750604080516000815260208101909152611272565b6040805133602082015290810185905260009060600160408051808303601f1901815282825280516020918201206000818152600183528390206060850184525464ffffffffff81168552600160281b810462ffffff16928501839052600160401b9004831b67ffffffffffffffff19169284019290925290925085106112205750506040805160008152602081019091529050611272565b602081015162ffffff166112348686613040565b11156112525784816020015162ffffff1661124f9190613053565b93505b61126d8160400151826000015164ffffffffff168787611279565b925050505b9392505050565b6040805185821c6020820181905291810185905260608082018590526080820184905291906000908190620333039060a00160408051601f19818403018152908290526112c591613024565b600060405180830381855afa9150503d8060008114611300576040519150601f19603f3d011682016040523d82523d6000602084013e611305565b606091505b5091509150816113505760405162461bcd60e51b81526020600482015260166024820152756661696c656420746f2073797374656d47657452617760501b6044820152606401610a52565b808060200190518101906113649190613066565b93505050505b949350505050565b61137c8133610e70565b50565b600060017f00000000000000000000000000000000000000000000000000000000000000051b816113b082886130e9565b90506000866020015162ffffff167f0000000000000000000000000000000000000000000000000000000000001000836113ea91906130fd565b10611423577fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e6858051906020012014935050505061136a565b817f000000000000000000000000000000000000000000000000000000000000100060018960200151611456919061311c565b62ffffff166114659190613138565b036115685760006114967f0000000000000000000000000000000000000000000000000000000000001000846130fd565b886020015162ffffff166114aa9190613053565b602087018190209250905060006114e1827f0000000000000000000000000000000000000000000000000000000000001000613053565b90508015611561576000816001600160401b038111156115035761150361298a565b6040519080825280601f01601f19166020018201604052801561152d576020820181803683370190505b5090506000808360208401209150838560208c010120905080821461155d5760009850505050505050505061136a565b5050505b5050611571565b50835160208501205b600061157e828489611b5f565b604089015167ffffffffffffffff19918216911614945050505050949350505050565b6040805160208101849052908101829052600090620333059060600160408051601f19818403018152908290526115d791613024565b6000604051808303816000865af19150503d8060008114611614576040519150601f19603f3d011682016040523d82523d6000602084013e611619565b606091505b505090508061166a5760405162461bcd60e51b815260206004820152601960248201527f6661696c656420746f2073797374656d52656d6f7665526177000000000000006044820152606401610a52565b505050565b6040805160609185901c906000908190620333049061169b906002908b9087908b908b9060200161314c565b60408051601f19818403018152908290526116b591613024565b600060405180830381855afa9150503d80600081146116f0576040519150601f19603f3d011682016040523d82523d6000602084013e6116f5565b606091505b5091509150816113505760405162461bcd60e51b815260206004820152602560248201527f6661696c656420746f2073797374656d556e6d61736b4368756e6b57697468456044820152640e8d0c2e6d60db1b6064820152608401610a52565b6000805461178d9064ffffffffff167f000000000000000000000000000000000000000000000000000000000000000a1c6001612fb9565b64ffffffffff16905090565b878411156117dd5760405162461bcd60e51b81526020600482015260116024820152706d696e6564547320746f6f206c6172676560781b6044820152606401610a52565b6001861b600080806117f08b858a611c93565b604080516020808201939093526001600160a01b038e1681830152606081018d905260808082018d90528251808303909101815260a09091019091528051910120919450925090506118468b8b838c8a8a611e7d565b9050600061185684600019613138565b905060006118638361224a565b61186c8361224a565b60405160200161187d929190613199565b60408051601f19818403018152919052905080828411156118b15760405162461bcd60e51b8152600401610a529190612e23565b5050506118c28b858b8b86866122a1565b505050505050505050505050565b6000610c387f00000000000000000000000000000000000000000000000000000000000000006119207f000000000000000000000000000000000000000000000000000000000000000185613053565b612443565b6000825160000361193857506000610c38565b600082600184865161194a9190613040565b6119549190613053565b61195e9190613138565b9050600060018211156119795761197482612484565b61197c565b60015b90506000816001600160401b038111156119985761199861298a565b6040519080825280602002602001820160405280156119c1578160200160208202803683370190505b50905060005b82811015611a47576000806119dc88846130fd565b9050885181106119ed575050611a47565b6000818a516119fc9190613053565b9050888110611a085750875b808260208c010120925082858581518110611a2557611a25613205565b6020026020010181815250505050508080611a3f9061321b565b9150506119c7565b508192505b82600114611b305760005b611a62600285613138565b811015611b1d5781611a758260026130fd565b81518110611a8557611a85613205565b602002602001015182826002611a9b91906130fd565b611aa6906001613040565b81518110611ab657611ab6613205565b6020026020010151604051602001611ad8929190918252602082015260400190565b60405160208183030381529060405280519060200120828281518110611b0057611b00613205565b602090810291909101015280611b158161321b565b915050611a57565b50611b29600284613138565b9250611a4c565b80600081518110611b4357611b43613205565b6020026020010151935050505092915050565b610c3e426124c1565b805160009084906001811b8510611bac5760405162461bcd60e51b81526020600482015260116024820152706368756e6b4964206f766572666c6f777360781b6044820152606401610a52565b60005b81811015611c8857611bc26002876130e9565b600003611c1b5782858281518110611bdc57611bdc613205565b6020026020010151604051602001611bfe929190918252602082015260400190565b604051602081830303815290604052805190602001209250611c69565b848181518110611c2d57611c2d613205565b602002602001015183604051602001611c50929190918252602082015260400190565b6040516020818303038152906040528051906020012092505b611c74600287613138565b955080611c808161321b565b915050611baf565b509095945050505050565b600060606000846001600160401b03811115611cb157611cb161298a565b604051908082528060200260200182016040528015611cda578160200160208202803683370190505b50600093509150829050805b85811015611e73576000611cfa8289613040565b6000818152600360205260409020600181015491925090871015611d545760405162461bcd60e51b81526020600482015260116024820152701b5a5b9959151cc81d1bdbc81cdb585b1b607a1b6044820152606401610a52565b611de281887f000000000000000000000000000000000000000000000000000000000000012c7f00000000000000000000000000000000000000000000000000000000000000287f00000000000000000000000000000000000000000000000000000000000004007f00000000000000000000000000000000000000000000000000000000000000646125b4565b858481518110611df457611df4613205565b602002602001018181525050848381518110611e1257611e12613205565b602002602001015186611e259190613040565b81546040805160208101889052908101859052606081019190915290965060800160405160208183030381529060405280519060200120935050508080611e6b9061321b565b915050611ce6565b5093509350939050565b60007f0000000000000000000000000000000000000000000000000000000000000010825114611eef5760405162461bcd60e51b815260206004820152601f60248201527f6461746120767320636865636b733a206c656e677468206d69736d61746368006044820152606401610a52565b7f0000000000000000000000000000000000000000000000000000000000000010835114611f695760405162461bcd60e51b815260206004820152602160248201527f70726f6f667320767320636865636b733a206c656e677468206d69736d6174636044820152600d60fb1b6064820152608401610a52565b60007f0000000000000000000000000000000000000000000000000000000000000005611fb6887f000000000000000000000000000000000000000000000000000000000000000a613040565b611fc09190613040565b6001901b905060005b7f000000000000000000000000000000000000000000000000000000000000001081101561223d5760007f000000000000000000000000000000000000000000000000000000000000100090508085838151811061202957612029613205565b602002602001015151146120745760405162461bcd60e51b8152602060048201526012602482015271696e76616c69642070726f6f662073697a6560701b6044820152606401610a52565b6000612080848a6130e9565b905060006120ce7f00000000000000000000000000000000000000000000000000000000000000057f000000000000000000000000000000000000000000000000000000000000000a613040565b6120db908d901b83613040565b7f000000000000000000000000000000000000000000000000000000000000000581901c6000818152600260209081526040808320548352600182528083208151606081018352905464ffffffffff81168252600160281b810462ffffff1693820193909352600160401b909204811b67ffffffffffffffff19169082018190528b519495509293909261218d918691908f908e908c90811061218057612180613205565b602002602001015161166f565b90506121b484838d8a815181106121a6576121a6613205565b60200260200101518461137f565b6121f75760405162461bcd60e51b815260206004820152601460248201527334b73b30b634b21030b1b1b2b9b990383937b7b360611b6044820152606401610a52565b60008a888151811061220b5761220b613205565b602002602001015190508d81526020870181209d508681525050505050505080806122359061321b565b915050611fc9565b5094979650505050505050565b6060816000036122745750506040805180820190915260048152630307830360e41b602082015290565b8160005b811561229757806122888161321b565b915050600882901c9150612278565b61136a8482612682565b6000806122ac611755565b905060005b878110156123875760006122c5828b613040565b9050828111612374576000818152600360205260409020600181015461232f907f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000a1b908a61281d565b6123399086613040565b9450612372600360008481526020019081526020016000208989868151811061236457612364613205565b60200260200101518961287c565b505b508061237f8161321b565b9150506122b1565b5060006127106123b77f00000000000000000000000000000000000000000000000000000000000003e8856130fd565b6123c19190613138565b905060006123cf8285613053565b604051909150419083156108fc029084906000818181858888f193505050501580156123ff573d6000803e3d6000fd5b506040516001600160a01b0389169082156108fc029083906000818181858888f19350505050158015612436573d6000803e3d6000fd5b5050505050505050505050565b600060806124717f0000000000000000000000000000000000000000000000000000000000000000846128a0565b61247b90856130fd565b901c9392505050565b6000612491600183613053565b91505b61249f600183613053565b8216156124ba576124b1600183613053565b82169150612494565b5060011b90565b60005460017f000000000000000000000000000000000000000000000000000000000000000a81901b916124fe9164ffffffffff90911690612fb9565b64ffffffffff1661250f91906130e9565b60000361137c57600080547f000000000000000000000000000000000000000000000000000000000000000a9061254e9064ffffffffff166001612fb9565b64ffffffffff16901c60016125639190612fb9565b64ffffffffff16600081815260036020819052604082206001908101869055929350916125909084613053565b81526020808201929092526040908101600090812054938152600390925290205550565b6000808760010154876125c79190613053565b60028901549091508682101561261e5784816125e38885613138565b6125ee906001613053565b6125f891906130fd565b6126029190613138565b61260c9082613040565b9050838110156126195750825b612676565b60008582600161262e8a87613138565b6126389190613053565b61264291906130fd565b61264c9190613138565b9050816126598683613040565b111561266757849150612674565b6126718183613053565b91505b505b98975050505050505050565b606060006126918360026130fd565b61269c906002613040565b6001600160401b038111156126b3576126b361298a565b6040519080825280601f01601f1916602001820160405280156126dd576020820181803683370190505b509050600360fc1b816000815181106126f8576126f8613205565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061272757612727613205565b60200101906001600160f81b031916908160001a905350600061274b8460026130fd565b612756906001613040565b90505b60018111156127ce576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061278a5761278a613205565b1a60f81b8282815181106127a0576127a0613205565b60200101906001600160f81b031916908160001a90535060049490941c936127c781613234565b9050612759565b5083156112725760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610a52565b600061136a8461284d7f000000000000000000000000000000000000000000000000000000000000000186613053565b6128777f000000000000000000000000000000000000000000000000000000000000000186613053565b6128ac565b600384015461288c906001613040565b600385015583556002830155600190910155565b60006112728383612922565b600060806128da7f0000000000000000000000000000000000000000000000000000000000000000846128a0565b6129047f0000000000000000000000000000000000000000000000000000000000000000866128a0565b61290e9190613053565b61291890866130fd565b901c949350505050565b6000600160801b5b8215611272578260011660010361294c57608061294785836130fd565b901c90505b608061295885806130fd565b901c9350612967600284613138565b925061292a565b80356001600160a01b038116811461298557600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b03811182821017156129c2576129c261298a565b60405290565b604051601f8201601f191681016001600160401b03811182821017156129f0576129f061298a565b604052919050565b60006001600160401b03821115612a1157612a1161298a565b5060051b60200190565b600082601f830112612a2c57600080fd5b81356020612a41612a3c836129f8565b6129c8565b82815260059290921b84018101918181019086841115612a6057600080fd5b8286015b84811015612a7b5780358352918301918301612a64565b509695505050505050565b60006001600160401b03821115612a9f57612a9f61298a565b50601f01601f191660200190565b600082601f830112612abe57600080fd5b8135612acc612a3c82612a86565b818152846020838601011115612ae157600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112612b0f57600080fd5b81356020612b1f612a3c836129f8565b82815260059290921b84018101918181019086841115612b3e57600080fd5b8286015b84811015612a7b5780356001600160401b03811115612b615760008081fd5b612b6f8986838b0101612aad565b845250918301918301612b42565b600080600080600080600060e0888a031215612b9857600080fd5b8735965060208801359550612baf6040890161296e565b9450606088013593506080880135925060a08801356001600160401b0380821115612bd957600080fd5b818a0191508a601f830112612bed57600080fd5b8135612bfb612a3c826129f8565b8082825260208201915060208360051b86010192508d831115612c1d57600080fd5b602085015b83811015612c56578481351115612c3857600080fd5b612c488f60208335890101612a1b565b835260209283019201612c22565b509550505060c08a0135915080821115612c6f57600080fd5b50612c7c8a828b01612afe565b91505092959891949750929550565b60008060408385031215612c9e57600080fd5b50508035926020909101359150565b60008060408385031215612cc057600080fd5b8235915060208301356001600160401b03811115612cdd57600080fd5b612ce985828601612aad565b9150509250929050565b600060208284031215612d0557600080fd5b5035919050565b60008060408385031215612d1f57600080fd5b82359150612d2f6020840161296e565b90509250929050565b803567ffffffffffffffff198116811461298557600080fd5b600080600060608486031215612d6657600080fd5b83359250612d7660208501612d38565b915060408401356001600160401b03811115612d9157600080fd5b612d9d86828701612aad565b9150509250925092565b600080600060608486031215612dbc57600080fd5b505081359360208301359350604090920135919050565b60005b83811015612dee578181015183820152602001612dd6565b50506000910152565b60008151808452612e0f816020860160208601612dd3565b601f01601f19169290920160200192915050565b6020815260006112726020830184612df7565b60008060008060808587031215612e4c57600080fd5b612e5585612d38565b966020860135965060408601359560600135945092505050565b60008060008084860360c0811215612e8657600080fd5b853594506060601f1982011215612e9c57600080fd5b50612ea56129a0565b602086013564ffffffffff81168114612ebd57600080fd5b8152604086013562ffffff81168114612ed557600080fd5b6020820152612ee660608701612d38565b6040820152925060808501356001600160401b0380821115612f0757600080fd5b612f1388838901612a1b565b935060a0870135915080821115612f2957600080fd5b50612f3687828801612aad565b91505092959194509250565b60008060008060808587031215612f5857600080fd5b84356001600160401b038082168214612f7057600080fd5b819550612f7f60208801612d38565b9450612f8d6040880161296e565b93506060870135915080821115612f2957600080fd5b634e487b7160e01b600052601160045260246000fd5b64ffffffffff818116838216019080821115612fd757612fd7612fa3565b5092915050565b64ffffffffff828116828216039080821115612fd757612fd7612fa3565b83815282602082015260606040820152600061301b6060830184612df7565b95945050505050565b60008251613036818460208701612dd3565b9190910192915050565b80820180821115610c3857610c38612fa3565b81810381811115610c3857610c38612fa3565b60006020828403121561307857600080fd5b81516001600160401b0381111561308e57600080fd5b8201601f8101841361309f57600080fd5b80516130ad612a3c82612a86565b8181528560208385010111156130c257600080fd5b61301b826020830160208601612dd3565b634e487b7160e01b600052601260045260246000fd5b6000826130f8576130f86130d3565b500690565b600081600019048311821515161561311757613117612fa3565b500290565b62ffffff828116828216039080821115612fd757612fd7612fa3565b600082613147576131476130d3565b500490565b6001600160401b03868116825285166020820152604081018490526001600160a01b038316606082015260a06080820181905260009061318e90830184612df7565b979650505050505050565b753234b333103737ba1036b0ba31b41d903430b9b4181d60511b8152600083516131ca816016850160208801612dd3565b6e10103932b8bab4b932b22234b3331d60891b60169184019182015283516131f9816025840160208801612dd3565b01602501949350505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161322d5761322d612fa3565b5060010190565b60008161324357613243612fa3565b50600019019056fea26469706673582212204fca2ca323dc60ce2f3d4311cf00f0f33ac3b656154fcd642a749218f334557164736f6c63430008100033"), + common.HexToAddress("0x0000000000000000000000000000000003330001"): common.Hex2Bytes("60806040526004361061025c5760003560e01c8063749cf28211610144578063b1e1a344116100b6578063d32897131161007a578063d32897131461091c578063d4044b3314610950578063dca0051114610984578063dd7e57d4146109a7578063df80ca55146109be578063e7a84c46146109d357600080fd5b8063b1e1a34414610854578063b2aebe8814610874578063c4a942cb14610894578063c5d3490c146108c8578063ca2af623146108fc57600080fd5b8063896b499111610108578063896b49911461071f578063919c6eae1461073657806395bc26731461076a578063a097365f1461078a578063a4a8435e146107be578063afd5644d146107f257600080fd5b8063749cf2821461063d57806378e979251461066a578063812d2e721461069e5780638612af34146106d25780638891ce9c146106ff57600080fd5b8063429dd7ad116101dd5780636620dfc5116101a15780636620dfc5146104e45780636cece5f8146105185780636d951bc5146105385780636da6d51e1461056c578063739b482f1461059b57806373e8b3d4146105cf57600080fd5b8063429dd7ad1461041557806344e77d991461044957806349bdd6f51461045c5780634e86235e1461047c57806354b02ba4146104b057600080fd5b8063258ae58211610224578063258ae5821461032957806327c845dc146102f257806328de3c9b14610349578063390df0b6146103ad5780633cb2fecc146103e157600080fd5b806304cbaa51146102615780630fce307b1461029057806315853983146102d25780631aff59e2146102f45780631ccbc6da14610314575b600080fd5b34801561026d57600080fd5b5060045461027b9060ff1681565b60405190151581526020015b60405180910390f35b34801561029c57600080fd5b506102c47f6387d10d3fe6d4fcb51c9f9caf0c34f88526afc3d0c6a2b80adfceeea2b4a70181565b604051908152602001610287565b3480156102de57600080fd5b506102f26102ed366004612b7d565b6109ea565b005b34801561030057600080fd5b506102f261030f366004612c8b565b610a03565b34801561032057600080fd5b506102c4610b04565b34801561033557600080fd5b5061027b610344366004612cad565b610b14565b34801561035557600080fd5b5061038d610364366004612cf3565b600360208190526000918252604090912080546001820154600283015492909301549092919084565b604080519485526020850193909352918301526060820152608001610287565b3480156103b957600080fd5b506102c47fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e681565b3480156103ed57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000081565b34801561042157600080fd5b506000546104339064ffffffffff1681565b60405164ffffffffff9091168152602001610287565b6102f2610457366004612cad565b610c40565b34801561046857600080fd5b506102f2610477366004612d0c565b610e70565b34801561048857600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000001181565b3480156104bc57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000012c81565b3480156104f057600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000581565b34801561052457600080fd5b506102f2610533366004612d51565b61109e565b34801561054457600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000271081565b34801561057857600080fd5b506105836203330281565b6040516001600160a01b039091168152602001610287565b3480156105a757600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000040081565b3480156105db57600080fd5b5061027b6105ea366004612cf3565b60408051336020808301919091528183019390935281518082038301815260609091018252805190830120600090815260019092529081902054600160401b9004901b67ffffffffffffffff1916151590565b34801561064957600080fd5b5061065d610658366004612da7565b611168565b6040516102879190612e23565b34801561067657600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000181565b3480156106aa57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000012c81565b3480156106de57600080fd5b506106e7600281565b6040516001600160401b039091168152602001610287565b34801561070b57600080fd5b5061065d61071a366004612e36565b611279565b34801561072b57600080fd5b506105836203330581565b34801561074257600080fd5b506102c47f00000000000000000000000000000000000000000000000000000000000003e881565b34801561077657600080fd5b506102f2610785366004612cf3565b611372565b34801561079657600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000002000081565b3480156107ca57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000081565b3480156107fe57600080fd5b506102c461080d366004612cf3565b6040805133602080830191909152818301939093528151808203830181526060909101825280519083012060009081526001909252902054600160281b900462ffffff1690565b34801561086057600080fd5b5061027b61086f366004612e6f565b61137f565b34801561088057600080fd5b506102f261088f366004612c8b565b6115a1565b3480156108a057600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000100081565b3480156108d457600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000000a81565b34801561090857600080fd5b5061065d610917366004612f42565b61166f565b34801561092857600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000001081565b34801561095c57600080fd5b506102c47f000000000000000000000000000000000000000000000000000000000000001b81565b34801561099057600080fd5b5061065d61099f366004612cad565b606092915050565b3480156109b357600080fd5b506105836203330381565b3480156109ca57600080fd5b506102c4611755565b3480156109df57600080fd5b506105836203330481565b6109fa4288888888888888611799565b50505050505050565b60045460ff1615610a5b5760405162461bcd60e51b815260206004820152601960248201527f616c726561647920696e697469616c697a65642073686172640000000000000060448201526064015b60405180910390fd5b6004805460ff1916600190811790915560036020527f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92f008390557f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff8290556000527fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054d919091557fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c55565b6000610b0f426118d0565b905090565b60408051336020820152908101839052600090819060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff81168652600160281b810462ffffff1693860193909352600160401b909204831b67ffffffffffffffff1916928401839052935003610bcd5760405162461bcd60e51b815260206004820152600c60248201526b1add881b9bdd08195e1a5cdd60a21b6044820152606401610a52565b8351816020015162ffffff1614610be957600092505050610c38565b6000610c15857f0000000000000000000000000000000000000000000000000000000000001000611925565b9050806001600160401b03191682604001516001600160401b0319161493505050505b92915050565b565b7f000000000000000000000000000000000000000000000000000000000002000081511115610ca25760405162461bcd60e51b815260206004820152600e60248201526d6461746120746f6f206c6172676560901b6044820152606401610a52565b610caa611b56565b6040805133602082015290810183905260009060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff81168652600160281b810462ffffff1693860193909352600160401b909204831b67ffffffffffffffff1916928401839052935003610dbf57610d32610b04565b341015610d765760405162461bcd60e51b81526020600482015260126024820152711b9bdd08195b9bdd59da081c185e5b595b9d60721b6044820152606401610a52565b6000805464ffffffffff90811680845282526002602052604082208490559054610da291166001612fb9565b6000805464ffffffffff191664ffffffffff929092169190911790555b825162ffffff166020820152610df5837f0000000000000000000000000000000000000000000000000000000000001000611925565b67ffffffffffffffff19908116604080840191825260008581526001602090815290829020855181549287015194519384901c600160401b0262ffffff909516600160281b029290951664ffffffffff909516948517919091176001600160401b031692909217909155610e6a91908561109e565b50505050565b6040805133602082015290810183905260009060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff8116808752600160281b820462ffffff1694870194909452600160401b9004841b67ffffffffffffffff191693850184905290945090919003610f2b5760405162461bcd60e51b815260206004820152600c60248201526b1add881b9bdd08195e1a5cdd60a21b6044820152606401610a52565b6040805160608101825260008082526020808301828152838501838152888452600192839052858420945185549251915190961c600160401b0262ffffff91909116600160281b0267ffffffffffffffff199290921664ffffffffff96871617919091176001600160401b031617909255805490926002928492610faf9216612fde565b64ffffffffff908116825260208083019390935260409182016000908120548683168083526002808752858420839055828452600196879052948320805464ffffffffff1916909117905581549095509093849261100d9216612fde565b64ffffffffff90811682526020820192909252604001600090812092909255905461103b9160019116612fde565b6000805464ffffffffff191664ffffffffff9283169081179091556110619184166115a1565b846001600160a01b03166108fc611076610b04565b6040518115909202916000818181858888f193505050501580156109fa573d6000803e3d6000fd5b604080519083901c9060009062033302906110c190879085908790602001612ffc565b60408051601f19818403018152908290526110db91613024565b6000604051808303816000865af19150503d8060008114611118576040519150601f19603f3d011682016040523d82523d6000602084013e61111d565b606091505b50509050806111615760405162461bcd60e51b815260206004820152601060248201526f6661696c656420746f2070757452617760801b6044820152606401610a52565b5050505050565b6060816000036111875750604080516000815260208101909152611272565b6040805133602082015290810185905260009060600160408051808303601f1901815282825280516020918201206000818152600183528390206060850184525464ffffffffff81168552600160281b810462ffffff16928501839052600160401b9004831b67ffffffffffffffff19169284019290925290925085106112205750506040805160008152602081019091529050611272565b602081015162ffffff166112348686613040565b11156112525784816020015162ffffff1661124f9190613053565b93505b61126d8160400151826000015164ffffffffff168787611279565b925050505b9392505050565b6040805185821c6020820181905291810185905260608082018590526080820184905291906000908190620333039060a00160408051601f19818403018152908290526112c591613024565b600060405180830381855afa9150503d8060008114611300576040519150601f19603f3d011682016040523d82523d6000602084013e611305565b606091505b5091509150816113505760405162461bcd60e51b81526020600482015260166024820152756661696c656420746f2073797374656d47657452617760501b6044820152606401610a52565b808060200190518101906113649190613066565b93505050505b949350505050565b61137c8133610e70565b50565b600060017f00000000000000000000000000000000000000000000000000000000000000051b816113b082886130e9565b90506000866020015162ffffff167f0000000000000000000000000000000000000000000000000000000000001000836113ea91906130fd565b10611423577fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e6858051906020012014935050505061136a565b817f000000000000000000000000000000000000000000000000000000000000100060018960200151611456919061311c565b62ffffff166114659190613138565b036115685760006114967f0000000000000000000000000000000000000000000000000000000000001000846130fd565b886020015162ffffff166114aa9190613053565b602087018190209250905060006114e1827f0000000000000000000000000000000000000000000000000000000000001000613053565b90508015611561576000816001600160401b038111156115035761150361298a565b6040519080825280601f01601f19166020018201604052801561152d576020820181803683370190505b5090506000808360208401209150838560208c010120905080821461155d5760009850505050505050505061136a565b5050505b5050611571565b50835160208501205b600061157e828489611b5f565b604089015167ffffffffffffffff19918216911614945050505050949350505050565b6040805160208101849052908101829052600090620333059060600160408051601f19818403018152908290526115d791613024565b6000604051808303816000865af19150503d8060008114611614576040519150601f19603f3d011682016040523d82523d6000602084013e611619565b606091505b505090508061166a5760405162461bcd60e51b815260206004820152601960248201527f6661696c656420746f2073797374656d52656d6f7665526177000000000000006044820152606401610a52565b505050565b6040805160609185901c906000908190620333049061169b906002908b9087908b908b9060200161314c565b60408051601f19818403018152908290526116b591613024565b600060405180830381855afa9150503d80600081146116f0576040519150601f19603f3d011682016040523d82523d6000602084013e6116f5565b606091505b5091509150816113505760405162461bcd60e51b815260206004820152602560248201527f6661696c656420746f2073797374656d556e6d61736b4368756e6b57697468456044820152640e8d0c2e6d60db1b6064820152608401610a52565b6000805461178d9064ffffffffff167f000000000000000000000000000000000000000000000000000000000000000a1c6001612fb9565b64ffffffffff16905090565b878411156117dd5760405162461bcd60e51b81526020600482015260116024820152706d696e6564547320746f6f206c6172676560781b6044820152606401610a52565b6001861b600080806117f08b858a611c93565b604080516020808201939093526001600160a01b038e1681830152606081018d905260808082018d90528251808303909101815260a09091019091528051910120919450925090506118468b8b838c8a8a611e7d565b9050600061185684600019613138565b905060006118638361224a565b61186c8361224a565b60405160200161187d929190613199565b60408051601f19818403018152919052905080828411156118b15760405162461bcd60e51b8152600401610a529190612e23565b5050506118c28b858b8b86866122a1565b505050505050505050505050565b6000610c387f00000000000000000000000000000000000000000000000000000000000000006119207f000000000000000000000000000000000000000000000000000000000000000185613053565b612443565b6000825160000361193857506000610c38565b600082600184865161194a9190613040565b6119549190613053565b61195e9190613138565b9050600060018211156119795761197482612484565b61197c565b60015b90506000816001600160401b038111156119985761199861298a565b6040519080825280602002602001820160405280156119c1578160200160208202803683370190505b50905060005b82811015611a47576000806119dc88846130fd565b9050885181106119ed575050611a47565b6000818a516119fc9190613053565b9050888110611a085750875b808260208c010120925082858581518110611a2557611a25613205565b6020026020010181815250505050508080611a3f9061321b565b9150506119c7565b508192505b82600114611b305760005b611a62600285613138565b811015611b1d5781611a758260026130fd565b81518110611a8557611a85613205565b602002602001015182826002611a9b91906130fd565b611aa6906001613040565b81518110611ab657611ab6613205565b6020026020010151604051602001611ad8929190918252602082015260400190565b60405160208183030381529060405280519060200120828281518110611b0057611b00613205565b602090810291909101015280611b158161321b565b915050611a57565b50611b29600284613138565b9250611a4c565b80600081518110611b4357611b43613205565b6020026020010151935050505092915050565b610c3e426124c1565b805160009084906001811b8510611bac5760405162461bcd60e51b81526020600482015260116024820152706368756e6b4964206f766572666c6f777360781b6044820152606401610a52565b60005b81811015611c8857611bc26002876130e9565b600003611c1b5782858281518110611bdc57611bdc613205565b6020026020010151604051602001611bfe929190918252602082015260400190565b604051602081830303815290604052805190602001209250611c69565b848181518110611c2d57611c2d613205565b602002602001015183604051602001611c50929190918252602082015260400190565b6040516020818303038152906040528051906020012092505b611c74600287613138565b955080611c808161321b565b915050611baf565b509095945050505050565b600060606000846001600160401b03811115611cb157611cb161298a565b604051908082528060200260200182016040528015611cda578160200160208202803683370190505b50600093509150829050805b85811015611e73576000611cfa8289613040565b6000818152600360205260409020600181015491925090871015611d545760405162461bcd60e51b81526020600482015260116024820152701b5a5b9959151cc81d1bdbc81cdb585b1b607a1b6044820152606401610a52565b611de281887f000000000000000000000000000000000000000000000000000000000000012c7f000000000000000000000000000000000000000000000000000000000000012c7f00000000000000000000000000000000000000000000000000000000000004007f00000000000000000000000000000000000000000000000000000000000027106125b4565b858481518110611df457611df4613205565b602002602001018181525050848381518110611e1257611e12613205565b602002602001015186611e259190613040565b81546040805160208101889052908101859052606081019190915290965060800160405160208183030381529060405280519060200120935050508080611e6b9061321b565b915050611ce6565b5093509350939050565b60007f0000000000000000000000000000000000000000000000000000000000000010825114611eef5760405162461bcd60e51b815260206004820152601f60248201527f6461746120767320636865636b733a206c656e677468206d69736d61746368006044820152606401610a52565b7f0000000000000000000000000000000000000000000000000000000000000010835114611f695760405162461bcd60e51b815260206004820152602160248201527f70726f6f667320767320636865636b733a206c656e677468206d69736d6174636044820152600d60fb1b6064820152608401610a52565b60007f0000000000000000000000000000000000000000000000000000000000000005611fb6887f000000000000000000000000000000000000000000000000000000000000000a613040565b611fc09190613040565b6001901b905060005b7f000000000000000000000000000000000000000000000000000000000000001081101561223d5760007f000000000000000000000000000000000000000000000000000000000000100090508085838151811061202957612029613205565b602002602001015151146120745760405162461bcd60e51b8152602060048201526012602482015271696e76616c69642070726f6f662073697a6560701b6044820152606401610a52565b6000612080848a6130e9565b905060006120ce7f00000000000000000000000000000000000000000000000000000000000000057f000000000000000000000000000000000000000000000000000000000000000a613040565b6120db908d901b83613040565b7f000000000000000000000000000000000000000000000000000000000000000581901c6000818152600260209081526040808320548352600182528083208151606081018352905464ffffffffff81168252600160281b810462ffffff1693820193909352600160401b909204811b67ffffffffffffffff19169082018190528b519495509293909261218d918691908f908e908c90811061218057612180613205565b602002602001015161166f565b90506121b484838d8a815181106121a6576121a6613205565b60200260200101518461137f565b6121f75760405162461bcd60e51b815260206004820152601460248201527334b73b30b634b21030b1b1b2b9b990383937b7b360611b6044820152606401610a52565b60008a888151811061220b5761220b613205565b602002602001015190508d81526020870181209d508681525050505050505080806122359061321b565b915050611fc9565b5094979650505050505050565b6060816000036122745750506040805180820190915260048152630307830360e41b602082015290565b8160005b811561229757806122888161321b565b915050600882901c9150612278565b61136a8482612682565b6000806122ac611755565b905060005b878110156123875760006122c5828b613040565b9050828111612374576000818152600360205260409020600181015461232f907f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000a1b908a61281d565b6123399086613040565b9450612372600360008481526020019081526020016000208989868151811061236457612364613205565b60200260200101518961287c565b505b508061237f8161321b565b9150506122b1565b5060006127106123b77f00000000000000000000000000000000000000000000000000000000000003e8856130fd565b6123c19190613138565b905060006123cf8285613053565b604051909150419083156108fc029084906000818181858888f193505050501580156123ff573d6000803e3d6000fd5b506040516001600160a01b0389169082156108fc029083906000818181858888f19350505050158015612436573d6000803e3d6000fd5b5050505050505050505050565b600060806124717f0000000000000000000000000000000000000000000000000000000000000000846128a0565b61247b90856130fd565b901c9392505050565b6000612491600183613053565b91505b61249f600183613053565b8216156124ba576124b1600183613053565b82169150612494565b5060011b90565b60005460017f000000000000000000000000000000000000000000000000000000000000000a81901b916124fe9164ffffffffff90911690612fb9565b64ffffffffff1661250f91906130e9565b60000361137c57600080547f000000000000000000000000000000000000000000000000000000000000000a9061254e9064ffffffffff166001612fb9565b64ffffffffff16901c60016125639190612fb9565b64ffffffffff16600081815260036020819052604082206001908101869055929350916125909084613053565b81526020808201929092526040908101600090812054938152600390925290205550565b6000808760010154876125c79190613053565b60028901549091508682101561261e5784816125e38885613138565b6125ee906001613053565b6125f891906130fd565b6126029190613138565b61260c9082613040565b9050838110156126195750825b612676565b60008582600161262e8a87613138565b6126389190613053565b61264291906130fd565b61264c9190613138565b9050816126598683613040565b111561266757849150612674565b6126718183613053565b91505b505b98975050505050505050565b606060006126918360026130fd565b61269c906002613040565b6001600160401b038111156126b3576126b361298a565b6040519080825280601f01601f1916602001820160405280156126dd576020820181803683370190505b509050600360fc1b816000815181106126f8576126f8613205565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061272757612727613205565b60200101906001600160f81b031916908160001a905350600061274b8460026130fd565b612756906001613040565b90505b60018111156127ce576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061278a5761278a613205565b1a60f81b8282815181106127a0576127a0613205565b60200101906001600160f81b031916908160001a90535060049490941c936127c781613234565b9050612759565b5083156112725760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610a52565b600061136a8461284d7f000000000000000000000000000000000000000000000000000000000000000186613053565b6128777f000000000000000000000000000000000000000000000000000000000000000186613053565b6128ac565b600384015461288c906001613040565b600385015583556002830155600190910155565b60006112728383612922565b600060806128da7f0000000000000000000000000000000000000000000000000000000000000000846128a0565b6129047f0000000000000000000000000000000000000000000000000000000000000000866128a0565b61290e9190613053565b61291890866130fd565b901c949350505050565b6000600160801b5b8215611272578260011660010361294c57608061294785836130fd565b901c90505b608061295885806130fd565b901c9350612967600284613138565b925061292a565b80356001600160a01b038116811461298557600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b03811182821017156129c2576129c261298a565b60405290565b604051601f8201601f191681016001600160401b03811182821017156129f0576129f061298a565b604052919050565b60006001600160401b03821115612a1157612a1161298a565b5060051b60200190565b600082601f830112612a2c57600080fd5b81356020612a41612a3c836129f8565b6129c8565b82815260059290921b84018101918181019086841115612a6057600080fd5b8286015b84811015612a7b5780358352918301918301612a64565b509695505050505050565b60006001600160401b03821115612a9f57612a9f61298a565b50601f01601f191660200190565b600082601f830112612abe57600080fd5b8135612acc612a3c82612a86565b818152846020838601011115612ae157600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112612b0f57600080fd5b81356020612b1f612a3c836129f8565b82815260059290921b84018101918181019086841115612b3e57600080fd5b8286015b84811015612a7b5780356001600160401b03811115612b615760008081fd5b612b6f8986838b0101612aad565b845250918301918301612b42565b600080600080600080600060e0888a031215612b9857600080fd5b8735965060208801359550612baf6040890161296e565b9450606088013593506080880135925060a08801356001600160401b0380821115612bd957600080fd5b818a0191508a601f830112612bed57600080fd5b8135612bfb612a3c826129f8565b8082825260208201915060208360051b86010192508d831115612c1d57600080fd5b602085015b83811015612c56578481351115612c3857600080fd5b612c488f60208335890101612a1b565b835260209283019201612c22565b509550505060c08a0135915080821115612c6f57600080fd5b50612c7c8a828b01612afe565b91505092959891949750929550565b60008060408385031215612c9e57600080fd5b50508035926020909101359150565b60008060408385031215612cc057600080fd5b8235915060208301356001600160401b03811115612cdd57600080fd5b612ce985828601612aad565b9150509250929050565b600060208284031215612d0557600080fd5b5035919050565b60008060408385031215612d1f57600080fd5b82359150612d2f6020840161296e565b90509250929050565b803567ffffffffffffffff198116811461298557600080fd5b600080600060608486031215612d6657600080fd5b83359250612d7660208501612d38565b915060408401356001600160401b03811115612d9157600080fd5b612d9d86828701612aad565b9150509250925092565b600080600060608486031215612dbc57600080fd5b505081359360208301359350604090920135919050565b60005b83811015612dee578181015183820152602001612dd6565b50506000910152565b60008151808452612e0f816020860160208601612dd3565b601f01601f19169290920160200192915050565b6020815260006112726020830184612df7565b60008060008060808587031215612e4c57600080fd5b612e5585612d38565b966020860135965060408601359560600135945092505050565b60008060008084860360c0811215612e8657600080fd5b853594506060601f1982011215612e9c57600080fd5b50612ea56129a0565b602086013564ffffffffff81168114612ebd57600080fd5b8152604086013562ffffff81168114612ed557600080fd5b6020820152612ee660608701612d38565b6040820152925060808501356001600160401b0380821115612f0757600080fd5b612f1388838901612a1b565b935060a0870135915080821115612f2957600080fd5b50612f3687828801612aad565b91505092959194509250565b60008060008060808587031215612f5857600080fd5b84356001600160401b038082168214612f7057600080fd5b819550612f7f60208801612d38565b9450612f8d6040880161296e565b93506060870135915080821115612f2957600080fd5b634e487b7160e01b600052601160045260246000fd5b64ffffffffff818116838216019080821115612fd757612fd7612fa3565b5092915050565b64ffffffffff828116828216039080821115612fd757612fd7612fa3565b83815282602082015260606040820152600061301b6060830184612df7565b95945050505050565b60008251613036818460208701612dd3565b9190910192915050565b80820180821115610c3857610c38612fa3565b81810381811115610c3857610c38612fa3565b60006020828403121561307857600080fd5b81516001600160401b0381111561308e57600080fd5b8201601f8101841361309f57600080fd5b80516130ad612a3c82612a86565b8181528560208385010111156130c257600080fd5b61301b826020830160208601612dd3565b634e487b7160e01b600052601260045260246000fd5b6000826130f8576130f86130d3565b500690565b600081600019048311821515161561311757613117612fa3565b500290565b62ffffff828116828216039080821115612fd757612fd7612fa3565b600082613147576131476130d3565b500490565b6001600160401b03868116825285166020820152604081018490526001600160a01b038316606082015260a06080820181905260009061318e90830184612df7565b979650505050505050565b753234b333103737ba1036b0ba31b41d903430b9b4181d60511b8152600083516131ca816016850160208801612dd3565b6e10103932b8bab4b932b22234b3331d60891b60169184019182015283516131f9816025840160208801612dd3565b01602501949350505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161322d5761322d612fa3565b5060010190565b60008161324357613243612fa3565b50600019019056fea26469706673582212204fca2ca323dc60ce2f3d4311cf00f0f33ac3b656154fcd642a749218f334557164736f6c63430008100033"), // Get the url of Web3qBridge: https://github.com/QuarkChain/staking-contracts/blob/cross_chain_event/contracts/token/Web3qBridge.sol // contract at 0x0000000000000000000000000000000003330002 is complied Web3qBridge 0.8.9 solc (enable optimized) tokenManager: common.Hex2Bytes("60806040526004361061009c5760003560e01c8063885b012e11610064578063885b012e146101455780638929268814610184578063cde5f63b1461019b578063dbc11758146101a3578063ee271935146101d2578063ee9277b1146101f257600080fd5b80632362e53a146100a15780632996f972146100de5780632f6af8cc146100f55780633ffe450814610118578063474d6dea1461012f575b600080fd5b3480156100ad57600080fd5b506000546100c1906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ea57600080fd5b506100c16203332381565b34801561010157600080fd5b5061010a600a81565b6040519081526020016100d5565b34801561012457600080fd5b506100c16203332181565b34801561013b57600080fd5b5061010a60025481565b34801561015157600080fd5b506101826101603660046106b8565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b005b34801561019057600080fd5b506100c16203332281565b61018261023d565b3480156101af57600080fd5b506101c36101be3660046106dc565b610297565b6040516100d593929190610773565b3480156101de57600080fd5b506101826101ed3660046107dc565b61031f565b3480156101fe57600080fd5b5061022d61020d3660046107dc565b600160209081526000928352604080842090915290825290205460ff1681565b60405190151581526020016100d5565b61024733346104b3565b60028054906000610257836107fe565b90915550506002546040513481523391907fc838383de55ec352dbaa3387ea63cfc867d4bddf19389bce783dbde403459c769060200160405180910390a3565b60006060806000806102ac8a8a8a8a8a61055a565b91509150816102f0576000818060200190518101906102cb91906108bd565b90508060405162461bcd60e51b81526004016102e7919061090e565b60405180910390fd5b6000806000838060200190518101906103099190610941565b919f909e50909c509a5050505050505050505050565b600082815260016020908152604080832084845290915290205460ff16156103895760405162461bcd60e51b815260206004820152601a60248201527f746865206275726e206c6f6720686173206265656e207573656400000000000060448201526064016102e7565b600082815260016020818152604080842085855282528320805460ff1916909217909155819081906103c39060049087908790600a610297565b60005492955090935091506001600160a01b038085169116146104215760405162461bcd60e51b81526020600482015260166024820152750c6dedce8e4c2c6e840c2c8c8e440dcde40dac2e8c6d60531b60448201526064016102e7565b60008260018151811061043657610436610a20565b602002602001015160001c90506000828060200190518101906104599190610a36565b905061046582826105fe565b816001600160a01b031686887fea683109724089070580fcd9f3e5f4a7e585bd0eb900a9cdc15903d6e82445ec846040516104a291815260200190565b60405180910390a450505050505050565b604080516001600160a01b0384166020820152908101829052600090620333239060600160408051601f19818403018152908290526104f191610a4f565b6000604051808303816000865af19150503d806000811461052e576040519150601f19603f3d011682016040523d82523d6000602084013e610533565b606091505b50509050806105555760405163ac5ca12160e01b815260040160405180910390fd5b505050565b604080516020810187905290810185905260608181018590526080820184905260a08201839052600091829060c00160408051601f1981840301815290829052915062033321906105ac908390610a4f565b6000604051808303816000865af19150503d80600081146105e9576040519150601f19603f3d011682016040523d82523d6000602084013e6105ee565b606091505b5092509250509550959350505050565b604080516001600160a01b0384166020820152908101829052600090620333229060600160408051601f198184030181529082905261063c91610a4f565b6000604051808303816000865af19150503d8060008114610679576040519150601f19603f3d011682016040523d82523d6000602084013e61067e565b606091505b505090508061055557604051635caede6760e11b815260040160405180910390fd5b6001600160a01b03811681146106b557600080fd5b50565b6000602082840312156106ca57600080fd5b81356106d5816106a0565b9392505050565b600080600080600060a086880312156106f457600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b60005b8381101561073257818101518382015260200161071a565b83811115610741576000848401525b50505050565b6000815180845261075f816020860160208601610717565b601f01601f19169290920160200192915050565b6001600160a01b038416815260606020808301829052845191830182905260009185820191906080850190845b818110156107bc578451835293830193918301916001016107a0565b505084810360408601526107d08187610747565b98975050505050505050565b600080604083850312156107ef57600080fd5b50508035926020909101359150565b600060001982141561082057634e487b7160e01b600052601160045260246000fd5b5060010190565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561086657610866610827565b604052919050565b600067ffffffffffffffff83111561088857610888610827565b61089b601f8401601f191660200161083d565b90508281528383830111156108af57600080fd5b6106d5836020830184610717565b6000602082840312156108cf57600080fd5b815167ffffffffffffffff8111156108e657600080fd5b8201601f810184136108f757600080fd5b6109068482516020840161086e565b949350505050565b6020815260006106d56020830184610747565b600082601f83011261093257600080fd5b6106d58383516020850161086e565b60008060006060848603121561095657600080fd5b8351610961816106a0565b8093505060208085015167ffffffffffffffff8082111561098157600080fd5b818701915087601f83011261099557600080fd5b8151818111156109a7576109a7610827565b8060051b6109b685820161083d565b918252838101850191858101908b8411156109d057600080fd5b948601945b838610156109ee578551825294860194908601906109d5565b60408b0151909850955050505080831115610a0857600080fd5b5050610a1686828701610921565b9150509250925092565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610a4857600080fd5b5051919050565b60008251610a61818460208701610717565b919091019291505056fea2646970667358221220b1003dd9a162c1405348aa2ed55f52d945e62f6e81f49565728124e95cce688564736f6c63430008090033"), @@ -776,7 +777,10 @@ func (l *sstoragePisaPutRaw) RunWith(env *PrecompiledContractCallEnv, input []by if putLen > maxKVSize { return nil, errors.New("put len too large") } - evm.StateDB.SstorageWrite(caller, kvIdx, kvHash, getData(input, dataPtr+32, putLen)) + err := evm.StateDB.SstorageWrite(caller, kvIdx, kvHash, getData(input, dataPtr+32, putLen)) + if err != nil { + return nil, err + } log.Info("sstoragePisaPutRaw() returns", "caller", caller, "kvidx", kvIdx, "dataPtr", dataPtr, "maxKVSize", maxKVSize) return nil, nil @@ -877,7 +881,11 @@ func (l *sstoragePisaUnmaskDaggerData) RunWith(env *PrecompiledContractCallEnv, pb := make([]byte, 64) binary.BigEndian.PutUint64(pb[32-8:32], 32) binary.BigEndian.PutUint64(pb[64-8:64], uint64(len(unmaskedChunk))) - log.Debug("sstoragePisaUnmaskDaggerData() returns", "encodeType", encodeType, "chunkIdx", chunkIdx, "kvHash", kvHash, "miner", miner, "datalen", datalen) + if bytes.Compare(unmaskedChunk[:20], make([]byte, 20)) != 0 { + log.Warn("sstoragePisaUnmaskDaggerData() returns", "encodeType", encodeType, "chunkIdx", chunkIdx, + "kvHash", kvHash, "miner", miner, "datalen", datalen, "masked chunk data", maskedChunkData[:20], + "unmasked chunk data", unmaskedChunk[:20], "kvidx", chunkIdx/32, "chunkidx", chunkIdx%32) + } return append(pb, unmaskedChunk...), nil } diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 0b2e5539cb13..5037bb4167dd 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -21,17 +21,17 @@ import ( "encoding/hex" "encoding/json" "fmt" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/sstorage" "io/ioutil" "math/big" "testing" "time" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/sstorage" ) // precompiledTest defines the input/output pairs for precompiled contract tests. @@ -186,7 +186,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { // Keep it as uint64, multiply 100 to get two digit float later mgasps := (100 * 1000 * gasUsed) / elapsed bench.ReportMetric(float64(mgasps)/100, "mgas/s") - //Check if it is correct + // Check if it is correct if err != nil { bench.Error(err) return diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index fdc2efe71563..cbb0202983a7 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -91,12 +91,11 @@ var Defaults = Config{ Recommit: 3 * time.Second, }, SStorMiner: sstorminer.Config{ - RandomChecks: 16, - MinimumDiff: new(big.Int).SetUint64(10000), - TargetIntervalSec: new(big.Int).SetUint64(300), - Cutoff: new(big.Int).SetUint64(40), - DiffAdjDivisor: new(big.Int).SetUint64(1024), - Recommit: 15 * time.Second, + RandomChecks: 16, + MinimumDiff: new(big.Int).SetUint64(10000), + Cutoff: new(big.Int).SetUint64(300), // equal to TargetIntervalSec + DiffAdjDivisor: new(big.Int).SetUint64(1024), + Recommit: 15 * time.Second, }, TxPool: core.DefaultTxPoolConfig, RPCGasCap: 50000000, diff --git a/sstorminer/miner.go b/sstorminer/miner.go index 320f80957514..c55d32dd8e85 100644 --- a/sstorminer/miner.go +++ b/sstorminer/miner.go @@ -42,16 +42,17 @@ type apiBackend interface { GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) SendTx(ctx context.Context, signedTx *types.Transaction) error SuggestGasTipCap(ctx context.Context) (*big.Int, error) + GetPoolTransaction(txHash common.Hash) *types.Transaction + GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) } // Config is the configuration parameters of mining. type Config struct { - RandomChecks int - MinimumDiff *big.Int - TargetIntervalSec *big.Int - Cutoff *big.Int - DiffAdjDivisor *big.Int - Recommit time.Duration // The time interval for miner to re-create mining work. + RandomChecks int + MinimumDiff *big.Int + Cutoff *big.Int + DiffAdjDivisor *big.Int + Recommit time.Duration // The time interval for miner to re-create mining work. } // Miner creates blocks and searches for proof-of-work values. diff --git a/sstorminer/worker.go b/sstorminer/worker.go index e5d8df45ac6d..a3007e01d870 100644 --- a/sstorminer/worker.go +++ b/sstorminer/worker.go @@ -60,6 +60,8 @@ const ( minRecommitInterval = 1 * time.Second mineTimeOut = uint64(10) + + transactionOutdatedTime = 120 // Second ) var ( @@ -85,6 +87,8 @@ type BlockChain interface { SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription + GetSstorageLastKvIdx(contract common.Address) (uint64, error) + State() (*state.StateDB, error) } @@ -98,6 +102,7 @@ type TXSigner struct { // task contains all information for consensus engine sealing and result submitting. type task struct { worker *worker + result *result storageContract common.Address minerContract common.Address shardIdx uint64 @@ -113,10 +118,10 @@ type task struct { mu sync.RWMutex // The lock used to protect the state } -func expectedDiff(lastMineTime uint64, difficulty *big.Int, minedTime uint64, targetIntervalSec, cutoff, diffAdjDivisor, minDiff *big.Int) *big.Int { +func expectedDiff(lastMineTime uint64, difficulty *big.Int, minedTime uint64, cutoff, diffAdjDivisor, minDiff *big.Int) *big.Int { interval := new(big.Int).SetUint64(minedTime - lastMineTime) diff := difficulty - if interval.Cmp(targetIntervalSec) < 0 { + if interval.Cmp(cutoff) < 0 { // diff = diff + (diff-interval*diff/cutoff)/diffAdjDivisor diff = new(big.Int).Add(diff, new(big.Int).Div( new(big.Int).Sub(diff, new(big.Int).Div(new(big.Int).Mul(interval, diff), cutoff)), diffAdjDivisor)) @@ -137,7 +142,7 @@ func expectedDiff(lastMineTime uint64, difficulty *big.Int, minedTime uint64, ta } func (t *task) expectedDiff(minedTime uint64) *big.Int { - return expectedDiff(t.info.LastMineTime, t.info.Difficulty, minedTime, t.worker.config.TargetIntervalSec, + return expectedDiff(t.info.LastMineTime, t.info.Difficulty, minedTime, t.worker.config.Cutoff, t.worker.config.DiffAdjDivisor, t.worker.config.MinimumDiff) } @@ -192,6 +197,8 @@ type result struct { chunkIdxs []uint64 encodedData [][]byte proofs [][]common.Hash + submitTxHash common.Hash + submitTxTime int64 } type txSorter struct { @@ -218,57 +225,6 @@ func (s *txSorter) Less(i, j int) bool { return tip1.Cmp(tip2) < 0 } -type priceOracle struct { - chainConfig *params.ChainConfig - baseFee *big.Int - suggestGasTip *big.Int - blockNumber uint64 - ignoreUnder *big.Int - limit int -} - -// The TipGap provided in the SuggestGasTipCap method (provided by EthAPIBackend) will extract the smallest 3 TipGaps -// from each of the latest n blocks, sort them, and take the median according to the set percentile. -// -// To make it simple, if the transaction volume is small, suggestGasTip set to 0, otherwise the third smallest GasTip -// in the latest block is used. -// -// updatePriceOracle update suggestGasTip according to block. -func (o *priceOracle) updatePriceOracle(block *types.Block) { - if block.NumberU64() < o.blockNumber { - return - } - // if block GasUsed smaller than GasLimit / 2, only baseFee is needed, - // so suggestGasTip can be set to 0. - if block.GasUsed() < block.GasLimit()/2 { - o.suggestGasTip = new(big.Int).SetUint64(0) - return - } - - // otherwise, the third smallest GasTip is used. - signer := types.MakeSigner(o.chainConfig, block.Number()) - sorter := newSorter(block.Transactions(), block.BaseFee()) - sort.Sort(sorter) - - var prices []*big.Int - for _, tx := range sorter.txs { - tip, _ := tx.EffectiveGasTip(block.BaseFee()) - if o.ignoreUnder != nil && tip.Cmp(o.ignoreUnder) == -1 { - continue - } - sender, err := types.Sender(signer, tx) - if err == nil && sender != block.Coinbase() { - prices = append(prices, tip) - if len(prices) >= o.limit { - break - } - } - } - if len(prices) > 0 { - o.suggestGasTip = prices[len(prices)-1] - } -} - // newWorkReq represents a request for new sealing work submitting with relative interrupt notifier. type newWorkReq struct { timestamp int64 @@ -283,7 +239,6 @@ type worker struct { eth Backend apiBackend apiBackend chain BlockChain - priceOracle *priceOracle // Subscriptions mux *event.TypeMux @@ -313,14 +268,14 @@ type worker struct { newResultHook func(*result) } -func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, api apiBackend, chain BlockChain, mux *event.TypeMux, txSigner *TXSigner, - minerContract common.Address, init bool) *worker { +func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, api apiBackend, chain BlockChain, + mux *event.TypeMux, txSigner *TXSigner, minerContract common.Address, init bool) *worker { worker := &worker{ config: config, chainConfig: chainConfig, eth: eth, - mux: mux, apiBackend: api, + mux: mux, chain: chain, tasks: make([]*task, 0), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), @@ -354,16 +309,6 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, eth Backend, api } } - curBlock := eth.BlockChain().CurrentBlock() - worker.priceOracle = &priceOracle{ - chainConfig: chainConfig, - baseFee: curBlock.BaseFee(), - blockNumber: curBlock.NumberU64(), - ignoreUnder: new(big.Int).SetUint64(2 * params.GWei), - limit: 3, - } - worker.priceOracle.updatePriceOracle(curBlock) - // Subscribe events for blockchain worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) @@ -470,7 +415,6 @@ func (w *worker) newWorkLoop(recommit time.Duration) { case interval := <-w.resubmitIntervalCh: // Adjust resubmit interval explicitly by user. if interval < minRecommitInterval { - log.Warn("Sanitizing miner recommit interval", "provided", interval, "updated", minRecommitInterval) interval = minRecommitInterval } log.Info("Miner recommit interval update", "from", minRecommit, "to", interval) @@ -576,6 +520,14 @@ func (w *worker) resultLoop() { } } +func (w *worker) isTransactionOutdated(txHash common.Hash, submitTxTime int64) bool { + tx := w.apiBackend.GetPoolTransaction(txHash) + if tx != nil && submitTxTime+transactionOutdatedTime > time.Now().Unix() { + return false + } + return true +} + // updateTaskInfo aborts in-flight transaction execution with given signal and resubmits a new one. func (w *worker) updateTaskInfo(root common.Hash, timestamp int64) { w.mu.Lock() @@ -585,19 +537,39 @@ func (w *worker) updateTaskInfo(root common.Hash, timestamp int64) { return } updated := false - for _, task := range w.tasks { - info, err := w.chain.GetSstorageMiningInfo(root, task.minerContract, task.shardIdx) + for _, t := range w.tasks { + info, err := w.chain.GetSstorageMiningInfo(root, t.minerContract, t.shardIdx) if err != nil { log.Warn("failed to get sstorage mining info", "error", err.Error()) continue } - if task.info == nil || !info.Equal(task.info) { - task.info = info - task.setState(TaskStateNoStart) + if t.info == nil || !info.Equal(t.info) { + t.info = info + t.result = nil + t.setState(TaskStateNoStart) + updated = true + log.Info("update t info", "shard idx", t.shardIdx, "MiningHash", info.MiningHash.Hex(), + "LastMineTime", t.info.LastMineTime, "Difficulty", info.Difficulty, "BlockMined", info.BlockMined) + continue + } + if t.result != nil && t.result.submitTxTime != 0 { // result has been submitted + ctx := context.Background() + receipts, _ := w.apiBackend.GetReceipts(ctx, t.result.submitTxHash) + if receipts != nil && receipts[0].Status == types.ReceiptStatusSuccessful { + // tx has been exec and success, this should not happen, it should be covered by info update. + continue + } else if receipts == nil { + // if tx in pool less than 120 seconds, then wait for tx to exec, otherwise re-mine task + isOutdated := w.isTransactionOutdated(t.result.submitTxHash, t.result.submitTxTime) + if !isOutdated { + continue + } + log.Info("Transaction outdated", "submitTxTime", t.result.submitTxTime, "now", time.Now().Unix(), "tx hash", t.result.submitTxHash.Hex()) + } + + t.result = nil + t.setState(TaskStateNoStart) updated = true - log.Info("update task info", "shard idx", task.shardIdx, "MiningHash", - info.MiningHash.Hex(), "LastMineTime", task.info.LastMineTime, "Difficulty", - info.Difficulty, "BlockMined", info.BlockMined) } } @@ -692,8 +664,6 @@ func (w *worker) mineTask(t *task) (bool, error) { // todo shard len can be not 1 later diff, _, hash0, err := w.calculateDiffAndInitHash(t, uint64(1)<= 0 { - log.Warn("calculateDiffAndInitHash", "diff", diff, "hash1", hash1.Hex(), "minedTs", minedTs, - "MiningHash", t.info.MiningHash, "LastMineTime", t.info.LastMineTime, "nonce", nonce, "miner", t.miner.Hex()) proofs := make([][]common.Hash, len(kvIdxs)) kvs, err := w.chain.ReadKVsByIndexList(t.storageContract, kvIdxs, true) if err != nil { @@ -729,7 +697,7 @@ func (w *worker) mineTask(t *task) (bool, error) { } } t.setState(TaskStateMined) - w.resultCh <- &result{ + t.result = &result{ task: t, startShardId: t.shardIdx, shardLenBits: 0, @@ -740,7 +708,9 @@ func (w *worker) mineTask(t *task) (bool, error) { chunkIdxs: chunkIdxs, encodedData: dataSet, proofs: proofs, + submitTxTime: 0, } + w.resultCh <- t.result return true, nil } @@ -776,7 +746,6 @@ func (w *worker) submitMinedResult(result *result) error { signedTx, err := w.signer.SignFn(w.signer.Account, types.NewTx(baseTx), w.chainConfig.ChainID) if err != nil { - log.Warn("worker::submitMinedResult() >>>>>> sign tx error <<<<<<", "err", err) return err } @@ -784,7 +753,9 @@ func (w *worker) submitMinedResult(result *result) error { if err != nil { return fmt.Errorf("SendTransaction hash %s, ERROR %s ", signedTx.Hash().Hex(), err.Error()) } - log.Warn("Submit mining tx", "hash", signedTx.Hash().Hex()) + result.submitTxTime = time.Now().Unix() + result.submitTxHash = signedTx.Hash() + log.Info("Submit mining tx", "hash", signedTx.Hash().Hex()) return nil } diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go index f7c94ab1aaad..a1c1a7c2132c 100644 --- a/sstorminer/worker_test.go +++ b/sstorminer/worker_test.go @@ -46,15 +46,6 @@ import ( "github.com/holiman/uint256" ) -const ( - // testCode is the testing contract binary code which will initialises some - // variables in constructor - testCode = "0x60806040527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060005534801561003457600080fd5b5060fc806100436000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c4dae8814603757806398a213cf146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506084565b005b60005481565b806000819055507fe9e44f9f7da8c559de847a3232b57364adc0354f15a2cd8dc636d54396f9587a6000546040518082815260200191505060405180910390a15056fea265627a7a723058208ae31d9424f2d0bc2a3da1a5dd659db2d71ec322a17db8f87e19e209e3a1ff4a64736f6c634300050a0032" - - // testGas is the gas required for contract deployment. - testGas = 144109 -) - var ( // Test chain configurations testTxPoolConfig core.TxPoolConfig @@ -75,16 +66,14 @@ var ( minerContract = common.HexToAddress("0x0000000000000000000000000000000000000001") contract = common.HexToAddress("0x0000000000000000000000000000000003330001") kvEntriesBits = uint64(9) - kvEntries = uint64(1) << 9 blocks = 5 defaultConfig = &Config{ - RandomChecks: 16, - MinimumDiff: new(big.Int).SetUint64(1), - TargetIntervalSec: new(big.Int).SetUint64(3), - Cutoff: new(big.Int).SetUint64(40), - DiffAdjDivisor: new(big.Int).SetUint64(1024), - Recommit: 1 * time.Second, + RandomChecks: 16, + MinimumDiff: new(big.Int).SetUint64(1), + Cutoff: new(big.Int).SetUint64(40), + DiffAdjDivisor: new(big.Int).SetUint64(1024), + Recommit: 1 * time.Second, } diff = new(big.Int).SetUint64(1024) @@ -180,6 +169,14 @@ func (api *mockApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, erro return new(big.Int).SetUint64(6000000), nil } +func (api *mockApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { + return nil +} + +func (api *mockApiBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { + return nil, nil +} + // testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing. type testWorkerBackend struct { db ethdb.Database @@ -385,19 +382,6 @@ func verify(root []byte, dataHash common.Hash, chunkIdx uint64, proofs []common. return bytes.Compare(root[:24], r.Bytes()[:24]) == 0 } -func verifyWithMinTree(root []byte, dataHash common.Hash, chunkIdx uint64, proofs []common.Hash) bool { - nMinChunkBits := uint64(len(proofs)) - if chunkIdx >= uint64(1)<= uint64(1)<