diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 230184ab88b5..707b915265d6 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -133,6 +133,9 @@ var ( utils.MinerNoVerifyFlag, utils.SstorageShardFlag, utils.SstorageFileFlag, + utils.SstorageMineFlag, + utils.SstorageTXSignerFlag, + utils.SstorageMinerContractFlag, utils.NATFlag, utils.NoDiscoverFlag, utils.DiscoveryV5Flag, diff --git a/cmd/sstorage/main.go b/cmd/sstorage/main.go index 998d00004a80..58e8652f8f1a 100644 --- a/cmd/sstorage/main.go +++ b/cmd/sstorage/main.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "bytes" "fmt" "io" "os" @@ -15,7 +16,7 @@ import ( ) var ( - chunkLen *uint64 + kvLen *uint64 miner *string filenames *[]string @@ -62,8 +63,14 @@ 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() { - 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 +117,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) } @@ -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; 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) + } + 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/cmd/utils/flags.go b/cmd/utils/flags.go index 4e9ba11fa22f..d663eb67edf5 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -578,6 +578,18 @@ var ( Usage: "Add sharded storage data file", Value: nil, } + SstorageMineFlag = cli.BoolFlag{ + Name: "sstorage.mine", + Usage: "Enable sstorage mining", + } + SstorageTXSignerFlag = cli.StringFlag{ + Name: "sstorage.txsigner", + Usage: "Account used to sign tx submit to sstorage miner contract", + } + SstorageMinerContractFlag = cli.StringFlag{ + Name: "sstorage.minercontract", + Usage: "Sstorage miner contract", + } // Logging and debug settings EthStatsURLFlag = cli.StringFlag{ Name: "ethstats", @@ -1137,6 +1149,15 @@ 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(SstorageTXSignerFlag.Name) { + cfg.SstorageTXSigner = ctx.GlobalString(SstorageTXSignerFlag.Name) + } + if ctx.GlobalIsSet(SstorageMinerContractFlag.Name) { + cfg.SstorageMinerContract = ctx.GlobalString(SstorageMinerContractFlag.Name) + } sstorage.InitializeConfig() for _, s := range cfg.SstorageShards { 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/blockchain.go b/core/blockchain.go index 515cc27478d8..fd9251b24631 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -50,7 +50,6 @@ import ( "github.com/ethereum/go-ethereum/trie" lru "github.com/hashicorp/golang-lru" "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) var ( @@ -2383,7 +2382,9 @@ func (bc *BlockChain) PreExecuteBlock(block *types.Block) (err error) { return } -var emptyHash = common.Hash{} +var ( + emptyHash = common.Hash{} +) type SstorageMetadata struct { KVIdx uint64 @@ -2410,13 +2411,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) @@ -2426,7 +2421,7 @@ 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; // } position := getSlotHash(2, uint256.NewInt(index).Bytes32()) @@ -2465,20 +2460,48 @@ 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 } - 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, providerAddr %s, data %s", + common.Bytes2Hex(root[:24]), common.Bytes2Hex(meta.HashInMeta), providerAddr.Hex(), common.Bytes2Hex(data)) } 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) { @@ -2535,13 +2558,13 @@ 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 } - 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) + log.Warn("write kv fail", "kvIdx", vkv.Idx, "kvHash", common.Bytes2Hex(meta.HashInMeta), "error", err) } if success { inserted = append(inserted, vkv.Idx) @@ -2550,6 +2573,48 @@ func (bc *BlockChain) VerifyAndWriteKV(contract common.Address, data map[uint64] return synced, syncedBytes, inserted, nil } +// ReadKVsByIndexList Read the KVs by a list of KV index. +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, returnEmpty) +} + +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()) + } + + val := stateDB.GetState(contract, uint256.NewInt(0).Bytes32()) + lastIndex := new(big.Int).SetBytes(val.Bytes()).Uint64() + + res := make([]*KV, 0) + for _, idx := range indexes { + 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 { + 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 { + kv := KV{idx, data} + res = append(res, &kv) + } + } + + return res, nil +} + // ReadEncodedKVsByIndexList Read the masked KVs by a list of KV index. func (bc *BlockChain) ReadEncodedKVsByIndexList(contract common.Address, shardId uint64, indexes []uint64) (common.Address, []*KV, error) { sm := sstorage.ContractToShardManager[contract] @@ -2627,10 +2692,61 @@ 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 } +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(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) + } + 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()) +} + func (bc *BlockChain) setMindReading(chainConfig *params.ChainConfig) error { if chainConfig.MindReading != nil { bc.mindReading.EnableBlockNumber = chainConfig.MindReading.EnableBlockNumber diff --git a/core/genesis.go b/core/genesis.go index 8b1893a1766a..2def810e5988 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -441,10 +441,10 @@ 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{1, 1, 1, 1}, } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index d7a34788df68..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("6080604052600436106102675760003560e01c806378e9792511610144578063b2aebe88116100b6578063d32897131161007a578063d328971314610962578063d4044b3314610996578063dca00511146109ca578063dd7e57d4146109ed578063df80ca5514610a04578063e7a84c4614610a1957600080fd5b8063b2aebe881461087f578063bf8b887c1461089f578063c4a942cb146108da578063c5d3490c1461090e578063ca2af6231461094257600080fd5b8063919c6eae11610108578063919c6eae1461074157806395bc267314610775578063a097365f14610795578063a4a8435e146107c9578063afd5644d146107fd578063b1e1a3441461085f57600080fd5b806378e9792514610675578063812d2e72146106a95780638612af34146106dd5780638891ce9c1461070a578063896b49911461072a57600080fd5b806349bdd6f5116101dd5780636d951bc5116101a15780636d951bc5146105295780636da6d51e1461055d57806372f2b9dc1461058c578063739b482f146105a657806373e8b3d4146105da578063749cf2821461064857600080fd5b806349bdd6f51461044d5780634e86235e1461046d57806354b02ba4146104a15780636620dfc5146104d55780636cece5f81461050957600080fd5b806327c845dc1161022f57806327c845dc146102d357806328de3c9b1461033a578063390df0b61461039e5780633cb2fecc146103d2578063429dd7ad1461040657806344e77d991461043a57600080fd5b80630fce307b1461026c57806315853983146102b35780631aff59e2146102d55780631ccbc6da146102f5578063258ae5821461030a575b600080fd5b34801561027857600080fd5b506102a07fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e681565b6040519081526020015b60405180910390f35b3480156102bf57600080fd5b506102d36102ce3660046129c1565b610a30565b005b3480156102e157600080fd5b506102d36102f0366004612acf565b610a49565b34801561030157600080fd5b506102a0610b3f565b34801561031657600080fd5b5061032a610325366004612af1565b610b4f565b60405190151581526020016102aa565b34801561034657600080fd5b5061037e610355366004612b37565b600360208190526000918252604090912080546001820154600283015492909301549092919084565b6040805194855260208501939093529183015260608201526080016102aa565b3480156103aa57600080fd5b506102a07fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e681565b3480156103de57600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000081565b34801561041257600080fd5b506000546104249064ffffffffff1681565b60405164ffffffffff90911681526020016102aa565b6102d3610448366004612af1565b610c7b565b34801561045957600080fd5b506102d3610468366004612b50565b610eab565b34801561047957600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000c81565b3480156104ad57600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000002881565b3480156104e157600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000081565b34801561051557600080fd5b506102d3610524366004612b95565b6110d9565b34801561053557600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000006481565b34801561056957600080fd5b506105746203330281565b6040516001600160a01b0390911681526020016102aa565b34801561059857600080fd5b5060045461032a9060ff1681565b3480156105b257600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000040081565b3480156105e657600080fd5b5061032a6105f5366004612b37565b60408051336020808301919091528183019390935281518082038301815260609091018252805190830120600090815260019092529081902054600160401b9004901b67ffffffffffffffff1916151590565b34801561065457600080fd5b50610668610663366004612beb565b6111a3565b6040516102aa9190612c67565b34801561068157600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000181565b3480156106b557600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000012c81565b3480156106e957600080fd5b506106f2600281565b6040516001600160401b0390911681526020016102aa565b34801561071657600080fd5b50610668610725366004612c7a565b6112b4565b34801561073657600080fd5b506105746203330581565b34801561074d57600080fd5b506102a07f00000000000000000000000000000000000000000000000000000000000003e881565b34801561078157600080fd5b506102d3610790366004612b37565b6113ad565b3480156107a157600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000100081565b3480156107d557600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000081565b34801561080957600080fd5b506102a0610818366004612b37565b6040805133602080830191909152818301939093528151808203830181526060909101825280519083012060009081526001909252902054600160281b900462ffffff1690565b34801561086b57600080fd5b5061032a61087a366004612cb3565b6113ba565b34801561088b57600080fd5b506102d361089a366004612acf565b6115dc565b3480156108ab57600080fd5b506108cc6108ba366004612af1565b80519181526020820181209181529091565b6040516102aa929190612d86565b3480156108e657600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000100081565b34801561091a57600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000000681565b34801561094e57600080fd5b5061066861095d366004612d9f565b6116a3565b34801561096e57600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000001081565b3480156109a257600080fd5b506102a07f000000000000000000000000000000000000000000000000000000000000001281565b3480156109d657600080fd5b506106686109e5366004612af1565b606092915050565b3480156109f957600080fd5b506105746203330381565b348015610a1057600080fd5b506102a0611789565b348015610a2557600080fd5b506105746203330481565b610a4042888888888888886117cd565b50505050505050565b60045460ff1615610a965760405162461bcd60e51b8152602060048201526012602482015271185b1c9958591e481a5b9a5d081cda185c9960721b60448201526064015b60405180910390fd5b6004805460ff1916600190811790915560036020527f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92f008390557f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff8290556000527fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054d919091557fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c55565b6000610b4a426118ea565b905090565b60408051336020820152908101839052600090819060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff81168652600160281b810462ffffff1693860193909352600160401b909204831b67ffffffffffffffff1916928401839052935003610c085760405162461bcd60e51b815260206004820152600c60248201526b1add881b9bdd08195e1a5cdd60a21b6044820152606401610a8d565b8351816020015162ffffff1614610c2457600092505050610c73565b6000610c50857f000000000000000000000000000000000000000000000000000000000000100061193f565b9050806001600160401b03191682604001516001600160401b0319161493505050505b92915050565b565b7f000000000000000000000000000000000000000000000000000000000000100081511115610cdd5760405162461bcd60e51b815260206004820152600e60248201526d6461746120746f6f206c6172676560901b6044820152606401610a8d565b610ce5611b70565b6040805133602082015290810183905260009060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff81168652600160281b810462ffffff1693860193909352600160401b909204831b67ffffffffffffffff1916928401839052935003610dfa57610d6d610b3f565b341015610db15760405162461bcd60e51b81526020600482015260126024820152711b9bdd08195b9bdd59da081c185e5b595b9d60721b6044820152606401610a8d565b6000805464ffffffffff90811680845282526002602052604082208490559054610ddd91166001612e16565b6000805464ffffffffff191664ffffffffff929092169190911790555b825162ffffff166020820152610e30837f000000000000000000000000000000000000000000000000000000000000100061193f565b67ffffffffffffffff19908116604080840191825260008581526001602090815290829020855181549287015194519384901c600160401b0262ffffff909516600160281b029290951664ffffffffff909516948517919091176001600160401b031692909217909155610ea59190856110d9565b50505050565b6040805133602082015290810183905260009060600160408051808303601f1901815282825280516020918201206000818152600183528381206060860185525464ffffffffff8116808752600160281b820462ffffff1694870194909452600160401b9004841b67ffffffffffffffff191693850184905290945090919003610f665760405162461bcd60e51b815260206004820152600c60248201526b1add881b9bdd08195e1a5cdd60a21b6044820152606401610a8d565b6040805160608101825260008082526020808301828152838501838152888452600192839052858420945185549251915190961c600160401b0262ffffff91909116600160281b0267ffffffffffffffff199290921664ffffffffff96871617919091176001600160401b031617909255805490926002928492610fea9216612e3b565b64ffffffffff908116825260208083019390935260409182016000908120548683168083526002808752858420839055828452600196879052948320805464ffffffffff191690911790558154909550909384926110489216612e3b565b64ffffffffff9081168252602082019290925260400160009081209290925590546110769160019116612e3b565b6000805464ffffffffff191664ffffffffff92831690811790915561109c9184166115dc565b846001600160a01b03166108fc6110b1610b3f565b6040518115909202916000818181858888f19350505050158015610a40573d6000803e3d6000fd5b604080519083901c9060009062033302906110fc90879085908790602001612e59565b60408051601f198184030181529082905261111691612e81565b6000604051808303816000865af19150503d8060008114611153576040519150601f19603f3d011682016040523d82523d6000602084013e611158565b606091505b505090508061119c5760405162461bcd60e51b815260206004820152601060248201526f6661696c656420746f2070757452617760801b6044820152606401610a8d565b5050505050565b6060816000036111c257506040805160008152602081019091526112ad565b6040805133602082015290810185905260009060600160408051808303601f1901815282825280516020918201206000818152600183528390206060850184525464ffffffffff81168552600160281b810462ffffff16928501839052600160401b9004831b67ffffffffffffffff191692840192909252909250851061125b57505060408051600081526020810190915290506112ad565b602081015162ffffff1661126f8686612e9d565b111561128d5784816020015162ffffff1661128a9190612eb0565b93505b6112a88160400151826000015164ffffffffff1687876112b4565b925050505b9392505050565b6040805185821c6020820181905291810185905260608082018590526080820184905291906000908190620333039060a00160408051601f198184030181529082905261130091612e81565b600060405180830381855afa9150503d806000811461133b576040519150601f19603f3d011682016040523d82523d6000602084013e611340565b606091505b50915091508161138b5760405162461bcd60e51b81526020600482015260166024820152756661696c656420746f2073797374656d47657452617760501b6044820152606401610a8d565b8080602001905181019061139f9190612ec3565b93505050505b949350505050565b6113b78133610eab565b50565b600060017f00000000000000000000000000000000000000000000000000000000000000001b816113eb8288612f46565b90506000866020015162ffffff167f0000000000000000000000000000000000000000000000000000000000001000836114259190612f5a565b1061145e577fa8bae11751799de4dbe638406c5c9642c0e791f2a65e852a05ba4fdf0d88e3e685805190602001201493505050506113a5565b817f0000000000000000000000000000000000000000000000000000000000001000600189602001516114919190612f79565b62ffffff166114a09190612f95565b036115a35760006114d17f000000000000000000000000000000000000000000000000000000000000100084612f5a565b886020015162ffffff166114e59190612eb0565b6020870181902092509050600061151c827f0000000000000000000000000000000000000000000000000000000000001000612eb0565b9050801561159c576000816001600160401b0381111561153e5761153e6127ce565b6040519080825280601f01601f191660200182016040528015611568576020820181803683370190505b5090506000808360208401209150838560208c0101209050808214611598576000985050505050505050506113a5565b5050505b50506115ac565b50835160208501205b60006115b9828489611b79565b604089015167ffffffffffffffff19918216911614945050505050949350505050565b6040805160208101849052908101829052600090620333059060600160408051601f198184030181529082905261161291612e81565b6000604051808303816000865af19150503d806000811461164f576040519150601f19603f3d011682016040523d82523d6000602084013e611654565b606091505b505090508061169e5760405162461bcd60e51b81526020600482015260166024820152756661696c656420746f2073797374656d50757452617760501b6044820152606401610a8d565b505050565b6040805160609185901c90600090819062033304906116cf906002908b9087908b908b90602001612fa9565b60408051601f19818403018152908290526116e991612e81565b600060405180830381855afa9150503d8060008114611724576040519150601f19603f3d011682016040523d82523d6000602084013e611729565b606091505b50915091508161138b5760405162461bcd60e51b815260206004820152602560248201527f6661696c656420746f2073797374656d556e6d61736b4368756e6b57697468456044820152640e8d0c2e6d60db1b6064820152608401610a8d565b600080546117c19064ffffffffff167f00000000000000000000000000000000000000000000000000000000000000061c6001612e16565b64ffffffffff16905090565b878411156118115760405162461bcd60e51b81526020600482015260116024820152706d696e6564547320746f6f206c6172676560781b6044820152606401610a8d565b6001861b600080806118248b858a611cad565b604080516020808201939093526001600160a01b038e1681830152606081018d905260808082018d90528251808303909101815260a090910190915280519101209194509250905061187a8b8b838c8a8a611e97565b9050600061188a84600019612f95565b9050808211156118cd5760405162461bcd60e51b815260206004820152600e60248201526d0c8d2cccc40dcdee840dac2e8c6d60931b6044820152606401610a8d565b506118dc8b858b8b8686612280565b505050505050505050505050565b6000610c737f000000000000000000000000000000000000000000000000000000000000000061193a7f000000000000000000000000000000000000000000000000000000000000000185612eb0565b612422565b6000825160000361195257506000610c73565b60008260018486516119649190612e9d565b61196e9190612eb0565b6119789190612f95565b9050600060018211156119935761198e82612463565b611996565b60015b90506000816001600160401b038111156119b2576119b26127ce565b6040519080825280602002602001820160405280156119db578160200160208202803683370190505b50905060005b82811015611a61576000806119f68884612f5a565b905088518110611a07575050611a61565b6000818a51611a169190612eb0565b9050888110611a225750875b808260208c010120925082858581518110611a3f57611a3f612ff6565b6020026020010181815250505050508080611a599061300c565b9150506119e1565b508192505b82600114611b4a5760005b611a7c600285612f95565b811015611b375781611a8f826002612f5a565b81518110611a9f57611a9f612ff6565b602002602001015182826002611ab59190612f5a565b611ac0906001612e9d565b81518110611ad057611ad0612ff6565b6020026020010151604051602001611af2929190918252602082015260400190565b60405160208183030381529060405280519060200120828281518110611b1a57611b1a612ff6565b602090810291909101015280611b2f8161300c565b915050611a71565b50611b43600284612f95565b9250611a66565b80600081518110611b5d57611b5d612ff6565b6020026020010151935050505092915050565b610c79426124a0565b805160009084906001811b8510611bc65760405162461bcd60e51b81526020600482015260116024820152706368756e6b4964206f766572666c6f777360781b6044820152606401610a8d565b60005b81811015611ca257611bdc600287612f46565b600003611c355782858281518110611bf657611bf6612ff6565b6020026020010151604051602001611c18929190918252602082015260400190565b604051602081830303815290604052805190602001209250611c83565b848181518110611c4757611c47612ff6565b602002602001015183604051602001611c6a929190918252602082015260400190565b6040516020818303038152906040528051906020012092505b611c8e600287612f95565b955080611c9a8161300c565b915050611bc9565b509095945050505050565b600060606000846001600160401b03811115611ccb57611ccb6127ce565b604051908082528060200260200182016040528015611cf4578160200160208202803683370190505b50600093509150829050805b85811015611e8d576000611d148289612e9d565b6000818152600360205260409020600181015491925090871015611d6e5760405162461bcd60e51b81526020600482015260116024820152701b5a5b9959151cc81d1bdbc81cdb585b1b607a1b6044820152606401610a8d565b611dfc81887f000000000000000000000000000000000000000000000000000000000000012c7f00000000000000000000000000000000000000000000000000000000000000287f00000000000000000000000000000000000000000000000000000000000004007f0000000000000000000000000000000000000000000000000000000000000064612593565b858481518110611e0e57611e0e612ff6565b602002602001018181525050848381518110611e2c57611e2c612ff6565b602002602001015186611e3f9190612e9d565b81546040805160208101889052908101859052606081019190915290965060800160405160208183030381529060405280519060200120935050508080611e859061300c565b915050611d00565b5093509350939050565b60007f0000000000000000000000000000000000000000000000000000000000000010825114611f095760405162461bcd60e51b815260206004820152601f60248201527f6461746120767320636865636b733a206c656e677468206d69736d61746368006044820152606401610a8d565b7f0000000000000000000000000000000000000000000000000000000000000010835114611f835760405162461bcd60e51b815260206004820152602160248201527f70726f6f667320767320636865636b733a206c656e677468206d69736d6174636044820152600d60fb1b6064820152608401610a8d565b60007f0000000000000000000000000000000000000000000000000000000000000000611fd0887f0000000000000000000000000000000000000000000000000000000000000006612e9d565b611fda9190612e9d565b6001901b905060005b7f0000000000000000000000000000000000000000000000000000000000000010811015612273577f000000000000000000000000000000000000000000000000000000000000100084828151811061203e5761203e612ff6565b602002602001015151146120895760405162461bcd60e51b8152602060048201526012602482015271696e76616c69642070726f6f662073697a6560701b6044820152606401610a8d565b60006120958389612f46565b905060006120e37f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000006612e9d565b6120f0908c901b83612e9d565b7f000000000000000000000000000000000000000000000000000000000000000081901c6000818152600260209081526040808320548352600182528083208151606081018352905464ffffffffff81168252600160281b810462ffffff1693820193909352600160401b909204811b67ffffffffffffffff19169082018190528a51949550929390926121a2918691908e908d908b90811061219557612195612ff6565b60200260200101516116a3565b90506121c984838c89815181106121bb576121bb612ff6565b6020026020010151846113ba565b61220c5760405162461bcd60e51b815260206004820152601460248201527334b73b30b634b21030b1b1b2b9b990383937b7b360611b6044820152606401610a8d565b600089878151811061222057612220612ff6565b6020908102919091018101519d8e527f00000000000000000000000000000000000000000000000000000000000010009081018e209d525085945061226b935084925061300c915050565b915050611fe3565b5094979650505050505050565b60008061228b611789565b905060005b878110156123665760006122a4828b612e9d565b9050828111612353576000818152600360205260409020600181015461230e907f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000061b908a612661565b6123189086612e9d565b9450612351600360008481526020019081526020016000208989868151811061234357612343612ff6565b6020026020010151896126c0565b505b508061235e8161300c565b915050612290565b5060006127106123967f00000000000000000000000000000000000000000000000000000000000003e885612f5a565b6123a09190612f95565b905060006123ae8285612eb0565b604051909150419083156108fc029084906000818181858888f193505050501580156123de573d6000803e3d6000fd5b506040516001600160a01b0389169082156108fc029083906000818181858888f19350505050158015612415573d6000803e3d6000fd5b5050505050505050505050565b600060806124507f0000000000000000000000000000000000000000000000000000000000000000846126e4565b61245a9085612f5a565b901c9392505050565b6000612470600183612eb0565b91505b61247e600183612eb0565b82161561249957612490600183612eb0565b82169150612473565b5060011b90565b60005460017f000000000000000000000000000000000000000000000000000000000000000681901b916124dd9164ffffffffff90911690612e16565b64ffffffffff166124ee9190612f46565b6000036113b757600080547f00000000000000000000000000000000000000000000000000000000000000069061252d9064ffffffffff166001612e16565b64ffffffffff16901c60016125429190612e16565b64ffffffffff166000818152600360208190526040822060019081018690559293509161256f9084612eb0565b81526020808201929092526040908101600090812054938152600390925290205550565b6000808760010154876125a69190612eb0565b6002890154909150868210156125fd5784816125c28885612f95565b6125cd906001612eb0565b6125d79190612f5a565b6125e19190612f95565b6125eb9082612e9d565b9050838110156125f85750825b612655565b60008582600161260d8a87612f95565b6126179190612eb0565b6126219190612f5a565b61262b9190612f95565b9050816126388683612e9d565b111561264657849150612653565b6126508183612eb0565b91505b505b98975050505050505050565b60006113a5846126917f000000000000000000000000000000000000000000000000000000000000000186612eb0565b6126bb7f000000000000000000000000000000000000000000000000000000000000000186612eb0565b6126f0565b60038401546126d0906001612e9d565b600385015583556002830155600190910155565b60006112ad8383612766565b6000608061271e7f0000000000000000000000000000000000000000000000000000000000000000846126e4565b6127487f0000000000000000000000000000000000000000000000000000000000000000866126e4565b6127529190612eb0565b61275c9086612f5a565b901c949350505050565b6000600160801b5b82156112ad578260011660010361279057608061278b8583612f5a565b901c90505b608061279c8580612f5a565b901c93506127ab600284612f95565b925061276e565b80356001600160a01b03811681146127c957600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715612806576128066127ce565b60405290565b604051601f8201601f191681016001600160401b0381118282101715612834576128346127ce565b604052919050565b60006001600160401b03821115612855576128556127ce565b5060051b60200190565b600082601f83011261287057600080fd5b813560206128856128808361283c565b61280c565b82815260059290921b840181019181810190868411156128a457600080fd5b8286015b848110156128bf57803583529183019183016128a8565b509695505050505050565b60006001600160401b038211156128e3576128e36127ce565b50601f01601f191660200190565b600082601f83011261290257600080fd5b8135612910612880826128ca565b81815284602083860101111561292557600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f83011261295357600080fd5b813560206129636128808361283c565b82815260059290921b8401810191818101908684111561298257600080fd5b8286015b848110156128bf5780356001600160401b038111156129a55760008081fd5b6129b38986838b01016128f1565b845250918301918301612986565b600080600080600080600060e0888a0312156129dc57600080fd5b87359650602088013595506129f3604089016127b2565b9450606088013593506080880135925060a08801356001600160401b0380821115612a1d57600080fd5b818a0191508a601f830112612a3157600080fd5b8135612a3f6128808261283c565b8082825260208201915060208360051b86010192508d831115612a6157600080fd5b602085015b83811015612a9a578481351115612a7c57600080fd5b612a8c8f6020833589010161285f565b835260209283019201612a66565b509550505060c08a0135915080821115612ab357600080fd5b50612ac08a828b01612942565b91505092959891949750929550565b60008060408385031215612ae257600080fd5b50508035926020909101359150565b60008060408385031215612b0457600080fd5b8235915060208301356001600160401b03811115612b2157600080fd5b612b2d858286016128f1565b9150509250929050565b600060208284031215612b4957600080fd5b5035919050565b60008060408385031215612b6357600080fd5b82359150612b73602084016127b2565b90509250929050565b803567ffffffffffffffff19811681146127c957600080fd5b600080600060608486031215612baa57600080fd5b83359250612bba60208501612b7c565b915060408401356001600160401b03811115612bd557600080fd5b612be1868287016128f1565b9150509250925092565b600080600060608486031215612c0057600080fd5b505081359360208301359350604090920135919050565b60005b83811015612c32578181015183820152602001612c1a565b50506000910152565b60008151808452612c53816020860160208601612c17565b601f01601f19169290920160200192915050565b6020815260006112ad6020830184612c3b565b60008060008060808587031215612c9057600080fd5b612c9985612b7c565b966020860135965060408601359560600135945092505050565b60008060008084860360c0811215612cca57600080fd5b853594506060601f1982011215612ce057600080fd5b50612ce96127e4565b602086013564ffffffffff81168114612d0157600080fd5b8152604086013562ffffff81168114612d1957600080fd5b6020820152612d2a60608701612b7c565b6040820152925060808501356001600160401b0380821115612d4b57600080fd5b612d578883890161285f565b935060a0870135915080821115612d6d57600080fd5b50612d7a878288016128f1565b91505092959194509250565b8281526040602082015260006113a56040830184612c3b565b60008060008060808587031215612db557600080fd5b84356001600160401b038082168214612dcd57600080fd5b819550612ddc60208801612b7c565b9450612dea604088016127b2565b93506060870135915080821115612d6d57600080fd5b634e487b7160e01b600052601160045260246000fd5b64ffffffffff818116838216019080821115612e3457612e34612e00565b5092915050565b64ffffffffff828116828216039080821115612e3457612e34612e00565b838152826020820152606060408201526000612e786060830184612c3b565b95945050505050565b60008251612e93818460208701612c17565b9190910192915050565b80820180821115610c7357610c73612e00565b81810381811115610c7357610c73612e00565b600060208284031215612ed557600080fd5b81516001600160401b03811115612eeb57600080fd5b8201601f81018413612efc57600080fd5b8051612f0a612880826128ca565b818152856020838501011115612f1f57600080fd5b612e78826020830160208601612c17565b634e487b7160e01b600052601260045260246000fd5b600082612f5557612f55612f30565b500690565b6000816000190483118215151615612f7457612f74612e00565b500290565b62ffffff828116828216039080821115612e3457612e34612e00565b600082612fa457612fa4612f30565b500490565b6001600160401b03868116825285166020820152604081018490526001600160a01b038316606082015260a060808201819052600090612feb90830184612c3b565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161301e5761301e612e00565b506001019056fea2646970667358221220818e32029f91fbabb52182f57c50b99971de6a63ca4b85bf469137b3d7523f7264736f6c63430008100033"), + 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 } @@ -1318,7 +1326,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 +1697,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 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/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 1f6e0e8b75ad..bbe9ed1d281e 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -60,6 +60,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. @@ -92,9 +93,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 @@ -189,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) } @@ -297,6 +298,28 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { 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") + } + if config.SstorageMinerContract == "" { + return nil, fmt.Errorf("miner contract is needed when the sstorage mine is enabled.") + } + minerContract := common.HexToAddress(config.SstorageMinerContract) + 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, eth.APIBackend, &config.SStorMiner, chainConfig, eth.EventMux(), &sstorminer.TXSigner{signer, wallet.SignTx}, minerContract) + eth.sstorMiner.Start() + } + gpoParams := config.GPO if gpoParams.Default == nil { gpoParams.Default = config.Miner.GasPrice @@ -496,6 +519,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.IsSstorMining() { + go s.sstorMiner.Start() + } +} + +// StopSstorMining terminates the sstorage miner. +func (s *Ethereum) StopSstorMining() { + s.sstorMiner.Stop() +} + +func (s *Ethereum) IsSstorMining() bool { return s.sstorMiner.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/downloader/downloader.go b/eth/downloader/downloader.go index e48613435dac..6b33a1f16481 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -212,13 +212,16 @@ 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) // 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, 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) // GetSstorageLastKvIdx get LastKvIdx from a sstorage contract with latest stateDB. GetSstorageLastKvIdx(contract common.Address) (uint64, error) @@ -241,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/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/ethconfig/config.go b/eth/ethconfig/config.go index bd8f714338f1..cbb0202983a7 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -39,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. @@ -89,6 +90,13 @@ var Defaults = Config{ GasPrice: big.NewInt(params.GWei), Recommit: 3 * time.Second, }, + SStorMiner: sstorminer.Config{ + 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, RPCEVMTimeout: 5 * time.Second, @@ -174,6 +182,9 @@ type Config struct { // Mining options Miner miner.Config + // Sstorage Mining options + SStorMiner sstorminer.Config + // Ethash options Ethash ethash.Config @@ -219,15 +230,18 @@ type Config struct { ValChainId uint64 ValidatorChangeEpochId uint64 - //MindReading Config + // MindReading Config IsMiner bool MindReadingEnableBlockNumber *big.Int MindReadingSupportChainId uint64 MindReadingCallRpc string // Sstorage config - SstorageFiles []string `toml:",omitempty"` - SstorageShards []string `toml:",omitempty"` + SstorageFiles []string `toml:",omitempty"` + SstorageShards []string `toml:",omitempty"` + SstorageMine bool + SstorageTXSigner string + SstorageMinerContract string } // CreateConsensusEngine creates a consensus engine for the given chain configuration. diff --git a/eth/protocols/sstorage/sync.go b/eth/protocols/sstorage/sync.go index 95a62742f9ed..750e46a2a9e7 100644 --- a/eth/protocols/sstorage/sync.go +++ b/eth/protocols/sstorage/sync.go @@ -21,14 +21,14 @@ import ( "errors" "fmt" "math/rand" + "runtime" "sort" "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" @@ -45,7 +45,7 @@ const ( maxConcurrency = 16 - minSubTaskSize = 512 + minSubTaskSize = 16 ) // ErrCancelled is returned from sstorage syncing if the operation was prematurely @@ -53,6 +53,8 @@ const ( var ErrCancelled = errors.New("sync cancelled") var ( + maxEmptyTaskTreads int + empty = make([]byte, 0) requestTimeoutInMillisecond = 1000 * time.Millisecond // Millisecond ) @@ -134,16 +136,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 @@ -172,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 @@ -207,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 @@ -246,13 +263,16 @@ 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) // 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, 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) // GetSstorageLastKvIdx get LastKvIdx from a sstorage contract with latest stateDB. GetSstorageLastKvIdx(contract common.Address) (uint64, error) @@ -270,6 +290,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 @@ -288,10 +309,15 @@ 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 + emptyKVToFill uint64 + emptyKVFilled uint64 + startTime time.Time // Time instance when sstorage sync started logTime time.Time // Time instance when status was Last reported @@ -301,10 +327,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, @@ -315,10 +345,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), } } @@ -431,6 +462,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): @@ -465,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 { @@ -475,12 +509,19 @@ 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 } + 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 } } @@ -504,13 +545,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, @@ -522,6 +557,17 @@ func (s *Syncer) loadSyncStatus() { kvTask: &task, Indexes: make(map[uint64]int64), } + + first, limit := sm.KvEntries()*sid, sm.KvEntries()*(sid+1) + firstEmpty, limitForEmpty := uint64(0), uint64(0) + if first >= lastKvIndex { + firstEmpty, limitForEmpty = first, limit + limit = first + } else if limit >= lastKvIndex { + firstEmpty, limitForEmpty = lastKvIndex, limit + limit = lastKvIndex + } + subTasks := make([]*kvSubTask, 0) // split task for a shard to 16 subtasks and if one batch is too small // set to minSubTaskSize @@ -544,33 +590,68 @@ func (s *Syncer) loadSyncStatus() { } subTasks = append(subTasks, &subTask) - first = last + 1 + first = last + } + + subEmptyTasks := make([]*kvSubEmptyTask, 0) + if limitForEmpty > 0 { + s.emptyKVToFill += limitForEmpty - firstEmpty + maxEmptyTaskSize := (limitForEmpty - firstEmpty + uint64(maxEmptyTaskTreads)) / uint64(maxEmptyTaskTreads) + 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 + } } - task.HealTask, task.KvSubTasks = &healTask, subTasks + 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 } } 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 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 { @@ -585,8 +666,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 } @@ -606,7 +689,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 } } @@ -614,7 +703,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)) @@ -690,7 +779,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, @@ -828,6 +917,50 @@ 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() + + // 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, 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 + filled := next - start + s.emptyKVFilled += filled + if s.emptyKVToFill > filled { + s.emptyKVToFill -= filled + } + if eTask.next >= eTask.Last { + eTask.done = true + } + eTask.isRunning = false + s.runningEmptyTaskTreads-- + }(subTask, task.Contract, start, last-1) + } + } +} + // 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) { @@ -981,7 +1114,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 @@ -1079,7 +1212,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() @@ -1174,7 +1307,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() @@ -1250,6 +1383,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/protocols/sstorage/sync_test.go b/eth/protocols/sstorage/sync_test.go index 25a50c18c541..85ca67c33c5b 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" @@ -36,12 +37,12 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/sstorage" "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) var ( - contract = common.HexToAddress("0x0000000000000000000000000000000003330001") - kvEntries = uint64(512) + contract = common.HexToAddress("0x0000000000000000000000000000000003330001") + kvEntriesBits = uint64(9) + kvEntries = uint64(1) << 9 ) type ( @@ -62,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 @@ -77,7 +103,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 @@ -89,7 +115,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 @@ -106,11 +132,11 @@ func (c *blockChain) ReadEncodedKVsByIndexList(contract common.Address, shardId if sm == nil { 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) @@ -127,17 +153,17 @@ func (c *blockChain) ReadEncodedKVsByIndexList(contract common.Address, shardId return miner, res, nil } -func (c *blockChain) ReadEncodedKVsByIndexRange(contract common.Address, shardId uint64, origin uint64, - limit uint64, bytes uint64) (common.Address, []*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 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++ { @@ -293,7 +319,6 @@ func createKVRequestResponse(t *testPeer, id uint64, stateDB *state.StateDB, con } else { values = append(values, &core.KV{Idx: idx, Data: bs}) } - } } @@ -317,7 +342,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 @@ -333,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{} { @@ -383,13 +398,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 @@ -412,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) } @@ -424,10 +433,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 { @@ -436,10 +447,9 @@ func createSstorage(contract common.Address, shardIdxList []uint64, kvSize, 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) } @@ -451,6 +461,9 @@ func createSstorage(contract common.Address, shardIdxList []uint64, kvSize, } 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) @@ -467,20 +480,28 @@ 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()) } + 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 { + val = make([]byte, sstorage.CHUNK_SIZE) + sval, ok, err = shardData.TryReadChunk(idx, common.BytesToHash(make([]byte, 24))) + 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)) @@ -635,7 +656,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") } @@ -674,7 +695,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") } @@ -708,7 +729,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() { @@ -718,7 +740,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") } @@ -761,7 +783,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") } @@ -814,7 +836,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") } @@ -861,7 +883,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") } @@ -906,10 +928,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") } @@ -951,7 +973,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") } @@ -995,7 +1017,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 { @@ -1016,7 +1038,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) @@ -1042,7 +1064,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 { @@ -1089,7 +1111,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 { @@ -1133,10 +1155,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") } 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/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/params/config.go b/params/config.go index 73f88c9e3142..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("0xe1b551a47236ea806a1a9f6a9082ab989cffd999a44caa6015edc27136e0aab5") + Web3QTestnetGenesisHash = common.HexToHash("0xaba50efdf3572a2d25dab20e08bb5d9f30aea360c74aff451e702cf749094d70") Web3QGalileoGenesisHash = common.HexToHash("0xa576a985390f3a643e2acdeaed074cc9866c99f6bdf3ca8c49ec959054703745") ) diff --git a/sstorage/data_file.go b/sstorage/data_file.go index 7a873926f192..b6f10e5c3cb5 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 @@ -67,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, @@ -250,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 +} diff --git a/sstorage/data_shard.go b/sstorage/data_shard.go index 2daaeb38f109..e2ff23e86e57 100644 --- a/sstorage/data_shard.go +++ b/sstorage/data_shard.go @@ -99,7 +99,39 @@ func (ds *DataShard) GetStorageFile(chunkIdx uint64) *DataFile { return nil } -// 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 + }) +} + +// 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) + return decodeChunk(cdata, ds.dataFiles[0].encodeType, encodeKey) + }) +} + +// 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") + } + 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, idx) + 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 @@ -114,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") @@ -231,7 +263,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") } @@ -246,24 +278,16 @@ 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 { - return nil + return err } } return nil 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_config.go b/sstorage/shard_config.go index 69480d0c5a2f..ee3e9b9e4df1 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"), 17, 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) } } @@ -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/sstorage/shard_manager.go b/sstorage/shard_manager.go index 0ad471114ddb..a8fd47e34676 100644 --- a/sstorage/shard_manager.go +++ b/sstorage/shard_manager.go @@ -9,29 +9,55 @@ import ( type ShardManager struct { shardMap map[uint64]*DataShard contractAddress common.Address + kvSizeBits uint64 kvSize uint64 + chunksPerKvBits uint64 chunksPerKv uint64 + kvEntriesBits uint64 kvEntries 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, + kvSizeBits: kvSizeBits, + kvSize: 1 << kvSizeBits, + kvEntriesBits: kvEntriesBits, + kvEntries: 1 << kvEntriesBits, + chunksPerKvBits: kvSizeBits - CHUNK_SIZE_BITS, + chunksPerKv: (1 << kvSizeBits) / CHUNK_SIZE, } } +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) { @@ -85,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) } @@ -126,7 +152,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) { @@ -139,6 +165,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/miner.go b/sstorminer/miner.go new file mode 100644 index 000000000000..c55d32dd8e85 --- /dev/null +++ b/sstorminer/miner.go @@ -0,0 +1,153 @@ +// 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 ( + "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" +) + +// 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 +} + +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 + 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. +type Miner struct { + mux *event.TypeMux + worker *worker + eth Backend + exitCh chan struct{} + startCh chan struct{} + stopCh chan struct{} + + wg sync.WaitGroup +} + +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, api, eth.BlockChain(), mux, txSigner, minerContract, false), + } + 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() + + // Subscribe sstorage SstorSyncDone evnet + events := miner.mux.Subscribe(sstorage.SstorSyncDone{}) + defer func() { + if !events.Closed() { + events.Unsubscribe() + } + }() + + shouldStart := false + canStart := false + dlEventCh := events.Chan() + for { + select { + case ev := <-dlEventCh: + if ev == nil { + // Unsubscription done, stop listening + dlEventCh = nil + continue + } + switch ev.Data.(type) { + case sstorage.SstorSyncDone: + canStart = true + if shouldStart { + miner.worker.start() + } + // Stop reacting to downloader events + events.Unsubscribe() + } + case <-miner.startCh: + 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() { + miner.startCh <- struct{}{} +} + +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() +} + +// 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 new file mode 100644 index 000000000000..0c4957de94b0 --- /dev/null +++ b/sstorminer/miner_test.go @@ -0,0 +1,199 @@ +// 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 ( + "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/protocols/sstorage" + "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 +} + +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) + waitForMiningState(t, miner, false) + // start miner, but the + miner.Start() + waitForMiningState(t, miner, false) + // Start the downloader + mux.Post(sstorage.SstorSyncDone{}) + waitForMiningState(t, miner, true) +} + +func TestMinerStartStopAfterSyncDoneEvents(t *testing.T) { + miner, mux, cleanup := createMiner(t) + defer cleanup(false) + miner.Start() + waitForMiningState(t, miner, false) + // Start the downloader + mux.Post(sstorage.SstorSyncDone{}) + waitForMiningState(t, miner, true) + + miner.Stop() + waitForMiningState(t, miner, false) + + miner.Start() + waitForMiningState(t, miner, true) + + miner.Stop() + waitForMiningState(t, miner, false) +} + +func TestStartAfterDownload(t *testing.T) { + miner, mux, cleanup := createMiner(t) + defer cleanup(false) + waitForMiningState(t, miner, false) + // Stop the downloader and wait for the update loop to run + mux.Post(sstorage.SstorSyncDone{}) + waitForMiningState(t, miner, false) + // Starting the miner after the downloader should not work + miner.Start() + waitForMiningState(t, miner, true) +} + +func TestStartStopMiner(t *testing.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() + waitForMiningState(t, miner, false) +} + +func TestCloseMiner(t *testing.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 + miner.Close() + waitForMiningState(t, miner, false) +} + +// 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{} + // 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, nil, &config, chainConfig, mux, nil, common.Address{}) + cleanup := func(skipMiner bool) { + bc.Stop() + engine.Close() + pool.Stop() + if !skipMiner { + miner.Close() + } + } + return miner, mux, cleanup +} diff --git a/sstorminer/worker.go b/sstorminer/worker.go new file mode 100644 index 000000000000..a3007e01d870 --- /dev/null +++ b/sstorminer/worker.go @@ -0,0 +1,768 @@ +// 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 ( + "context" + "fmt" + "math/big" + "math/rand" + "sort" + "strings" + "sync" + "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" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + 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\":\"bytes32[][]\",\"name\":\"proofsDim2\",\"type\":\"bytes32[][]\"},{\"internalType\":\"bytes[]\",\"name\":\"maskedData\",\"type\":\"bytes[]\"}],\"name\":\"mine\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + MineFunc = "mine" + gas = uint64(3000000) +) + +const ( + // resultQueueSize is the size of channel listening to sealing result. + resultQueueSize = 10 + + // chainHeadChanSize is the size of channel listening to ChainHeadEvent. + chainHeadChanSize = 10 + + // minRecommitInterval is the minimal time interval to recreate the sealing block with + // any newly arrived transactions. + minRecommitInterval = 1 * time.Second + + mineTimeOut = uint64(10) + + transactionOutdatedTime = 120 // Second +) + +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)) +) + +const ( + TaskStateNoStart = iota + TaskStateMining + TaskStateMined +) + +type BlockChain interface { + CurrentBlock() *types.Block + + InsertChain(chain types.Blocks) (int, error) + + 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 + + GetSstorageLastKvIdx(contract common.Address) (uint64, error) + + 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 { + worker *worker + result *result + storageContract common.Address + minerContract 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 +} + +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(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)) + if diff.Cmp(minDiff) < 0 { + diff = minDiff + } + } else { + // dec := (interval*diff/cutoff - diff) / diffAdjDivisor + dec := new(big.Int).Div(new(big.Int).Sub(new(big.Int).Div(new(big.Int).Mul(interval, diff), cutoff), diff), diffAdjDivisor) + if new(big.Int).Add(dec, minDiff).Cmp(diff) > 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.Cutoff, t.worker.config.DiffAdjDivisor, t.worker.config.MinimumDiff) +} + +func (t *task) getState() uint64 { + t.mu.Lock() + defer t.mu.Unlock() + return t.state +} + +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 { + 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 + startShardId uint64 + shardLenBits uint64 + miner common.Address + minedTs uint64 + nonce uint64 + kvIdxs []uint64 + chunkIdxs []uint64 + encodedData [][]byte + proofs [][]common.Hash + submitTxHash common.Hash + submitTxTime int64 +} + +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 +} + +// 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 +// and gathering the sealing result. +type worker struct { + config *Config + chainConfig *params.ChainConfig + engine consensus.Engine + eth Backend + apiBackend apiBackend + chain BlockChain + + // Subscriptions + mux *event.TypeMux + chainHeadCh chan core.ChainHeadEvent + chainHeadSub event.Subscription + client *ethclient.Client + signer *TXSigner + + // Channels + newWorkCh chan *newWorkReq + taskCh chan *task + resultCh chan *result + startCh chan struct{} + taskStartCh chan struct{} + resultSubmitFailCh chan struct{} + exitCh chan struct{} + taskDoneCh chan struct{} + resubmitIntervalCh chan time.Duration + + wg sync.WaitGroup + mu sync.RWMutex // The lock used to protect the coinbase and extra fields + + tasks tasks + running int32 + + newTaskHook func(*task) + 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 { + worker := &worker{ + config: config, + chainConfig: chainConfig, + eth: eth, + apiBackend: api, + mux: mux, + chain: chain, + tasks: make([]*task, 0), + chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), + newWorkCh: make(chan *newWorkReq), + 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{}), + resultSubmitFailCh: make(chan struct{}), + signer: txSigner, + } + for addr, sm := range sstor.ContractToShardManager { + for idx, shard := range sm.ShardMap() { + task := task{ + worker: worker, + 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) + } + } + + // 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() // can change to multi threads to run task + + // Submit first work to initialize pending state. + if init { + worker.startCh <- struct{}{} + } + return worker +} + +// setRecommitInterval updates the interval for miner sealing work recommitting. +func (w *worker) setRecommitInterval(interval time.Duration) { + select { + case w.resubmitIntervalCh <- interval: + case <-w.exitCh: + } +} + +// 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 +} + +// 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.storageContract == 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.storageContract == 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() { + atomic.StoreInt32(&w.running, 0) + close(w.exitCh) + w.wg.Wait() +} + +// 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 ( + minRecommit = recommit // minimal resubmit interval specified by user. + ) + + timer := time.NewTimer(0) + defer timer.Stop() + <-timer.C // discard the initial tick + + for { + select { + case <-w.startCh: + w.updateTaskInfo(w.chain.CurrentBlock().Root(), time.Now().Unix()) + timer.Reset(recommit) + + case head := <-w.chainHeadCh: + 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() { + timer.Reset(recommit) + w.updateTaskInfo(w.chain.CurrentBlock().Root(), time.Now().Unix()) + } + + case interval := <-w.resubmitIntervalCh: + // Adjust resubmit interval explicitly by user. + if interval < minRecommitInterval { + interval = minRecommitInterval + } + log.Info("Miner recommit interval update", "from", minRecommit, "to", interval) + recommit = interval + + 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() + + var stopCh chan struct{} + interrupt := func() { + if stopCh != nil { + close(stopCh) + stopCh = nil + } + } + + for { + select { + case <-w.newWorkCh: + interrupt() + stopCh = make(chan struct{}) + w.commitWork(stopCh) + + case <-w.taskDoneCh: + interrupt() + stopCh = make(chan struct{}) + w.commitWork(stopCh) + + case <-w.taskStartCh: + interrupt() + 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(): + 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() + + for { + select { + case task := <-w.taskCh: + if w.newTaskHook != nil { + w.newTaskHook(task) + } + _, err := w.mineTask(task) + if err != nil { + log.Warn("mine task fail", "err", err.Error()) + } + w.taskDoneCh <- struct{}{} + + case <-w.exitCh: + 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 result := <-w.resultCh: + if w.newResultHook != nil { + w.newResultHook(result) + } + + // todo refer to the current layer 2 to process submissions + 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 + } + } +} + +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() + defer w.mu.Unlock() + + if !w.isRunning() { + return + } + updated := false + 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 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 + } + } + + if updated { + select { + case w.newWorkCh <- &newWorkReq{timestamp: timestamp}: + case <-w.exitCh: + return + } + } +} + +// 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 + } + // sort and find the task with smallest diff 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 + } + } + }() +} + +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 := t.shardIdx + i + if minedTs < t.info.LastMineTime { + err = fmt.Errorf("minedTs too small") + } + diffs[i] = t.expectedDiff(minedTs) + diff = new(big.Int).Add(diff, diffs[i]) + hash0 = crypto.Keccak256Hash(hash0.Bytes(), uint64ToByte32(shardId), t.info.MiningHash.Bytes()) + } + + return diff, diffs, hash0, nil +} + +func (w *worker) hashimoto(t *task, shardLenBits uint64, hash0 common.Hash) (common.Hash, [][]byte, []uint64, []uint64, error) { + 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(hash0.Bytes()).Uint64()%(uint64(1)<> t.chunkSizeBits + chunkIdxs[i] = chunkIdx % (1 << t.chunkSizeBits) + hash0 = crypto.Keccak256Hash(hash0.Bytes(), data) + } else { + if !exist { + err = fmt.Errorf("chunk not support: chunkIdxs %d", chunkIdx) + } + return hash0, dataSet, kvIdxs, chunkIdxs, err + } + } + + return hash0, dataSet, kvIdxs, chunkIdxs, nil +} + +func (w *worker) mineTask(t *task) (bool, error) { + if t.getState() == TaskStateMined { + return true, nil + } + minedTs := uint64(time.Now().Unix()) + // using random nonce, so we can run multi mine with threads + rand.Seed(int64(minedTs)) + nonce := rand.Uint64() % 1000000 + var ( + dataSet [][]byte + kvIdxs []uint64 + chunkIdxs []uint64 + shardLenBits uint64 = 0 + ) + + // todo shard len can be not 1 later + diff, _, hash0, err := w.calculateDiffAndInitHash(t, uint64(1)< uint64(time.Now().Unix()) { + hash1 := crypto.Keccak256Hash(hash0.Bytes(), addressToByte32(t.miner), uint64ToByte32(minedTs), uint64ToByte32(nonce)) + hash1, dataSet, kvIdxs, chunkIdxs, err = w.hashimoto(t, shardLenBits, hash1) + + if requiredDiff.Cmp(new(big.Int).SetBytes(hash1.Bytes())) >= 0 { + proofs := make([][]common.Hash, len(kvIdxs)) + kvs, err := w.chain.ReadKVsByIndexList(t.storageContract, 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 := sstor.GetProofWithMinTree(kvs[i].Data, t.chunkSizeBits, chunkIdxs[i]) + if err != nil { + return false, err + } + proofs[i] = ps + } + } + t.setState(TaskStateMined) + t.result = &result{ + task: t, + startShardId: t.shardIdx, + shardLenBits: 0, + miner: t.miner, + minedTs: minedTs, + nonce: nonce, + kvIdxs: kvIdxs, + chunkIdxs: chunkIdxs, + encodedData: dataSet, + proofs: proofs, + submitTxTime: 0, + } + w.resultCh <- t.result + + return true, nil + } + nonce++ + } + + return false, nil +} + +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 + } + + 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.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 { + return err + } + + err = w.apiBackend.SendTx(ctx, signedTx) + if err != nil { + return fmt.Errorf("SendTransaction hash %s, ERROR %s ", signedTx.Hash().Hex(), err.Error()) + } + result.submitTxTime = time.Now().Unix() + result.submitTxHash = signedTx.Hash() + log.Info("Submit mining tx", "hash", signedTx.Hash().Hex()) + return nil +} + +func uint64ToByte32(u uint64) []byte { + return common.BigToHash(new(big.Int).SetUint64(u)).Bytes() +} + +func addressToByte32(addr common.Address) []byte { + return common.BytesToHash(addr.Bytes()).Bytes() +} diff --git a/sstorminer/worker_test.go b/sstorminer/worker_test.go new file mode 100644 index 000000000000..a1c1a7c2132c --- /dev/null +++ b/sstorminer/worker_test.go @@ -0,0 +1,847 @@ +// 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 ( + "bytes" + "context" + "encoding/binary" + "errors" + "fmt" + "math/big" + "math/rand" + "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/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/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/sstorage" + "github.com/holiman/uint256" +) + +var ( + // Test chain configurations + testTxPoolConfig core.TxPoolConfig + ethashChainConfig *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 + + minerContract = common.HexToAddress("0x0000000000000000000000000000000000000001") + contract = common.HexToAddress("0x0000000000000000000000000000000003330001") + kvEntriesBits = uint64(9) + blocks = 5 + + defaultConfig = &Config{ + 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) + blockMined = new(big.Int).SetUint64(1) +) + +func init() { + testTxPoolConfig = core.DefaultTxPoolConfig + testTxPoolConfig.Journal = "" + ethashChainConfig = new(params.ChainConfig) + *ethashChainConfig = *params.TestChainConfig + + 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()) +} + +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(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)) + 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 { + 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 +} + +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 +} + +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 + txPool *core.TxPool + 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 { + var gspec = core.Genesis{ + Config: chainConfig, + Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, + } + + 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) + } + } + + return &testWorkerBackend{ + db: db, + chain: chain, + txPool: txpool, + genesis: &gspec, + } +} + +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 newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, shardIdxList []uint64, + chunkSizeBits uint64, shardIsFull bool) (*worker, map[uint64]*core.MiningInfo, []string, *testWorkerBackend) { + shards, files := createSstorage(contract, shardIdxList, sstorage.CHUNK_SIZE_BITS+chunkSizeBits, kvEntriesBits, 1, common.Address{}) + if shards == nil { + t.Fatalf("createSstorage failed") + } + + backend := newTestWorkerBackend(t, chainConfig, engine, shardIdxList, db, blocks, shardIsFull) + backend.txPool.AddLocals(pendingTxs) + + stateDB, _ := backend.chain.State() + wchain := wrapBlockChain{ + BlockChain: backend.chain, + stateDB: stateDB, + } + infos := wchain.initMiningInfos(shardIdxList, diff, blockMined) + makeKVStorage(stateDB, contract, shardIdxList, 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 { + info := new(core.MiningInfo) + info.MiningHash = crypto.Keccak256Hash(pinfo.MiningHash.Bytes()) + info.LastMineTime = uint64(time.Now().Unix()) + info.Difficulty = diff + info.BlockMined = blockMined + chain.saveMiningInfo(0, info) + blocks, _ := core.GenerateChain(ethashChainConfig, chain.CurrentBlock(), engine, db, 1, func(i int, gen *core.BlockGen) { + gen.SetCoinbase(testBankAddress) + }) + if _, err := chain.InsertChain(blocks); err != nil { + return fmt.Errorf("failed to insert origin chain: %v", err) + } + 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 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 { + hash := common.Hash{} + 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) + } + + root = meta.HashInMeta + } + + vr := verifyWithMinTree(root, hash, r.chunkIdxs[i], proofs) + if !vr { + return fmt.Errorf("verify proofs fail for index %d fail", r.kvIdxs[i]) + } + } + return nil +} + +func verifyWithMinTree(root []byte, dataHash common.Hash, chunkIdx uint64, proofs []common.Hash) bool { + nMinChunkBits := uint64(len(proofs)) + if chunkIdx >= uint64(1)< 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) + } + } +} + +func testWork_ProofsCreateAndVerifyWithMinTree(test *testing.T, val []byte, chunkPerKVBits uint64) { + chunkPerKV := uint64(1) << chunkPerKVBits + dataLen := uint64(len(val)) + + root := sstorage.MerkleRootWithMinTree(val) + for i := uint64(0); i < chunkPerKV; i++ { + hash := common.Hash{} + off := i * sstorage.CHUNK_SIZE + + ps, err := sstorage.GetProofWithMinTree(val, 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) + } + } +} 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) }