Repository: pactus-project/pactus
Pull request #671 (2023-09-01 11:48:48)

feat: implementing pip-0004 `indexing public key`
implementation of PIP-0004 which is defined here:
- PIP https://github.com/pactus-project/PIPs/pull/8

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pactus-project/pactus/pull/671?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pactus-project) Report
> Merging [#671](https://app.codecov.io/gh/pactus-project/pactus/pull/671?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pactus-project) (7ef7d8f) into [main](https://app.codecov.io/gh/pactus-project/pactus/commit/b51c1d62c0654ab1e7d5db575372a1c61824c8b4?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pactus-project) (b51c1d6) will **decrease** coverage by `0.14%`.
> The diff coverage is `81.12%`.


```diff
@@            Coverage Diff             @@
##             main     #671      +/-   ##
==========================================
- Coverage   83.89%   83.76%   -0.14%     
==========================================
  Files         163      166       +3     
  Lines        7929     8005      +76     
==========================================
+ Hits         6652     6705      +53     
- Misses        978      997      +19     
- Partials      299      303       +4     
```



Patch:
diff --git a/state/facade.go b/state/facade.go
index 95ba6951..6d1ead8e 100644
--- a/state/facade.go
+++ b/state/facade.go
@@ -39,8 +39,9 @@ type Facade interface {
 	PendingTx(id tx.ID) *tx.Tx
 	AddPendingTx(trx *tx.Tx) error
 	AddPendingTxAndBroadcast(trx *tx.Tx) error
-	StoredBlock(height uint32) *store.StoredBlock
-	StoredTx(id tx.ID) *store.StoredTx
+	MakeCommittedBlock(data []byte, height uint32, blockHash hash.Hash) *store.CommittedBlock
+	CommittedBlock(height uint32) *store.CommittedBlock
+	CommittedTx(id tx.ID) *store.CommittedTx
 	BlockHash(height uint32) hash.Hash
 	BlockHeight(hash hash.Hash) uint32
 	AccountByAddress(addr crypto.Address) *account.Account
diff --git a/state/lastinfo/last_info.go b/state/lastinfo/last_info.go
index 35e636d6..2a0f7ae2 100644
--- a/state/lastinfo/last_info.go
+++ b/state/lastinfo/last_info.go
@@ -123,7 +123,10 @@ func (li *LastInfo) RestoreLastInfo(store store.Store, committeeSize int) (commi
 		return nil, fmt.Errorf("unable to retrieve block %v: %w", height, err)
 	}
 
-	lastBlock := sb.ToBlock()
+	lastBlock, err := sb.ToBlock()
+	if err != nil {
+		return nil, err
+	}
 
 	li.lastBlockHeight = height
 	li.lastCert = cert
diff --git a/state/mock.go b/state/mock.go
index f221597e..688ace71 100644
--- a/state/mock.go
+++ b/state/mock.go
@@ -51,8 +51,7 @@ func MockingState(ts *testsuite.TestSuite) *MockState {
 
 func (m *MockState) CommitTestBlocks(num int) {
 	for i := 0; i < num; i++ {
-		lastHash := m.LastBlockHash()
-		blk := m.ts.GenerateTestBlock(nil, &lastHash)
+		blk := m.ts.GenerateTestBlock(nil)
 		cert := m.ts.GenerateTestCertificate()
 
 		m.TestStore.SaveBlock(m.LastBlockHeight()+1, blk, cert)
@@ -113,7 +112,7 @@ func (m *MockState) Close() error {
 }
 
 func (m *MockState) ProposeBlock(_ crypto.Signer, _ crypto.Address, _ int16) (*block.Block, error) {
-	b := m.ts.GenerateTestBlock(nil, nil)
+	b := m.ts.GenerateTestBlock(nil)
 	return b, nil
 }
 
@@ -162,7 +161,16 @@ func (m *MockState) CommitteePower() int64 {
 	return m.TestCommittee.TotalPower()
 }
 
-func (m *MockState) StoredBlock(height uint32) *store.StoredBlock {
+func (m *MockState) MakeCommittedBlock(data []byte, height uint32, blockHash hash.Hash) *store.CommittedBlock {
+	return &store.CommittedBlock{
+		Store:     m.TestStore,
+		Data:      data,
+		BlockHash: blockHash,
+		Height:    height,
+	}
+}
+
+func (m *MockState) CommittedBlock(height uint32) *store.CommittedBlock {
 	m.lk.RLock()
 	defer m.lk.RUnlock()
 
@@ -170,7 +178,7 @@ func (m *MockState) StoredBlock(height uint32) *store.StoredBlock {
 	return b
 }
 
-func (m *MockState) StoredTx(id tx.ID) *store.StoredTx {
+func (m *MockState) CommittedTx(id tx.ID) *store.CommittedTx {
 	m.lk.RLock()
 	defer m.lk.RUnlock()
 
diff --git a/state/state.go b/state/state.go
index 03d0aeec..7674558b 100644
--- a/state/state.go
+++ b/state/state.go
@@ -107,7 +107,12 @@ func (st *state) tryLoadLastInfo() error {
 	if err != nil {
 		return err
 	}
-	blockOne := blockOneInfo.ToBlock()
+
+	blockOne, err := blockOneInfo.ToBlock()
+	if err != nil {
+		return err
+	}
+
 	if !genStateRoot.EqualsTo(blockOne.Header().StateRoot()) {
 		return fmt.Errorf("invalid genesis doc")
 	}
@@ -620,7 +625,16 @@ func (st *state) IsValidator(addr crypto.Address) bool {
 	return st.store.HasValidator(addr)
 }
 
-func (st *state) StoredBlock(height uint32) *store.StoredBlock {
+func (st *state) MakeCommittedBlock(data []byte, height uint32, blockHash hash.Hash) *store.CommittedBlock {
+	return &store.CommittedBlock{
+		Store:     st.store,
+		Data:      data,
+		BlockHash: blockHash,
+		Height:    height,
+	}
+}
+
+func (st *state) CommittedBlock(height uint32) *store.CommittedBlock {
 	b, err := st.store.Block(height)
 	if err != nil {
 		st.logger.Trace("error on retrieving block", "err", err)
@@ -629,7 +643,7 @@ func (st *state) StoredBlock(height uint32) *store.StoredBlock {
 	return b
 }
 
-func (st *state) StoredTx(id tx.ID) *store.StoredTx {
+func (st *state) CommittedTx(id tx.ID) *store.CommittedTx {
 	tx, err := st.store.Transaction(id)
 	if err != nil {
 		st.logger.Trace("searching transaction in local store failed", "id", id, "err", err)
diff --git a/store/block.go b/store/block.go
index f062ce72..1cf8ebea 100644
--- a/store/block.go
+++ b/store/block.go
@@ -3,6 +3,7 @@ package store
 import (
 	"bytes"
 
+	"github.com/pactus-project/pactus/crypto"
 	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/types/block"
 	"github.com/pactus-project/pactus/util"
@@ -12,6 +13,10 @@ import (
 )
 
 func blockKey(height uint32) []byte { return append(blockPrefix, util.Uint32ToSlice(height)...) }
+func publicKeyKey(addr crypto.Address) []byte {
+	return append(publicKeyPrefix, addr.Bytes()...)
+}
+
 func blockHashKey(hash hash.Hash) []byte {
 	return append(blockHeightPrefix, hash.Bytes()...)
 }
@@ -62,11 +67,24 @@ func (bs *blockStore) saveBlock(batch *leveldb.Batch, height uint32, block *bloc
 		regs[i].height = height
 		regs[i].offset = uint32(offset)
 
+		pubKey := trx.PublicKey()
+		if pubKey != nil {
+			if !bs.hasPublicKey(trx.Payload().Signer()) {
+				publicKeyKey := publicKeyKey(trx.Payload().Signer())
+				batch.Put(publicKeyKey, pubKey.Bytes())
+			} else {
+				// we have indexed this public key, se we can remove it
+				trx.SetPublicKey(nil)
+			}
+		}
+
 		err := trx.Encode(w)
 		if err != nil {
 			panic(err) // Should we panic?
 		}
 		regs[i].length = uint32(w.Len() - offset)
+
+		trx.SetPublicKey(pubKey)
 	}
 	blockKey := blockKey(height)
 	blockHashKey := blockHashKey(blockHash)
@@ -101,3 +119,11 @@ func (bs *blockStore) hasBlock(height uint32) bool {
 	}
 	return has
 }
+
+func (bs *blockStore) hasPublicKey(addr crypto.Address) bool {
+	has, err := bs.db.Has(publicKeyKey(addr), nil)
+	if err != nil {
+		return false
+	}
+	return has
+}
diff --git a/store/errors.go b/store/errors.go
new file mode 100644
index 00000000..287dda5f
--- /dev/null
+++ b/store/errors.go
@@ -0,0 +1,18 @@
+package store
+
+import (
+	"fmt"
+
+	"github.com/pactus-project/pactus/crypto"
+)
+
+// PublicKeyNotFoundError is returned when the public key associated with an address
+// is not found in the store.
+type PublicKeyNotFoundError struct {
+	Address crypto.Address
+}
+
+func (e PublicKeyNotFoundError) Error() string {
+	return fmt.Sprintf("public key not found for: %s",
+		e.Address.String())
+}
diff --git a/store/interface.go b/store/interface.go
index 391f0c13..aa9cb511 100644
--- a/store/interface.go
+++ b/store/interface.go
@@ -2,6 +2,7 @@ package store
 
 import (
 	"github.com/pactus-project/pactus/crypto"
+	"github.com/pactus-project/pactus/crypto/bls"
 	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/types/account"
 	"github.com/pactus-project/pactus/types/block"
@@ -17,52 +18,77 @@ import (
 
 // TODO: How to undo or rollback at least for last 21 blocks
 
-type StoredBlock struct {
+type CommittedBlock struct {
+	Store
+
 	BlockHash hash.Hash
 	Height    uint32
 	Data      []byte
 }
 
-func (s *StoredBlock) ToBlock() *block.Block {
-	b, err := block.FromBytes(s.Data)
+func (s *CommittedBlock) ToBlock() (*block.Block, error) {
+	blk, err := block.FromBytes(s.Data)
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
-	if !b.Hash().EqualsTo(s.BlockHash) {
-		panic("invalid data. block hash does not match")
+
+	trxs := blk.Transactions()
+	for i := 0; i < trxs.Len(); i++ {
+		trx := trxs[i]
+		if trx.IsPublicKeyStriped() {
+			pub, err := s.PublicKey(trx.Payload().Signer())
+			if err != nil {
+				return nil, PublicKeyNotFoundError{
+					Address: trx.Payload().Signer(),
+				}
+			}
+			trx.SetPublicKey(pub)
+		}
 	}
-	return b
+
+	return blk, nil
 }
 
-type StoredTx struct {
+type CommittedTx struct {
+	Store
+
 	TxID      tx.ID
 	Height    uint32
 	BlockTime uint32
 	Data      []byte
 }
 
-func (s *StoredTx) ToTx() *tx.Tx {
+func (s *CommittedTx) ToTx() (*tx.Tx, error) {
 	trx, err := tx.FromBytes(s.Data)
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
-	if !trx.ID().EqualsTo(s.TxID) {
-		panic("invalid data. transaction id does not match")
+
+	if trx.IsPublicKeyStriped() {
+		pub, err := s.PublicKey(trx.Payload().Signer())
+		if err != nil {
+			return nil, PublicKeyNotFoundError{
+				Address: trx.Payload().Signer(),
+			}
+		}
+		trx.SetPublicKey(pub)
 	}
-	return trx
+
+	return trx, nil
 }
 
 type Reader interface {
-	Block(height uint32) (*StoredBlock, error)
+	Block(height uint32) (*CommittedBlock, error)
 	BlockHeight(hash hash.Hash) uint32
 	BlockHash(height uint32) hash.Hash
 	RecentBlockByStamp(stamp hash.Stamp) (uint32, *block.Block)
-	Transaction(id tx.ID) (*StoredTx, error)
+	Transaction(id tx.ID) (*CommittedTx, error)
+	PublicKey(addr crypto.Address) (*bls.PublicKey, error)
 	HasAccount(crypto.Address) bool
 	Account(addr crypto.Address) (*account.Account, error)
 	AccountByNumber(number int32) (*account.Account, error)
 	TotalAccounts() int32
-	HasValidator(crypto.Address) bool
+	HasValidator(addr crypto.Address) bool
 	ValidatorAddresses() []crypto.Address
 	Validator(addr crypto.Address) (*validator.Validator, error)
 	ValidatorByNumber(num int32) (*validator.Validator, error)
diff --git a/store/mock.go b/store/mock.go
index 2c8e5bd1..9a7952af 100644
--- a/store/mock.go
+++ b/store/mock.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 
 	"github.com/pactus-project/pactus/crypto"
+	"github.com/pactus-project/pactus/crypto/bls"
 	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/types/account"
 	"github.com/pactus-project/pactus/types/block"
@@ -18,9 +19,9 @@ var _ Store = &MockStore{}
 type MockStore struct {
 	ts *testsuite.TestSuite
 
-	Blocks     map[uint32]block.Block
-	Accounts   map[crypto.Address]account.Account
-	Validators map[crypto.Address]validator.Validator
+	Blocks     map[uint32]*block.Block
+	Accounts   map[crypto.Address]*account.Account
+	Validators map[crypto.Address]*validator.Validator
 	LastCert   *certificate.Certificate
 	LastHeight uint32
 }
@@ -28,17 +29,17 @@ type MockStore struct {
 func MockingStore(ts *testsuite.TestSuite) *MockStore {
 	return &MockStore{
 		ts:         ts,
-		Blocks:     make(map[uint32]block.Block),
-		Accounts:   make(map[crypto.Address]account.Account),
-		Validators: make(map[crypto.Address]validator.Validator),
+		Blocks:     make(map[uint32]*block.Block),
+		Accounts:   make(map[crypto.Address]*account.Account),
+		Validators: make(map[crypto.Address]*validator.Validator),
 	}
 }
 
-func (m *MockStore) Block(height uint32) (*StoredBlock, error) {
+func (m *MockStore) Block(height uint32) (*CommittedBlock, error) {
 	b, ok := m.Blocks[height]
 	if ok {
 		d, _ := b.Bytes()
-		return &StoredBlock{
+		return &CommittedBlock{
 			BlockHash: b.Hash(),
 			Height:    height,
 			Data:      d,
@@ -64,12 +65,23 @@ func (m *MockStore) BlockHeight(hash hash.Hash) uint32 {
 	return 0
 }
 
-func (m *MockStore) Transaction(id tx.ID) (*StoredTx, error) {
+func (m *MockStore) PublicKey(addr crypto.Address) (*bls.PublicKey, error) {
+	for _, block := range m.Blocks {
+		for _, trx := range block.Transactions() {
+			if trx.Payload().Signer() == addr {
+				return trx.PublicKey().(*bls.PublicKey), nil
+			}
+		}
+	}
+	return nil, ErrNotFound
+}
+
+func (m *MockStore) Transaction(id tx.ID) (*CommittedTx, error) {
 	for height, block := range m.Blocks {
 		for _, trx := range block.Transactions() {
 			if trx.ID() == id {
 				d, _ := trx.Bytes()
-				return &StoredTx{
+				return &CommittedTx{
 					TxID:      id,
 					Height:    height,
 					BlockTime: block.Header().UnixTime(),
@@ -89,7 +101,7 @@ func (m *MockStore) HasAccount(addr crypto.Address) bool {
 func (m *MockStore) Account(addr crypto.Address) (*account.Account, error) {
 	a, ok := m.Accounts[addr]
 	if ok {
-		return &a, nil
+		return a.Clone(), nil
 	}
 	return nil, fmt.Errorf("not found")
 }
@@ -97,14 +109,14 @@ func (m *MockStore) Account(addr crypto.Address) (*account.Account, error) {
 func (m *MockStore) AccountByNumber(number int32) (*account.Account, error) {
 	for _, v := range m.Accounts {
 		if v.Number() == number {
-			return &v, nil
+			return v.Clone(), nil
 		}
 	}
 	return nil, fmt.Errorf("not found")
 }
 
 func (m *MockStore) UpdateAccount(addr crypto.Address, acc *account.Account) {
-	m.Accounts[addr] = *acc
+	m.Accounts[addr] = acc
 }
 
 func (m *MockStore) TotalAccounts() int32 {
@@ -127,7 +139,7 @@ func (m *MockStore) ValidatorAddresses() []crypto.Address {
 func (m *MockStore) Validator(addr crypto.Address) (*validator.Validator, error) {
 	v, ok := m.Validators[addr]
 	if ok {
-		return &v, nil
+		return v.Clone(), nil
 	}
 	return nil, fmt.Errorf("not found")
 }
@@ -135,14 +147,14 @@ func (m *MockStore) Validator(addr crypto.Address) (*validator.Validator, error)
 func (m *MockStore) ValidatorByNumber(num int32) (*validator.Validator, error) {
 	for _, v := range m.Validators {
 		if v.Number() == num {
-			return &v, nil
+			return v.Clone(), nil
 		}
 	}
 	return nil, fmt.Errorf("not found")
 }
 
 func (m *MockStore) UpdateValidator(val *validator.Validator) {
-	m.Validators[val.Address()] = *val
+	m.Validators[val.Address()] = val
 }
 
 func (m *MockStore) TotalValidators() int32 {
@@ -159,8 +171,7 @@ func (m *MockStore) HasAnyBlock() bool {
 
 func (m *MockStore) IterateAccounts(consumer func(crypto.Address, *account.Account) (stop bool)) {
 	for addr, acc := range m.Accounts {
-		cloned := acc
-		stopped := consumer(addr, &cloned)
+		stopped := consumer(addr, acc.Clone())
 		if stopped {
 			return
 		}
@@ -169,8 +180,7 @@ func (m *MockStore) IterateAccounts(consumer func(crypto.Address, *account.Accou
 
 func (m *MockStore) IterateValidators(consumer func(*validator.Validator) (stop bool)) {
 	for _, val := range m.Validators {
-		cloned := val
-		stopped := consumer(&cloned)
+		stopped := consumer(val.Clone())
 		if stopped {
 			return
 		}
@@ -178,7 +188,7 @@ func (m *MockStore) IterateValidators(consumer func(*validator.Validator) (stop
 }
 
 func (m *MockStore) SaveBlock(height uint32, b *block.Block, cert *certificate.Certificate) {
-	m.Blocks[height] = *b
+	m.Blocks[height] = b
 	m.LastHeight = height
 	m.LastCert = cert
 }
@@ -193,7 +203,7 @@ func (m *MockStore) LastCertificate() (uint32, *certificate.Certificate) {
 func (m *MockStore) RecentBlockByStamp(stamp hash.Stamp) (uint32, *block.Block) {
 	for h, b := range m.Blocks {
 		if b.Stamp().EqualsTo(stamp) {
-			return h, &b
+			return h, b
 		}
 	}
 
@@ -217,7 +227,7 @@ func (m *MockStore) AddTestAccount() (*account.Account, crypto.Signer) {
 }
 
 func (m *MockStore) AddTestBlock(height uint32) *block.Block {
-	blk := m.ts.GenerateTestBlock(nil, nil)
+	blk := m.ts.GenerateTestBlock(nil)
 	cert := m.ts.GenerateTestCertificate()
 	m.SaveBlock(height, blk, cert)
 	return blk
@@ -230,14 +240,14 @@ func (m *MockStore) RandomTestAcc() (crypto.Address, *account.Account) {
 		if addr == crypto.TreasuryAddress {
 			continue
 		}
-		return addr, &acc
+		return addr, acc
 	}
 	panic("no account in sandbox")
 }
 
 func (m *MockStore) RandomTestVal() *validator.Validator {
 	for _, val := range m.Validators {
-		return &val
+		return val
 	}
 	panic("no validator in sandbox")
 }
diff --git a/store/store.go b/store/store.go
index 6b6b8086..bbb288d1 100644
--- a/store/store.go
+++ b/store/store.go
@@ -6,6 +6,7 @@ import (
 	"sync"
 
 	"github.com/pactus-project/pactus/crypto"
+	"github.com/pactus-project/pactus/crypto/bls"
 	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/types/account"
 	"github.com/pactus-project/pactus/types/block"
@@ -36,6 +37,7 @@ var (
 	accountPrefix     = []byte{0x05}
 	validatorPrefix   = []byte{0x07}
 	blockHeightPrefix = []byte{0x09}
+	publicKeyPrefix   = []byte{0x0a}
 )
 
 func tryGet(db *leveldb.DB, key []byte) ([]byte, error) {
@@ -93,8 +95,9 @@ func NewStore(conf *Config, stampLookupCapacity int) (Store, error) {
 		height = lastHeight - uint32(stampLookupCapacity)
 	}
 	for ; height <= lastHeight; height++ {
-		storedBlock, _ := s.Block(height)
-		s.updateStampLookup(height, storedBlock.ToBlock())
+		committedBlock, _ := s.Block(height)
+		block, _ := committedBlock.ToBlock()
+		s.updateStampLookup(height, block)
 	}
 
 	return s, nil
@@ -141,7 +144,7 @@ func (s *store) SaveBlock(height uint32, block *block.Block, cert *certificate.C
 	s.updateStampLookup(height, block)
 }
 
-func (s *store) Block(height uint32) (*StoredBlock, error) {
+func (s *store) Block(height uint32) (*CommittedBlock, error) {
 	s.lk.Lock()
 	defer s.lk.Unlock()
 
@@ -155,7 +158,8 @@ func (s *store) Block(height uint32) (*StoredBlock, error) {
 		return nil, err
 	}
 
-	return &StoredBlock{
+	return &CommittedBlock{
+		Store:     s,
 		BlockHash: blockHash,
 		Height:    height,
 		Data:      data[hash.HashSize:],
@@ -193,7 +197,20 @@ func (s *store) RecentBlockByStamp(stamp hash.Stamp) (uint32, *block.Block) {
 	return 0, nil
 }
 
-func (s *store) Transaction(id tx.ID) (*StoredTx, error) {
+func (s *store) PublicKey(addr crypto.Address) (*bls.PublicKey, error) {
+	bs, err := tryGet(s.db, publicKeyKey(addr))
+	if err != nil {
+		return nil, err
+	}
+	pubKey, err := bls.PublicKeyFromBytes(bs)
+	if err != nil {
+		return nil, err
+	}
+
+	return pubKey, err
+}
+
+func (s *store) Transaction(id tx.ID) (*CommittedTx, error) {
 	s.lk.Lock()
 	defer s.lk.Unlock()
 
@@ -212,7 +229,8 @@ func (s *store) Transaction(id tx.ID) (*StoredTx, error) {
 	}
 	blockTime := util.SliceToUint32(data[hash.HashSize+1 : hash.HashSize+5])
 
-	return &StoredTx{
+	return &CommittedTx{
+		Store:     s,
 		TxID:      id,
 		Height:    pos.height,
 		BlockTime: blockTime,
diff --git a/sync/bundle/message/blocks_response.go b/sync/bundle/message/blocks_response.go
index 6ed5835f..8a607550 100644
--- a/sync/bundle/message/blocks_response.go
+++ b/sync/bundle/message/blocks_response.go
@@ -13,29 +13,29 @@ const (
 )
 
 type BlocksResponseMessage struct {
-	ResponseCode    ResponseCode             `cbor:"1,keyasint"`
-	SessionID       int                      `cbor:"2,keyasint"`
-	From            uint32                   `cbor:"3,keyasint"`
-	BlocksData      [][]byte                 `cbor:"4,keyasint"`
-	LastCertificate *certificate.Certificate `cbor:"5,keyasint"`
-	Reason          string                   `cbor:"6,keyasint"`
+	ResponseCode        ResponseCode             `cbor:"1,keyasint"`
+	SessionID           int                      `cbor:"2,keyasint"`
+	From                uint32                   `cbor:"3,keyasint"`
+	CommittedBlocksData [][]byte                 `cbor:"4,keyasint"`
+	LastCertificate     *certificate.Certificate `cbor:"5,keyasint"`
+	Reason              string                   `cbor:"6,keyasint"`
 }
 
 func NewBlocksResponseMessage(code ResponseCode, reason string, sid int, from uint32,
 	blocksData [][]byte, lastCert *certificate.Certificate,
 ) *BlocksResponseMessage {
 	return &BlocksResponseMessage{
-		ResponseCode:    code,
-		SessionID:       sid,
-		From:            from,
-		BlocksData:      blocksData,
-		LastCertificate: lastCert,
-		Reason:          reason,
+		ResponseCode:        code,
+		SessionID:           sid,
+		From:                from,
+		CommittedBlocksData: blocksData,
+		LastCertificate:     lastCert,
+		Reason:              reason,
 	}
 }
 
 func (m *BlocksResponseMessage) BasicCheck() error {
-	if m.From == 0 && len(m.BlocksData) != 0 {
+	if m.From == 0 && len(m.CommittedBlocksData) != 0 {
 		return errors.Errorf(errors.ErrInvalidHeight, "unexpected block for height zero")
 	}
 	if m.LastCertificate != nil {
@@ -52,12 +52,12 @@ func (m *BlocksResponseMessage) Type() Type {
 }
 
 func (m *BlocksResponseMessage) Count() uint32 {
-	return uint32(len(m.BlocksData))
+	return uint32(len(m.CommittedBlocksData))
 }
 
 func (m *BlocksResponseMessage) To() uint32 {
 	// response message without any block
-	if len(m.BlocksData) == 0 {
+	if len(m.CommittedBlocksData) == 0 {
 		return 0
 	}
 	return m.From + m.Count() - 1
diff --git a/sync/handler_blocks_request.go b/sync/handler_blocks_request.go
index 709d50d2..7d09e47a 100644
--- a/sync/handler_blocks_request.go
+++ b/sync/handler_blocks_request.go
@@ -106,6 +106,7 @@ func (handler *blocksRequestHandler) PrepareBundle(m message.Message) *bundle.Bu
 
 func (handler *blocksRequestHandler) sendBlocks(msg message.Message, to peer.ID, sessionID int) {
 	if err := handler.sendTo(msg, to); err != nil {
+		// TODO: test me
 		// Let's close the session with this peer because we couldn't establish a connection.
 		// This helps to free sessions and ask blocks from other peers.
 		handler.peerSet.CloseSession(sessionID)
diff --git a/sync/handler_blocks_response.go b/sync/handler_blocks_response.go
index 4732a400..34eb5958 100644
--- a/sync/handler_blocks_response.go
+++ b/sync/handler_blocks_response.go
@@ -2,9 +2,9 @@ package sync
 
 import (
 	"github.com/libp2p/go-libp2p/core/peer"
+	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/sync/bundle"
 	"github.com/pactus-project/pactus/sync/bundle/message"
-	"github.com/pactus-project/pactus/types/block"
 )
 
 type blocksResponseHandler struct {
@@ -25,8 +25,9 @@ func (handler *blocksResponseHandler) ParseMessage(m message.Message, initiator
 		handler.logger.Warn("blocks request is rejected", "pid", initiator, "response", msg.ResponseCode)
 	} else {
 		height := msg.From
-		for _, d := range msg.BlocksData {
-			b, err := block.FromBytes(d)
+		for _, data := range msg.CommittedBlocksData {
+			committedBlock := handler.state.MakeCommittedBlock(data, height, hash.UndefHash)
+			b, err := committedBlock.ToBlock()
 			if err != nil {
 				return err
 			}
@@ -39,6 +40,7 @@ func (handler *blocksResponseHandler) ParseMessage(m message.Message, initiator
 		handler.cache.AddCertificate(msg.From, msg.LastCertificate)
 		handler.tryCommitBlocks()
 	}
+
 	handler.updateSession(msg.SessionID, initiator, msg.ResponseCode)
 
 	return nil
@@ -50,3 +52,41 @@ func (handler *blocksResponseHandler) PrepareBundle(m message.Message) *bundle.B
 
 	return bdl
 }
+
+func (handler *blocksResponseHandler) updateSession(sessionID int, pid peer.ID, code message.ResponseCode) {
+	s := handler.peerSet.FindSession(sessionID)
+	if s == nil {
+		// TODO: test me
+		handler.logger.Debug("session not found or closed", "session-id", sessionID)
+		return
+	}
+
+	if s.PeerID() != pid {
+		// TODO: test me
+		handler.logger.Warn("unknown peer", "session-id", sessionID, "pid", pid)
+		return
+	}
+
+	s.SetLastResponseCode(code)
+
+	switch code {
+	case message.ResponseCodeRejected:
+		handler.logger.Debug("session rejected, close session", "session-id", sessionID)
+		handler.peerSet.CloseSession(sessionID)
+		handler.peerSet.IncreaseSendFailedCounter(pid)
+		handler.updateBlockchain()
+
+	case message.ResponseCodeMoreBlocks:
+		handler.logger.Debug("peer responding us. keep session open", "session-id", sessionID)
+
+	case message.ResponseCodeNoMoreBlocks:
+		handler.logger.Debug("peer has no more block. close session", "session-id", sessionID)
+		handler.peerSet.CloseSession(sessionID)
+		handler.updateBlockchain()
+
+	case message.ResponseCodeSynced:
+		handler.logger.Debug("peer informed us we are synced. close session", "session-id", sessionID)
+		handler.peerSet.CloseSession(sessionID)
+		handler.moveConsensusToNewHeight()
+	}
+}
diff --git a/sync/sync.go b/sync/sync.go
index b3af78da..5aacf0cc 100644
--- a/sync/sync.go
+++ b/sync/sync.go
@@ -468,7 +468,7 @@ func (sync *synchronizer) prepareBlocks(from uint32, count uint32) [][]byte {
 	blocks := make([][]byte, 0, count)
 
 	for h := from; h < from+count; h++ {
-		b := sync.state.StoredBlock(h)
+		b := sync.state.CommittedBlock(h)
 		if b == nil {
 			sync.logger.Warn("unable to find a block", "height", h)
 			return nil
@@ -479,39 +479,3 @@ func (sync *synchronizer) prepareBlocks(from uint32, count uint32) [][]byte {
 
 	return blocks
 }
-
-func (sync *synchronizer) updateSession(sessionID int, pid peer.ID, code message.ResponseCode) {
-	s := sync.peerSet.FindSession(sessionID)
-	if s == nil {
-		sync.logger.Debug("session not found or closed", "session-id", sessionID)
-		return
-	}
-
-	if s.PeerID() != pid {
-		sync.logger.Warn("peer ID is not known", "session-id", sessionID, "pid", pid)
-		return
-	}
-
-	s.SetLastResponseCode(code)
-
-	switch code {
-	case message.ResponseCodeRejected:
-		sync.logger.Debug("session rejected, close session", "session-id", sessionID)
-		sync.peerSet.CloseSession(sessionID)
-		sync.peerSet.IncreaseSendFailedCounter(pid)
-		sync.updateBlockchain()
-
-	case message.ResponseCodeMoreBlocks:
-		sync.logger.Debug("peer responding us. keep session open", "session-id", sessionID)
-
-	case message.ResponseCodeNoMoreBlocks:
-		sync.logger.Debug("peer has no more block. close session", "session-id", sessionID)
-		sync.peerSet.CloseSession(sessionID)
-		sync.updateBlockchain()
-
-	case message.ResponseCodeSynced:
-		sync.logger.Debug("peer informed us we are synced. close session", "session-id", sessionID)
-		sync.peerSet.CloseSession(sessionID)
-		sync.moveConsensusToNewHeight()
-	}
-}
diff --git a/types/block/block.go b/types/block/block.go
index a2b0f994..ed0d7f62 100644
--- a/types/block/block.go
+++ b/types/block/block.go
@@ -14,7 +14,6 @@ import (
 	"github.com/pactus-project/pactus/types/tx"
 	"github.com/pactus-project/pactus/util"
 	"github.com/pactus-project/pactus/util/encoding"
-	"github.com/pactus-project/pactus/util/errors"
 )
 
 type Block struct {
@@ -77,25 +76,36 @@ func (b *Block) BasicCheck() error {
 		return err
 	}
 	if b.Transactions().Len() == 0 {
-		return errors.Errorf(errors.ErrInvalidBlock, "block at least should have one transaction")
+		// block at least should have one transaction
+		return BasicCheckError{
+			Reason: "no subsidy transaction",
+		}
 	}
 	if b.Transactions().Len() > 1000 {
-		return errors.Errorf(errors.ErrInvalidBlock, "block is full")
+		return BasicCheckError{
+			Reason: "block is full",
+		}
 	}
 	if b.PrevCertificate() != nil {
 		if err := b.PrevCertificate().BasicCheck(); err != nil {
-			return err
+			return BasicCheckError{
+				Reason: fmt.Sprintf("invalid certificate: %s", err.Error()),
+			}
 		}
 	} else {
 		// Genesis block checks
 		if !b.Header().PrevBlockHash().IsUndef() {
-			return errors.Errorf(errors.ErrInvalidBlock, "invalid previous block hash")
+			return BasicCheckError{
+				Reason: "invalid genesis block hash",
+			}
 		}
 	}
 
 	for _, trx := range b.Transactions() {
 		if err := trx.BasicCheck(); err != nil {
-			return errors.Errorf(errors.ErrInvalidBlock, err.Error())
+			return BasicCheckError{
+				Reason: fmt.Sprintf("invalid transaction: %s", err.Error()),
+			}
 		}
 	}
 
diff --git a/types/block/errors.go b/types/block/errors.go
new file mode 100644
index 00000000..cbe42f7b
--- /dev/null
+++ b/types/block/errors.go
@@ -0,0 +1,10 @@
+package block
+
+// BasicCheckError is returned when the basic check on the certificate fails.
+type BasicCheckError struct {
+	Reason string
+}
+
+func (e BasicCheckError) Error() string {
+	return "block basic check failed: " + e.Reason
+}
diff --git a/types/block/header.go b/types/block/header.go
index ae8eac24..c75820b4 100644
--- a/types/block/header.go
+++ b/types/block/header.go
@@ -1,6 +1,7 @@
 package block
 
 import (
+	"fmt"
 	"io"
 	"time"
 
@@ -8,7 +9,6 @@ import (
 	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/sortition"
 	"github.com/pactus-project/pactus/util/encoding"
-	"github.com/pactus-project/pactus/util/errors"
 )
 
 type Header struct {
@@ -76,10 +76,14 @@ func NewHeader(version uint8, time time.Time, stateRoot, prevBlockHash hash.Hash
 
 func (h *Header) BasicCheck() error {
 	if err := h.data.StateRoot.BasicCheck(); err != nil {
-		return errors.Errorf(errors.ErrInvalidBlock, "invalid state root")
+		return BasicCheckError{
+			Reason: fmt.Sprintf("invalid state root: %s", err.Error()),
+		}
 	}
 	if err := h.data.ProposerAddress.BasicCheck(); err != nil {
-		return errors.Errorf(errors.ErrInvalidBlock, "invalid proposer address")
+		return BasicCheckError{
+			Reason: fmt.Sprintf("invalid proposer address: %s", err.Error()),
+		}
 	}
 
 	return nil
diff --git a/types/tx/errors.go b/types/tx/errors.go
new file mode 100644
index 00000000..2fe7ddf3
--- /dev/null
+++ b/types/tx/errors.go
@@ -0,0 +1,21 @@
+package tx
+
+import "github.com/pactus-project/pactus/types/tx/payload"
+
+// BasicCheckError is returned when the basic check on the certificate fails.
+type BasicCheckError struct {
+	Reason string
+}
+
+func (e BasicCheckError) Error() string {
+	return "transaction basic check failed: " + e.Reason
+}
+
+// InvalidPayloadTypeError is returned when the payload type is not valid.
+type InvalidPayloadTypeError struct {
+	PayloadType payload.Type
+}
+
+func (e InvalidPayloadTypeError) Error() string {
+	return "invalid payload type: " + e.PayloadType.String()
+}
diff --git a/types/tx/tx.go b/types/tx/tx.go
index 450356b1..15bd331c 100644
--- a/types/tx/tx.go
+++ b/types/tx/tx.go
@@ -10,17 +10,16 @@ import (
 	"github.com/pactus-project/pactus/crypto/bls"
 	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/types/tx/payload"
+	"github.com/pactus-project/pactus/util"
 	"github.com/pactus-project/pactus/util/encoding"
-	"github.com/pactus-project/pactus/util/errors"
 )
 
 const (
-	versionLatest = 0x01
-	flagLockTime  = 0x80
+	versionLatest        = 0x01
+	flagStripedPublicKey = 0x01
+	maxMemoLength        = 64
 )
 
-const maxMemoLength = 64
-
 type ID = hash.Hash
 
 type Tx struct {
@@ -31,23 +30,15 @@ type Tx struct {
 }
 
 type txData struct {
-	// Version format
-	//  7             0
-	// +-+-+-+-+-+-+-+-+
-	// |L|R|R|R|VERSION|
-	// +-+-+-+-+-+-+-+-+
-	// L: Lock Time transaction
-	// R: Reserved bit
-	//
+	Flags     uint8
 	Version   uint8
 	Stamp     hash.Stamp
-	LockTime  uint32
 	Sequence  int32
 	Fee       int64
-	Payload   payload.Payload
 	Memo      string
-	PublicKey crypto.PublicKey
+	Payload   payload.Payload
 	Signature crypto.Signature
+	PublicKey crypto.PublicKey
 }
 
 func NewTx(stamp hash.Stamp, seq int32, pld payload.Payload, fee int64,
@@ -67,14 +58,13 @@ func NewTx(stamp hash.Stamp, seq int32, pld payload.Payload, fee int64,
 	return trx
 }
 
-func NewLockTimeTx(lockTime uint32, seq int32, pld payload.Payload, fee int64,
+func NewLockTimeTx(_ /*lockTime*/ uint32, seq int32, pld payload.Payload, fee int64,
 	memo string,
 ) *Tx {
 	trx := &Tx{
 		data: txData{
-			LockTime: lockTime,
 			Sequence: seq,
-			Version:  versionLatest | flagLockTime,
+			Version:  versionLatest,
 			Payload:  pld,
 			Fee:      fee,
 			Memo:     memo,
@@ -127,15 +117,15 @@ func (tx *Tx) Signature() crypto.Signature {
 }
 
 func (tx *Tx) LockTime() uint32 {
-	return tx.data.LockTime
+	return 0
 }
 
 func (tx *Tx) IsStamped() bool {
-	return tx.data.Version&flagLockTime == 0x00
+	return true
 }
 
 func (tx *Tx) IsLockTime() bool {
-	return tx.data.Version&flagLockTime == flagLockTime
+	return false
 }
 
 func (tx *Tx) SetSignature(sig crypto.Signature) {
@@ -146,6 +136,11 @@ func (tx *Tx) SetSignature(sig crypto.Signature) {
 func (tx *Tx) SetPublicKey(pub crypto.PublicKey) {
 	tx.basicChecked = false
 	tx.data.PublicKey = pub
+	if pub == nil {
+		tx.data.Flags = util.SetFlag(tx.data.Flags, flagStripedPublicKey)
+	} else {
+		tx.data.Flags = util.UnsetFlag(tx.data.Flags, flagStripedPublicKey)
+	}
 }
 
 func (tx *Tx) BasicCheck() error {
@@ -153,22 +148,35 @@ func (tx *Tx) BasicCheck() error {
 		return nil
 	}
 	if tx.Version() != versionLatest {
-		return errors.Errorf(errors.ErrInvalidTx, "invalid version")
+		return BasicCheckError{
+			Reason: fmt.Sprintf("invalid version: %d", tx.Version()),
+		}
 	}
 	if tx.Sequence() < 0 {
-		return errors.Error(errors.ErrInvalidSequence)
+		return BasicCheckError{
+			Reason: fmt.Sprintf("invalid sequence: %d", tx.Sequence()),
+		}
 	}
+	// TODO: Define it globally ( 21*1e14 )?
 	if tx.Payload().Value() < 0 || tx.Payload().Value() > 21*1e14 {
-		return errors.Error(errors.ErrInvalidAmount)
-	}
-	if err := tx.checkFee(); err != nil {
-		return err
+		return BasicCheckError{
+			Reason: fmt.Sprintf("invalid amount: %d", tx.Payload().Value()),
+		}
 	}
-	if err := tx.Payload().BasicCheck(); err != nil {
-		return err
+	if tx.Fee() < 0 || tx.Fee() > 21*1e14 {
+		return BasicCheckError{
+			Reason: fmt.Sprintf("invalid fee: %d", tx.Fee()),
+		}
 	}
 	if len(tx.Memo()) > maxMemoLength {
-		return errors.Error(errors.ErrInvalidMemo)
+		return BasicCheckError{
+			Reason: fmt.Sprintf("memo length exceeded: %d", len(tx.Memo())),
+		}
+	}
+	if err := tx.Payload().BasicCheck(); err != nil {
+		return BasicCheckError{
+			Reason: fmt.Sprintf("invalid payload: %s", err.Error()),
+		}
 	}
 	if err := tx.checkSignature(); err != nil {
 		return err
@@ -179,41 +187,39 @@ func (tx *Tx) BasicCheck() error {
 	return nil
 }
 
-func (tx *Tx) checkFee() error {
-	if tx.IsFreeTx() {
-		if tx.Fee() != 0 {
-			return errors.Errorf(errors.ErrInvalidFee, "fee should set to zero")
-		}
-	} else {
-		if tx.Fee() <= 0 {
-			return errors.Errorf(errors.ErrInvalidFee, "fee should not be negative")
-		}
-	}
-
-	return nil
-}
-
 func (tx *Tx) checkSignature() error {
 	if tx.IsSubsidyTx() {
 		if tx.PublicKey() != nil {
-			return errors.Errorf(errors.ErrInvalidPublicKey, "subsidy transaction should not have public key")
+			return BasicCheckError{
+				Reason: "public key set for subsidy transaction",
+			}
 		}
 		if tx.Signature() != nil {
-			return errors.Errorf(errors.ErrInvalidSignature, "subsidy transaction should not have signature")
+			return BasicCheckError{
+				Reason: "signature set for subsidy transaction",
+			}
 		}
 	} else {
 		if tx.PublicKey() == nil {
-			return errors.Errorf(errors.ErrInvalidPublicKey, "no public key")
+			return BasicCheckError{
+				Reason: "no public key",
+			}
 		}
 		if tx.Signature() == nil {
-			return errors.Errorf(errors.ErrInvalidSignature, "no signature")
+			return BasicCheckError{
+				Reason: "no signature",
+			}
 		}
 		if err := tx.PublicKey().VerifyAddress(tx.Payload().Signer()); err != nil {
-			return err
+			return BasicCheckError{
+				Reason: fmt.Sprintf("invalid address: %s", err.Error()),
+			}
 		}
 		bs := tx.SignBytes()
 		if err := tx.PublicKey().Verify(bs, tx.Signature()); err != nil {
-			return errors.Error(errors.ErrInvalidSignature)
+			return BasicCheckError{
+				Reason: fmt.Sprintf("invalid signature: %s", tx.Signature().String()),
+			}
 		}
 	}
 	return nil
@@ -250,22 +256,29 @@ func (tx *Tx) UnmarshalCBOR(bs []byte) error {
 
 // SerializeSize returns the number of bytes it would take to serialize the transaction.
 func (tx *Tx) SerializeSize() int {
-	n := 150 +
+	n := 7 +
 		encoding.VarIntSerializeSize(uint64(tx.Sequence())) +
 		encoding.VarIntSerializeSize(uint64(tx.Fee())) +
 		encoding.VarStringSerializeSize(tx.Memo())
 	if tx.Payload() != nil {
 		n += tx.Payload().SerializeSize()
 	}
+	if tx.data.Signature != nil {
+		n += bls.SignatureSize
+	}
+	if tx.data.PublicKey != nil {
+		n += bls.PublicKeySize
+	}
 
 	return n
 }
 
 func (tx *Tx) Encode(w io.Writer) error {
-	err := tx.EncodeWithNoSignatory(w)
+	err := tx.encodeWithNoSignatory(w)
 	if err != nil {
 		return err
 	}
+
 	if tx.data.Signature != nil {
 		err = tx.data.Signature.Encode(w)
 		if err != nil {
@@ -282,8 +295,8 @@ func (tx *Tx) Encode(w io.Writer) error {
 	return nil
 }
 
-func (tx *Tx) EncodeWithNoSignatory(w io.Writer) error {
-	err := encoding.WriteElements(w, tx.data.Version, tx.data.Stamp)
+func (tx *Tx) encodeWithNoSignatory(w io.Writer) error {
+	err := encoding.WriteElements(w, tx.data.Flags, tx.data.Version, tx.data.Stamp)
 	if err != nil {
 		return err
 	}
@@ -295,23 +308,23 @@ func (tx *Tx) EncodeWithNoSignatory(w io.Writer) error {
 	if err != nil {
 		return err
 	}
-	err = encoding.WriteElement(w, uint8(tx.data.Payload.Type()))
+	err = encoding.WriteVarString(w, tx.data.Memo)
 	if err != nil {
 		return err
 	}
-	err = tx.data.Payload.Encode(w)
+	err = encoding.WriteElement(w, uint8(tx.data.Payload.Type()))
 	if err != nil {
 		return err
 	}
-	err = encoding.WriteVarString(w, tx.data.Memo)
+	err = tx.data.Payload.Encode(w)
 	if err != nil {
 		return err
 	}
 	return nil
 }
 
-func (tx *Tx) DecodeWithNoSignatory(r io.Reader) error {
-	err := encoding.ReadElements(r, &tx.data.Version, &tx.data.Stamp)
+func (tx *Tx) Decode(r io.Reader) error {
+	err := encoding.ReadElements(r, &tx.data.Flags, &tx.data.Version, &tx.data.Stamp)
 	if err != nil {
 		return err
 	}
@@ -328,13 +341,18 @@ func (tx *Tx) DecodeWithNoSignatory(r io.Reader) error {
 	}
 	tx.data.Fee = int64(fee)
 
+	tx.data.Memo, err = encoding.ReadVarString(r)
+	if err != nil {
+		return err
+	}
+
 	payloadType := uint8(0)
 	err = encoding.ReadElement(r, &payloadType)
 	if err != nil {
 		return err
 	}
 
-	switch payload.Type(payloadType) {
+	switch t := payload.Type(payloadType); t {
 	case payload.TypeTransfer:
 		tx.data.Payload = &payload.TransferPayload{}
 	case payload.TypeBond:
@@ -347,25 +365,15 @@ func (tx *Tx) DecodeWithNoSignatory(r io.Reader) error {
 		tx.data.Payload = &payload.SortitionPayload{}
 
 	default:
-		return errors.Errorf(errors.ErrInvalidTx, "invalid payload")
+		return InvalidPayloadTypeError{
+			PayloadType: t,
+		}
 	}
 
 	err = tx.data.Payload.Decode(r)
 	if err != nil {
 		return err
 	}
-	tx.data.Memo, err = encoding.ReadVarString(r)
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-func (tx *Tx) Decode(r io.Reader) error {
-	err := tx.DecodeWithNoSignatory(r)
-	if err != nil {
-		return err
-	}
 
 	if !tx.IsSubsidyTx() {
 		sig := new(bls.Signature)
@@ -375,12 +383,14 @@ func (tx *Tx) Decode(r io.Reader) error {
 		}
 		tx.data.Signature = sig
 
-		pub := new(bls.PublicKey)
-		err = pub.Decode(r)
-		if err != nil {
-			return err
+		if !tx.IsPublicKeyStriped() {
+			pub := new(bls.PublicKey)
+			err = pub.Decode(r)
+			if err != nil {
+				return err
+			}
+			tx.data.PublicKey = pub
 		}
-		tx.data.PublicKey = pub
 	}
 
 	return nil
@@ -395,11 +405,11 @@ func (tx *Tx) String() string {
 
 func (tx *Tx) SignBytes() []byte {
 	buf := bytes.Buffer{}
-	err := tx.EncodeWithNoSignatory(&buf)
+	err := tx.encodeWithNoSignatory(&buf)
 	if err != nil {
 		return nil
 	}
-	return buf.Bytes()
+	return buf.Bytes()[1:] // Exclude flags
 }
 
 func (tx *Tx) ID() ID {
@@ -441,3 +451,13 @@ func (tx *Tx) IsWithdrawTx() bool {
 func (tx *Tx) IsFreeTx() bool {
 	return tx.IsSubsidyTx() || tx.IsSortitionTx() || tx.IsUnbondTx()
 }
+
+// StripPublicKey removes public key from the transaction.
+func (tx *Tx) StripPublicKey() {
+	tx.SetPublicKey(nil)
+}
+
+// IsPublicKeyStriped returns true if the public key stripped from the transaction.
+func (tx *Tx) IsPublicKeyStriped() bool {
+	return util.IsFlagSet(tx.data.Flags, flagStripedPublicKey)
+}
diff --git a/util/utils.go b/util/utils.go
index ae262c52..041ea21d 100644
--- a/util/utils.go
+++ b/util/utils.go
@@ -91,19 +91,19 @@ func RandUint64(max uint64) uint64 {
 }
 
 // SetFlag applies mask to the flags.
-func SetFlag(flags, mask int) int {
+func SetFlag[T constraints.Integer](flags, mask T) T {
 	flags = flags | mask
 	return flags
 }
 
 // UnsetFlag removes mask from the flags.
-func UnsetFlag(flags, mask int) int {
+func UnsetFlag[T constraints.Integer](flags, mask T) T {
 	flags = flags & ^mask
 	return flags
 }
 
 // IsFlagSet checks if the mask is set for the given flags.
-func IsFlagSet(flags, mask int) bool {
+func IsFlagSet[T constraints.Integer](flags, mask T) bool {
 	return flags&mask == mask
 }
 
diff --git a/www/grpc/blockchain.go b/www/grpc/blockchain.go
index b434e117..c0fe34cd 100644
--- a/www/grpc/blockchain.go
+++ b/www/grpc/blockchain.go
@@ -100,18 +100,21 @@ func (s *blockchainServer) GetBlock(_ context.Context,
 	req *pactus.GetBlockRequest,
 ) (*pactus.GetBlockResponse, error) {
 	height := req.GetHeight()
-	storedBlock := s.state.StoredBlock(height)
-	if storedBlock == nil {
-		return nil, status.Errorf(codes.InvalidArgument, "block not found")
+	committedBlock := s.state.CommittedBlock(height)
+	if committedBlock == nil {
+		return nil, status.Errorf(codes.NotFound, "block not found")
 	}
 	res := &pactus.GetBlockResponse{
-		Height: storedBlock.Height,
-		Hash:   storedBlock.BlockHash.Bytes(),
-		Data:   storedBlock.Data,
+		Height: committedBlock.Height,
+		Hash:   committedBlock.BlockHash.Bytes(),
+		Data:   committedBlock.Data,
 	}
 
 	if req.Verbosity > pactus.BlockVerbosity_BLOCK_DATA {
-		block := storedBlock.ToBlock()
+		block, err := committedBlock.ToBlock()
+		if err != nil {
+			return nil, status.Errorf(codes.Internal, err.Error())
+		}
 		blockTime := block.Header().UnixTime()
 		seed := block.Header().SortitionSeed()
 		cert := block.PrevCertificate()
@@ -169,7 +172,7 @@ func (s *blockchainServer) GetAccount(_ context.Context,
 	}
 	acc := s.state.AccountByAddress(addr)
 	if acc == nil {
-		return nil, status.Errorf(codes.InvalidArgument, "account not found")
+		return nil, status.Errorf(codes.NotFound, "account not found")
 	}
 	res := &pactus.GetAccountResponse{
 		Account: accountToProto(acc),
diff --git a/www/grpc/transaction.go b/www/grpc/transaction.go
index 86b182a7..39a004d4 100644
--- a/www/grpc/transaction.go
+++ b/www/grpc/transaction.go
@@ -25,19 +25,28 @@ func (s *transactionServer) GetTransaction(_ context.Context,
 	if err != nil {
 		return nil, status.Errorf(codes.InvalidArgument, "invalid transaction ID: %v", err.Error())
 	}
-	// TODO: Use RawTransaction here
-	storedTx := s.state.StoredTx(id)
-	if storedTx == nil {
+
+	committedTx := s.state.CommittedTx(id)
+	if committedTx == nil {
 		return nil, status.Errorf(codes.InvalidArgument, "transaction not found")
 	}
 
 	res := &pactus.GetTransactionResponse{
-		BlockHeight: storedTx.Height,
-		BlockTime:   storedTx.BlockTime,
+		BlockHeight: committedTx.Height,
+		BlockTime:   committedTx.BlockTime,
 	}
 
-	if req.Verbosity > pactus.TransactionVerbosity_TRANSACTION_DATA {
-		res.Transaction = transactionToProto(storedTx.ToTx())
+	if req.Verbosity == pactus.TransactionVerbosity_TRANSACTION_DATA {
+		res.Transaction = &pactus.TransactionInfo{
+			Data: committedTx.Data,
+			Id:   committedTx.TxID.Bytes(),
+		}
+	} else {
+		trx, err := committedTx.ToTx()
+		if err != nil {
+			return nil, status.Errorf(codes.Internal, err.Error())
+		}
+		res.Transaction = transactionToProto(trx)
 	}
 
 	return res, nil


Test patch:
diff --git a/consensus/propose_test.go b/consensus/propose_test.go
index 416fd805..5c50f547 100644
--- a/consensus/propose_test.go
+++ b/consensus/propose_test.go
@@ -22,7 +22,7 @@ func TestSetProposalInvalidProposer(t *testing.T) {
 	assert.Nil(t, td.consY.RoundProposal(0))
 
 	addr := td.signers[tIndexB].Address()
-	b := td.GenerateTestBlock(&addr, nil)
+	b := td.GenerateTestBlock(&addr)
 	p := proposal.NewProposal(1, 0, b)
 
 	td.consY.SetProposal(p)
@@ -36,8 +36,8 @@ func TestSetProposalInvalidProposer(t *testing.T) {
 func TestSetProposalInvalidBlock(t *testing.T) {
 	td := setup(t)
 
-	a := td.signers[tIndexB].Address()
-	invBlock := td.GenerateTestBlock(&a, nil)
+	addr := td.signers[tIndexB].Address()
+	invBlock := td.GenerateTestBlock(&addr)
 	p := proposal.NewProposal(1, 2, invBlock)
 	td.signers[tIndexB].SignMsg(p)
 
@@ -52,8 +52,8 @@ func TestSetProposalInvalidBlock(t *testing.T) {
 func TestSetProposalInvalidHeight(t *testing.T) {
 	td := setup(t)
 
-	a := td.signers[tIndexB].Address()
-	invBlock := td.GenerateTestBlock(&a, nil)
+	addr := td.signers[tIndexB].Address()
+	invBlock := td.GenerateTestBlock(&addr)
 	p := proposal.NewProposal(2, 0, invBlock)
 	td.signers[tIndexB].SignMsg(p)
 
diff --git a/execution/execution_test.go b/execution/execution_test.go
index a61dd313..7e3d717d 100644
--- a/execution/execution_test.go
+++ b/execution/execution_test.go
@@ -7,7 +7,6 @@ import (
 	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/sandbox"
 	"github.com/pactus-project/pactus/types/tx"
-	"github.com/pactus-project/pactus/types/tx/payload"
 	"github.com/pactus-project/pactus/util/errors"
 	"github.com/pactus-project/pactus/util/testsuite"
 	"github.com/stretchr/testify/assert"
@@ -143,84 +142,84 @@ func TestChecker(t *testing.T) {
 	})
 }
 
-func TestLockTime(t *testing.T) {
-	ts := testsuite.NewTestSuite(t)
-
-	executor := NewExecutor()
-	checker := NewChecker()
-	sb := sandbox.MockingSandbox(ts)
-
-	curHeight := 2 * sb.TestParams.TransactionToLiveInterval
-	sb.TestStore.AddTestBlock(curHeight)
-
-	t.Run("Should reject sortition transactions with lock time", func(t *testing.T) {
-		pub, prv := ts.RandBLSKeyPair()
-		signer := crypto.NewSigner(prv)
-		val := sb.MakeNewValidator(pub)
-		sb.UpdateValidator(val)
-
-		sb.TestAcceptSortition = true
-		pld := &payload.SortitionPayload{
-			Address: pub.Address(),
-			Proof:   ts.RandProof(),
-		}
-		trx := tx.NewLockTimeTx(curHeight+10, 1, pld, 0, "")
-		signer.SignMsg(trx)
-		err := executor.Execute(trx, sb)
-		assert.Error(t, err)
-	})
-
-	t.Run("Should reject subsidy transactions with lock time", func(t *testing.T) {
-		pld := &payload.TransferPayload{
-			Sender:   crypto.TreasuryAddress,
-			Receiver: ts.RandAddress(),
-			Amount:   1234,
-		}
-		trx := tx.NewLockTimeTx(curHeight+10, 1, pld, 0, "")
-		err := executor.Execute(trx, sb)
-		assert.Error(t, err)
-	})
-
-	t.Run("Should reject expired transactions", func(t *testing.T) {
-		signer := ts.RandSigner()
-		acc := sb.MakeNewAccount(signer.Address())
-		acc.AddToBalance(10000)
-		sb.UpdateAccount(signer.Address(), acc)
-		pld := &payload.TransferPayload{
-			Sender:   signer.Address(),
-			Receiver: ts.RandAddress(),
-			Amount:   1234,
-		}
-
-		trx := tx.NewLockTimeTx(curHeight-sb.TestParams.TransactionToLiveInterval, 1,
-			pld, sb.TestParams.MinimumFee, "")
-		signer.SignMsg(trx)
-		err := executor.Execute(trx, sb)
-		assert.Error(t, err)
-	})
-
-	t.Run("Not finalized transaction", func(t *testing.T) {
-		signer := ts.RandSigner()
-		acc := sb.MakeNewAccount(signer.Address())
-		acc.AddToBalance(10000)
-		sb.UpdateAccount(signer.Address(), acc)
-		pld := &payload.TransferPayload{
-			Sender:   signer.Address(),
-			Receiver: ts.RandAddress(),
-			Amount:   1234,
-		}
-
-		trx1 := tx.NewLockTimeTx(curHeight+sb.TestParams.TransactionToLiveInterval, 1,
-			pld, sb.TestParams.MinimumFee, "")
-		signer.SignMsg(trx1)
-		err := executor.Execute(trx1, sb)
-		assert.Error(t, err)
-
-		// In non-strict mode this transaction remains in pool
-		err = checker.Execute(trx1, sb)
-		assert.NoError(t, err)
-	})
-}
+// func TestLockTime(t *testing.T) {
+// 	ts := testsuite.NewTestSuite(t)
+
+// 	executor := NewExecutor()
+// 	checker := NewChecker()
+// 	sb := sandbox.MockingSandbox(ts)
+
+// 	curHeight := 2 * sb.TestParams.TransactionToLiveInterval
+// 	sb.TestStore.AddTestBlock(curHeight)
+
+// 	t.Run("Should reject sortition transactions with lock time", func(t *testing.T) {
+// 		pub, prv := ts.RandomBLSKeyPair()
+// 		signer := crypto.NewSigner(prv)
+// 		val := sb.MakeNewValidator(pub)
+// 		sb.UpdateValidator(val)
+
+// 		sb.TestAcceptSortition = true
+// 		pld := &payload.SortitionPayload{
+// 			Address: pub.Address(),
+// 			Proof:   ts.RandomProof(),
+// 		}
+// 		trx := tx.NewLockTimeTx(curHeight+10, 1, pld, 0, "")
+// 		signer.SignMsg(trx)
+// 		err := executor.Execute(trx, sb)
+// 		assert.Error(t, err)
+// 	})
+
+// 	t.Run("Should reject subsidy transactions with lock time", func(t *testing.T) {
+// 		pld := &payload.TransferPayload{
+// 			Sender:   crypto.TreasuryAddress,
+// 			Receiver: ts.RandomAddress(),
+// 			Amount:   1234,
+// 		}
+// 		trx := tx.NewLockTimeTx(curHeight+10, 1, pld, 0, "")
+// 		err := executor.Execute(trx, sb)
+// 		assert.Error(t, err)
+// 	})
+
+// 	t.Run("Should reject expired transactions", func(t *testing.T) {
+// 		signer := ts.RandSigner()
+// 		acc := sb.MakeNewAccount(signer.Address())
+// 		acc.AddToBalance(10000)
+// 		sb.UpdateAccount(signer.Address(), acc)
+// 		pld := &payload.TransferPayload{
+// 			Sender:   signer.Address(),
+// 			Receiver: ts.RandAddress(),
+// 			Amount:   1234,
+// 		}
+
+// 		trx := tx.NewLockTimeTx(curHeight-sb.TestParams.TransactionToLiveInterval, 1,
+// 			pld, sb.TestParams.MinimumFee, "")
+// 		signer.SignMsg(trx)
+// 		err := executor.Execute(trx, sb)
+// 		assert.Error(t, err)
+// 	})
+
+// 	t.Run("Not finalized transaction", func(t *testing.T) {
+// 		signer := ts.RandomSigner()
+// 		acc := sb.MakeNewAccount(signer.Address())
+// 		acc.AddToBalance(10000)
+// 		sb.UpdateAccount(signer.Address(), acc)
+// 		pld := &payload.TransferPayload{
+// 			Sender:   signer.Address(),
+// 			Receiver: ts.RandomAddress(),
+// 			Amount:   1234,
+// 		}
+
+// 		trx1 := tx.NewLockTimeTx(curHeight+sb.TestParams.TransactionToLiveInterval, 1,
+// 			pld, sb.TestParams.MinimumFee, "")
+// 		signer.SignMsg(trx1)
+// 		err := executor.Execute(trx1, sb)
+// 		assert.Error(t, err)
+
+// 		// In non-strict mode this transaction remains in pool
+// 		err = checker.Execute(trx1, sb)
+// 		assert.NoError(t, err)
+// 	})
+// }
 
 func TestFee(t *testing.T) {
 	ts := testsuite.NewTestSuite(t)
diff --git a/sandbox/sandbox_test.go b/sandbox/sandbox_test.go
index 1a026fe9..58fa4c6a 100644
--- a/sandbox/sandbox_test.go
+++ b/sandbox/sandbox_test.go
@@ -53,7 +53,7 @@ func setup(t *testing.T) *testData {
 	assert.Equal(t, sandbox.CurrentHeight(), uint32(1))
 	lastHeight := uint32(21)
 	for i := uint32(1); i < lastHeight; i++ {
-		b := ts.GenerateTestBlock(nil, nil)
+		b := ts.GenerateTestBlock(nil)
 		c := ts.GenerateTestCertificate()
 		store.SaveBlock(i, b, c)
 	}
diff --git a/state/lastinfo/last_info_test.go b/state/lastinfo/last_info_test.go
index c1bd43f2..48881cef 100644
--- a/state/lastinfo/last_info_test.go
+++ b/state/lastinfo/last_info_test.go
@@ -124,7 +124,7 @@ func TestRestoreFailed(t *testing.T) {
 
 		li := NewLastInfo()
 
-		td.store.Validators = make(map[crypto.Address]validator.Validator) // Reset Validators
+		td.store.Validators = make(map[crypto.Address]*validator.Validator) // Reset Validators
 		_, err := li.RestoreLastInfo(td.store, 4)
 		assert.Error(t, err)
 	})
@@ -134,7 +134,7 @@ func TestRestoreFailed(t *testing.T) {
 
 		li := NewLastInfo()
 
-		td.store.Blocks = make(map[uint32]block.Block) // Reset Blocks
+		td.store.Blocks = make(map[uint32]*block.Block) // Reset Blocks
 		_, err := li.RestoreLastInfo(td.store, 4)
 		assert.Error(t, err)
 	})
diff --git a/state/state_test.go b/state/state_test.go
index 6ea57c37..c93a5741 100644
--- a/state/state_test.go
+++ b/state/state_test.go
@@ -225,7 +225,7 @@ func TestCommitBlocks(t *testing.T) {
 	td := setup(t)
 
 	b1, c1 := td.makeBlockAndCertificate(t, 1, td.valSigner1, td.valSigner2, td.valSigner3)
-	invBlock := td.GenerateTestBlock(nil, nil)
+	invBlock := td.GenerateTestBlock(nil)
 	assert.Error(t, td.state1.CommitBlock(1, invBlock, c1))
 	// No error here but block is ignored, because the height is invalid
 	assert.NoError(t, td.state1.CommitBlock(2, b1, c1))
@@ -366,7 +366,7 @@ func TestBlockProposal(t *testing.T) {
 func TestInvalidBlock(t *testing.T) {
 	td := setup(t)
 
-	b := td.GenerateTestBlock(nil, nil)
+	b := td.GenerateTestBlock(nil)
 	assert.Error(t, td.state1.ValidateBlock(b))
 }
 
@@ -779,3 +779,16 @@ func TestCalcFee(t *testing.T) {
 		assert.Error(t, err)
 	}
 }
+
+func TestMakeCommittedBlock(t *testing.T) {
+	td := setup(t)
+
+	data := td.RandBytes(128)
+	height := td.RandHeight()
+	hash := td.RandHash()
+	cb := td.state1.MakeCommittedBlock(data, height, hash)
+	assert.Equal(t, data, cb.Data)
+	assert.Equal(t, hash, cb.BlockHash)
+	assert.Equal(t, height, cb.Height)
+	assert.NotNil(t, cb.Store)
+}
diff --git a/store/block_test.go b/store/block_test.go
index a9b8dfea..96bd1f1b 100644
--- a/store/block_test.go
+++ b/store/block_test.go
@@ -11,7 +11,7 @@ func TestBlockStore(t *testing.T) {
 	td := setup(t)
 
 	lastHeight, _ := td.store.LastCertificate()
-	b1 := td.GenerateTestBlock(nil, nil)
+	b1 := td.GenerateTestBlock(nil)
 	c1 := td.GenerateTestCertificate()
 
 	t.Run("Missed block, Should panic ", func(t *testing.T) {
diff --git a/store/store_test.go b/store/store_test.go
index 22d3a682..417996ac 100644
--- a/store/store_test.go
+++ b/store/store_test.go
@@ -42,7 +42,7 @@ func (td *testData) saveTestBlocks(t *testing.T, num int) {
 
 	lastHeight, _ := td.store.LastCertificate()
 	for i := 0; i < num; i++ {
-		b := td.GenerateTestBlock(nil, nil)
+		b := td.GenerateTestBlock(nil)
 		c := td.GenerateTestCertificate()
 
 		td.store.SaveBlock(lastHeight+uint32(i+1), b, c)
@@ -90,16 +90,17 @@ func TestRetrieveBlockAndTransactions(t *testing.T) {
 	td := setup(t)
 
 	height, _ := td.store.LastCertificate()
-	storedBlock, err := td.store.Block(height)
+	committedBlock, err := td.store.Block(height)
 	assert.NoError(t, err)
-	assert.Equal(t, height, storedBlock.Height)
-	block := storedBlock.ToBlock()
+	assert.Equal(t, height, committedBlock.Height)
+	block, _ := committedBlock.ToBlock()
 	for _, trx := range block.Transactions() {
-		storedTx, err := td.store.Transaction(trx.ID())
+		committedTx, err := td.store.Transaction(trx.ID())
 		assert.NoError(t, err)
-		assert.Equal(t, storedTx.TxID, trx.ID())
-		assert.Equal(t, storedTx.BlockTime, block.Header().UnixTime())
-		assert.Equal(t, storedTx.ToTx().ID(), trx.ID())
+		assert.Equal(t, committedTx.TxID, trx.ID())
+		assert.Equal(t, committedTx.BlockTime, block.Header().UnixTime())
+		trx2, _ := committedTx.ToTx()
+		assert.Equal(t, trx2.ID(), trx.ID())
 	}
 }
 
@@ -161,3 +162,54 @@ func TestRecentBlockByStamp(t *testing.T) {
 	assert.Zero(t, h)
 	assert.Nil(t, b)
 }
+
+func TestIndexingPublicKeys(t *testing.T) {
+	td := setup(t)
+
+	committedBlock, _ := td.store.Block(1)
+	blk, _ := committedBlock.ToBlock()
+	for _, trx := range blk.Transactions() {
+		addr := trx.Payload().Signer()
+		pub, found := td.store.PublicKey(addr)
+
+		assert.NoError(t, found)
+		assert.Equal(t, pub.Address(), addr)
+	}
+
+	pub, found := td.store.PublicKey(td.RandAddress())
+	assert.Error(t, found)
+	assert.Nil(t, pub)
+}
+
+func TestCommittedBlockToBlock(t *testing.T) {
+	td := setup(t)
+
+	// Use a tricky way to save transactions from the first block again.
+	committedBlock1, _ := td.store.Block(1)
+	committedBlock2, _ := td.store.Block(2)
+	blk1, _ := committedBlock1.ToBlock()
+	blk2, _ := committedBlock2.ToBlock()
+	td.store.SaveBlock(11, blk1, blk2.PrevCertificate())
+	err := td.store.WriteBatch()
+	assert.NoError(t, err)
+
+	// Ensure that the committed block can obtain the public key.
+	committedBlock11, err := td.store.Block(11)
+	assert.NoError(t, err)
+
+	blk11, err := committedBlock11.ToBlock()
+	assert.NoError(t, err)
+
+	err = blk11.BasicCheck()
+	assert.NoError(t, err)
+
+	// Ensure that the committed transactions can obtain the public key.
+	committedTrx, err := td.store.Transaction(blk11.Transactions()[0].ID())
+	assert.NoError(t, err)
+
+	trx, err := committedTrx.ToTx()
+	assert.NoError(t, err)
+
+	err = trx.BasicCheck()
+	assert.NoError(t, err)
+}
diff --git a/sync/bundle/bundle_test.go b/sync/bundle/bundle_test.go
index bce2cacb..fd8bfa06 100644
--- a/sync/bundle/bundle_test.go
+++ b/sync/bundle/bundle_test.go
@@ -37,7 +37,7 @@ func TestMessageCompress(t *testing.T) {
 
 	blocksData := [][]byte{}
 	for i := 0; i < 10; i++ {
-		b := ts.GenerateTestBlock(nil, nil)
+		b := ts.GenerateTestBlock(nil)
 		d, _ := b.Bytes()
 		blocksData = append(blocksData, d)
 	}
diff --git a/sync/bundle/message/block_announce_test.go b/sync/bundle/message/block_announce_test.go
index 8822499a..d21a5cb4 100644
--- a/sync/bundle/message/block_announce_test.go
+++ b/sync/bundle/message/block_announce_test.go
@@ -17,7 +17,7 @@ func TestBlockAnnounceMessage(t *testing.T) {
 	ts := testsuite.NewTestSuite(t)
 
 	t.Run("Invalid certificate", func(t *testing.T) {
-		b := ts.GenerateTestBlock(nil, nil)
+		b := ts.GenerateTestBlock(nil)
 		c := certificate.NewCertificate(0, 0, nil, nil, nil)
 		m := NewBlockAnnounceMessage(100, b, c)
 		err := m.BasicCheck()
@@ -28,7 +28,7 @@ func TestBlockAnnounceMessage(t *testing.T) {
 	})
 
 	t.Run("OK", func(t *testing.T) {
-		b := ts.GenerateTestBlock(nil, nil)
+		b := ts.GenerateTestBlock(nil)
 		c := ts.GenerateTestCertificate()
 		m := NewBlockAnnounceMessage(100, b, c)
 
diff --git a/sync/bundle/message/blocks_response_test.go b/sync/bundle/message/blocks_response_test.go
index 5df9ab12..a2923a54 100644
--- a/sync/bundle/message/blocks_response_test.go
+++ b/sync/bundle/message/blocks_response_test.go
@@ -19,7 +19,7 @@ func TestBlocksResponseMessage(t *testing.T) {
 
 	sid := 123
 	t.Run("Invalid certificate", func(t *testing.T) {
-		b := ts.GenerateTestBlock(nil, nil)
+		b := ts.GenerateTestBlock(nil)
 		c := certificate.NewCertificate(0, 0, nil, nil, nil)
 		d, _ := b.Bytes()
 		m := NewBlocksResponseMessage(ResponseCodeMoreBlocks, ResponseCodeMoreBlocks.String(), sid, 100, [][]byte{d}, c)
@@ -31,7 +31,7 @@ func TestBlocksResponseMessage(t *testing.T) {
 	})
 
 	t.Run("Unexpected block for height zero", func(t *testing.T) {
-		b := ts.GenerateTestBlock(nil, nil)
+		b := ts.GenerateTestBlock(nil)
 		d, _ := b.Bytes()
 		m := NewBlocksResponseMessage(ResponseCodeMoreBlocks, ResponseCodeMoreBlocks.String(), sid, 0, [][]byte{d}, nil)
 
@@ -39,8 +39,8 @@ func TestBlocksResponseMessage(t *testing.T) {
 	})
 
 	t.Run("OK", func(t *testing.T) {
-		b1 := ts.GenerateTestBlock(nil, nil)
-		b2 := ts.GenerateTestBlock(nil, nil)
+		b1 := ts.GenerateTestBlock(nil)
+		b2 := ts.GenerateTestBlock(nil)
 		d1, _ := b1.Bytes()
 		d2, _ := b2.Bytes()
 		m := NewBlocksResponseMessage(ResponseCodeMoreBlocks, ResponseCodeMoreBlocks.String(), sid, 100,
@@ -79,8 +79,8 @@ func TestLatestBlocksResponseCode(t *testing.T) {
 	})
 
 	t.Run("OK - MoreBlocks", func(t *testing.T) {
-		b1 := ts.GenerateTestBlock(nil, nil)
-		b2 := ts.GenerateTestBlock(nil, nil)
+		b1 := ts.GenerateTestBlock(nil)
+		b2 := ts.GenerateTestBlock(nil)
 		d1, _ := b1.Bytes()
 		d2, _ := b2.Bytes()
 		m := NewBlocksResponseMessage(ResponseCodeMoreBlocks, ResponseCodeMoreBlocks.String(), 1, 100, [][]byte{d1, d2}, nil)
diff --git a/sync/cache/cache_test.go b/sync/cache/cache_test.go
index 8195af4d..ef4eaca4 100644
--- a/sync/cache/cache_test.go
+++ b/sync/cache/cache_test.go
@@ -12,9 +12,8 @@ func TestCacheBlocks(t *testing.T) {
 
 	cache, _ := NewCache(10)
 
-	b1 := ts.GenerateTestBlock(nil, nil)
-	h1 := b1.Hash()
-	b2 := ts.GenerateTestBlock(nil, &h1)
+	b1 := ts.GenerateTestBlock(nil)
+	b2 := ts.GenerateTestBlock(nil)
 	testHeight := ts.RandHeight()
 
 	cache.AddBlock(testHeight, b1)
@@ -40,7 +39,7 @@ func TestClearCache(t *testing.T) {
 
 	cache, _ := NewCache(10)
 
-	b := ts.GenerateTestBlock(nil, nil)
+	b := ts.GenerateTestBlock(nil)
 
 	cache.AddBlock(2, b)
 
@@ -57,11 +56,11 @@ func TestCacheIsFull(t *testing.T) {
 
 	i := int32(0)
 	for ; i < 10; i++ {
-		b := ts.GenerateTestBlock(nil, nil)
+		b := ts.GenerateTestBlock(nil)
 		cache.AddBlock(uint32(i+1), b)
 	}
 
-	newBlock := ts.GenerateTestBlock(nil, nil)
+	newBlock := ts.GenerateTestBlock(nil)
 	cache.AddBlock(uint32(i+1), newBlock)
 
 	assert.NotNil(t, cache.GetBlock(uint32(i+1)))
diff --git a/sync/handler_block_announce_test.go b/sync/handler_block_announce_test.go
index 9c3a7fb1..7faa36b7 100644
--- a/sync/handler_block_announce_test.go
+++ b/sync/handler_block_announce_test.go
@@ -11,9 +11,9 @@ func TestParsingBlockAnnounceMessages(t *testing.T) {
 	td := setup(t, nil)
 
 	lastBlockHeight := td.state.LastBlockHeight()
-	b1 := td.GenerateTestBlock(nil, nil)
+	b1 := td.GenerateTestBlock(nil)
 	c1 := td.GenerateTestCertificate()
-	b2 := td.GenerateTestBlock(nil, nil)
+	b2 := td.GenerateTestBlock(nil)
 	c2 := td.GenerateTestCertificate()
 
 	pid := td.RandPeerID()
@@ -46,10 +46,9 @@ func TestParsingBlockAnnounceMessages(t *testing.T) {
 func TestBroadcastingBlockAnnounceMessages(t *testing.T) {
 	td := setup(t, nil)
 
+	blk, _ := td.state.CommittedBlock(td.state.LastBlockHeight()).ToBlock()
 	msg := message.NewBlockAnnounceMessage(
-		td.state.LastBlockHeight(),
-		td.state.StoredBlock(td.state.LastBlockHeight()).ToBlock(),
-		td.state.LastCertificate())
+		td.state.LastBlockHeight(), blk, td.state.LastCertificate())
 
 	t.Run("Not in the committee, should not broadcast block announce message", func(t *testing.T) {
 		td.sync.broadcast(msg)
diff --git a/sync/handler_blocks_request_test.go b/sync/handler_blocks_request_test.go
index aa788c8f..8e56ff7a 100644
--- a/sync/handler_blocks_request_test.go
+++ b/sync/handler_blocks_request_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
+// TODO: check error types and clean the test.
 func TestLatestBlocksRequestMessages(t *testing.T) {
 	config := testConfig()
 	config.NodeNetwork = false
@@ -42,7 +43,7 @@ func TestLatestBlocksRequestMessages(t *testing.T) {
 		})
 
 		t.Run("Request blocks more than `LatestBlockInterval`", func(t *testing.T) {
-			msg := message.NewBlocksRequestMessage(sid, 0, LatestBlockInterval+1)
+			msg := message.NewBlocksRequestMessage(sid, 10, LatestBlockInterval+1)
 			assert.Error(t, td.receivingNewMessage(td.sync, msg, pid))
 
 			bdl := td.shouldPublishMessageWithThisType(t, td.network, message.TypeBlocksResponse)
diff --git a/sync/handler_blocks_response_test.go b/sync/handler_blocks_response_test.go
index d4519272..c6787ebc 100644
--- a/sync/handler_blocks_response_test.go
+++ b/sync/handler_blocks_response_test.go
@@ -2,13 +2,18 @@ package sync
 
 import (
 	"fmt"
+	"io"
 	"testing"
+	"time"
 
 	"github.com/pactus-project/pactus/consensus"
 	"github.com/pactus-project/pactus/crypto"
 	"github.com/pactus-project/pactus/network"
 	"github.com/pactus-project/pactus/state"
+	"github.com/pactus-project/pactus/store"
 	"github.com/pactus-project/pactus/sync/bundle/message"
+	"github.com/pactus-project/pactus/types/block"
+	"github.com/pactus-project/pactus/types/tx"
 	"github.com/pactus-project/pactus/util/logger"
 	"github.com/pactus-project/pactus/util/testsuite"
 	"github.com/stretchr/testify/assert"
@@ -17,20 +22,41 @@ import (
 func TestInvalidBlockData(t *testing.T) {
 	td := setup(t, nil)
 
-	pid := td.RandPeerID()
-	sid := td.sync.peerSet.OpenSession(pid).SessionID()
-	msg := message.NewBlocksResponseMessage(message.ResponseCodeMoreBlocks, message.ResponseCodeMoreBlocks.String(), sid,
-		0, [][]byte{{1, 2, 3}}, nil)
+	blk := block.MakeBlock(1, time.Now(), nil, td.RandHash(), td.RandHash(),
+		td.GenerateTestCertificate(), td.RandSeed(), td.RandAddress())
+	data, _ := blk.Bytes()
+	tests := []struct {
+		data []byte
+		err  error
+	}{
+		{
+			td.RandBytes(16),
+			io.ErrUnexpectedEOF,
+		},
+		{
+			data,
+			block.BasicCheckError{
+				Reason: "no subsidy transaction",
+			},
+		},
+	}
 
-	assert.Error(t, td.receivingNewMessage(td.sync, msg, pid))
+	for _, test := range tests {
+		pid := td.RandPeerID()
+		sid := td.sync.peerSet.OpenSession(pid).SessionID()
+		msg := message.NewBlocksResponseMessage(message.ResponseCodeMoreBlocks, message.ResponseCodeMoreBlocks.String(), sid,
+			td.RandHeight(), [][]byte{test.data}, nil)
+
+		err := td.receivingNewMessage(td.sync, msg, pid)
+		assert.ErrorIs(t, err, test.err)
+	}
 }
 
 func TestOneBlockShorter(t *testing.T) {
 	td := setup(t, nil)
 
-	lastBlockHash := td.state.LastBlockHash()
 	lastBlockHeight := td.state.LastBlockHeight()
-	b1 := td.GenerateTestBlock(nil, &lastBlockHash)
+	b1 := td.GenerateTestBlock(nil)
 	c1 := td.GenerateTestCertificate()
 	d1, _ := b1.Bytes()
 	pid := td.RandPeerID()
@@ -67,6 +93,50 @@ func TestOneBlockShorter(t *testing.T) {
 	})
 }
 
+func TestStrippedPublicKey(t *testing.T) {
+	td := setup(t, nil)
+
+	// Add a peer
+	pid := td.RandPeerID()
+	pub, _ := td.RandBLSKeyPair()
+	td.addPeer(t, pub, pid, false)
+
+	blk1 := td.GenerateTestBlock(nil)
+	trx := *td.state.TestStore.Blocks[1].Transactions()[0]
+	trxs := []*tx.Tx{&trx}
+	blk2 := block.MakeBlock(1, time.Now(), trxs, td.RandHash(), td.RandHash(),
+		td.GenerateTestCertificate(), td.RandSeed(), td.RandAddress())
+
+	tests := []struct {
+		blk *block.Block
+		err error
+	}{
+		{
+			blk1,
+			store.PublicKeyNotFoundError{
+				Address: blk1.Transactions()[0].PublicKey().Address(),
+			},
+		},
+		{
+			blk2,
+			nil,
+		},
+	}
+
+	for _, test := range tests {
+		assert.NoError(t, test.blk.BasicCheck())
+		trx1 := test.blk.Transactions()[0]
+		trx1.StripPublicKey()
+		d1, _ := test.blk.Bytes()
+		sid := td.sync.peerSet.OpenSession(pid).SessionID()
+		msg := message.NewBlocksResponseMessage(message.ResponseCodeMoreBlocks, message.ResponseCodeRejected.String(), sid,
+			td.RandHeight(), [][]byte{d1}, nil)
+		err := td.receivingNewMessage(td.sync, msg, pid)
+
+		assert.ErrorIs(t, err, test.err)
+	}
+}
+
 // TestSyncing is an important test to verify the syncing process between two
 // test nodes, Alice and Bob. In real-world scenarios, multiple nodes are typically
 // involved, but the procedure remains similar.
diff --git a/sync/sync_test.go b/sync/sync_test.go
index 3fca8a73..6ca9f5dc 100644
--- a/sync/sync_test.go
+++ b/sync/sync_test.go
@@ -252,7 +252,7 @@ func TestDownload(t *testing.T) {
 	td := setup(t, nil)
 
 	ourBlockHeight := td.state.LastBlockHeight()
-	b := td.GenerateTestBlock(nil, nil)
+	b := td.GenerateTestBlock(nil)
 	c := td.GenerateTestCertificate()
 	pid := td.RandPeerID()
 	msg := message.NewBlockAnnounceMessage(ourBlockHeight+LatestBlockInterval+1, b, c)
diff --git a/types/account/account_test.go b/types/account/account_test.go
index 17dfc557..8abaa2c4 100644
--- a/types/account/account_test.go
+++ b/types/account/account_test.go
@@ -1,6 +1,7 @@
 package account_test
 
 import (
+	"encoding/hex"
 	"testing"
 
 	"github.com/pactus-project/pactus/crypto/hash"
@@ -26,20 +27,19 @@ func TestFromBytes(t *testing.T) {
 }
 
 func TestDecoding(t *testing.T) {
-	bs := []byte{
-		0x1, 0x0, 0x0, 0x0, // number
-		0x2, 0x0, 0x0, 0x0, // sequence
-		0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // balance
-	}
+	d, _ := hex.DecodeString(
+		"01000000" + // number
+			"02000000" + // sequence
+			"0300000000000000") // balance
 
-	acc, err := account.FromBytes(bs)
+	acc, err := account.FromBytes(d)
 	require.NoError(t, err)
 	assert.Equal(t, acc.Number(), int32(1))
 	assert.Equal(t, acc.Sequence(), int32(2))
 	assert.Equal(t, acc.Balance(), int64(3))
-	bs2, _ := acc.Bytes()
-	assert.Equal(t, bs, bs2)
-	assert.Equal(t, acc.Hash(), hash.CalcHash(bs))
+	d2, _ := acc.Bytes()
+	assert.Equal(t, d, d2)
+	assert.Equal(t, acc.Hash(), hash.CalcHash(d))
 	expected, _ := hash.FromString("74280903e6b73b79e56b1f15cee24c444776cfeee3bea9476b549b660176f773")
 	assert.Equal(t, acc.Hash(), expected)
 }
diff --git a/types/block/block_test.go b/types/block/block_test.go
index 96b229af..5a406920 100644
--- a/types/block/block_test.go
+++ b/types/block/block_test.go
@@ -3,109 +3,199 @@ package block_test
 import (
 	"encoding/hex"
 	"testing"
-	"time"
 
 	"github.com/fxamacker/cbor/v2"
 	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/types/block"
 	"github.com/pactus-project/pactus/types/certificate"
+	"github.com/pactus-project/pactus/types/tx"
+	"github.com/pactus-project/pactus/types/tx/payload"
 	"github.com/pactus-project/pactus/util"
 	"github.com/pactus-project/pactus/util/simplemerkle"
 	"github.com/pactus-project/pactus/util/testsuite"
 	"github.com/stretchr/testify/assert"
 )
 
-// TODO: check error type.
 func TestBasicCheck(t *testing.T) {
 	ts := testsuite.NewTestSuite(t)
 
 	t.Run("No transactions", func(t *testing.T) {
-		b0 := ts.GenerateTestBlock(nil, nil)
+		b0 := ts.GenerateTestBlock(nil)
 		b := block.NewBlock(b0.Header(), b0.PrevCertificate(), block.Txs{})
 
-		assert.Error(t, b.BasicCheck())
+		err := b.BasicCheck()
+		assert.ErrorIs(t, err, block.BasicCheckError{
+			Reason: "no subsidy transaction",
+		})
 	})
 
 	t.Run("Without the previous certificate", func(t *testing.T) {
-		b0 := ts.GenerateTestBlock(nil, nil)
+		b0 := ts.GenerateTestBlock(nil)
 		b := block.NewBlock(b0.Header(), nil, b0.Transactions())
 
-		assert.Error(t, b.BasicCheck())
+		err := b.BasicCheck()
+		assert.ErrorIs(t, err, block.BasicCheckError{
+			Reason: "invalid genesis block hash",
+		})
 	})
 
 	t.Run("Invalid certificate", func(t *testing.T) {
-		b0 := ts.GenerateTestBlock(nil, nil)
+		b0 := ts.GenerateTestBlock(nil)
 		cert0 := b0.PrevCertificate()
 		invCert := certificate.NewCertificate(0, 0, cert0.Committers(), cert0.Absentees(), cert0.Signature())
 		b := block.NewBlock(b0.Header(), invCert, b0.Transactions())
 
-		assert.Error(t, b.BasicCheck())
+		err := b.BasicCheck()
+		assert.ErrorIs(t, err, block.BasicCheckError{
+			Reason: "invalid certificate: certificate basic check failed: height is not positive: 0",
+		})
 	})
 
 	t.Run("Invalid transaction", func(t *testing.T) {
-		b0 := ts.GenerateTestBlock(nil, nil)
+		b0 := ts.GenerateTestBlock(nil)
 		trxs0 := b0.Transactions()
 		invalidSigner := ts.RandSigner()
 		invalidSigner.SignMsg(trxs0[0])
 		b := block.NewBlock(b0.Header(), b0.PrevCertificate(), trxs0)
 
-		assert.Error(t, b.BasicCheck())
+		err := b.BasicCheck()
+		assert.ErrorIs(t, err, block.BasicCheckError{
+			Reason: "invalid transaction: transaction basic check failed: invalid address: invalid address",
+		})
 	})
 
 	t.Run("Invalid state root hash", func(t *testing.T) {
 		d := ts.DecodingHex(
-			"0134ec9b649f1fe7230ecf98ede2eb097587f69d7e29fade7d1ba0d5d9383bd74c1704655b00000000000000000000000000000000000000" +
-				"00000000000000000000000000b9f39a3a63edeeeb24cec7daae01575168024a96b15def00da7ff84332acba24e3269dcfb5a592824ec174" +
-				"5551ffcbb9011c8e4ed0bfe587a05f2bffa6965455a6fd483a1a010000000100040312142101128d1e40bd0135faab80e264d2171b1b6b87" +
-				"00e87e1c7520e3f8c15930c7615eee6ef829ed5034c7d9254092f58b32d7ef010180b04fda9903c0f5ccf018010128d651affb92e704fcb9" +
-				"93249c076e276fa89f4301e4b5fa19f70684a771b6587ae2b4fcd14ece4dc3adb2be84f4f0010c746573742073656e642d7478a56ee5b7af" +
-				"5a4baeddd68a82556ca09ca0b1e3ae2587a0d202f5204193cf582a38a2e9bd08468a0321e6fd723741924c8144710257ea8629e7181a478f" +
-				"348664e6484a20655b2eca7aec933e53d537fc082b34da84b899aba4aa6a980dcd15e80cebc5837cf48ee01d4a65bad3e272855c6f9d4d2b" +
-				"a9b7b8ebce45f3e9113be1337d7e67d9fc9029705e0c2d3d824b31")
-
-		b, err := block.FromBytes(d)
-		assert.NoError(t, err)
-
-		assert.Error(t, b.BasicCheck())
+			"01" + // Version
+				"00000000" + // UnixTime
+				"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + // PrevBlockHash
+				"0000000000000000000000000000000000000000000000000000000000000000" + // StateRoot
+				"333333333333333333333333333333333333333333333333" + // SortitionSeed
+				"333333333333333333333333333333333333333333333333" +
+				"01AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + // ProposerAddress
+				"04030201" + // PrevCert: Height
+				"0100" + // PrevCert: Round
+				"0401020304" + // PrevCert: Committers
+				"0102" + // PrevCert: Absentees
+				"b53d79e156e9417e010fa21f2b2a96bee6be46fcd233295d" +
+				"2f697cdb9e782b6112ac01c80d0d9d64c2320664c77fa2a6" + // PrevCert: Signature
+				"01" + // Txs: Len
+				"00" + // Tx[0]: Flags
+				"01" + // Tx[0]: Version
+				"a1b2c3d4" + // Tx[0]: Stamp
+				"01" + // Tx[0]: Sequence
+				"01" + // Tx[0]: Fee
+				"00" + // Tx[0]: Memo
+				"01" + // Tx[0]: PayloadType
+				"00" + // Tx[0]: Sender (treasury)
+				"012222222222222222222222222222222222222222" + // Tx[0]: Receiver
+				"01") // Tx[0]: Amount
+
+		b, _ := block.FromBytes(d)
+
+		err := b.BasicCheck()
+		assert.ErrorIs(t, err, block.BasicCheckError{
+			Reason: "invalid state root: hash is zero",
+		})
 	})
 
 	t.Run("Invalid previous block hash", func(t *testing.T) {
 		d := ts.DecodingHex(
-			"0134ec9b64000000000000000000000000000000000000000000000000000000000000000082916efdb068bbec819457f5ce73bf8d2ca743" +
-				"e12c1946ee844e696e47bbe164b9f39a3a63edeeeb24cec7daae01575168024a96b15def00da7ff84332acba24e3269dcfb5a592824ec174" +
-				"5551ffcbb9011c8e4ed0bfe587a05f2bffa6965455a6fd483a1a010000000100040312142101128d1e40bd0135faab80e264d2171b1b6b87" +
-				"00e87e1c7520e3f8c15930c7615eee6ef829ed5034c7d9254092f58b32d7ef010180b04fda9903c0f5ccf018010128d651affb92e704fcb9" +
-				"93249c076e276fa89f4301e4b5fa19f70684a771b6587ae2b4fcd14ece4dc3adb2be84f4f0010c746573742073656e642d7478a56ee5b7af" +
-				"5a4baeddd68a82556ca09ca0b1e3ae2587a0d202f5204193cf582a38a2e9bd08468a0321e6fd723741924c8144710257ea8629e7181a478f" +
-				"348664e6484a20655b2eca7aec933e53d537fc082b34da84b899aba4aa6a980dcd15e80cebc5837cf48ee01d4a65bad3e272855c6f9d4d2b" +
-				"a9b7b8ebce45f3e9113be1337d7e67d9fc9029705e0c2d3d824b31")
+			"01" + // Version
+				"00000000" + // UnixTime
+				"0000000000000000000000000000000000000000000000000000000000000000" + // PrevBlockHash
+				"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + // StateRoot
+				"333333333333333333333333333333333333333333333333" + // SortitionSeed
+				"333333333333333333333333333333333333333333333333" +
+				"01AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + // ProposerAddress
+				"04030201" + // PrevCert: Height
+				"0100" + // PrevCert: Round
+				"0401020304" + // PrevCert: Committers
+				"0102" + // PrevCert: Absentees
+				"b53d79e156e9417e010fa21f2b2a96bee6be46fcd233295d" +
+				"2f697cdb9e782b6112ac01c80d0d9d64c2320664c77fa2a6" + // PrevCert: Signature
+				"01" + // Txs: Len
+				"00" + // Tx[0]: Flags
+				"01" + // Tx[0]: Version
+				"a1b2c3d4" + // Tx[0]: Stamp
+				"01" + // Tx[0]: Sequence
+				"01" + // Tx[0]: Fee
+				"00" + // Tx[0]: Memo
+				"01" + // Tx[0]: PayloadType
+				"00" + // Tx[0]: Sender (treasury)
+				"012222222222222222222222222222222222222222" + // Tx[0]: Receiver
+				"01") // Tx[0]: Amount
 
 		_, err := block.FromBytes(d)
-		assert.Error(t, err)
+		assert.ErrorIs(t, err, tx.InvalidPayloadTypeError{
+			PayloadType: payload.Type(0xb5),
+		})
 	})
 
 	t.Run("Invalid proposer address (type is 2)", func(t *testing.T) {
 		d := ts.DecodingHex(
-			"0134ec9b649f1fe7230ecf98ede2eb097587f69d7e29fade7d1ba0d5d9383bd74c1704655b82916efdb068bbec819457f5ce73bf8d2ca743" +
-				"e12c1946ee844e696e47bbe164b9f39a3a63edeeeb24cec7daae01575168024a96b15def00da7ff84332acba24e3269dcfb5a592824ec174" +
-				"5551ffcbb9021c8e4ed0bfe587a05f2bffa6965455a6fd483a1a010000000100040312142101128d1e40bd0135faab80e264d2171b1b6b87" +
-				"00e87e1c7520e3f8c15930c7615eee6ef829ed5034c7d9254092f58b32d7ef010180b04fda9903c0f5ccf018010128d651affb92e704fcb9" +
-				"93249c076e276fa89f4301e4b5fa19f70684a771b6587ae2b4fcd14ece4dc3adb2be84f4f0010c746573742073656e642d7478a56ee5b7af" +
-				"5a4baeddd68a82556ca09ca0b1e3ae2587a0d202f5204193cf582a38a2e9bd08468a0321e6fd723741924c8144710257ea8629e7181a478f" +
-				"348664e6484a20655b2eca7aec933e53d537fc082b34da84b899aba4aa6a980dcd15e80cebc5837cf48ee01d4a65bad3e272855c6f9d4d2b" +
-				"a9b7b8ebce45f3e9113be1337d7e67d9fc9029705e0c2d3d824b31")
-
-		b, err := block.FromBytes(d)
-		assert.NoError(t, err)
-
-		assert.Error(t, b.BasicCheck())
+			"01" + // Version
+				"00000000" + // UnixTime
+				"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + // PrevBlockHash
+				"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + // StateRoot
+				"333333333333333333333333333333333333333333333333" + // SortitionSeed
+				"333333333333333333333333333333333333333333333333" +
+				"02AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + // ProposerAddress
+				"04030201" + // PrevCert: Height
+				"0100" + // PrevCert: Round
+				"0401020304" + // PrevCert: Committers
+				"0102" + // PrevCert: Absentees
+				"b53d79e156e9417e010fa21f2b2a96bee6be46fcd233295d" +
+				"2f697cdb9e782b6112ac01c80d0d9d64c2320664c77fa2a6" + // PrevCert: Signature
+				"01" + // Txs: Len
+				"00" + // Tx[0]: Flags
+				"01" + // Tx[0]: Version
+				"a1b2c3d4" + // Tx[0]: Stamp
+				"01" + // Tx[0]: Sequence
+				"01" + // Tx[0]: Fee
+				"00" + // Tx[0]: Memo
+				"01" + // Tx[0]: PayloadType
+				"00" + // Tx[0]: Sender (treasury)
+				"012222222222222222222222222222222222222222" + // Tx[0]: Receiver
+				"01") // Tx[0]: Amount
+
+		b, _ := block.FromBytes(d)
+		err := b.BasicCheck()
+		assert.ErrorIs(t, err, block.BasicCheckError{
+			Reason: "invalid proposer address: invalid address: invalid address type",
+		})
 	})
 
 	t.Run("Ok", func(t *testing.T) {
-		b := ts.GenerateTestBlock(nil, nil)
+		d := ts.DecodingHex(
+			"01" + // Version
+				"00000000" + // UnixTime
+				"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + // PrevBlockHash
+				"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + // StateRoot
+				"333333333333333333333333333333333333333333333333" + // SortitionSeed
+				"333333333333333333333333333333333333333333333333" +
+				"01AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + // ProposerAddress
+				"04030201" + // PrevCert: Height
+				"0100" + // PrevCert: Round
+				"0401020304" + // PrevCert: Committers
+				"0102" + // PrevCert: Absentees
+				"b53d79e156e9417e010fa21f2b2a96bee6be46fcd233295d" +
+				"2f697cdb9e782b6112ac01c80d0d9d64c2320664c77fa2a6" + // PrevCert: Signature
+				"01" + // Txs: Len
+				"00" + // Tx[0]: Flags
+				"01" + // Tx[0]: Version
+				"a1b2c3d4" + // Tx[0]: Stamp
+				"01" + // Tx[0]: Sequence
+				"01" + // Tx[0]: Fee
+				"00" + // Tx[0]: Memo
+				"01" + // Tx[0]: PayloadType
+				"00" + // Tx[0]: Sender (treasury)
+				"012222222222222222222222222222222222222222" + // Tx[0]: Receiver
+				"01") // Tx[0]: Amount
+
+		b, _ := block.FromBytes(d)
 		assert.NoError(t, b.BasicCheck())
-		assert.LessOrEqual(t, b.Header().Time(), time.Now())
-		assert.NotZero(t, b.Header().UnixTime())
+		assert.Zero(t, b.Header().UnixTime())
 		assert.Equal(t, b.Header().Version(), uint8(1))
 	})
 }
@@ -113,7 +203,7 @@ func TestBasicCheck(t *testing.T) {
 func TestCBORMarshaling(t *testing.T) {
 	ts := testsuite.NewTestSuite(t)
 
-	b1 := ts.GenerateTestBlock(nil, nil)
+	b1 := ts.GenerateTestBlock(nil)
 	bz1, err := cbor.Marshal(b1)
 	assert.NoError(t, err)
 	var b2 block.Block
@@ -131,7 +221,7 @@ func TestCBORMarshaling(t *testing.T) {
 func TestEncodingBlock(t *testing.T) {
 	ts := testsuite.NewTestSuite(t)
 
-	blk := ts.GenerateTestBlock(nil, nil)
+	blk := ts.GenerateTestBlock(nil)
 	length := blk.SerializeSize()
 
 	for i := 0; i < length; i++ {
@@ -157,7 +247,7 @@ func TestEncodingBlock(t *testing.T) {
 func TestTxFromBytes(t *testing.T) {
 	ts := testsuite.NewTestSuite(t)
 
-	blk := ts.GenerateTestBlock(nil, nil)
+	blk := ts.GenerateTestBlock(nil)
 	bs, _ := blk.Bytes()
 	_, err := block.FromBytes(bs)
 	assert.NoError(t, err)
@@ -170,10 +260,31 @@ func TestTxFromBytes(t *testing.T) {
 
 func TestBlockHash(t *testing.T) {
 	d, _ := hex.DecodeString(
-		"0140da9b641551048b59a859946ca7f9ab95c9cf84da488a1a5c49ba643b29b653dc223bc20a4e9ff03158165f3d42" +
-			"4e2a74677bfe24a7295d1ce2e55ca3644cbe9a5a5e7d913b8e1ba6a020afbd5a25024a12b37cf8e1ed0b9498f91d75b294db0f95123d8593" +
-			"05aa5deea3d4216777e74310b6a601bb4d4d6b13c9b295781ab1533aea032978d4f89300001234050004060f1b23010fab4f72234cc7c120" +
-			"48bbbc616c005573d8ad4d5c6997996d6f488946cdd78410f0a400c4a7f9bdb41506bdf717a892fa00")
+		"01" + // Version
+			"00000000" + // UnixTime
+			"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + // PrevBlockHash
+			"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + // StateRoot
+			"333333333333333333333333333333333333333333333333" + // SortitionSeed
+			"333333333333333333333333333333333333333333333333" +
+			"01AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + // ProposerAddress
+			"04030201" + // PrevCert: Height
+			"0100" + // PrevCert: Round
+			"0401020304" + // PrevCert: Committers
+			"0102" + // PrevCert: Absentees
+			"b53d79e156e9417e010fa21f2b2a96bee6be46fcd233295d" +
+			"2f697cdb9e782b6112ac01c80d0d9d64c2320664c77fa2a6" + // PrevCert: Signature
+			"01" + // Txs: Len
+			"00" + // Tx[0]: Flags
+			"01" + // Tx[0]: Version
+			"a1b2c3d4" + // Tx[0]: Stamp
+			"01" + // Tx[0]: Sequence
+			"01" + // Tx[0]: Fee
+			"00" + // Tx[0]: Memo
+			"01" + // Tx[0]: PayloadType
+			"00" + // Tx[0]: Sender (treasury)
+			"012222222222222222222222222222222222222222" + // Tx[0]: Receiver
+			"01") // Tx[0]: Amount
+
 	b, err := block.FromBytes(d)
 	assert.NoError(t, err)
 	assert.Equal(t, b.SerializeSize(), len(d))
@@ -198,16 +309,16 @@ func TestBlockHash(t *testing.T) {
 	hashData = append(hashData, util.Int32ToSlice(int32(b.Transactions().Len()))...)
 
 	expected1 := hash.CalcHash(hashData)
-	expected2, _ := hash.FromString("03b77a2ac3d944c6fbef6a7e927ce285491bb5be413472adae56dee1f9ee3c97")
+	expected2, _ := hash.FromString("2f9e14e66a6d2e3695dad65d273414e5aec92949c7f64a5b18fc6a1bf8006db5")
 	assert.Equal(t, b.Hash(), expected1)
 	assert.Equal(t, b.Hash(), expected2)
-	assert.Equal(t, b.Stamp(), hash.Stamp{0x03, 0xb7, 0x7a, 0x2a})
+	assert.Equal(t, b.Stamp(), hash.Stamp{0x2f, 0x9e, 0x14, 0xe6})
 }
 
 func TestMakeBlock(t *testing.T) {
 	ts := testsuite.NewTestSuite(t)
 
-	b0 := ts.GenerateTestBlock(nil, nil)
+	b0 := ts.GenerateTestBlock(nil)
 	b1 := block.MakeBlock(1, util.Now(), b0.Transactions(),
 		b0.Header().PrevBlockHash(),
 		b0.Header().StateRoot(),
diff --git a/types/certificate/certificate_test.go b/types/certificate/certificate_test.go
index 9b1c48af..00922cee 100644
--- a/types/certificate/certificate_test.go
+++ b/types/certificate/certificate_test.go
@@ -1,11 +1,14 @@
 package certificate_test
 
 import (
+	"bytes"
+	"encoding/hex"
 	"fmt"
 	"testing"
 
 	"github.com/fxamacker/cbor/v2"
 	"github.com/pactus-project/pactus/crypto/bls"
+	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/types/certificate"
 	"github.com/pactus-project/pactus/types/validator"
 	"github.com/pactus-project/pactus/util"
@@ -13,6 +16,26 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func TestCertificate(t *testing.T) {
+	d, _ := hex.DecodeString(
+		"04030201" + // Height
+			"0100" + // Round
+			"0401020304" + // Committers
+			"0102" + // Absentees
+			"b53d79e156e9417e010fa21f2b2a96bee6be46fcd233295d2f697cdb9e782b6112ac01c80d0d9d64c2320664c77fa2a6") // Signature
+
+	h, _ := hash.FromString("6d5fee07c7cc35384f2f1bc695f6b6afa339df6d867dec0d324a60a48803e1aa")
+	r := bytes.NewReader(d)
+	cert := new(certificate.Certificate)
+	err := cert.Decode(r)
+	assert.NoError(t, err)
+	assert.Equal(t, cert.Height(), uint32(0x01020304))
+	assert.Equal(t, cert.Round(), int16(0x0001))
+	assert.Equal(t, cert.Committers(), []int32{1, 2, 3, 4})
+	assert.Equal(t, cert.Absentees(), []int32{2})
+	assert.Equal(t, cert.Hash(), h)
+}
+
 func TestCertificateCBORMarshaling(t *testing.T) {
 	ts := testsuite.NewTestSuite(t)
 
diff --git a/types/tx/tx_test.go b/types/tx/tx_test.go
index 403831bd..b3fc20ae 100644
--- a/types/tx/tx_test.go
+++ b/types/tx/tx_test.go
@@ -6,13 +6,11 @@ import (
 	"testing"
 
 	"github.com/fxamacker/cbor/v2"
-	"github.com/pactus-project/pactus/crypto"
 	"github.com/pactus-project/pactus/crypto/bls"
 	"github.com/pactus-project/pactus/crypto/hash"
 	"github.com/pactus-project/pactus/types/tx"
 	"github.com/pactus-project/pactus/types/tx/payload"
 	"github.com/pactus-project/pactus/util"
-	"github.com/pactus-project/pactus/util/errors"
 	"github.com/pactus-project/pactus/util/testsuite"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -34,62 +32,41 @@ func TestCBORMarshaling(t *testing.T) {
 func TestEncodingTx(t *testing.T) {
 	ts := testsuite.NewTestSuite(t)
 
-	tx1, _ := ts.GenerateTestTransferTx()
-	length := tx1.SerializeSize()
-
-	for i := 0; i < length; i++ {
-		w := util.NewFixedWriter(i)
-		assert.Error(t, tx1.Encode(w), "encode test %v failed", i)
-	}
-	w := util.NewFixedWriter(length)
-	assert.NoError(t, tx1.Encode(w))
-
-	for i := 0; i < length; i++ {
-		tx2 := new(tx.Tx)
-		r := util.NewFixedReader(i, w.Bytes())
-		assert.Error(t, tx2.Decode(r), "decode test %v failed", i)
-	}
-
-	tx2 := new(tx.Tx)
-	r := util.NewFixedReader(length, w.Bytes())
-	assert.NoError(t, tx2.Decode(r))
-	assert.Equal(t, tx1.ID(), tx2.ID())
-}
-
-func TestFromBytes(t *testing.T) {
-	ts := testsuite.NewTestSuite(t)
-
 	trx1, _ := ts.GenerateTestTransferTx()
 	trx2, _ := ts.GenerateTestBondTx()
 	trx3, _ := ts.GenerateTestUnbondTx()
 	trx4, _ := ts.GenerateTestWithdrawTx()
 	trx5, _ := ts.GenerateTestSortitionTx()
-	tests := []*tx.Tx{trx1, trx2, trx3, trx4, trx5}
 	assert.True(t, trx1.IsTransferTx())
 	assert.True(t, trx2.IsBondTx())
 	assert.True(t, trx3.IsUnbondTx())
 	assert.True(t, trx4.IsWithdrawTx())
 	assert.True(t, trx5.IsSortitionTx())
 
+	tests := []*tx.Tx{trx1, trx2, trx3, trx4, trx5}
 	for _, trx := range tests {
 		assert.NoError(t, trx.BasicCheck())
 		assert.NoError(t, trx.BasicCheck()) // double basic check
 
+		length := trx.SerializeSize()
+		for i := 0; i < length; i++ {
+			w := util.NewFixedWriter(i)
+			assert.Error(t, trx.Encode(w), "encode test %v failed", i)
+		}
+		w := util.NewFixedWriter(length)
+		assert.NoError(t, trx.Encode(w))
+
+		for i := 0; i < length; i++ {
+			trx2 := new(tx.Tx)
+			r := util.NewFixedReader(i, w.Bytes())
+			assert.Error(t, trx2.Decode(r), "decode test %v failed", i)
+		}
+
 		bz, err := trx.Bytes()
 		assert.NoError(t, err)
-		tx2, err := tx.FromBytes(bz)
+		trx2, err := tx.FromBytes(bz)
 		assert.NoError(t, err)
-		assert.Equal(t, trx.Version(), tx2.Version())
-		assert.Equal(t, trx.Stamp(), tx2.Stamp())
-		assert.Equal(t, trx.Sequence(), tx2.Sequence())
-		assert.Equal(t, trx.Payload().Value(), tx2.Payload().Value())
-		assert.Equal(t, trx.Payload().Signer(), tx2.Payload().Signer())
-		assert.Equal(t, trx.Payload().Type(), tx2.Payload().Type())
-		assert.Equal(t, trx.Fee(), tx2.Fee())
-		assert.Equal(t, trx.Memo(), tx2.Memo())
-		assert.Equal(t, trx.ID(), tx2.ID())
-		assert.True(t, trx.PublicKey().EqualsTo(tx2.PublicKey()))
-		assert.True(t, trx.Signature().EqualsTo(tx2.Signature()))
+		assert.Equal(t, trx.ID(), trx2.ID())
 	}
 
 	_, err := tx.FromBytes([]byte{1})
@@ -114,7 +91,9 @@ func TestBasicCheck(t *testing.T) {
 	t.Run("Invalid sequence", func(t *testing.T) {
 		trx := tx.NewSortitionTx(ts.RandStamp(), -1, ts.RandAddress(), ts.RandProof())
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidSequence)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid sequence: -1",
+		})
 	})
 
 	t.Run("Big memo, Should returns error", func(t *testing.T) {
@@ -124,7 +103,9 @@ func TestBasicCheck(t *testing.T) {
 			ts.RandAddress(), ts.RandInt64(1e9), bigMemo)
 
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidMemo)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "memo length exceeded: 65",
+		})
 	})
 
 	t.Run("Invalid payload, Should returns error", func(t *testing.T) {
@@ -134,7 +115,9 @@ func TestBasicCheck(t *testing.T) {
 			invAddr, 1e9, "invalid address")
 
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidAddress)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid payload: invalid address: invalid address type",
+		})
 	})
 
 	t.Run("Invalid amount", func(t *testing.T) {
@@ -142,7 +125,9 @@ func TestBasicCheck(t *testing.T) {
 			ts.RandAddress(), ts.RandAddress(), -1, 1, "invalid amount")
 
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidAmount)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid amount: -1",
+		})
 	})
 
 	t.Run("Invalid amount", func(t *testing.T) {
@@ -150,7 +135,29 @@ func TestBasicCheck(t *testing.T) {
 			ts.RandAddress(), ts.RandAddress(), 21*1e14+1, 1, "invalid amount")
 
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidAmount)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid amount: 2100000000000001",
+		})
+	})
+
+	t.Run("Invalid fee", func(t *testing.T) {
+		trx := tx.NewTransferTx(ts.RandStamp(), ts.RandInt32NonZero(100),
+			ts.RandAddress(), ts.RandAddress(), 1, -1, "invalid fee")
+
+		err := trx.BasicCheck()
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid fee: -1",
+		})
+	})
+
+	t.Run("Invalid fee", func(t *testing.T) {
+		trx := tx.NewTransferTx(ts.RandStamp(), ts.RandInt32NonZero(100),
+			ts.RandAddress(), ts.RandAddress(), 1, 21*1e14+1, "invalid fee")
+
+		err := trx.BasicCheck()
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid fee: 2100000000000001",
+		})
 	})
 
 	t.Run("Invalid signer address", func(t *testing.T) {
@@ -160,58 +167,51 @@ func TestBasicCheck(t *testing.T) {
 		signer.SignMsg(trx)
 
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidAddress)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid address: invalid address",
+		})
 	})
 
 	t.Run("Invalid version", func(t *testing.T) {
-		d := ts.DecodingHex("023513630b1a00010001703db2cca1f0deb29fb42b98bd9d12971b1160168094ebdc0300")
+		d := ts.DecodingHex(
+			"00" + // Flags
+				"02" + // Version
+				"a1b2c3d4" + // Stamp
+				"01" + // Sequence
+				"01" + // Fee
+				"00" + // Memo
+				"01" + // PayloadType
+				"00" + // Sender (treasury)
+				"012222222222222222222222222222222222222222" + // Receiver
+				"01") // Amount
+
 		trx, err := tx.FromBytes(d)
 		assert.NoError(t, err)
 		err = trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidTx)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid version: 2",
+		})
 	})
 }
 
-func TestInvalidFee(t *testing.T) {
+func TestInvalidPayloadType(t *testing.T) {
 	ts := testsuite.NewTestSuite(t)
 
-	t.Run("Invalid subsidy fee", func(t *testing.T) {
-		trx := tx.NewTransferTx(ts.RandStamp(), ts.RandInt32NonZero(100),
-			crypto.TreasuryAddress, ts.RandAddress(), 1e9, 1, "invalid fee")
-		assert.True(t, trx.IsSubsidyTx())
-		err := trx.BasicCheck()
-
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidFee)
-	})
-
-	t.Run("Invalid transfer fee", func(t *testing.T) {
-		trx := tx.NewTransferTx(ts.RandStamp(), ts.RandInt32NonZero(100),
-			crypto.TreasuryAddress, ts.RandAddress(), 1e9, 1, "invalid fee")
-		assert.True(t, trx.IsSubsidyTx())
-		err := trx.BasicCheck()
-
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidFee)
-	})
-
-	t.Run("Invalid sortition fee", func(t *testing.T) {
-		pld := &payload.SortitionPayload{
-			Address: ts.RandAddress(),
-			Proof:   ts.RandProof(),
-		}
-		trx := tx.NewTx(ts.RandStamp(), ts.RandInt32NonZero(100),
-			pld, 1, "invalid fee")
-
-		assert.True(t, trx.IsSortitionTx())
-		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidFee)
-	})
-
-	t.Run("Invalid fee", func(t *testing.T) {
-		trx := tx.NewTransferTx(ts.RandStamp(), ts.RandInt32NonZero(100),
-			ts.RandAddress(), ts.RandAddress(), 1, -1, "invalid fee")
-
-		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidFee)
+	d := ts.DecodingHex(
+		"00" + // Flags
+			"01" + // Version
+			"a1b2c3d4" + // Stamp
+			"01" + // Sequence
+			"01" + // Fee
+			"00" + // Memo
+			"06" + // PayloadType
+			"00" + // Sender (treasury)
+			"012222222222222222222222222222222222222222" + // Receiver
+			"01") // Amount
+
+	_, err := tx.FromBytes(d)
+	assert.ErrorIs(t, err, tx.InvalidPayloadTypeError{
+		PayloadType: payload.Type(6),
 	})
 }
 
@@ -225,16 +225,22 @@ func TestSubsidyTx(t *testing.T) {
 		trx := tx.NewSubsidyTx(stamp, 88, pub.Address(), 2500, "subsidy")
 		sig := prv.Sign(trx.SignBytes())
 		trx.SetSignature(sig)
+
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidSignature)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "signature set for subsidy transaction",
+		})
 	})
 
 	t.Run("Has public key", func(t *testing.T) {
 		stamp := ts.RandStamp()
 		trx := tx.NewSubsidyTx(stamp, 88, pub.Address(), 2500, "subsidy")
 		trx.SetPublicKey(pub)
+
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidPublicKey)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "public key set for subsidy transaction",
+		})
 	})
 }
 
@@ -249,15 +255,21 @@ func TestInvalidSignature(t *testing.T) {
 	t.Run("No signature", func(t *testing.T) {
 		trx, _ := ts.GenerateTestTransferTx()
 		trx.SetSignature(nil)
+
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidSignature)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "no signature",
+		})
 	})
 
 	t.Run("No public key", func(t *testing.T) {
 		trx, _ := ts.GenerateTestTransferTx()
 		trx.SetPublicKey(nil)
+
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidPublicKey)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "no public key",
+		})
 	})
 
 	pbInv, pvInv := ts.RandBLSKeyPair()
@@ -265,15 +277,21 @@ func TestInvalidSignature(t *testing.T) {
 		trx, _ := ts.GenerateTestTransferTx()
 		sig := pvInv.Sign(trx.SignBytes())
 		trx.SetSignature(sig)
+
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidSignature)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid signature: " + trx.Signature().String(),
+		})
 	})
 
 	t.Run("Invalid public key", func(t *testing.T) {
 		trx, _ := ts.GenerateTestTransferTx()
 		trx.SetPublicKey(pbInv)
+
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidAddress)
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid address: invalid address",
+		})
 	})
 
 	t.Run("Invalid sign Bytes", func(t *testing.T) {
@@ -282,37 +300,82 @@ func TestInvalidSignature(t *testing.T) {
 			"invalidate signature")
 		trx.SetPublicKey(trx0.PublicKey())
 		trx.SetSignature(trx0.Signature())
+
 		err := trx.BasicCheck()
-		assert.Equal(t, errors.Code(err), errors.ErrInvalidSignature)
-		assert.Error(t, trx.BasicCheck())
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid signature: " + trx.Signature().String(),
+		})
 	})
 
 	t.Run("Zero signature", func(t *testing.T) {
 		trx, _ := ts.GenerateTestTransferTx()
 		trx.SetSignature(&bls.Signature{})
-		assert.Error(t, trx.BasicCheck())
+
+		err := trx.BasicCheck()
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid signature: " +
+				"c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+		})
 	})
 
 	t.Run("Zero public key", func(t *testing.T) {
 		trx, _ := ts.GenerateTestTransferTx()
 		trx.SetPublicKey(&bls.PublicKey{})
-		assert.Error(t, trx.BasicCheck())
+
+		err := trx.BasicCheck()
+		assert.ErrorIs(t, err, tx.BasicCheckError{
+			Reason: "invalid address: invalid address",
+		})
 	})
 }
 
 func TestSignBytes(t *testing.T) {
 	d, _ := hex.DecodeString(
-		"01f10c077fcc04f5ef819fc9d6080101d3e45d249a39d806a1faec2fd85820db340b98e30168fc72a1a961933e694439b2e3c8751d27de5a" +
-			"d3b9c3dc91b9c9b59b010c746573742073656e642d7478b53d79e156e9417e010fa21f2b2a96bee6be46fcd233295d2f697cdb9e782b6112" +
-			"ac01c80d0d9d64c2320664c77fa2a68d82fa4fcac04a3b565267685e90db1b01420285d2f8295683c138c092c209479983ba159137077884" +
-			"6681b7b558e0611776208c0718006311c84b4a113335c70d1f5c7c5dd93a5625c4af51c48847abd0b590c055306162d2a03ca1cbf7bcc1")
-	h, _ := hash.FromString("2a04aef409194ff72e942346525428f6c030e2875be27205cb2ce46065ec543f")
+		"00" + // Flags
+			"01" + // Version
+			"a1b2c3d4" + // Stamp
+			"01" + // Sequence
+			"01" + // Fee
+			"00" + // Memo
+			"01" + // PayloadType
+			"013333333333333333333333333333333333333333" + // Sender
+			"012222222222222222222222222222222222222222" + // Receiver
+			"01" + // Amount
+			"b53d79e156e9417e010fa21f2b2a96bee6be46fcd233295d2f697cdb9e782b6112ac01c80d0d9d64c2320664c77fa2a6" + // Signature
+			"8d82fa4fcac04a3b565267685e90db1b01420285d2f8295683c138c092c209479983ba1591370778846681b7b558e061" + // PublicKey
+			"1776208c0718006311c84b4a113335c70d1f5c7c5dd93a5625c4af51c48847abd0b590c055306162d2a03ca1cbf7bcc1")
+
+	h, _ := hash.FromString("85b0af97280eda00256a0814452d5294128377ad27f62dd8643f403c230245e5")
 	trx, err := tx.FromBytes(d)
 	assert.NoError(t, err)
 	assert.Equal(t, trx.SerializeSize(), len(d))
 
-	sb := d[:len(d)-bls.PublicKeySize-bls.SignatureSize]
+	sb := d[1 : len(d)-bls.PublicKeySize-bls.SignatureSize]
 	assert.Equal(t, sb, trx.SignBytes())
 	assert.Equal(t, trx.ID(), h)
 	assert.Equal(t, trx.ID(), hash.CalcHash(sb))
 }
+
+func TestStripPublicKey(t *testing.T) {
+	ts := testsuite.NewTestSuite(t)
+
+	trx1, _ := ts.GenerateTestTransferTx()
+	id1 := trx1.ID()
+	assert.NoError(t, trx1.BasicCheck())
+
+	trx1.StripPublicKey()
+	assert.True(t, trx1.IsPublicKeyStriped())
+	assert.Equal(t, id1, trx1.ID())
+	assert.ErrorIs(t, trx1.BasicCheck(),
+		tx.BasicCheckError{
+			Reason: "no public key",
+		})
+
+	bs1, _ := trx1.Bytes()
+	trx2, _ := tx.FromBytes(bs1)
+	bs2, _ := trx2.Bytes()
+
+	assert.Equal(t, bs1, bs2)
+	assert.Equal(t, trx1.ID(), trx2.ID())
+	assert.Nil(t, trx2.PublicKey())
+}
diff --git a/types/validator/validator_test.go b/types/validator/validator_test.go
index 2057458d..bbc2e4af 100644
--- a/types/validator/validator_test.go
+++ b/types/validator/validator_test.go
@@ -1,6 +1,7 @@
 package validator_test
 
 import (
+	"encoding/hex"
 	"testing"
 
 	"github.com/pactus-project/pactus/crypto/bls"
@@ -40,24 +41,17 @@ func TestFromBytes(t *testing.T) {
 }
 
 func TestDecoding(t *testing.T) {
-	bs := []byte{
-		0x95, 0x16, 0x7c, 0x2a, 0x0d, 0x86, 0xec, 0x36, 0x04, 0x07, 0xbc, 0xe8, // public key
-		0x9b, 0x30, 0x46, 0x16, 0xe1, 0xd0, 0xf8, 0x3d, 0xbc, 0x20, 0x06, 0x42,
-		0xab, 0xea, 0x84, 0x05, 0xe1, 0x83, 0x83, 0x12, 0xfb, 0x82, 0x90, 0xb1,
-		0x23, 0x0e, 0xbe, 0x43, 0x69, 0xcf, 0x1b, 0x7f, 0x55, 0x69, 0x06, 0xc6,
-		0x10, 0xae, 0x92, 0xbc, 0xee, 0x54, 0x4a, 0x1a, 0xf7, 0x9e, 0x25, 0x99,
-		0x96, 0xe3, 0x68, 0xb1, 0x48, 0x51, 0xa1, 0xf8, 0x84, 0x42, 0x74, 0x69,
-		0x0b, 0x10, 0xdf, 0x98, 0x3b, 0xc2, 0x77, 0x6a, 0xb1, 0x0c, 0xc3, 0x7e,
-		0x49, 0xe1, 0x75, 0xbc, 0x7a, 0xe1, 0x7a, 0xc9, 0x19, 0xb8, 0xc3, 0x4c,
-		0x1, 0x0, 0x0, 0x0, // number
-		0x2, 0x0, 0x0, 0x0, // sequence
-		0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // balance
-		0x4, 0x0, 0x0, 0x0, // LastBondingHeight
-		0x5, 0x0, 0x0, 0x0, // UnbondingHeight
-		0x6, 0x0, 0x0, 0x0, // LastSortitionHeight
-	}
-
-	val, err := validator.FromBytes(bs)
+	d, _ := hex.DecodeString(
+		"8d82fa4fcac04a3b565267685e90db1b01420285d2f8295683c138c092c209479983ba1591370778846681b7b558e061" + // PublicKey
+			"1776208c0718006311c84b4a113335c70d1f5c7c5dd93a5625c4af51c48847abd0b590c055306162d2a03ca1cbf7bcc1" +
+			"01000000" + // Number
+			"02000000" + // Sequence
+			"0300000000000000" + // Stake
+			"04000000" + // LastBondingHeight
+			"05000000" + // UnbondingHeight
+			"06000000") // LastSortitionHeight
+
+	val, err := validator.FromBytes(d)
 	require.NoError(t, err)
 	assert.Equal(t, val.Number(), int32(1))
 	assert.Equal(t, val.Sequence(), int32(2))
@@ -65,12 +59,12 @@ func TestDecoding(t *testing.T) {
 	assert.Equal(t, val.LastBondingHeight(), uint32(4))
 	assert.Equal(t, val.UnbondingHeight(), uint32(5))
 	assert.Equal(t, val.LastSortitionHeight(), uint32(6))
-	bs2, _ := val.Bytes()
-	assert.Equal(t, bs, bs2)
-	assert.Equal(t, val.Hash(), hash.CalcHash(bs))
-	expected, _ := hash.FromString("76fea239a4586e8d9c2df9062b1958703341e3ece0f665c714da850101b61185")
+	d2, _ := val.Bytes()
+	assert.Equal(t, d, d2)
+	assert.Equal(t, val.Hash(), hash.CalcHash(d))
+	expected, _ := hash.FromString("6a42857652fbd8a7fbbe63b3d4b177cd4c93de2c29d633e33aa9e83d69831bda")
 	assert.Equal(t, val.Hash(), expected)
-	pub, _ := bls.PublicKeyFromBytes(bs[:96])
+	pub, _ := bls.PublicKeyFromBytes(d[:96])
 	assert.True(t, val.PublicKey().EqualsTo(pub))
 }
 
diff --git a/util/testsuite/testsuite.go b/util/testsuite/testsuite.go
index 94527669..85e0d3b9 100644
--- a/util/testsuite/testsuite.go
+++ b/util/testsuite/testsuite.go
@@ -269,7 +269,7 @@ func (ts *TestSuite) GenerateTestValidator(number int32) (*validator.Validator,
 }
 
 // GenerateTestBlock generates a block for testing purposes.
-func (ts *TestSuite) GenerateTestBlock(proposer *crypto.Address, prevBlockHash *hash.Hash) *block.Block {
+func (ts *TestSuite) GenerateTestBlock(proposer *crypto.Address) *block.Block {
 	if proposer == nil {
 		addr := ts.RandAddress()
 		proposer = &addr
@@ -287,19 +287,11 @@ func (ts *TestSuite) GenerateTestBlock(proposer *crypto.Address, prevBlockHash *
 	txs.Append(tx4)
 	txs.Append(tx5)
 
-	if prevBlockHash == nil {
-		h := ts.RandHash()
-		prevBlockHash = &h
-	}
 	cert := ts.GenerateTestCertificate()
-	if prevBlockHash.IsUndef() {
-		cert = nil
-	}
-	sortitionSeed := ts.RandSeed()
 	header := block.NewHeader(1, util.Now(),
 		ts.RandHash(),
-		*prevBlockHash,
-		sortitionSeed,
+		ts.RandHash(),
+		ts.RandSeed(),
 		*proposer)
 
 	return block.NewBlock(header, cert, txs)
@@ -325,7 +317,7 @@ func (ts *TestSuite) GenerateTestCertificate() *certificate.Certificate {
 func (ts *TestSuite) GenerateTestProposal(height uint32, round int16) (*proposal.Proposal, crypto.Signer) {
 	signer := ts.RandSigner()
 	addr := signer.Address()
-	b := ts.GenerateTestBlock(&addr, nil)
+	b := ts.GenerateTestBlock(&addr)
 	p := proposal.NewProposal(height, round, b)
 	signer.SignMsg(p)
 	return p, signer
diff --git a/www/grpc/blockchain_test.go b/www/grpc/blockchain_test.go
index ec3dc7cd..c4a9b854 100644
--- a/www/grpc/blockchain_test.go
+++ b/www/grpc/blockchain_test.go
@@ -24,7 +24,7 @@ func TestGetBlock(t *testing.T) {
 		assert.Nil(t, res)
 	})
 
-	t.Run("Should return an existing block data", func(t *testing.T) {
+	t.Run("Should return an existing block data (verbosity: 0)", func(t *testing.T) {
 		res, err := client.GetBlock(tCtx,
 			&pactus.GetBlockRequest{Height: height, Verbosity: pactus.BlockVerbosity_BLOCK_DATA})
 
@@ -37,7 +37,7 @@ func TestGetBlock(t *testing.T) {
 		assert.Empty(t, res.Txs)
 	})
 
-	t.Run("Should return object with verbosity 1 ", func(t *testing.T) {
+	t.Run("Should return object with  (verbosity: 1)", func(t *testing.T) {
 		res, err := client.GetBlock(tCtx,
 			&pactus.GetBlockRequest{Height: height, Verbosity: pactus.BlockVerbosity_BLOCK_INFO})
 
@@ -52,7 +52,7 @@ func TestGetBlock(t *testing.T) {
 		assert.Equal(t, res.PrevCert.Absentees, b.PrevCertificate().Absentees())
 	})
 
-	t.Run("Should return object with verbosity 2 ", func(t *testing.T) {
+	t.Run("Should return object with  (verbosity: 2)", func(t *testing.T) {
 		res, err := client.GetBlock(tCtx,
 			&pactus.GetBlockRequest{Height: height, Verbosity: pactus.BlockVerbosity_BLOCK_TRANSACTIONS})
 
diff --git a/www/grpc/transaction_test.go b/www/grpc/transaction_test.go
index 6776d3a8..bed74f56 100644
--- a/www/grpc/transaction_test.go
+++ b/www/grpc/transaction_test.go
@@ -17,7 +17,23 @@ func TestGetTransaction(t *testing.T) {
 	testBlock := tMockState.TestStore.AddTestBlock(1)
 	trx1 := testBlock.Transactions()[0]
 
-	t.Run("Should return transaction", func(t *testing.T) {
+	t.Run("Should return transaction (verbosity: 0)", func(t *testing.T) {
+		res, err := client.GetTransaction(tCtx, &pactus.GetTransactionRequest{
+			Id:        trx1.ID().Bytes(),
+			Verbosity: pactus.TransactionVerbosity_TRANSACTION_DATA,
+		})
+		data, _ := trx1.Bytes()
+
+		assert.NoError(t, err)
+		assert.NotNil(t, res)
+		assert.NotEmpty(t, res.Transaction)
+		assert.Equal(t, uint32(0x1), res.BlockHeight)
+		assert.Equal(t, trx1.ID().Bytes(), res.Transaction.Id)
+		assert.Equal(t, data, res.Transaction.Data)
+		assert.Nil(t, res.Transaction.Payload)
+	})
+
+	t.Run("Should return transaction (verbosity: 1)", func(t *testing.T) {
 		res, err := client.GetTransaction(tCtx, &pactus.GetTransactionRequest{
 			Id:        trx1.ID().Bytes(),
 			Verbosity: pactus.TransactionVerbosity_TRANSACTION_INFO,