Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion blockproducer/blocknode.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func newBlockNode(h uint32, b *types.BPBlock, p *blockNode) *blockNode {
}(),
height: h,

hash: b.SignedHeader.BlockHash,
hash: b.SignedHeader.DataHash,
block: b,
}
}
Expand Down
41 changes: 28 additions & 13 deletions blockproducer/blocknode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"testing"

"github.com/CovenantSQL/CovenantSQL/crypto/hash"
"github.com/CovenantSQL/CovenantSQL/crypto/verifier"
"github.com/CovenantSQL/CovenantSQL/types"
. "github.com/smartystreets/goconvey/convey"
)
Expand All @@ -29,39 +30,49 @@ func TestBlockNode(t *testing.T) {
var (
b0 = &types.BPBlock{
SignedHeader: types.BPSignedHeader{
BlockHash: hash.Hash{0x1},
DefaultHashSignVerifierImpl: verifier.DefaultHashSignVerifierImpl{
DataHash: hash.Hash{0x1},
},
},
}
b1 = &types.BPBlock{
SignedHeader: types.BPSignedHeader{
BPHeader: types.BPHeader{
ParentHash: b0.SignedHeader.BlockHash,
ParentHash: b0.SignedHeader.DataHash,
},
DefaultHashSignVerifierImpl: verifier.DefaultHashSignVerifierImpl{
DataHash: hash.Hash{0x2},
},
BlockHash: hash.Hash{0x2},
},
}
b2 = &types.BPBlock{
SignedHeader: types.BPSignedHeader{
BPHeader: types.BPHeader{
ParentHash: b1.SignedHeader.BlockHash,
ParentHash: b1.SignedHeader.DataHash,
},
DefaultHashSignVerifierImpl: verifier.DefaultHashSignVerifierImpl{
DataHash: hash.Hash{0x3},
},
BlockHash: hash.Hash{0x3},
},
}
b3 = &types.BPBlock{
SignedHeader: types.BPSignedHeader{
BPHeader: types.BPHeader{
ParentHash: b2.SignedHeader.BlockHash,
ParentHash: b2.SignedHeader.DataHash,
},
DefaultHashSignVerifierImpl: verifier.DefaultHashSignVerifierImpl{
DataHash: hash.Hash{0x4},
},
BlockHash: hash.Hash{0x4},
},
}
b4 = &types.BPBlock{
SignedHeader: types.BPSignedHeader{
BPHeader: types.BPHeader{
ParentHash: b3.SignedHeader.BlockHash,
ParentHash: b3.SignedHeader.DataHash,
},
DefaultHashSignVerifierImpl: verifier.DefaultHashSignVerifierImpl{
DataHash: hash.Hash{0x5},
},
BlockHash: hash.Hash{0x5},
},
}
n0 = newBlockNode(0, b0, nil)
Expand All @@ -73,17 +84,21 @@ func TestBlockNode(t *testing.T) {
b3p = &types.BPBlock{
SignedHeader: types.BPSignedHeader{
BPHeader: types.BPHeader{
ParentHash: b2.SignedHeader.BlockHash,
ParentHash: b2.SignedHeader.DataHash,
},
DefaultHashSignVerifierImpl: verifier.DefaultHashSignVerifierImpl{
DataHash: hash.Hash{0x6},
},
BlockHash: hash.Hash{0x6},
},
}
b4p = &types.BPBlock{
SignedHeader: types.BPSignedHeader{
BPHeader: types.BPHeader{
ParentHash: b3p.SignedHeader.BlockHash,
ParentHash: b3p.SignedHeader.DataHash,
},
DefaultHashSignVerifierImpl: verifier.DefaultHashSignVerifierImpl{
DataHash: hash.Hash{0x7},
},
BlockHash: hash.Hash{0x7},
},
}
n3p = newBlockNode(3, b3p, n2)
Expand Down
47 changes: 23 additions & 24 deletions blockproducer/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
"github.com/CovenantSQL/CovenantSQL/crypto/asymmetric"
"github.com/CovenantSQL/CovenantSQL/crypto/hash"
"github.com/CovenantSQL/CovenantSQL/crypto/kms"
"github.com/CovenantSQL/CovenantSQL/merkle"
"github.com/CovenantSQL/CovenantSQL/proto"
"github.com/CovenantSQL/CovenantSQL/route"
"github.com/CovenantSQL/CovenantSQL/rpc"
Expand Down Expand Up @@ -113,15 +112,29 @@ func NewChainWithContext(ctx context.Context, cfg *Config) (c *Chain, err error)
bus = chainbus.New()
)

if fi, err := os.Stat(cfg.DataFile); err == nil && fi.Mode().IsRegular() {
existed = true
// Verify genesis block in config
if cfg.Genesis == nil {
err = ErrNilGenesis
return
}
if ierr = cfg.Genesis.VerifyHash(); ierr != nil {
err = errors.Wrap(ierr, "failed to verify genesis block hash")
return
}

// Open storage
if fi, err := os.Stat(cfg.DataFile); err == nil && fi.Mode().IsRegular() {
existed = true
}
if st, ierr = openStorage(fmt.Sprintf("file:%s", cfg.DataFile)); ierr != nil {
err = errors.Wrap(ierr, "failed to open storage")
return
}
defer func() {
if err != nil {
st.Close()
}
}()

// Create initial state from genesis block and store
if !existed {
Expand All @@ -134,7 +147,7 @@ func NewChainWithContext(ctx context.Context, cfg *Config) (c *Chain, err error)
}
var sps = init.compileChanges(nil)
sps = append(sps, addBlock(0, cfg.Genesis))
sps = append(sps, updateIrreversible(cfg.Genesis.SignedHeader.BlockHash))
sps = append(sps, updateIrreversible(cfg.Genesis.SignedHeader.DataHash))
if ierr = store(st, sps, nil); ierr != nil {
err = errors.Wrap(ierr, "failed to initialize storage")
return
Expand All @@ -146,6 +159,11 @@ func NewChainWithContext(ctx context.Context, cfg *Config) (c *Chain, err error)
err = errors.Wrap(ierr, "failed to load data from storage")
return
}
if persistedGenesis := irre.ancestorByCount(0); persistedGenesis == nil ||
!persistedGenesis.hash.IsEqual(cfg.Genesis.BlockHash()) {
err = ErrGenesisHashNotMatch
return
}
for _, v := range heads {
log.WithFields(log.Fields{
"irre_hash": irre.hash.Short(4),
Expand Down Expand Up @@ -279,28 +297,9 @@ func (c *Chain) Stop() (err error) {
return
}

// checkBlock has following steps: 1. check parent block 2. checkTx 2. merkle tree 3. Hash 4. Signature.
func (c *Chain) checkBlock(b *types.BPBlock) (err error) {
rootHash := merkle.NewMerkle(b.GetTxHashes()).GetRoot()
if !b.SignedHeader.MerkleRoot.IsEqual(rootHash) {
return ErrInvalidMerkleTreeRoot
}

enc, err := b.SignedHeader.BPHeader.MarshalHash()
if err != nil {
return err
}
h := hash.THashH(enc)
if !b.BlockHash().IsEqual(&h) {
return ErrInvalidHash
}

return nil
}

func (c *Chain) pushBlock(b *types.BPBlock) (err error) {
var ierr error
if ierr = c.checkBlock(b); ierr != nil {
if ierr = b.Verify(); ierr != nil {
err = errors.Wrap(ierr, "failed to check block")
return
}
Expand Down
46 changes: 44 additions & 2 deletions blockproducer/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/CovenantSQL/CovenantSQL/proto"
"github.com/CovenantSQL/CovenantSQL/rpc"
"github.com/CovenantSQL/CovenantSQL/types"
"github.com/pkg/errors"
. "github.com/smartystreets/goconvey/convey"
)

Expand Down Expand Up @@ -121,7 +122,7 @@ func TestChain(t *testing.T) {
}),
},
}
err = genesis.PackAndSignBlock(testingPrivateKey)
err = genesis.SetHash()
So(err, ShouldBeNil)
begin = genesis.Timestamp()

Expand All @@ -147,7 +148,7 @@ func TestChain(t *testing.T) {

Convey("A new chain running before genesis time should be waiting for genesis", func() {
config.Genesis.SignedHeader.Timestamp = time.Now().Add(24 * time.Hour)
err = genesis.PackAndSignBlock(testingPrivateKey)
err = genesis.SetHash()
So(err, ShouldBeNil)
chain, err = NewChain(config)
So(err, ShouldBeNil)
Expand Down Expand Up @@ -312,6 +313,47 @@ func TestChain(t *testing.T) {
chain.stat()
})

Convey("The chain should report error if genesis in config is cleared", func() {
err = chain.Stop()
So(err, ShouldBeNil)
config.Genesis = nil
chain, err = NewChain(config)
So(err, ShouldEqual, ErrNilGenesis)
So(chain, ShouldBeNil)
})

Convey("The chain should report error if config is changed", func() {
err = chain.Stop()
So(err, ShouldBeNil)
config.Genesis.Transactions = append(
config.Genesis.Transactions,
types.NewBaseAccount(&types.Account{
Address: addr2,
TokenBalance: [5]uint64{1000, 1000, 1000, 1000, 1000},
}),
)
chain, err = NewChain(config)
So(errors.Cause(err), ShouldEqual, types.ErrMerkleRootVerification)
So(chain, ShouldBeNil)
})

Convey("The chain should report error if config is changed and rehashed", func() {
err = chain.Stop()
So(err, ShouldBeNil)
config.Genesis.Transactions = append(
config.Genesis.Transactions,
types.NewBaseAccount(&types.Account{
Address: addr2,
TokenBalance: [5]uint64{1000, 1000, 1000, 1000, 1000},
}),
)
err = config.Genesis.SetHash()
So(err, ShouldBeNil)
chain, err = NewChain(config)
So(err, ShouldEqual, ErrGenesisHashNotMatch)
So(chain, ShouldBeNil)
})

Convey("The chain APIs should return expected results", func() {
var (
bl *types.BPBlock
Expand Down
7 changes: 5 additions & 2 deletions blockproducer/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ var (
ErrInvalidHash = errors.New("Hash is invalid")
// ErrExistedTx defines existed tx error.
ErrExistedTx = errors.New("Tx existed")
// ErrInvalidMerkleTreeRoot defines invalid merkle tree root error.
ErrInvalidMerkleTreeRoot = errors.New("Block merkle tree root does not match the tx hashes")
// ErrParentNotMatch defines invalid parent hash.
ErrParentNotMatch = errors.New("Block's parent hash cannot match best block")
// ErrTooManyTransactionsInBlock defines error of too many transactions in a block.
Expand Down Expand Up @@ -70,8 +68,13 @@ var (
ErrMinerUserNotMatch = errors.New("miner and user do not match")
// ErrInsufficientAdvancePayment indicates that the advance payment is insufficient.
ErrInsufficientAdvancePayment = errors.New("insufficient advance payment")
// ErrNilGenesis indicates that the genesis block is nil in config.
ErrNilGenesis = errors.New("nil genesis block")
// ErrMultipleGenesis indicates that there're multiple genesis blocks while loading.
ErrMultipleGenesis = errors.New("multiple genesis blocks")
// ErrGenesisHashNotMatch indicates that the genesis block hash in config doesn't match
// the persisted one.
ErrGenesisHashNotMatch = errors.New("persisted genesis block hash not match")
// ErrInvalidGasPrice indicates that the gas price is invalid.
ErrInvalidGasPrice = errors.New("gas price is invalid")
// ErrInvalidMinerCount indicates that the miner node count is invalid.
Expand Down
23 changes: 13 additions & 10 deletions cmd/cqld/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ const (
func runNode(nodeID proto.NodeID, listenAddr string) (err error) {
rootPath := conf.GConf.WorkingRoot

genesis := loadGenesis()
genesis, err := loadGenesis()
if err != nil {
return
}

var masterKey []byte
if !conf.GConf.IsTestMode {
Expand Down Expand Up @@ -226,20 +229,16 @@ func initKayakTwoPC(rootDir string, node *proto.Node, peers *proto.Peers, h kt.H
return
}

func loadGenesis() *types.BPBlock {
func loadGenesis() (genesis *types.BPBlock, err error) {
genesisInfo := conf.GConf.BP.BPGenesis
log.WithField("config", genesisInfo).Info("load genesis config")

genesis := &types.BPBlock{
genesis = &types.BPBlock{
SignedHeader: types.BPSignedHeader{
BPHeader: types.BPHeader{
Version: genesisInfo.Version,
Producer: proto.AccountAddress(genesisInfo.Producer),
MerkleRoot: genesisInfo.MerkleRoot,
ParentHash: genesisInfo.ParentHash,
Timestamp: genesisInfo.Timestamp,
Version: genesisInfo.Version,
Timestamp: genesisInfo.Timestamp,
},
BlockHash: genesisInfo.BlockHash,
},
}

Expand All @@ -256,5 +255,9 @@ func loadGenesis() *types.BPBlock {
}))
}

return genesis
// Rewrite genesis merkle and block hash
if err = genesis.SetHash(); err != nil {
return
}
return
}
8 changes: 0 additions & 8 deletions conf/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,8 @@ type BaseAccountInfo struct {
type BPGenesisInfo struct {
// Version defines the block version
Version int32 `yaml:"Version"`
// Producer defines the block producer
Producer hash.Hash `yaml:"Producer"`
// MerkleRoot defines the transaction merkle tree's root
MerkleRoot hash.Hash `yaml:"MerkleRoot"`
// ParentHash defines the parent block's hash
ParentHash hash.Hash `yaml:"ParentHash"`
// Timestamp defines the initial time of chain
Timestamp time.Time `yaml:"Timestamp"`
// BlockHash defines the block hash of genesis block
BlockHash hash.Hash `yaml:"BlockHash"`
// BaseAccounts defines the base accounts for testnet
BaseAccounts []BaseAccountInfo `yaml:"BaseAccounts"`
}
Expand Down
8 changes: 2 additions & 6 deletions conf/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,8 @@ func TestConf(t *testing.T) {
},
ChainFileName: "",
BPGenesis: BPGenesisInfo{
Version: 1,
Producer: h,
MerkleRoot: h,
ParentHash: h,
Timestamp: time.Now().UTC(),
BlockHash: h,
Version: 1,
Timestamp: time.Now().UTC(),
},
}
Convey("LoadConfig", t, func() {
Expand Down
Loading