From 6bc4359589ce852b91a738b8180dc911aaa6a159 Mon Sep 17 00:00:00 2001 From: Paladz Date: Sun, 8 Apr 2018 15:17:26 +0800 Subject: [PATCH] Block node (#541) * separate orphan manage from protocol * savage version of blockIndex * elegent the blockindex * eleganet the leveldb part * make it runable on single node * pass the make test * process use channel * make the connect block safe from async --- account/accounts_test.go | 5 +- account/reserve_test.go | 2 +- api/miner.go | 5 +- asset/asset.go | 14 +- asset/asset_test.go | 5 +- consensus/difficulty/difficulty.go | 22 +++ database/leveldb/mainchain.go | 78 ----------- database/leveldb/mainchain_test.go | 65 --------- database/leveldb/store.go | 102 ++++++-------- database/storage/storage.pb.go | 62 ++------- database/storage/storage.proto | 10 -- mining/cpuminer/cpuminer.go | 2 +- mining/mining.go | 22 ++- netsync/message.go | 8 +- netsync/protocol_reactor.go | 4 +- node/node.go | 13 +- protocol/block.go | 83 ++++++----- protocol/blockindex.go | 209 ++++++++++++++++++++++++++++ protocol/orphan_manage.go | 78 +++++++++++ protocol/protocol.go | 246 +++++++++++++-------------------- {database => protocol}/store.go | 10 +- protocol/tx.go | 7 +- protocol/tx_test.go | 16 --- protocol/txpool_test.go | 8 +- protocol/validation.go | 144 +++++++++++++++++++ protocol/validation/validation.go | 171 ----------------------- protocol/validation/validation_test.go | 53 ------- test/util.go | 4 +- wallet/wallet.go | 2 +- wallet/wallet_test.go | 16 +-- 30 files changed, 703 insertions(+), 763 deletions(-) delete mode 100644 database/leveldb/mainchain.go delete mode 100644 database/leveldb/mainchain_test.go create mode 100755 protocol/blockindex.go create mode 100755 protocol/orphan_manage.go rename {database => protocol}/store.go (67%) create mode 100644 protocol/validation.go diff --git a/account/accounts_test.go b/account/accounts_test.go index e564e157..5cab2258 100644 --- a/account/accounts_test.go +++ b/account/accounts_test.go @@ -14,7 +14,6 @@ import ( "github.com/bytom/database/leveldb" "github.com/bytom/errors" "github.com/bytom/protocol" - "github.com/bytom/protocol/bc" "github.com/bytom/testutil" ) @@ -127,7 +126,7 @@ func TestUpdateAccountTags(t *testing.T) { store := leveldb.NewStore(testDB) txPool := protocol.NewTxPool() - chain, err := protocol.NewChain(bc.Hash{}, store, txPool) + chain, err := protocol.NewChain(store, txPool) if err != nil { t.Fatal(err) } @@ -224,7 +223,7 @@ func mockAccountManager(t *testing.T) *Manager { store := leveldb.NewStore(testDB) txPool := protocol.NewTxPool() - chain, err := protocol.NewChain(bc.Hash{}, store, txPool) + chain, err := protocol.NewChain(store, txPool) if err != nil { t.Fatal(err) } diff --git a/account/reserve_test.go b/account/reserve_test.go index 58438107..9905989b 100644 --- a/account/reserve_test.go +++ b/account/reserve_test.go @@ -118,7 +118,7 @@ func TestCancelReservation(t *testing.T) { func mockChain(testDB dbm.DB) (*protocol.Chain, error) { store := leveldb.NewStore(testDB) txPool := protocol.NewTxPool() - chain, err := protocol.NewChain(bc.Hash{}, store, txPool) + chain, err := protocol.NewChain(store, txPool) if err != nil { return nil, err } diff --git a/api/miner.go b/api/miner.go index a6b2f8e7..f59bad9f 100644 --- a/api/miner.go +++ b/api/miner.go @@ -3,8 +3,8 @@ package api import ( "context" - "github.com/bytom/protocol/bc/types" "github.com/bytom/protocol/bc" + "github.com/bytom/protocol/bc/types" ) func (a *API) getWork() Response { @@ -15,7 +15,6 @@ func (a *API) getWork() Response { return NewSuccessResponse(work) } - type SubmitWorkReq struct { BlockHeader *types.BlockHeader `json:"block_header"` } @@ -54,7 +53,7 @@ func (a *API) GetWork() (*GetWorkResp, error) { return nil, err } - seed, err := a.chain.GetSeed(bh.Height, &bh.PreviousBlockHash) + seed, err := a.chain.CalcNextSeed(&bh.PreviousBlockHash) if err != nil { return nil, err } diff --git a/asset/asset.go b/asset/asset.go index d35d7f46..d2b53a5d 100755 --- a/asset/asset.go +++ b/asset/asset.go @@ -85,19 +85,17 @@ var ( func NewRegistry(db dbm.DB, chain *protocol.Chain) *Registry { initNativeAsset() return &Registry{ - db: db, - chain: chain, - initialBlockHash: chain.InitialBlockHash, - cache: lru.New(maxAssetCache), - aliasCache: lru.New(maxAssetCache), + db: db, + chain: chain, + cache: lru.New(maxAssetCache), + aliasCache: lru.New(maxAssetCache), } } // Registry tracks and stores all known assets on a blockchain. type Registry struct { - db dbm.DB - chain *protocol.Chain - initialBlockHash bc.Hash + db dbm.DB + chain *protocol.Chain idGroup singleflight.Group aliasGroup singleflight.Group diff --git a/asset/asset_test.go b/asset/asset_test.go index 065e3179..db3edf7f 100644 --- a/asset/asset_test.go +++ b/asset/asset_test.go @@ -14,7 +14,6 @@ import ( "github.com/bytom/crypto/ed25519/chainkd" "github.com/bytom/database/leveldb" "github.com/bytom/protocol" - "github.com/bytom/protocol/bc" "github.com/bytom/testutil" ) @@ -99,7 +98,7 @@ func TestUpdateAssetTags(t *testing.T) { store := leveldb.NewStore(testDB) txPool := protocol.NewTxPool() - chain, err := protocol.NewChain(bc.Hash{}, store, txPool) + chain, err := protocol.NewChain(store, txPool) if err != nil { t.Fatal(err) } @@ -212,7 +211,7 @@ func TestListAssets(t *testing.T) { func mockChain(testDB dbm.DB) (*protocol.Chain, error) { store := leveldb.NewStore(testDB) txPool := protocol.NewTxPool() - chain, err := protocol.NewChain(bc.Hash{}, store, txPool) + chain, err := protocol.NewChain(store, txPool) if err != nil { return nil, err } diff --git a/consensus/difficulty/difficulty.go b/consensus/difficulty/difficulty.go index 14b389c7..82f967cc 100644 --- a/consensus/difficulty/difficulty.go +++ b/consensus/difficulty/difficulty.go @@ -9,6 +9,16 @@ import ( "github.com/bytom/protocol/bc/types" ) +var ( + // bigOne is 1 represented as a big.Int. It is defined here to avoid + // the overhead of creating it multiple times. + bigOne = big.NewInt(1) + + // oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid + // the overhead of creating it multiple times. + oneLsh256 = new(big.Int).Lsh(bigOne, 256) +) + // HashToBig convert bc.Hash to a difficulty int func HashToBig(hash *bc.Hash) *big.Int { // reverse the bytes of the hash (little-endian) to use it in the big @@ -22,6 +32,18 @@ func HashToBig(hash *bc.Hash) *big.Int { return new(big.Int).SetBytes(buf[:]) } +// CalcWork calculates a work value from difficulty bits. +func CalcWork(bits uint64) *big.Int { + difficultyNum := CompactToBig(bits) + if difficultyNum.Sign() <= 0 { + return big.NewInt(0) + } + + // (1 << 256) / (difficultyNum + 1) + denominator := new(big.Int).Add(difficultyNum, bigOne) + return new(big.Int).Div(oneLsh256, denominator) +} + // CompactToBig converts a compact representation of a whole unsigned integer // N to an big.Int. The representation is similar to IEEE754 floating point // numbers. Sign is not really being used. diff --git a/database/leveldb/mainchain.go b/database/leveldb/mainchain.go deleted file mode 100644 index 44774393..00000000 --- a/database/leveldb/mainchain.go +++ /dev/null @@ -1,78 +0,0 @@ -package leveldb - -import ( - "bytes" - - "github.com/golang/protobuf/proto" - dbm "github.com/tendermint/tmlibs/db" - - "github.com/bytom/database/storage" - "github.com/bytom/errors" - "github.com/bytom/protocol/bc" -) - -const mainchainPreFix = "MC:" - -func calcMainchainKey(hash *bc.Hash) []byte { - return []byte(mainchainPreFix + hash.String()) -} - -// DecodeMainchain decodes a Mainchain from bytes -func DecodeMainchain(data []byte) (map[uint64]*bc.Hash, error) { - var mainchainList storage.Mainchain - if err := proto.Unmarshal(data, &mainchainList); err != nil { - return nil, errors.Wrap(err, "unmarshaling Mainchain proto") - } - - mainchain := make(map[uint64]*bc.Hash) - for i, rawHash := range mainchainList.Hashs { - var b32 [32]byte - copy(b32[:], rawHash.Key) - hash := bc.NewHash(b32) - mainchain[uint64(i)] = &hash - } - - return mainchain, nil -} - -func saveMainchain(batch dbm.Batch, mainchain map[uint64]*bc.Hash, hash *bc.Hash) error { - var mainchainList storage.Mainchain - for i := 0; i < len(mainchain); i++ { - rawHash := &storage.Mainchain_Hash{Key: mainchain[uint64(i)].Bytes()} - mainchainList.Hashs = append(mainchainList.Hashs, rawHash) - } - - b, err := proto.Marshal(&mainchainList) - if err != nil { - return errors.Wrap(err, "marshaling Mainchain") - } - - batch.Set(calcMainchainKey(hash), b) - return nil -} - -func getMainchain(db dbm.DB, hash *bc.Hash) (map[uint64]*bc.Hash, error) { - data := db.Get(calcMainchainKey(hash)) - if data == nil { - return nil, errors.New("no this Mainchain") - } - - mainchain, err := DecodeMainchain(data) - if err != nil { - return nil, errors.Wrap(err, "decoding Mainchain") - } - return mainchain, nil -} - -func cleanMainchainDB(db dbm.DB, hash *bc.Hash) { - keepKey := calcMainchainKey(hash) - - iter := db.IteratorPrefix([]byte(mainchainPreFix)) - defer iter.Release() - for iter.Next() { - if key := iter.Key(); !bytes.Equal(key, keepKey) { - db.Delete(key) - } - } - db.SetSync(nil, nil) -} diff --git a/database/leveldb/mainchain_test.go b/database/leveldb/mainchain_test.go deleted file mode 100644 index bc721083..00000000 --- a/database/leveldb/mainchain_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package leveldb - -import ( - "bytes" - "os" - "testing" - - dbm "github.com/tendermint/tmlibs/db" - - "github.com/bytom/protocol/bc" - "github.com/bytom/testutil" -) - -func TestSaveMainchain(t *testing.T) { - testDB := dbm.NewDB("testdb", "leveldb", "temp") - defer os.RemoveAll("temp") - - inputMain := make(map[uint64]*bc.Hash) - for i := uint64(0); i <= uint64(10); i++ { - inputMain[i] = &bc.Hash{V0: i} - } - - saveHash := &bc.Hash{V0: 0} - batch := testDB.NewBatch() - if err := saveMainchain(batch, inputMain, saveHash); err != nil { - t.Errorf(err.Error()) - } - batch.Write() - - fetchMain, err := getMainchain(testDB, saveHash) - if err != nil { - t.Errorf(err.Error()) - } - - if !testutil.DeepEqual(inputMain, fetchMain) { - t.Errorf("inputMain and fetchMain is not equal") - } -} - -func TestCleanMainchainDB(t *testing.T) { - testDB := dbm.NewDB("testdb", "leveldb", "temp") - defer os.RemoveAll("temp") - - // Insert the test data - hash := &bc.Hash{} - for i := uint64(0); i <= uint64(10); i++ { - hash.V0 = i - testDB.Set(calcMainchainKey(hash), nil) - } - testDB.SetSync(nil, nil) - - // run the test function - cleanMainchainDB(testDB, hash) - - // check the clean result - iter := testDB.IteratorPrefix([]byte(mainchainPreFix)) - defer iter.Release() - - if !iter.Next() || !bytes.Equal(iter.Key(), calcMainchainKey(hash)) { - t.Errorf("latest mainchain get deleted from db") - } - if iter.Next() { - t.Errorf("more than one mainchain still saved in the db") - } -} diff --git a/database/leveldb/store.go b/database/leveldb/store.go index 1f3db46c..45a3faa5 100644 --- a/database/leveldb/store.go +++ b/database/leveldb/store.go @@ -1,15 +1,16 @@ package leveldb import ( + "encoding/binary" "encoding/json" "github.com/golang/protobuf/proto" "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" - "github.com/bytom/database" "github.com/bytom/database/storage" "github.com/bytom/errors" + "github.com/bytom/protocol" "github.com/bytom/protocol/bc" "github.com/bytom/protocol/bc/types" "github.com/bytom/protocol/state" @@ -19,18 +20,17 @@ var ( blockStoreKey = []byte("blockStore") blockPrefix = []byte("B:") blockHeaderPrefix = []byte("BH:") - blockSeedPrefix = []byte("BS:") txStatusPrefix = []byte("BTS:") ) -func loadBlockStoreStateJSON(db dbm.DB) database.BlockStoreStateJSON { +func loadBlockStoreStateJSON(db dbm.DB) protocol.BlockStoreStateJSON { bytes := db.Get(blockStoreKey) if bytes == nil { - return database.BlockStoreStateJSON{ + return protocol.BlockStoreStateJSON{ Height: 0, } } - bsj := database.BlockStoreStateJSON{} + bsj := protocol.BlockStoreStateJSON{} if err := json.Unmarshal(bytes, &bsj); err != nil { common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes)) } @@ -49,12 +49,11 @@ func calcBlockKey(hash *bc.Hash) []byte { return append(blockPrefix, hash.Bytes()...) } -func calcBlockHeaderKey(hash *bc.Hash) []byte { - return append(blockHeaderPrefix, hash.Bytes()...) -} - -func calcSeedKey(hash *bc.Hash) []byte { - return append(blockSeedPrefix, hash.Bytes()...) +func calcBlockHeaderKey(height uint64, hash *bc.Hash) []byte { + buf := [8]byte{} + binary.LittleEndian.PutUint64(buf[:], height) + key := append(blockHeaderPrefix, buf[:]...) + return append(key, hash.Bytes()...) } func calcTxStatusKey(hash *bc.Hash) []byte { @@ -100,32 +99,6 @@ func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) { return s.cache.lookup(hash) } -// GetBlockHeader return the block by given hash -func (s *Store) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) { - bytez := s.db.Get(calcBlockHeaderKey(hash)) - if bytez == nil { - return nil, errors.New("can't find the block header by given hash") - } - - bh := &types.BlockHeader{} - err := bh.UnmarshalText(bytez) - return bh, err -} - -// GetSeed will return the seed of given block -func (s *Store) GetSeed(hash *bc.Hash) (*bc.Hash, error) { - data := s.db.Get(calcSeedKey(hash)) - if data == nil { - return nil, errors.New("can't find the seed by given hash") - } - - seed := &bc.Hash{} - if err := proto.Unmarshal(data, seed); err != nil { - return nil, errors.Wrap(err, "unmarshaling seed") - } - return seed, nil -} - // GetTransactionsUtxo will return all the utxo that related to the input txs func (s *Store) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error { return getTransactionsUtxo(s.db, view, txs) @@ -146,17 +119,43 @@ func (s *Store) GetTransactionStatus(hash *bc.Hash) (*bc.TransactionStatus, erro } // GetStoreStatus return the BlockStoreStateJSON -func (s *Store) GetStoreStatus() database.BlockStoreStateJSON { +func (s *Store) GetStoreStatus() protocol.BlockStoreStateJSON { return loadBlockStoreStateJSON(s.db) } -// GetMainchain read the mainchain map from db -func (s *Store) GetMainchain(hash *bc.Hash) (map[uint64]*bc.Hash, error) { - return getMainchain(s.db, hash) +func (s *Store) LoadBlockIndex() (*protocol.BlockIndex, error) { + blockIndex := protocol.NewBlockIndex() + bhIter := s.db.IteratorPrefix(blockHeaderPrefix) + defer bhIter.Release() + + var lastNode *protocol.BlockNode + for bhIter.Next() { + bh := &types.BlockHeader{} + if err := bh.UnmarshalText(bhIter.Value()); err != nil { + return nil, err + } + + var parent *protocol.BlockNode + if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash { + parent = lastNode + } else { + parent = blockIndex.GetNode(&bh.PreviousBlockHash) + } + + node, err := protocol.NewBlockNode(bh, parent) + if err != nil { + return nil, err + } + + blockIndex.AddNode(node) + lastNode = node + } + + return blockIndex, nil } -// SaveBlock persists a new block in the database. -func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus, seed *bc.Hash) error { +// SaveBlock persists a new block in the protocol. +func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error { binaryBlock, err := block.MarshalText() if err != nil { return errors.Wrap(err, "Marshal block meta") @@ -172,41 +171,30 @@ func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus, seed *bc return errors.Wrap(err, "marshal block transaction status") } - binarySeed, err := proto.Marshal(seed) - if err != nil { - return errors.Wrap(err, "marshal block seed") - } - blockHash := block.Hash() batch := s.db.NewBatch() batch.Set(calcBlockKey(&blockHash), binaryBlock) - batch.Set(calcBlockHeaderKey(&blockHash), binaryBlockHeader) + batch.Set(calcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader) batch.Set(calcTxStatusKey(&blockHash), binaryTxStatus) - batch.Set(calcSeedKey(&blockHash), binarySeed) batch.Write() return nil } // SaveChainStatus save the core's newest status && delete old status -func (s *Store) SaveChainStatus(block *types.Block, view *state.UtxoViewpoint, m map[uint64]*bc.Hash) error { +func (s *Store) SaveChainStatus(block *types.Block, view *state.UtxoViewpoint) error { hash := block.Hash() batch := s.db.NewBatch() - if err := saveMainchain(batch, m, &hash); err != nil { - return err - } - if err := saveUtxoView(batch, view); err != nil { return err } - bytes, err := json.Marshal(database.BlockStoreStateJSON{Height: block.Height, Hash: &hash}) + bytes, err := json.Marshal(protocol.BlockStoreStateJSON{Height: block.Height, Hash: &hash}) if err != nil { return err } batch.Set(blockStoreKey, bytes) batch.Write() - cleanMainchainDB(s.db, &hash) return nil } diff --git a/database/storage/storage.pb.go b/database/storage/storage.pb.go index 79f370ca..8303680b 100644 --- a/database/storage/storage.pb.go +++ b/database/storage/storage.pb.go @@ -9,7 +9,6 @@ It is generated from these files: It has these top-level messages: UtxoEntry - Mainchain */ package storage @@ -60,61 +59,22 @@ func (m *UtxoEntry) GetSpent() bool { return false } -// Mainchain represents a mainchain of the blockchain -type Mainchain struct { - Hashs []*Mainchain_Hash `protobuf:"bytes,1,rep,name=hashs" json:"hashs,omitempty"` -} - -func (m *Mainchain) Reset() { *m = Mainchain{} } -func (m *Mainchain) String() string { return proto.CompactTextString(m) } -func (*Mainchain) ProtoMessage() {} -func (*Mainchain) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -func (m *Mainchain) GetHashs() []*Mainchain_Hash { - if m != nil { - return m.Hashs - } - return nil -} - -type Mainchain_Hash struct { - Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` -} - -func (m *Mainchain_Hash) Reset() { *m = Mainchain_Hash{} } -func (m *Mainchain_Hash) String() string { return proto.CompactTextString(m) } -func (*Mainchain_Hash) ProtoMessage() {} -func (*Mainchain_Hash) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} } - -func (m *Mainchain_Hash) GetKey() []byte { - if m != nil { - return m.Key - } - return nil -} - func init() { proto.RegisterType((*UtxoEntry)(nil), "chain.core.txdb.internal.storage.UtxoEntry") - proto.RegisterType((*Mainchain)(nil), "chain.core.txdb.internal.storage.Mainchain") - proto.RegisterType((*Mainchain_Hash)(nil), "chain.core.txdb.internal.storage.Mainchain.Hash") } func init() { proto.RegisterFile("storage.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 212 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0x41, 0x4b, 0xc4, 0x30, - 0x10, 0x85, 0x89, 0xdd, 0x55, 0x3b, 0xab, 0x20, 0xc1, 0x43, 0xf0, 0x20, 0x61, 0x4f, 0x3d, 0x0d, - 0xa2, 0xff, 0x60, 0x45, 0xd9, 0x8b, 0x97, 0x80, 0x17, 0x6f, 0x69, 0x0c, 0x9b, 0xb0, 0x35, 0x29, - 0xc9, 0x1c, 0xda, 0x7f, 0x2f, 0x4d, 0x8b, 0xf4, 0xe6, 0x6d, 0xde, 0x63, 0x3e, 0xf8, 0x78, 0x70, - 0x9b, 0x29, 0x26, 0x7d, 0xb2, 0xd8, 0xa7, 0x48, 0x91, 0x4b, 0xe3, 0xb4, 0x0f, 0x68, 0x62, 0xb2, - 0x48, 0xc3, 0x77, 0x8b, 0x3e, 0x90, 0x4d, 0x41, 0x77, 0xb8, 0xfc, 0xed, 0x0d, 0xd4, 0x9f, 0x34, - 0xc4, 0xb7, 0x40, 0x69, 0xe4, 0x8f, 0x00, 0x3e, 0xbf, 0x46, 0x1f, 0x0e, 0x3a, 0x5b, 0xc1, 0x24, - 0x6b, 0xae, 0xd5, 0xaa, 0xe1, 0x12, 0x76, 0x6d, 0x17, 0xcd, 0xf9, 0x68, 0xfd, 0xc9, 0x91, 0xb8, - 0x90, 0xac, 0xd9, 0xa8, 0x75, 0xc5, 0xef, 0x61, 0x9b, 0x7b, 0x1b, 0x48, 0x54, 0x05, 0x9e, 0xc3, - 0xfe, 0x07, 0xea, 0x0f, 0xed, 0x43, 0x91, 0xe1, 0xef, 0xb0, 0x75, 0x3a, 0xbb, 0x2c, 0x98, 0xac, - 0x9a, 0xdd, 0xf3, 0x13, 0xfe, 0xe7, 0x88, 0x7f, 0x2c, 0x1e, 0x75, 0x76, 0x6a, 0xc6, 0x1f, 0x04, - 0x6c, 0xa6, 0xc8, 0xef, 0xa0, 0x3a, 0xdb, 0xb1, 0xd8, 0xde, 0xa8, 0xe9, 0x3c, 0xd4, 0x5f, 0x57, - 0x0b, 0xda, 0x5e, 0x96, 0x1d, 0x5e, 0x7e, 0x03, 0x00, 0x00, 0xff, 0xff, 0xca, 0xf0, 0xed, 0x5a, - 0x18, 0x01, 0x00, 0x00, + // 154 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2d, 0x2e, 0xc9, 0x2f, + 0x4a, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x52, 0x48, 0xce, 0x48, 0xcc, 0xcc, + 0xd3, 0x4b, 0xce, 0x2f, 0x4a, 0xd5, 0x2b, 0xa9, 0x48, 0x49, 0xd2, 0xcb, 0xcc, 0x2b, 0x49, 0x2d, + 0xca, 0x4b, 0xcc, 0xd1, 0x83, 0xaa, 0x53, 0x4a, 0xe6, 0xe2, 0x0c, 0x2d, 0xa9, 0xc8, 0x77, 0xcd, + 0x2b, 0x29, 0xaa, 0x14, 0x92, 0xe3, 0xe2, 0xca, 0x2c, 0x76, 0xce, 0xcf, 0xcc, 0x73, 0x4a, 0x2c, + 0x4e, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x08, 0x42, 0x12, 0x11, 0x52, 0xe0, 0xe2, 0x4e, 0xca, + 0xc9, 0x4f, 0xce, 0xf6, 0x48, 0xcd, 0x4c, 0xcf, 0x28, 0x91, 0x60, 0x52, 0x60, 0xd4, 0x60, 0x09, + 0x42, 0x16, 0x12, 0x12, 0xe1, 0x62, 0x2d, 0x2e, 0x48, 0xcd, 0x2b, 0x91, 0x60, 0x06, 0x6b, 0x86, + 0x70, 0x9c, 0x38, 0xa3, 0xd8, 0xa1, 0xf6, 0x25, 0xb1, 0x81, 0x1d, 0x66, 0x0c, 0x08, 0x00, 0x00, + 0xff, 0xff, 0xcf, 0xe4, 0x5a, 0x5c, 0xa9, 0x00, 0x00, 0x00, } diff --git a/database/storage/storage.proto b/database/storage/storage.proto index 5b778593..5e36b1c3 100644 --- a/database/storage/storage.proto +++ b/database/storage/storage.proto @@ -7,13 +7,3 @@ message UtxoEntry { uint64 blockHeight = 2; bool spent = 3; } - -// Mainchain represents a mainchain of the blockchain -message Mainchain { - - repeated Hash hashs = 1; - - message Hash { - bytes key = 1; - } -} diff --git a/mining/cpuminer/cpuminer.go b/mining/cpuminer/cpuminer.go index 3c3effcf..5cbf0c00 100644 --- a/mining/cpuminer/cpuminer.go +++ b/mining/cpuminer/cpuminer.go @@ -49,7 +49,7 @@ type CPUMiner struct { // target difficulty. func (m *CPUMiner) solveBlock(block *types.Block, ticker *time.Ticker, quit chan struct{}) bool { header := &block.BlockHeader - seed, err := m.chain.GetSeed(header.Height, &header.PreviousBlockHash) + seed, err := m.chain.CalcNextSeed(&header.PreviousBlockHash) if err != nil { return false } diff --git a/mining/mining.go b/mining/mining.go index adb474a8..8d06d3d3 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -13,7 +13,6 @@ import ( "github.com/bytom/account" "github.com/bytom/blockchain/txbuilder" "github.com/bytom/consensus" - "github.com/bytom/consensus/difficulty" "github.com/bytom/errors" "github.com/bytom/protocol" "github.com/bytom/protocol/bc" @@ -68,27 +67,26 @@ func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager txFee := uint64(0) // get preblock info for generate next block - preBlock := c.BestBlock() - preBcBlock := types.MapBlock(preBlock) - nextBlockHeight := preBlock.Height + 1 - - var compareDiffBH *types.BlockHeader - if compareDiffBlock, err := c.GetBlockByHeight(preBlock.Height - consensus.BlocksPerRetarget); err == nil { - compareDiffBH = &compareDiffBlock.BlockHeader + preBlockHeader := c.BestBlockHeader() + preBlockHash := preBlockHeader.Hash() + nextBlockHeight := preBlockHeader.Height + 1 + nextBits, err := c.CalcNextBits(&preBlockHash) + if err != nil { + return nil, err } b = &types.Block{ BlockHeader: types.BlockHeader{ Version: 1, Height: nextBlockHeight, - PreviousBlockHash: preBlock.Hash(), + PreviousBlockHash: preBlockHash, Timestamp: uint64(time.Now().Unix()), BlockCommitment: types.BlockCommitment{}, - Bits: difficulty.CalcNextRequiredDifficulty(&preBlock.BlockHeader, compareDiffBH), + Bits: nextBits, }, - Transactions: []*types.Tx{nil}, } bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: nextBlockHeight}} + b.Transactions = []*types.Tx{nil} txs := txPool.GetTransactions() sort.Sort(ByTime(txs)) @@ -102,7 +100,7 @@ func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager continue } - gasStatus, err := validation.ValidateTx(tx, preBcBlock) + gasStatus, err := validation.ValidateTx(tx, bcBlock) if err != nil { if !gasStatus.GasVaild { log.WithField("error", err).Error("mining block generate skip tx due to") diff --git a/netsync/message.go b/netsync/message.go index 4a17f813..eee3c1b9 100644 --- a/netsync/message.go +++ b/netsync/message.go @@ -151,10 +151,10 @@ type StatusResponseMessage struct { } //NewStatusResponseMessage construct get status response msg -func NewStatusResponseMessage(block *types.Block) *StatusResponseMessage { +func NewStatusResponseMessage(blockHeader *types.BlockHeader) *StatusResponseMessage { return &StatusResponseMessage{ - Height: block.Height, - RawHash: block.Hash().Byte32(), + Height: blockHeader.Height, + RawHash: blockHeader.Hash().Byte32(), } } @@ -187,7 +187,7 @@ func NewMinedBlockMessage(block *types.Block) (*MineBlockMessage, error) { //GetMineBlock get mine block from msg func (m *MineBlockMessage) GetMineBlock() (*types.Block, error) { block := &types.Block{} - if err:=block.UnmarshalText(m.RawBlock);err!=nil{ + if err := block.UnmarshalText(m.RawBlock); err != nil { return nil, err } return block, nil diff --git a/netsync/protocol_reactor.go b/netsync/protocol_reactor.go index 17288597..b857da71 100644 --- a/netsync/protocol_reactor.go +++ b/netsync/protocol_reactor.go @@ -177,8 +177,8 @@ func (pr *ProtocolReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) { pr.blockKeeper.AddBlock(msg.GetBlock(), src.Key) case *StatusRequestMessage: - block := pr.chain.BestBlock() - src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{NewStatusResponseMessage(block)}) + blockHeader := pr.chain.BestBlockHeader() + src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{NewStatusResponseMessage(blockHeader)}) case *StatusResponseMessage: peerStatus := &initalPeerStatus{ diff --git a/node/node.go b/node/node.go index f8f13825..cf87f51e 100755 --- a/node/node.go +++ b/node/node.go @@ -73,23 +73,12 @@ func NewNode(config *cfg.Config) *Node { cmn.Exit(cmn.Fmt("Failed to start switch: %v", err)) } - genesisBlock := cfg.GenerateGenesisBlock() - txPool := protocol.NewTxPool() - chain, err := protocol.NewChain(genesisBlock.Hash(), store, txPool) + chain, err := protocol.NewChain(store, txPool) if err != nil { cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err)) } - if chain.BestBlockHash() == nil { - if err := chain.SaveBlock(genesisBlock); err != nil { - cmn.Exit(cmn.Fmt("Failed to save genesisBlock to store: %v", err)) - } - if err := chain.ConnectBlock(genesisBlock); err != nil { - cmn.Exit(cmn.Fmt("Failed to connect genesisBlock to chain: %v", err)) - } - } - var accounts *account.Manager = nil var assets *asset.Registry = nil var wallet *w.Wallet = nil diff --git a/protocol/block.go b/protocol/block.go index a83bb371..4f4289c4 100755 --- a/protocol/block.go +++ b/protocol/block.go @@ -7,7 +7,6 @@ import ( "github.com/bytom/protocol/bc" "github.com/bytom/protocol/bc/types" "github.com/bytom/protocol/state" - "github.com/bytom/protocol/validation" ) var ( @@ -21,7 +20,7 @@ var ( // BlockExist check is a block in chain or orphan func (c *Chain) BlockExist(hash *bc.Hash) bool { - return c.orphanManage.BlockExist(hash) || c.store.BlockExist(hash) + return c.orphanManage.BlockExist(hash) || c.index.BlockExist(hash) } // GetBlockByHash return a block by given hash @@ -31,13 +30,11 @@ func (c *Chain) GetBlockByHash(hash *bc.Hash) (*types.Block, error) { // GetBlockByHeight return a block by given height func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) { - c.state.cond.L.Lock() - hash, ok := c.state.mainChain[height] - c.state.cond.L.Unlock() - if !ok { + node := c.index.NodeByHeight(height) + if node == nil { return nil, errors.New("can't find block in given hight") } - return c.GetBlockByHash(hash) + return c.store.GetBlock(&node.Hash) } // ConnectBlock append block to end of chain @@ -62,8 +59,7 @@ func (c *Chain) connectBlock(block *types.Block) (err error) { return err } - blockHash := block.Hash() - if err := c.setState(block, utxoView, map[uint64]*bc.Hash{block.Height: &blockHash}); err != nil { + if err := c.setState(block, utxoView); err != nil { return err } @@ -78,12 +74,12 @@ func (c *Chain) getReorganizeBlocks(block *types.Block) ([]*types.Block, []*type detachBlocks := []*types.Block{} ancestor := block - for !c.inMainchain(ancestor) { + for !c.index.InMainchain(block.Hash()) { attachBlocks = append([]*types.Block{ancestor}, attachBlocks...) ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash) } - for d := c.state.block; d.Hash() != ancestor.Hash(); d, _ = c.GetBlockByHash(&d.PreviousBlockHash) { + for d, _ := c.store.GetBlock(c.state.hash); d.Hash() != ancestor.Hash(); d, _ = c.store.GetBlock(&d.PreviousBlockHash) { detachBlocks = append(detachBlocks, d) } @@ -93,7 +89,6 @@ func (c *Chain) getReorganizeBlocks(block *types.Block) ([]*types.Block, []*type func (c *Chain) reorganizeChain(block *types.Block) error { attachBlocks, detachBlocks := c.getReorganizeBlocks(block) utxoView := state.NewUtxoViewpoint() - chainChanges := map[uint64]*bc.Hash{} for _, d := range detachBlocks { detachBlock := types.MapBlock(d) @@ -122,36 +117,31 @@ func (c *Chain) reorganizeChain(block *types.Block) error { if err := utxoView.ApplyBlock(attachBlock, txStatus); err != nil { return err } - chainChanges[a.Height] = &attachBlock.ID - // TODO: put this action in better place - c.orphanManage.Delete(&attachBlock.ID) } - return c.setState(block, utxoView, chainChanges) + return c.setState(block, utxoView) } // SaveBlock will validate and save block into storage func (c *Chain) SaveBlock(block *types.Block) error { - preBlock, _ := c.GetBlockByHash(&block.PreviousBlockHash) - blockEnts := types.MapBlock(block) - prevEnts := types.MapBlock(preBlock) - - seed, err := c.GetSeed(block.Height, &block.PreviousBlockHash) - if err != nil { - return err + if err := c.validateBlock(blockEnts); err != nil { + return errors.Sub(ErrBadBlock, err) } - if err := validation.ValidateBlock(blockEnts, prevEnts, seed, c.store); err != nil { - return errors.Sub(ErrBadBlock, err) + if err := c.store.SaveBlock(block, blockEnts.TransactionStatus); err != nil { + return err } - if err := c.store.SaveBlock(block, blockEnts.TransactionStatus, seed); err != nil { + c.orphanManage.Delete(&blockEnts.ID) + parent := c.index.GetNode(&block.PreviousBlockHash) + node, err := NewBlockNode(&block.BlockHeader, parent) + if err != nil { return err } - blockHash := block.Hash() - log.WithFields(log.Fields{"height": block.Height, "hash": blockHash.String()}).Info("Block saved on disk") + c.index.AddNode(node) + log.WithFields(log.Fields{"height": block.Height, "hash": blockEnts.ID.String()}).Info("Block saved on disk") return nil } @@ -182,12 +172,35 @@ func (c *Chain) findBestChainTail(block *types.Block) (bestBlock *types.Block) { } } - c.orphanManage.Delete(&blockHash) return } -// ProcessBlock is the entry for handle block insert +type processBlockResponse struct { + isOrphan bool + err error +} + +type processBlockMsg struct { + block *types.Block + reply chan processBlockResponse +} + func (c *Chain) ProcessBlock(block *types.Block) (bool, error) { + reply := make(chan processBlockResponse, 1) + c.processBlockCh <- &processBlockMsg{block: block, reply: reply} + response := <-reply + return response.isOrphan, response.err +} + +func (c *Chain) blockProcesser() { + for msg := range c.processBlockCh { + isOrphan, err := c.processBlock(msg.block) + msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err} + } +} + +// ProcessBlock is the entry for handle block insert +func (c *Chain) processBlock(block *types.Block) (bool, error) { blockHash := block.Hash() if c.BlockExist(&blockHash) { log.WithField("hash", blockHash.String()).Info("Skip process due to block already been handled") @@ -202,13 +215,15 @@ func (c *Chain) ProcessBlock(block *types.Block) (bool, error) { } bestBlock := c.findBestChainTail(block) - c.state.cond.L.Lock() - defer c.state.cond.L.Unlock() - if c.state.block.Hash() == bestBlock.PreviousBlockHash { + bestMainChain := c.index.BestNode() + bestBlockHash := bestBlock.Hash() + bestNode := c.index.GetNode(&bestBlockHash) + + if bestNode.parent == bestMainChain { return false, c.connectBlock(bestBlock) } - if bestBlock.Height > c.state.block.Height && bestBlock.Bits >= c.state.block.Bits { + if bestNode.height > bestMainChain.height && bestNode.workSum.Cmp(bestMainChain.workSum) >= 0 { return false, c.reorganizeChain(bestBlock) } diff --git a/protocol/blockindex.go b/protocol/blockindex.go new file mode 100755 index 00000000..f95cb17f --- /dev/null +++ b/protocol/blockindex.go @@ -0,0 +1,209 @@ +package protocol + +import ( + "errors" + "math/big" + "sort" + "sync" + + "github.com/bytom/common" + "github.com/bytom/consensus" + "github.com/bytom/consensus/difficulty" + "github.com/bytom/protocol/bc" + "github.com/bytom/protocol/bc/types" +) + +// approxNodesPerDay is an approximation of the number of new blocks there are +// in a week on average. +const approxNodesPerDay = 24 * 24 + +// BlockNode represents a block within the block chain and is primarily used to +// aid in selecting the best chain to be the main chain. +type BlockNode struct { + parent *BlockNode // parent is the parent block for this node. + Hash bc.Hash // hash of the block. + seed *bc.Hash // seed hash of the block + workSum *big.Int // total amount of work in the chain up to + + version uint64 + height uint64 + timestamp uint64 + nonce uint64 + bits uint64 + transactionsMerkleRoot bc.Hash + transactionStatusHash bc.Hash +} + +func NewBlockNode(bh *types.BlockHeader, parent *BlockNode) (*BlockNode, error) { + if bh.Height != 0 && parent == nil { + return nil, errors.New("parent node can not be nil") + } + + node := &BlockNode{ + parent: parent, + Hash: bh.Hash(), + workSum: difficulty.CalcWork(bh.Bits), + version: bh.Version, + height: bh.Height, + timestamp: bh.Timestamp, + nonce: bh.Nonce, + bits: bh.Bits, + transactionsMerkleRoot: bh.TransactionsMerkleRoot, + transactionStatusHash: bh.TransactionStatusHash, + } + + if bh.Height == 0 { + node.seed = consensus.InitialSeed + } else { + node.seed = parent.CalcNextSeed() + node.workSum = node.workSum.Add(parent.workSum, node.workSum) + } + return node, nil +} + +// blockHeader convert a node to the header struct +func (node *BlockNode) blockHeader() *types.BlockHeader { + previousBlockHash := bc.Hash{} + if node.parent != nil { + previousBlockHash = node.parent.Hash + } + return &types.BlockHeader{ + Version: node.version, + Height: node.height, + PreviousBlockHash: previousBlockHash, + Timestamp: node.timestamp, + Nonce: node.nonce, + Bits: node.bits, + BlockCommitment: types.BlockCommitment{ + TransactionsMerkleRoot: node.transactionsMerkleRoot, + TransactionStatusHash: node.transactionStatusHash, + }, + } +} + +func (node *BlockNode) CalcPastMedianTime() uint64 { + timestamps := []uint64{} + iterNode := node + for i := 0; i < consensus.MedianTimeBlocks && iterNode != nil; i++ { + timestamps = append(timestamps, iterNode.timestamp) + iterNode = iterNode.parent + } + + sort.Sort(common.TimeSorter(timestamps)) + return timestamps[len(timestamps)/2] +} + +// CalcNextBits calculate the seed for next block +func (node *BlockNode) CalcNextBits() uint64 { + if node.height%consensus.BlocksPerRetarget != 0 || node.height == 0 { + return node.bits + } + + compareNode := node.parent + for compareNode.height%consensus.BlocksPerRetarget != 0 { + compareNode = compareNode.parent + } + return difficulty.CalcNextRequiredDifficulty(node.blockHeader(), compareNode.blockHeader()) +} + +// CalcNextSeed calculate the seed for next block +func (node *BlockNode) CalcNextSeed() *bc.Hash { + if node.height%consensus.SeedPerRetarget == 0 { + return &node.Hash + } + return node.seed +} + +// BlockIndex is the struct for help chain trace block chain as tree +type BlockIndex struct { + sync.RWMutex + + index map[bc.Hash]*BlockNode + mainChain []*BlockNode +} + +// NewBlockIndex will create a empty BlockIndex +func NewBlockIndex() *BlockIndex { + return &BlockIndex{ + index: make(map[bc.Hash]*BlockNode), + mainChain: make([]*BlockNode, 0, approxNodesPerDay), + } +} + +// AddNode will add node to the index map +func (bi *BlockIndex) AddNode(node *BlockNode) { + bi.Lock() + bi.index[node.Hash] = node + bi.Unlock() +} + +// GetNode will search node from the index map +func (bi *BlockIndex) GetNode(hash *bc.Hash) *BlockNode { + bi.RLock() + defer bi.RUnlock() + return bi.index[*hash] +} + +func (bi *BlockIndex) BestNode() *BlockNode { + bi.RLock() + defer bi.RUnlock() + return bi.mainChain[len(bi.mainChain)-1] +} + +// BlockExist check does the block existed in blockIndex +func (bi *BlockIndex) BlockExist(hash *bc.Hash) bool { + bi.RLock() + _, ok := bi.index[*hash] + bi.RUnlock() + return ok +} + +// TODO: THIS FUNCTION MIGHT BE DELETED +func (bi *BlockIndex) InMainchain(hash bc.Hash) bool { + bi.RLock() + defer bi.RUnlock() + + node, ok := bi.index[hash] + if !ok { + return false + } + return bi.nodeByHeight(node.height) == node +} + +func (bi *BlockIndex) nodeByHeight(height uint64) *BlockNode { + if height >= uint64(len(bi.mainChain)) { + return nil + } + return bi.mainChain[height] +} + +// NodeByHeight returns the block node at the specified height. +func (bi *BlockIndex) NodeByHeight(height uint64) *BlockNode { + bi.RLock() + defer bi.RUnlock() + return bi.nodeByHeight(height) +} + +// SetMainChain will set the the mainChain array +func (bi *BlockIndex) SetMainChain(node *BlockNode) { + bi.Lock() + defer bi.Unlock() + + needed := node.height + 1 + if uint64(cap(bi.mainChain)) < needed { + nodes := make([]*BlockNode, needed, needed+approxNodesPerDay) + copy(nodes, bi.mainChain) + bi.mainChain = nodes + } else { + i := uint64(len(bi.mainChain)) + bi.mainChain = bi.mainChain[0:needed] + for ; i < needed; i++ { + bi.mainChain[i] = nil + } + } + + for node != nil && bi.mainChain[node.height] != node { + bi.mainChain[node.height] = node + node = node.parent + } +} diff --git a/protocol/orphan_manage.go b/protocol/orphan_manage.go new file mode 100755 index 00000000..04b0ab81 --- /dev/null +++ b/protocol/orphan_manage.go @@ -0,0 +1,78 @@ +package protocol + +import ( + "sync" + + "github.com/bytom/protocol/bc" + "github.com/bytom/protocol/bc/types" +) + +// OrphanManage is use to handle all the orphan block +type OrphanManage struct { + //TODO: add orphan cached block limit + orphan map[bc.Hash]*types.Block + preOrphans map[bc.Hash][]*bc.Hash + mtx sync.RWMutex +} + +// NewOrphanManage return a new orphan block +func NewOrphanManage() *OrphanManage { + return &OrphanManage{ + orphan: make(map[bc.Hash]*types.Block), + preOrphans: make(map[bc.Hash][]*bc.Hash), + } +} + +// BlockExist check is the block in OrphanManage +func (o *OrphanManage) BlockExist(hash *bc.Hash) bool { + o.mtx.RLock() + _, ok := o.orphan[*hash] + o.mtx.RUnlock() + return ok +} + +// Add will add the block to OrphanManage +func (o *OrphanManage) Add(block *types.Block) { + blockHash := block.Hash() + o.mtx.Lock() + defer o.mtx.Unlock() + + if _, ok := o.orphan[blockHash]; ok { + return + } + + o.orphan[blockHash] = block + o.preOrphans[block.PreviousBlockHash] = append(o.preOrphans[block.PreviousBlockHash], &blockHash) +} + +// Delete will delelte the block from OrphanManage +func (o *OrphanManage) Delete(hash *bc.Hash) { + o.mtx.Lock() + defer o.mtx.Unlock() + block, ok := o.orphan[*hash] + if !ok { + return + } + delete(o.orphan, *hash) + + preOrphans, ok := o.preOrphans[block.PreviousBlockHash] + if !ok || len(preOrphans) == 1 { + delete(o.preOrphans, block.PreviousBlockHash) + return + } + + for i, preOrphan := range preOrphans { + if preOrphan == hash { + o.preOrphans[block.PreviousBlockHash] = append(preOrphans[:i], preOrphans[i+1:]...) + return + } + } +} + +// Get return the orphan block by hash +func (o *OrphanManage) Get(hash *bc.Hash) (*types.Block, bool) { + o.mtx.RLock() + block, ok := o.orphan[*hash] + o.mtx.RUnlock() + return block, ok +} diff --git a/protocol/protocol.go b/protocol/protocol.go index 38f405df..26178150 100755 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -2,11 +2,10 @@ package protocol import ( "context" + "math/big" "sync" - "time" - "github.com/bytom/consensus" - "github.com/bytom/database" + "github.com/bytom/config" "github.com/bytom/database/storage" "github.com/bytom/errors" "github.com/bytom/protocol/bc" @@ -15,7 +14,10 @@ import ( ) // maxCachedValidatedTxs is the max number of validated txs to cache. -const maxCachedValidatedTxs = 1000 +const ( + maxCachedValidatedTxs = 1000 + maxProcessBlockChSize = 1024 +) var ( // ErrTheDistantFuture is returned when waiting for a blockheight @@ -23,131 +25,88 @@ var ( ErrTheDistantFuture = errors.New("block height too far in future") ) -// OrphanManage is use to handle all the orphan block -type OrphanManage struct { - //TODO: add orphan cached block limit - orphan map[bc.Hash]*types.Block - preOrphans map[bc.Hash][]*bc.Hash - mtx sync.RWMutex -} - -// NewOrphanManage return a new orphan block -func NewOrphanManage() *OrphanManage { - return &OrphanManage{ - orphan: make(map[bc.Hash]*types.Block), - preOrphans: make(map[bc.Hash][]*bc.Hash), - } -} - -// BlockExist check is the block in OrphanManage -func (o *OrphanManage) BlockExist(hash *bc.Hash) bool { - o.mtx.RLock() - _, ok := o.orphan[*hash] - o.mtx.RUnlock() - return ok -} - -// Add will add the block to OrphanManage -func (o *OrphanManage) Add(block *types.Block) { - blockHash := block.Hash() - o.mtx.Lock() - defer o.mtx.Unlock() - - if _, ok := o.orphan[blockHash]; ok { - return - } - - o.orphan[blockHash] = block - o.preOrphans[block.PreviousBlockHash] = append(o.preOrphans[block.PreviousBlockHash], &blockHash) -} - -// Delete will delelte the block from OrphanManage -func (o *OrphanManage) Delete(hash *bc.Hash) { - o.mtx.Lock() - defer o.mtx.Unlock() - block, ok := o.orphan[*hash] - if !ok { - return - } - delete(o.orphan, *hash) - - preOrphans, ok := o.preOrphans[block.PreviousBlockHash] - if !ok || len(preOrphans) == 1 { - delete(o.preOrphans, block.PreviousBlockHash) - return - } - - for i, preOrphan := range preOrphans { - if preOrphan == hash { - o.preOrphans[block.PreviousBlockHash] = append(preOrphans[:i], preOrphans[i+1:]...) - return - } - } -} - -// Get return the orphan block by hash -func (o *OrphanManage) Get(hash *bc.Hash) (*types.Block, bool) { - o.mtx.RLock() - block, ok := o.orphan[*hash] - o.mtx.RUnlock() - return block, ok -} - // Chain provides a complete, minimal blockchain database. It // delegates the underlying storage to other objects, and uses // validation logic from package validation to decide what // objects can be safely stored. type Chain struct { - InitialBlockHash bc.Hash - MaxIssuanceWindow time.Duration // only used by generators - - orphanManage *OrphanManage - txPool *TxPool + index *BlockIndex + orphanManage *OrphanManage + txPool *TxPool + processBlockCh chan *processBlockMsg state struct { - cond sync.Cond - block *types.Block - hash *bc.Hash - mainChain map[uint64]*bc.Hash + cond sync.Cond + hash *bc.Hash + height uint64 + workSum *big.Int } - store database.Store + + store Store } // NewChain returns a new Chain using store as the underlying storage. -func NewChain(initialBlockHash bc.Hash, store database.Store, txPool *TxPool) (*Chain, error) { +func NewChain(store Store, txPool *TxPool) (*Chain, error) { c := &Chain{ - InitialBlockHash: initialBlockHash, - orphanManage: NewOrphanManage(), - store: store, - txPool: txPool, + orphanManage: NewOrphanManage(), + store: store, + txPool: txPool, + processBlockCh: make(chan *processBlockMsg, maxProcessBlockChSize), } c.state.cond.L = new(sync.Mutex) - storeStatus := store.GetStoreStatus() - - if storeStatus.Hash == nil { - c.state.mainChain = make(map[uint64]*bc.Hash) - return c, nil - } - c.state.hash = storeStatus.Hash var err error - if c.state.block, err = store.GetBlock(storeStatus.Hash); err != nil { - return nil, err + if storeStatus := store.GetStoreStatus(); storeStatus.Hash != nil { + c.state.hash = storeStatus.Hash + } else { + if err = c.initChainStatus(); err != nil { + return nil, err + } } - if c.state.mainChain, err = store.GetMainchain(storeStatus.Hash); err != nil { + + if c.index, err = store.LoadBlockIndex(); err != nil { return nil, err } + + bestNode := c.index.GetNode(c.state.hash) + c.index.SetMainChain(bestNode) + c.state.height = bestNode.height + c.state.workSum = bestNode.workSum + go c.blockProcesser() return c, nil } +func (c *Chain) initChainStatus() error { + genesisBlock := config.GenerateGenesisBlock() + txStatus := bc.NewTransactionStatus() + for i, _ := range genesisBlock.Transactions { + txStatus.SetStatus(i, false) + } + + if err := c.store.SaveBlock(genesisBlock, txStatus); err != nil { + return err + } + + utxoView := state.NewUtxoViewpoint() + bcBlock := types.MapBlock(genesisBlock) + if err := utxoView.ApplyBlock(bcBlock, txStatus); err != nil { + return err + } + + if err := c.store.SaveChainStatus(genesisBlock, utxoView); err != nil { + return err + } + + hash := genesisBlock.Hash() + c.state.hash = &hash + return nil +} + // Height returns the current height of the blockchain. func (c *Chain) Height() uint64 { c.state.cond.L.Lock() defer c.state.cond.L.Unlock() - if c.state.block == nil { - return 0 - } - return c.state.block.Height + return c.state.height } // BestBlockHash return the hash of the chain tail block @@ -157,41 +116,15 @@ func (c *Chain) BestBlockHash() *bc.Hash { return c.state.hash } -func (c *Chain) inMainchain(block *types.Block) bool { - hash, ok := c.state.mainChain[block.Height] - if !ok { - return false - } - return *hash == block.Hash() -} - // InMainChain checks wheather a block is in the main chain -func (c *Chain) InMainChain(height uint64, hash bc.Hash) bool { - c.state.cond.L.Lock() - h, ok := c.state.mainChain[height] - c.state.cond.L.Unlock() - if !ok { - return false - } - - return *h == hash -} - -// Timestamp returns the latest known block timestamp. -func (c *Chain) Timestamp() uint64 { - c.state.cond.L.Lock() - defer c.state.cond.L.Unlock() - if c.state.block == nil { - return 0 - } - return c.state.block.Timestamp +func (c *Chain) InMainChain(hash bc.Hash) bool { + return c.index.InMainchain(hash) } // BestBlock returns the chain tail block -func (c *Chain) BestBlock() *types.Block { - c.state.cond.L.Lock() - defer c.state.cond.L.Unlock() - return c.state.block +func (c *Chain) BestBlockHeader() *types.BlockHeader { + node := c.index.BestNode() + return node.blockHeader() } // GetUtxo try to find the utxo status in db @@ -199,14 +132,22 @@ func (c *Chain) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) { return c.store.GetUtxo(hash) } -// GetSeed return the seed for the given block -func (c *Chain) GetSeed(height uint64, preBlock *bc.Hash) (*bc.Hash, error) { - if height == 0 { - return consensus.InitialSeed, nil - } else if height%consensus.SeedPerRetarget == 0 { - return preBlock, nil +// CalcNextSeed return the seed for the given block +func (c *Chain) CalcNextSeed(preBlock *bc.Hash) (*bc.Hash, error) { + node := c.index.GetNode(preBlock) + if node == nil { + return nil, errors.New("can't find preblock in the blockindex") } - return c.store.GetSeed(preBlock) + return node.CalcNextSeed(), nil +} + +// CalcNextBits return the seed for the given block +func (c *Chain) CalcNextBits(preBlock *bc.Hash) (uint64, error) { + node := c.index.GetNode(preBlock) + if node == nil { + return 0, errors.New("can't find preblock in the blockindex") + } + return node.CalcNextBits(), nil } // GetTransactionStatus return the transaction status of give block @@ -220,18 +161,21 @@ func (c *Chain) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) err } // This function must be called with mu lock in above level -func (c *Chain) setState(block *types.Block, view *state.UtxoViewpoint, m map[uint64]*bc.Hash) error { - blockHash := block.Hash() - c.state.block = block - c.state.hash = &blockHash - for k, v := range m { - c.state.mainChain[k] = v - } - - if err := c.store.SaveChainStatus(block, view, c.state.mainChain); err != nil { +func (c *Chain) setState(block *types.Block, view *state.UtxoViewpoint) error { + if err := c.store.SaveChainStatus(block, view); err != nil { return err } + c.state.cond.L.Lock() + defer c.state.cond.L.Unlock() + + blockHash := block.Hash() + node := c.index.GetNode(&blockHash) + c.index.SetMainChain(node) + c.state.hash = &blockHash + c.state.height = node.height + c.state.workSum = node.workSum + c.state.cond.Broadcast() return nil } @@ -269,7 +213,7 @@ func (c *Chain) BlockWaiter(height uint64) <-chan struct{} { go func() { c.state.cond.L.Lock() defer c.state.cond.L.Unlock() - for c.state.block.Height < height { + for c.state.height < height { c.state.cond.Wait() } ch <- struct{}{} diff --git a/database/store.go b/protocol/store.go similarity index 67% rename from database/store.go rename to protocol/store.go index 28e7be3d..e77431f5 100644 --- a/database/store.go +++ b/protocol/store.go @@ -1,4 +1,4 @@ -package database +package protocol import ( "github.com/bytom/database/storage" @@ -12,16 +12,14 @@ type Store interface { BlockExist(*bc.Hash) bool GetBlock(*bc.Hash) (*types.Block, error) - GetBlockHeader(*bc.Hash) (*types.BlockHeader, error) - GetMainchain(*bc.Hash) (map[uint64]*bc.Hash, error) GetStoreStatus() BlockStoreStateJSON - GetSeed(*bc.Hash) (*bc.Hash, error) GetTransactionStatus(*bc.Hash) (*bc.TransactionStatus, error) GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error GetUtxo(*bc.Hash) (*storage.UtxoEntry, error) - SaveBlock(*types.Block, *bc.TransactionStatus, *bc.Hash) error - SaveChainStatus(*types.Block, *state.UtxoViewpoint, map[uint64]*bc.Hash) error + LoadBlockIndex() (*BlockIndex, error) + SaveBlock(*types.Block, *bc.TransactionStatus) error + SaveChainStatus(*types.Block, *state.UtxoViewpoint) error } // BlockStoreStateJSON represents the core's db status diff --git a/protocol/tx.go b/protocol/tx.go index 55fcd863..4ed51777 100644 --- a/protocol/tx.go +++ b/protocol/tx.go @@ -13,9 +13,10 @@ var ErrBadTx = errors.New("invalid transaction") // ValidateTx validates the given transaction. A cache holds // per-transaction validation results and is consulted before // performing full validation. -func (c *Chain) ValidateTx(tx *types.Tx) (bool,error) { +func (c *Chain) ValidateTx(tx *types.Tx) (bool, error) { newTx := tx.Tx - block := types.MapBlock(c.BestBlock()) + bh := c.BestBlockHeader() + block := types.MapBlock(&types.Block{BlockHeader: *bh}) if ok := c.txPool.HaveTransaction(&newTx.ID); ok { return false, c.txPool.GetErrCache(&newTx.ID) } @@ -24,7 +25,7 @@ func (c *Chain) ValidateTx(tx *types.Tx) (bool,error) { view := c.txPool.GetTransactionUTXO(tx.Tx) if err := c.GetTransactionsUtxo(view, []*bc.Tx{newTx}); err != nil { c.txPool.AddErrCache(&newTx.ID, err) - return false, err + return false, err } if err := view.ApplyTransaction(block, newTx, false); err != nil { return true, err diff --git a/protocol/tx_test.go b/protocol/tx_test.go index 8111e5a4..c7b060b3 100644 --- a/protocol/tx_test.go +++ b/protocol/tx_test.go @@ -14,22 +14,6 @@ import ( "github.com/bytom/testutil" ) -/*func TestBadMaxIssuanceWindow(t *testing.T) { - ctx := context.Background() - c, b1 := newTestChain(t, time.Now()) - c.MaxIssuanceWindow = time.Second - - issueTx, _, _ := issue(t, nil, nil, 1) - - got, _, err := c.GenerateBlock(ctx, b1, state.Empty(), time.Now(), []*types.Tx{issueTx}) - if err != nil { - t.Fatal(err) - } - if len(got.Transactions) != 0 { - t.Error("expected issuance past max issuance window to be rejected") - } -}*/ - type testDest struct { privKey ed25519.PrivateKey } diff --git a/protocol/txpool_test.go b/protocol/txpool_test.go index 2136dd82..fefbd76d 100644 --- a/protocol/txpool_test.go +++ b/protocol/txpool_test.go @@ -5,6 +5,7 @@ import ( "github.com/bytom/consensus" "github.com/bytom/protocol/bc/types" + "github.com/bytom/protocol/vm/vmutil" ) func TestTxPool(t *testing.T) { @@ -50,13 +51,16 @@ func TestTxPool(t *testing.T) { } func mockCoinbaseTx(serializedSize uint64, amount uint64) *types.Tx { + cp, _ := vmutil.DefaultCoinbaseProgram() oldTx := &types.TxData{ SerializedSize: serializedSize, + Inputs: []*types.TxInput{ + types.NewCoinbaseInput(nil), + }, Outputs: []*types.TxOutput{ - types.NewTxOutput(*consensus.BTMAssetID, amount, []byte{1}), + types.NewTxOutput(*consensus.BTMAssetID, amount, cp), }, } - return &types.Tx{ TxData: *oldTx, Tx: types.MapTx(oldTx), diff --git a/protocol/validation.go b/protocol/validation.go new file mode 100644 index 00000000..9e1f1125 --- /dev/null +++ b/protocol/validation.go @@ -0,0 +1,144 @@ +package protocol + +import ( + "time" + + "github.com/bytom/consensus" + "github.com/bytom/consensus/difficulty" + "github.com/bytom/errors" + "github.com/bytom/protocol/bc" + "github.com/bytom/protocol/validation" +) + +var ( + errBadTimestamp = errors.New("block timestamp is not in the vaild range") + errBadBits = errors.New("block bits is invaild") + errMismatchedBlock = errors.New("mismatched block") + errMismatchedMerkleRoot = errors.New("mismatched merkle root") + errMismatchedTxStatus = errors.New("mismatched transaction status") + errMismatchedValue = errors.New("mismatched value") + errMisorderedBlockHeight = errors.New("misordered block height") + errMisorderedBlockTime = errors.New("misordered block time") + errNoPrevBlock = errors.New("no previous block") + errOverflow = errors.New("arithmetic overflow/underflow") + errOverBlockLimit = errors.New("block's gas is over the limit") + errWorkProof = errors.New("invalid difficulty proof of work") + errVersionRegression = errors.New("version regression") + errWrongBlockSize = errors.New("block size is too big") + errWrongTransactionStatus = errors.New("transaction status is wrong") + errWrongCoinbaseTransaction = errors.New("wrong coinbase transaction") + errNotStandardTx = errors.New("gas transaction is not standard transaction") +) + +// ValidateBlock validates a block and the transactions within. +// It does not run the consensus program; for that, see ValidateBlockSig. +func (c *Chain) validateBlock(b *bc.Block) error { + parent := c.index.GetNode(b.PreviousBlockId) + if parent == nil { + return errors.WithDetailf(errNoPrevBlock, "height %d", b.Height) + } + if err := validateBlockAgainstPrev(b, parent); err != nil { + return err + } + + if !difficulty.CheckProofOfWork(&b.ID, parent.CalcNextSeed(), b.BlockHeader.Bits) { + return errWorkProof + } + + b.TransactionStatus = bc.NewTransactionStatus() + coinbaseValue := consensus.BlockSubsidy(b.BlockHeader.Height) + gasUsed := uint64(0) + for i, tx := range b.Transactions { + gasStatus, err := validation.ValidateTx(tx, b) + gasOnlyTx := false + if err != nil { + if gasStatus == nil || !gasStatus.GasVaild { + return errors.Wrapf(err, "validity of transaction %d of %d", i, len(b.Transactions)) + } + gasOnlyTx = true + } + b.TransactionStatus.SetStatus(i, gasOnlyTx) + coinbaseValue += gasStatus.BTMValue + gasUsed += uint64(gasStatus.GasUsed) + } + + if gasUsed > consensus.MaxBlockGas { + return errOverBlockLimit + } + + // check the coinbase output entry value + if err := validateCoinbase(b.Transactions[0], coinbaseValue); err != nil { + return err + } + + txRoot, err := bc.TxMerkleRoot(b.Transactions) + if err != nil { + return errors.Wrap(err, "computing transaction merkle root") + } + + if txRoot != *b.TransactionsRoot { + return errors.WithDetailf(errMismatchedMerkleRoot, "computed %x, current block wants %x", txRoot.Bytes(), b.TransactionsRoot.Bytes()) + } + + txStatusHash, err := bc.TxStatusMerkleRoot(b.TransactionStatus.VerifyStatus) + if err != nil { + return err + } + + if txStatusHash != *b.TransactionStatusHash { + return errMismatchedTxStatus + } + return nil +} + +func validateBlockTime(b *bc.Block, parent *BlockNode) error { + if b.Timestamp > uint64(time.Now().Unix())+consensus.MaxTimeOffsetSeconds { + return errBadTimestamp + } + + if b.Timestamp <= parent.CalcPastMedianTime() { + return errBadTimestamp + } + return nil +} + +func validateCoinbase(tx *bc.Tx, value uint64) error { + resultEntry := tx.Entries[*tx.TxHeader.ResultIds[0]] + output, ok := resultEntry.(*bc.Output) + if !ok { + return errors.Wrap(errWrongCoinbaseTransaction, "decode output") + } + + if output.Source.Value.Amount != value { + return errors.Wrap(errWrongCoinbaseTransaction, "dismatch output value") + } + + inputEntry := tx.Entries[tx.InputIDs[0]] + input, ok := inputEntry.(*bc.Coinbase) + if !ok { + return errors.Wrap(errWrongCoinbaseTransaction, "decode input") + } + if input.Arbitrary != nil && len(input.Arbitrary) > consensus.CoinbaseArbitrarySizeLimit { + return errors.Wrap(errWrongCoinbaseTransaction, "coinbase arbitrary is over size") + } + return nil +} + +func validateBlockAgainstPrev(b *bc.Block, parent *BlockNode) error { + if b.Version < parent.version { + return errors.WithDetailf(errVersionRegression, "previous block verson %d, current block version %d", parent.version, b.Version) + } + if b.Height != parent.height+1 { + return errors.WithDetailf(errMisorderedBlockHeight, "previous block height %d, current block height %d", parent.height, b.Height) + } + if b.Bits != parent.CalcNextBits() { + return errBadBits + } + if parent.Hash != *b.PreviousBlockId { + return errors.WithDetailf(errMismatchedBlock, "previous block ID %x, current block wants %x", parent.Hash.Bytes(), b.PreviousBlockId.Bytes()) + } + if err := validateBlockTime(b, parent); err != nil { + return err + } + return nil +} diff --git a/protocol/validation/validation.go b/protocol/validation/validation.go index b5b60c08..7c3a33c1 100644 --- a/protocol/validation/validation.go +++ b/protocol/validation/validation.go @@ -2,14 +2,9 @@ package validation import ( "fmt" - "sort" - "time" - "github.com/bytom/common" "github.com/bytom/consensus" - "github.com/bytom/consensus/difficulty" "github.com/bytom/consensus/segwit" - "github.com/bytom/database" "github.com/bytom/errors" "github.com/bytom/math/checked" "github.com/bytom/protocol/bc" @@ -115,8 +110,6 @@ type validationState struct { } var ( - errBadTimestamp = errors.New("block timestamp is not in the vaild range") - errBadBits = errors.New("block bits is invaild") errGasCalculate = errors.New("gas usage calculate got a math error") errEmptyResults = errors.New("transaction has no results") errMismatchedAssetID = errors.New("mismatched asset id") @@ -551,170 +544,6 @@ func checkValidDest(vs *validationState, vd *bc.ValueDestination) error { return nil } -// ValidateBlock validates a block and the transactions within. -// It does not run the consensus program; for that, see ValidateBlockSig. -func ValidateBlock(b, prev *bc.Block, seed *bc.Hash, store database.Store) error { - if b.Height > 0 { - if prev == nil { - return errors.WithDetailf(errNoPrevBlock, "height %d", b.Height) - } - if err := validateBlockAgainstPrev(b, prev); err != nil { - return err - } - if err := validateBlockTime(b, store); err != nil { - return err - } - if err := validateBlockBits(b, prev, store); err != nil { - return err - } - } - - if !difficulty.CheckProofOfWork(&b.ID, seed, b.BlockHeader.Bits) { - return errWorkProof - } - - b.TransactionStatus = bc.NewTransactionStatus() - coinbaseValue := consensus.BlockSubsidy(b.BlockHeader.Height) - gasUsed := uint64(0) - for i, tx := range b.Transactions { - gasStatus, err := ValidateTx(tx, b) - gasOnlyTx := false - if err != nil { - if gasStatus == nil || !gasStatus.GasVaild { - return errors.Wrapf(err, "validity of transaction %d of %d", i, len(b.Transactions)) - } - gasOnlyTx = true - } - b.TransactionStatus.SetStatus(i, gasOnlyTx) - coinbaseValue += gasStatus.BTMValue - gasUsed += uint64(gasStatus.GasUsed) - } - - if gasUsed > consensus.MaxBlockGas { - return errOverBlockLimit - } - - // check the coinbase output entry value - if err := validateCoinbase(b.Transactions[0], coinbaseValue); err != nil { - return err - } - - txRoot, err := bc.TxMerkleRoot(b.Transactions) - if err != nil { - return errors.Wrap(err, "computing transaction merkle root") - } - - if txRoot != *b.TransactionsRoot { - return errors.WithDetailf(errMismatchedMerkleRoot, "computed %x, current block wants %x", txRoot.Bytes(), b.TransactionsRoot.Bytes()) - } - - txStatusHash, err := bc.TxStatusMerkleRoot(b.TransactionStatus.VerifyStatus) - if err != nil { - return err - } - - if txStatusHash != *b.TransactionStatusHash { - return errMismatchedTxStatus - } - return nil -} - -func validateBlockBits(b, prev *bc.Block, store database.Store) error { - if prev.Height%consensus.BlocksPerRetarget != 0 || prev.Height == 0 { - if b.Bits != prev.Bits { - return errBadBits - } - return nil - } - - lastBH, err := store.GetBlockHeader(b.PreviousBlockId) - if err != nil { - return err - } - - compareBH, err := store.GetBlockHeader(&lastBH.PreviousBlockHash) - if err != nil { - return err - } - - for compareBH.Height%consensus.BlocksPerRetarget != 0 { - if compareBH, err = store.GetBlockHeader(&compareBH.PreviousBlockHash); err != nil { - return err - } - } - - if b.Bits != difficulty.CalcNextRequiredDifficulty(lastBH, compareBH) { - return errBadBits - } - return nil -} - -func validateBlockTime(b *bc.Block, store database.Store) error { - if b.Timestamp > uint64(time.Now().Unix())+consensus.MaxTimeOffsetSeconds { - return errBadTimestamp - } - - iterBH, err := store.GetBlockHeader(b.PreviousBlockId) - if err != nil { - return err - } - - timestamps := []uint64{} - for len(timestamps) < consensus.MedianTimeBlocks { - timestamps = append(timestamps, iterBH.Timestamp) - if iterBH.Height == 0 { - break - } - iterBH, err = store.GetBlockHeader(&iterBH.PreviousBlockHash) - if err != nil { - return err - } - } - - sort.Sort(common.TimeSorter(timestamps)) - medianTime := timestamps[len(timestamps)/2] - if b.Timestamp <= medianTime { - return errBadTimestamp - } - return nil -} - -func validateCoinbase(tx *bc.Tx, value uint64) error { - resultEntry := tx.Entries[*tx.TxHeader.ResultIds[0]] - output, ok := resultEntry.(*bc.Output) - if !ok { - return errors.Wrap(errWrongCoinbaseTransaction, "decode output") - } - - if output.Source.Value.Amount != value { - return errors.Wrap(errWrongCoinbaseTransaction, "dismatch output value") - } - - inputEntry := tx.Entries[tx.InputIDs[0]] - input, ok := inputEntry.(*bc.Coinbase) - if !ok { - return errors.Wrap(errWrongCoinbaseTransaction, "decode input") - } - if input.Arbitrary != nil && len(input.Arbitrary) > consensus.CoinbaseArbitrarySizeLimit { - return errors.Wrap(errWrongCoinbaseTransaction, "coinbase arbitrary is over size") - } - return nil -} - -func validateBlockAgainstPrev(b, prev *bc.Block) error { - if b.Version < prev.Version { - return errors.WithDetailf(errVersionRegression, "previous block verson %d, current block version %d", prev.Version, b.Version) - } - if b.Height != prev.Height+1 { - return errors.WithDetailf(errMisorderedBlockHeight, "previous block height %d, current block height %d", prev.Height, b.Height) - } - - if prev.ID != *b.PreviousBlockId { - return errors.WithDetailf(errMismatchedBlock, "previous block ID %x, current block wants %x", prev.ID.Bytes(), b.PreviousBlockId.Bytes()) - } - return nil -} - func validateStandardTx(tx *bc.Tx) error { for _, id := range tx.InputIDs { e, ok := tx.Entries[id] diff --git a/protocol/validation/validation_test.go b/protocol/validation/validation_test.go index 3f839ad1..a5c90301 100644 --- a/protocol/validation/validation_test.go +++ b/protocol/validation/validation_test.go @@ -404,59 +404,6 @@ func TestTxValidation(t *testing.T) { } } -func TestValidateBlock(t *testing.T) { - cases := []struct { - block *bc.Block - err error - }{ - { - block: &bc.Block{ - BlockHeader: &bc.BlockHeader{ - Height: 0, - Bits: 2305843009230471167, - PreviousBlockId: &bc.Hash{}, - }, - Transactions: []*bc.Tx{mockCoinbaseTx(1470000000000000000)}, - }, - err: nil, - }, - { - block: &bc.Block{ - BlockHeader: &bc.BlockHeader{ - Height: 0, - Bits: 2305843009230471167, - PreviousBlockId: &bc.Hash{}, - }, - Transactions: []*bc.Tx{mockCoinbaseTx(1)}, - }, - err: errWrongCoinbaseTransaction, - }, - } - - txStatus := bc.NewTransactionStatus() - txStatus.SetStatus(0, false) - txStatusHash, err := bc.TxStatusMerkleRoot(txStatus.VerifyStatus) - if err != nil { - t.Error(err) - } - - for _, c := range cases { - txRoot, err := bc.TxMerkleRoot(c.block.Transactions) - if err != nil { - t.Errorf("computing transaction merkle root error: %v", err) - continue - } - - c.block.BlockHeader.TransactionStatus = bc.NewTransactionStatus() - c.block.TransactionsRoot = &txRoot - c.block.TransactionStatusHash = &txStatusHash - - if err = ValidateBlock(c.block, nil, &bc.Hash{}, nil); rootErr(err) != c.err { - t.Errorf("got error %s, want %s", err, c.err) - } - } -} - func TestCoinbase(t *testing.T) { CbTx := mockCoinbaseTx(5000000000) cases := []struct { diff --git a/test/util.go b/test/util.go index 62767080..5191c7e2 100644 --- a/test/util.go +++ b/test/util.go @@ -9,7 +9,6 @@ import ( "github.com/bytom/account" "github.com/bytom/blockchain/pseudohsm" "github.com/bytom/blockchain/txbuilder" - cfg "github.com/bytom/config" "github.com/bytom/consensus" "github.com/bytom/crypto/ed25519/chainkd" "github.com/bytom/database/leveldb" @@ -27,8 +26,7 @@ func MockTxPool() *protocol.TxPool { func MockChain(testDB dbm.DB) (*protocol.Chain, error) { store := leveldb.NewStore(testDB) txPool := MockTxPool() - genesisBlock := cfg.GenerateGenesisBlock() - chain, err := protocol.NewChain(genesisBlock.Hash(), store, txPool) + chain, err := protocol.NewChain(store, txPool) if err != nil { return nil, err } diff --git a/wallet/wallet.go b/wallet/wallet.go index 3bed7c9e..e4c05159 100755 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -190,7 +190,7 @@ func (w *Wallet) walletUpdater() { for { getRescanNotification(w) checkRescanStatus(w) - for !w.chain.InMainChain(w.status.BestHeight, w.status.BestHash) { + for !w.chain.InMainChain(w.status.BestHash) { block, err := w.chain.GetBlockByHash(&w.status.BestHash) if err != nil { log.WithField("err", err).Error("walletUpdater GetBlockByHash") diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go index 05138c49..13bf2419 100755 --- a/wallet/wallet_test.go +++ b/wallet/wallet_test.go @@ -14,7 +14,6 @@ import ( "github.com/bytom/asset" "github.com/bytom/blockchain/pseudohsm" "github.com/bytom/blockchain/txbuilder" - cfg "github.com/bytom/config" "github.com/bytom/consensus" "github.com/bytom/crypto/ed25519/chainkd" "github.com/bytom/crypto/sha3pool" @@ -37,7 +36,7 @@ func TestWalletUpdate(t *testing.T) { store := leveldb.NewStore(testDB) txPool := protocol.NewTxPool() - chain, err := protocol.NewChain(bc.Hash{}, store, txPool) + chain, err := protocol.NewChain(store, txPool) if err != nil { t.Fatal(err) } @@ -80,7 +79,7 @@ func TestWalletUpdate(t *testing.T) { block := mockSingleBlock(tx) txStatus := bc.NewTransactionStatus() - store.SaveBlock(block, txStatus, consensus.InitialSeed) + store.SaveBlock(block, txStatus) err = w.attachBlock(block) if err != nil { @@ -111,20 +110,11 @@ func TestExportAndImportPrivKey(t *testing.T) { store := leveldb.NewStore(testDB) txPool := protocol.NewTxPool() - chain, err := protocol.NewChain(bc.Hash{}, store, txPool) + chain, err := protocol.NewChain(store, txPool) if err != nil { t.Fatal(err) } - genesisBlock := cfg.GenerateGenesisBlock() - - if err = chain.SaveBlock(genesisBlock); err != nil { - t.Fatal(err) - } - if err = chain.ConnectBlock(genesisBlock); err != nil { - t.Fatal(err) - } - acntManager := account.NewManager(testDB, chain) reg := asset.NewRegistry(testDB, chain) -- 2.11.0