"github.com/bytom/database/leveldb"
"github.com/bytom/errors"
"github.com/bytom/protocol"
- "github.com/bytom/protocol/bc"
"github.com/bytom/testutil"
)
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)
}
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)
}
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
}
import (
"context"
- "github.com/bytom/protocol/bc/types"
"github.com/bytom/protocol/bc"
+ "github.com/bytom/protocol/bc/types"
)
func (a *API) getWork() Response {
return NewSuccessResponse(work)
}
-
type SubmitWorkReq struct {
BlockHeader *types.BlockHeader `json:"block_header"`
}
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
}
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
"github.com/bytom/crypto/ed25519/chainkd"
"github.com/bytom/database/leveldb"
"github.com/bytom/protocol"
- "github.com/bytom/protocol/bc"
"github.com/bytom/testutil"
)
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)
}
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
}
"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
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.
+++ /dev/null
-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)
-}
+++ /dev/null
-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")
- }
-}
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"
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))
}
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 {
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)
}
// 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")
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
}
It has these top-level messages:
UtxoEntry
- Mainchain
*/
package storage
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,
}
uint64 blockHeight = 2;
bool spent = 3;
}
-
-// Mainchain represents a mainchain of the blockchain
-message Mainchain {
-
- repeated Hash hashs = 1;
-
- message Hash {
- bytes key = 1;
- }
-}
// 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
}
"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"
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))
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")
}
//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(),
}
}
//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
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{
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
"github.com/bytom/protocol/bc"
"github.com/bytom/protocol/bc/types"
"github.com/bytom/protocol/state"
- "github.com/bytom/protocol/validation"
)
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
// 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
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
}
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)
}
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)
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
}
}
}
- 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")
}
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)
}
--- /dev/null
+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
+ }
+}
--- /dev/null
+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
+}
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"
)
// 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
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
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
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
}
// 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
}
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{}{}
-package database
+package protocol
import (
"github.com/bytom/database/storage"
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
// 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)
}
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
"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
}
"github.com/bytom/consensus"
"github.com/bytom/protocol/bc/types"
+ "github.com/bytom/protocol/vm/vmutil"
)
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),
--- /dev/null
+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
+}
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"
}
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")
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]
}
}
-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 {
"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"
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
}
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")
"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"
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)
}
block := mockSingleBlock(tx)
txStatus := bc.NewTransactionStatus()
- store.SaveBlock(block, txStatus, consensus.InitialSeed)
+ store.SaveBlock(block, txStatus)
err = w.attachBlock(block)
if err != nil {
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)