OSDN Git Service

Block node (#541)
authorPaladz <yzhu101@uottawa.ca>
Sun, 8 Apr 2018 07:17:26 +0000 (15:17 +0800)
committerGitHub <noreply@github.com>
Sun, 8 Apr 2018 07:17:26 +0000 (15:17 +0800)
* 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

30 files changed:
account/accounts_test.go
account/reserve_test.go
api/miner.go
asset/asset.go
asset/asset_test.go
consensus/difficulty/difficulty.go
database/leveldb/mainchain.go [deleted file]
database/leveldb/mainchain_test.go [deleted file]
database/leveldb/store.go
database/storage/storage.pb.go
database/storage/storage.proto
mining/cpuminer/cpuminer.go
mining/mining.go
netsync/message.go
netsync/protocol_reactor.go
node/node.go
protocol/block.go
protocol/blockindex.go [new file with mode: 0755]
protocol/orphan_manage.go [new file with mode: 0755]
protocol/protocol.go
protocol/store.go [moved from database/store.go with 67% similarity]
protocol/tx.go
protocol/tx_test.go
protocol/txpool_test.go
protocol/validation.go [new file with mode: 0644]
protocol/validation/validation.go
protocol/validation/validation_test.go
test/util.go
wallet/wallet.go
wallet/wallet_test.go

index e564e15..5cab225 100644 (file)
@@ -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)
        }
index 5843810..9905989 100644 (file)
@@ -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
        }
index a6b2f8e..f59bad9 100644 (file)
@@ -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
        }
index d35d7f4..d2b53a5 100755 (executable)
@@ -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
index 065e317..db3edf7 100644 (file)
@@ -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
        }
index 14b389c..82f967c 100644 (file)
@@ -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 (file)
index 4477439..0000000
+++ /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 (file)
index bc72108..0000000
+++ /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")
-       }
-}
index 1f3db46..45a3faa 100644 (file)
@@ -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
 }
index 79f370c..8303680 100644 (file)
@@ -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,
 }
index 5b77859..5e36b1c 100644 (file)
@@ -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;
-  }
-}
index 3c3effc..5cbf0c0 100644 (file)
@@ -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
        }
index adb474a..8d06d3d 100644 (file)
@@ -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")
index 4a17f81..eee3c1b 100644 (file)
@@ -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
index 1728859..b857da7 100644 (file)
@@ -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{
index f8f1382..cf87f51 100755 (executable)
@@ -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
index a83bb37..4f4289c 100755 (executable)
@@ -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 (executable)
index 0000000..f95cb17
--- /dev/null
@@ -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 (executable)
index 0000000..04b0ab8
--- /dev/null
@@ -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
+}
index 38f405d..2617815 100755 (executable)
@@ -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{}{}
similarity index 67%
rename from database/store.go
rename to protocol/store.go
index 28e7be3..e77431f 100644 (file)
@@ -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
index 55fcd86..4ed5177 100644 (file)
@@ -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
index 8111e5a..c7b060b 100644 (file)
@@ -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
 }
index 2136dd8..fefbd76 100644 (file)
@@ -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 (file)
index 0000000..9e1f112
--- /dev/null
@@ -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
+}
index b5b60c0..7c3a33c 100644 (file)
@@ -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]
index 3f839ad..a5c9030 100644 (file)
@@ -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 {
index 6276708..5191c7e 100644 (file)
@@ -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
        }
index 3bed7c9..e4c0515 100755 (executable)
@@ -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")
index 05138c4..13bf241 100755 (executable)
@@ -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)