Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batch update 2019-04-27 6PM PDT #2

Merged
merged 1 commit into from
Apr 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ clean:
$(GOCLEAN)
rm -f ./bin/$(BUILD_TARGET_SERVER)
rm -f ./bin/$(BUILD_TARGET_TXINJ)
rm -f chain.db
rm -f block.dat

.PHONY: run
run:
Expand Down
37 changes: 30 additions & 7 deletions blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package blockchain
import (
"math"
"os"
"sort"

"github.com/golang/glog"
"github.com/golang/protobuf/proto"
Expand All @@ -29,7 +30,7 @@ const (
)

var (
// ErrInvalidBlock is the error returned when the block is not vliad
// ErrInvalidBlock is the error returned when the block is not valid
ErrInvalidBlock = errors.New("failed to validate the block")
)

Expand Down Expand Up @@ -186,7 +187,12 @@ func (bc *Blockchain) ValidateBlock(blk *Block) error {
// Note: the coinbase transaction will be added to the given transactions
// when minting a new block.
func (bc *Blockchain) MintNewBlock(txs []*Tx, toaddr, data string) *Block {
txs = append(txs, NewCoinbaseTx(toaddr, bc.config.Chain.BlockReward, data))
cbTx := NewCoinbaseTx(toaddr, bc.config.Chain.BlockReward, data)
if cbTx == nil {
glog.Error("Cannot create coinbase transaction")
return nil
}
txs = append(txs, cbTx)
return NewBlock(bc.chainID, bc.height+1, bc.tip, txs)
}

Expand Down Expand Up @@ -260,22 +266,26 @@ func CreateBlockchain(address string, cfg *config.Config) *Blockchain {
glog.Error("cannot find db")
return nil
}
chain := NewBlockchain(db, cfg)

chain := NewBlockchain(db, cfg)
if dbFileExist {
glog.Info("Blockchain already exists.")

if err := chain.Init(); err != nil {
glog.Fatalf("Failed to create Blockchain, error = %v", err)
glog.Errorf("Failed to create Blockchain, error = %v", err)
return nil
}
return chain
}

// create genesis block
cbtx := NewCoinbaseTx(address, cfg.Chain.TotalSupply, GenesisCoinbaseData)
genesis := NewBlock(chain.chainID, 0, cp.ZeroHash32B, []*Tx{cbtx})
genesis.Header.timestamp = 0
gen, err := LoadGenesisWithPath(DefaultGenesisPath)
if err != nil {
return nil
}
// Temporarily let genesis TotalSupply point to cfg.Chain.TotalSupply
gen.TotalSupply = cfg.Chain.TotalSupply
genesis := NewGenesisBlock(gen)

// Genesis block has height 0
if genesis.Header.height != 0 {
Expand Down Expand Up @@ -332,6 +342,13 @@ func (bc *Blockchain) createTx(from iotxaddress.Address, amount uint64, to []*Pa
out = append(out, bc.Utk.CreateTxOutputUtxo(from.Address, change))
}

// Sort TxInput in lexicographical order based on TxHash + OutIndex
sort.Sort(txInSorter(in))

// Sort TxOutput in lexicographical order based on Value + LockScript and reset OutIndex
sort.Sort(txOutSorter(out))
resetOutIndex(out)

return NewTx(1, in, out, 0)
}

Expand All @@ -344,3 +361,9 @@ func (bc *Blockchain) CreateTransaction(from iotxaddress.Address, amount uint64,
func (bc *Blockchain) CreateRawTransaction(from iotxaddress.Address, amount uint64, to []*Payee) *Tx {
return bc.createTx(from, amount, to, true)
}

func resetOutIndex(out []*TxOutput) {
for i := 0; i < len(out); i++ {
out[i].outIndex = int32(i)
}
}
131 changes: 131 additions & 0 deletions blockchain/blockdao.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (c) 2018 IoTeX
// This is an alpha (internal) release and is not suitable for production. This source code is provided ‘as is’ and no
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
// License 2.0 that can be found in the LICENSE file.

package blockchain

import (
"github.com/iotexproject/iotex-core/common/service"
"github.com/iotexproject/iotex-core/common/utils"
"github.com/iotexproject/iotex-core/crypto"
"github.com/iotexproject/iotex-core/db"
"github.com/pkg/errors"
)

const (
blockNS = "blocks"
blockHashHeightMappingNS = "hash<->height"
)

var (
hashPrefix = []byte("hash.")
heightPrefix = []byte("height.")
topHeightKey = []byte("height")
)

type blockDAO struct {
service.CompositeService
kvstore db.KVStore
}

// newBlockDAO instantiates a block DAO
func newBlockDAO(kvstore db.KVStore) *blockDAO {
blockDAO := &blockDAO{kvstore: kvstore}
blockDAO.AddService(kvstore)
return blockDAO
}

// Start starts block DAO and initiates the top height if it doesn't exist
func (dao *blockDAO) Start() error {
err := dao.CompositeService.Start()
if err != nil {
return errors.Wrap(err, "failed to start child services")
}

// set init height value
err = dao.kvstore.PutIfNotExists(blockNS, topHeightKey, make([]byte, 4))
if err != nil {
return errors.Wrap(err, "failed to write initial value for top height")
}
return nil
}

// getBlockHash returns the block hash by height
func (dao *blockDAO) getBlockHash(height uint32) (crypto.Hash32B, error) {
key := append(heightPrefix, utils.Uint32ToBytes(height)...)
value, err := dao.kvstore.Get(blockHashHeightMappingNS, key)
var hash crypto.Hash32B
if err != nil {
return hash, errors.Wrap(err, "failed to get block hash")
}
copy(hash[:], value)
return hash, nil
}

// getBlockHeight returns the block height by hash
func (dao *blockDAO) getBlockHeight(hash crypto.Hash32B) (uint32, error) {
key := append(hashPrefix, hash[:]...)
value, err := dao.kvstore.Get(blockHashHeightMappingNS, key)
if err != nil {
return 0, errors.Wrap(err, "failed to get block height")
}
return utils.BytesToUint32(value), nil
}

// getBlock returns a block
func (dao *blockDAO) getBlock(hash crypto.Hash32B) (*Block, error) {
value, err := dao.kvstore.Get(blockNS, hash[:])
if err != nil {
return nil, errors.Wrap(err, "failed to get block")
}
blk := Block{}
err = blk.Deserialize(value)
if err != nil {
return nil, errors.Wrap(err, "failed to deserialize block")
}
return &blk, nil
}

// getBlockchainHeight returns the blockchain height
func (dao *blockDAO) getBlockchainHeight() (uint32, error) {
value, err := dao.kvstore.Get(blockNS, topHeightKey)
if err != nil {
return 0, errors.Wrap(err, "failed to get top height")
}
return utils.BytesToUint32(value), nil
}

// putBlock puts a block
func (dao *blockDAO) putBlock(blk *Block) error {
hash := blk.HashBlock()
height := utils.Uint32ToBytes(blk.Height())
serialized, err := blk.Serialize()
if err != nil {
return errors.Wrap(err, "failed to serialize block")
}
err = dao.kvstore.Put(blockNS, hash[:], serialized)
if err != nil {
return errors.Wrap(err, "failed to put block")
}
hashKey := append(hashPrefix, hash[:]...)
err = dao.kvstore.Put(blockHashHeightMappingNS, hashKey, height)
if err != nil {
return errors.Wrap(err, "failed to put hash -> height mapping")
}
heightKey := append(heightPrefix, height...)
err = dao.kvstore.Put(blockHashHeightMappingNS, heightKey, hash[:])
if err != nil {
return errors.Wrap(err, "failed to put height -> hash mapping")
}
value, err := dao.kvstore.Get(blockNS, topHeightKey)
topHeight := utils.BytesToUint32(value)
if blk.Height() > topHeight {
dao.kvstore.Put(blockNS, topHeightKey, height)
if err != nil {
return errors.Wrap(err, "failed to get top height")
}
}
return nil
}
112 changes: 112 additions & 0 deletions blockchain/blockdao_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright (c) 2018 IoTeX
// This is an alpha (internal) release and is not suitable for production. This source code is provided ‘as is’ and no
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
// License 2.0 that can be found in the LICENSE file.

package blockchain

import (
"hash/fnv"
"testing"

"github.com/iotexproject/iotex-core/crypto"
"github.com/iotexproject/iotex-core/db"
"github.com/iotexproject/iotex-core/test/testaddress"
"github.com/stretchr/testify/assert"
)

func TestBlockDAO(t *testing.T) {
getBlocks := func() []*Block {
amount := uint64(50 << 22)
// create testing transactions
cbtx1 := NewCoinbaseTx(testaddress.Addrinfo["alfa"].Address, amount, GenesisCoinbaseData)
cbtx2 := NewCoinbaseTx(testaddress.Addrinfo["bravo"].Address, amount, GenesisCoinbaseData)
cbtx3 := NewCoinbaseTx(testaddress.Addrinfo["charlie"].Address, amount, GenesisCoinbaseData)

hash1 := crypto.Hash32B{}
fnv.New32().Sum(hash1[:])
blk1 := NewBlock(0, 1, hash1, []*Tx{cbtx1})
hash2 := crypto.Hash32B{}
fnv.New32().Sum(hash2[:])
blk2 := NewBlock(0, 2, hash2, []*Tx{cbtx2})
hash3 := crypto.Hash32B{}
fnv.New32().Sum(hash3[:])
blk3 := NewBlock(0, 3, hash3, []*Tx{cbtx3})
return []*Block{blk1, blk2, blk3}
}

blks := getBlocks()
assert.Equal(t, 3, len(blks))

dao := newBlockDAO(db.NewMemKVStore())
err := dao.Init()
assert.Nil(t, err)
err = dao.Start()
assert.Nil(t, err)
defer func() {
err = dao.Stop()
assert.Nil(t, err)
}()

height, err := dao.getBlockchainHeight()
assert.Nil(t, err)
assert.Equal(t, uint32(0), height)

// block put order is 0 2 1
err = dao.putBlock(blks[0])
assert.Nil(t, err)
blk, err := dao.getBlock(blks[0].HashBlock())
assert.Nil(t, err)
assert.NotNil(t, blk)
assert.Equal(t, blks[0].Tranxs[0].Hash(), blk.Tranxs[0].Hash())
height, err = dao.getBlockchainHeight()
assert.Nil(t, err)
assert.Equal(t, uint32(1), height)

err = dao.putBlock(blks[2])
assert.Nil(t, err)
blk, err = dao.getBlock(blks[2].HashBlock())
assert.Nil(t, err)
assert.NotNil(t, blk)
assert.Equal(t, blks[2].Tranxs[0].Hash(), blk.Tranxs[0].Hash())
height, err = dao.getBlockchainHeight()
assert.Nil(t, err)
assert.Equal(t, uint32(3), height)

err = dao.putBlock(blks[1])
assert.Nil(t, err)
blk, err = dao.getBlock(blks[1].HashBlock())
assert.Nil(t, err)
assert.NotNil(t, blk)
assert.Equal(t, blks[1].Tranxs[0].Hash(), blk.Tranxs[0].Hash())
height, err = dao.getBlockchainHeight()
assert.Nil(t, err)
assert.Equal(t, uint32(3), height)

// test getting hash by height
hash, err := dao.getBlockHash(1)
assert.Nil(t, err)
assert.Equal(t, blks[0].HashBlock(), hash)

hash, err = dao.getBlockHash(2)
assert.Nil(t, err)
assert.Equal(t, blks[1].HashBlock(), hash)

hash, err = dao.getBlockHash(3)
assert.Nil(t, err)
assert.Equal(t, blks[2].HashBlock(), hash)

// test getting height by hash
height, err = dao.getBlockHeight(blks[0].HashBlock())
assert.Nil(t, err)
assert.Equal(t, blks[0].Height(), height)

height, err = dao.getBlockHeight(blks[1].HashBlock())
assert.Nil(t, err)
assert.Equal(t, blks[1].Height(), height)

height, err = dao.getBlockHeight(blks[2].HashBlock())
assert.Nil(t, err)
assert.Equal(t, blks[2].Height(), height)
}
Loading