Skip to content

Commit

Permalink
Merge pull request #66 from elastos/release_v0.0.1
Browse files Browse the repository at this point in the history
Merge Release v0.0.1 into dev
  • Loading branch information
bocheng0000 authored Feb 20, 2019
2 parents 3ac9381 + 7cd30a6 commit 01280b3
Show file tree
Hide file tree
Showing 150 changed files with 9,598 additions and 7,689 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ idea/
build/
*.patch
.idea/
GoOnchain.iml
.DS_Store
*.orig
*.rej
Expand All @@ -22,5 +21,7 @@ Log
*.lock
*.json
vendor/
header/
DATA/
ela-wallet
service
14 changes: 10 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
BUILD=go build
VERSION := $(shell git describe --abbrev=4 --dirty --always --tags)

BUILD_SPV_CLI =$(BUILD) -ldflags "-X main.Version=$(VERSION)" -o ela-wallet client.go
BUILD_SPV_SERVICE =$(BUILD) -ldflags "-X main.Version=$(VERSION)" -o service main.go
BUILD_CLIENT =$(BUILD) -ldflags "-X main.Version=$(VERSION)" -o ela-wallet log.go config.go client.go
BUILD_SERVICE =$(BUILD) -ldflags "-X main.Version=$(VERSION)" -o service log.go config.go spvwallet.go main.go

all:
$(BUILD_SPV_CLI)
$(BUILD_SPV_SERVICE)
$(BUILD_CLIENT)
$(BUILD_SERVICE)

client:
$(BUILD_CLIENT)

service:
$(BUILD_SERVICE)
262 changes: 262 additions & 0 deletions blockchain/blockchain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
package blockchain

import (
"errors"
"math/big"
"sync"

"github.com/elastos/Elastos.ELA.SPV/database"
"github.com/elastos/Elastos.ELA.SPV/util"

"github.com/elastos/Elastos.ELA/common"
)

const (
MaxBlockLocatorHashes = 100
)

var zeroHash = common.Uint256{}

var OrphanBlockError = errors.New("block does not extend any known blocks")

/*
BlockChain is the database of blocks, also when a new transaction or block commit,
BlockChain will verify them with stored blocks.
*/
type BlockChain struct {
lock sync.RWMutex
db database.ChainStore
}

// NewBlockChain returns a new BlockChain instance.
func New(genesisHeader util.BlockHeader, db database.ChainStore) (*BlockChain, error) {
// Init genesis header
_, err := db.Headers().GetBest()
if err != nil {
storeHeader := &util.Header{BlockHeader: genesisHeader, TotalWork: new(big.Int)}
if err := db.Headers().Put(storeHeader, true); err != nil {
return nil, err
}
}

return &BlockChain{db: db}, nil
}

func (b *BlockChain) CommitBlock(block *util.Block) (newTip, reorg bool, newHeight, fps uint32, err error) {
b.lock.Lock()
defer b.lock.Unlock()
newTip = false
reorg = false
var header = &block.Header
var commonAncestor *util.Header
// Fetch our current best header from the db
bestHeader, err := b.db.Headers().GetBest()
if err != nil {
return false, false, 0, 0, err
}
tipHash := bestHeader.Hash()
var parentHeader *util.Header

// If the tip is also the parent of this header, then we can save a database read by skipping
// the lookup of the parent header. Otherwise (ophan?) we need to fetch the parent.
if hash := block.Previous(); hash.IsEqual(tipHash) {
parentHeader = bestHeader
} else {
parentHeader, err = b.db.Headers().GetPrevious(header)
if err != nil {
return false, false, 0, 0, OrphanBlockError
}
}
valid := b.checkHeader(header, parentHeader)
if !valid {
return false, false, 0, 0, nil
}
// If this block is already the tip, return
headerHash := header.Hash()
if tipHash.IsEqual(headerHash) {
return false, false, 0, 0, nil
}
// Add the work of this header to the total work stored at the previous header
cumulativeWork := new(big.Int).Add(parentHeader.TotalWork, CalcWork(header.Bits()))

// If the cumulative work is greater than the total work of our best header
// then we have a new best header. Update the chain tip and check for a reorg.
if cumulativeWork.Cmp(bestHeader.TotalWork) > 0 {
newTip = true

// If this header is not extending the previous best header then we have a reorg.
if !tipHash.IsEqual(parentHeader.Hash()) {
reorg = true
}
}

// At this point, we have done header check, so store it into database.
newHeight = parentHeader.Height + 1
header.Height = newHeight
header.TotalWork = cumulativeWork
fps, err = b.db.CommitBlock(block, newTip)
if err != nil {
return newTip, reorg, 0, 0, err
}

// If not meet a reorg, just return.
if !reorg {
return newTip, reorg, newHeight, fps, nil
}

// Find common ancestor of the fork chain, so we can rollback chain to the
// point where fork has happened.
commonAncestor, err = b.getCommonAncestor(header, bestHeader)
if err != nil {
// This should not happen, because we didn't store orphan blocks in
// database, all headers should be connected.
log.Errorf("Error calculating common ancestor: %s", err.Error())
return newTip, reorg, 0, 0, err
}

// Process block chain reorganize.
log.Infof("REORG!!! At block %d, Wiped out %d blocks",
bestHeader.Height, bestHeader.Height-commonAncestor.Height)
err = b.db.ProcessReorganize(commonAncestor, bestHeader, header)
if err != nil {
return newTip, reorg, 0, 0, err
}
return newTip, reorg, newHeight, fps, nil
}

func (b *BlockChain) checkHeader(header *util.Header, prevHeader *util.Header) bool {
// Get hash of n-1 header
prevHash := prevHeader.Hash()
height := prevHeader.Height

// Check if headers link together. That whole 'blockchain' thing.
if prevHash.IsEqual(header.Previous()) == false {
log.Errorf("Headers %d and %d don't link.\n", height, height+1)
return false
}

// Check if there's a valid proof of work. That whole "Bitcoin" thing.
if !checkProofOfWork(*header) {
log.Debugf("Block %d bad proof of work.\n", height+1)
return false
}

return true // it must have worked if there's no errors and got to the end.
}

// Returns last header before reorg point
func (b *BlockChain) getCommonAncestor(bestHeader, prevTip *util.Header) (*util.Header, error) {
var err error
rollback := func(parent *util.Header, n int) (*util.Header, error) {
for i := 0; i < n; i++ {
parent, err = b.db.Headers().GetPrevious(parent)
if err != nil {
return parent, err
}
}
return parent, nil
}

majority := bestHeader
minority := prevTip
if bestHeader.Height > prevTip.Height {
majority, err = rollback(majority, int(bestHeader.Height-prevTip.Height))
if err != nil {
return nil, err
}
} else if prevTip.Height > bestHeader.Height {
minority, err = rollback(minority, int(prevTip.Height-bestHeader.Height))
if err != nil {
return nil, err
}
}

for {
majorityHash := majority.Hash()
minorityHash := minority.Hash()
if majorityHash.IsEqual(minorityHash) {
return majority, nil
}
majority, err = b.db.Headers().GetPrevious(majority)
if err != nil {
return nil, err
}
minority, err = b.db.Headers().GetPrevious(minority)
if err != nil {
return nil, err
}
}
}

// HaveBlock returns whether or not the chain instance has the block represented
// by the passed hash. This includes checking the various places a block can
// be like part of the main chain, on a side chain, or in the orphan pool.
//
// This function is safe for concurrent access.
func (b *BlockChain) HaveBlock(hash *common.Uint256) bool {
header, _ := b.db.Headers().Get(hash)
return header != nil
}

// LatestBlockLocator returns a block locator for current last block,
// which is a array of block hashes stored in blockchain
func (b *BlockChain) LatestBlockLocator() []*common.Uint256 {
b.lock.RLock()
defer b.lock.RUnlock()

var ret []*common.Uint256
parent, err := b.db.Headers().GetBest()
if err != nil { // No headers stored return empty locator
return ret
}

rollback := func(parent *util.Header, n int) (*util.Header, error) {
for i := 0; i < n; i++ {
parent, err = b.db.Headers().GetPrevious(parent)
if err != nil {
return parent, err
}
}
return parent, nil
}

step := 1
start := 0
for {
if start >= 9 {
step *= 2
start = 0
}
hash := parent.Hash()
ret = append(ret, &hash)
if len(ret) >= MaxBlockLocatorHashes {
break
}
parent, err = rollback(parent, step)
if err != nil {
break
}
start += 1
}
return ret
}

// BestHeight return current best chain height.
func (b *BlockChain) BestHeight() uint32 {
best, err := b.db.Headers().GetBest()
if err != nil {
return 0
}
return best.Height
}

// Close the blockchain
func (b *BlockChain) Clear() error {
return b.db.Clear()
}

// Close the blockchain
func (b *BlockChain) Close() error {
b.lock.Lock()
return b.db.Close()
}
86 changes: 86 additions & 0 deletions blockchain/difficulty.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package blockchain

import (
"math/big"

"github.com/elastos/Elastos.ELA.SPV/util"
"github.com/elastos/Elastos.ELA/common"
)

var PowLimit = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 255), big.NewInt(1))

func CalcWork(bits uint32) *big.Int {
// Return a work value of zero if the passed difficulty bits represent
// a negative number. Note this should not happen in practice with valid
// blocks, but an invalid block could trigger it.
difficultyNum := CompactToBig(bits)
if difficultyNum.Sign() <= 0 {
return big.NewInt(0)
}

// (1 << 256) / (difficultyNum + 1)
denominator := new(big.Int).Add(difficultyNum, big.NewInt(1))
return new(big.Int).Div(new(big.Int).Lsh(big.NewInt(1), 256), denominator)
}

func checkProofOfWork(header util.Header) bool {
// The target difficulty must be larger than zero.
target := CompactToBig(header.Bits())
if target.Sign() <= 0 {
return false
}

// The target difficulty must be less than the maximum allowed.
if target.Cmp(PowLimit) > 0 {
return false
}

// The block hash must be less than the claimed target.
hash := header.PowHash()
hashNum := HashToBig(&hash)
if hashNum.Cmp(target) > 0 {
return false
}

return true
}

func HashToBig(hash *common.Uint256) *big.Int {
// A Hash is in little-endian, but the big package wants the bytes in
// big-endian, so reverse them.
buf := *hash
blen := len(buf)
for i := 0; i < blen/2; i++ {
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
}

return new(big.Int).SetBytes(buf[:])
}

func CompactToBig(compact uint32) *big.Int {
// Extract the mantissa, sign bit, and exponent.
mantissa := compact & 0x007fffff
isNegative := compact&0x00800000 != 0
exponent := uint(compact >> 24)

// Since the base for the exponent is 256, the exponent can be treated
// as the number of bytes to represent the full 256-bit number. So,
// treat the exponent as the number of bytes and shift the mantissa
// right or left accordingly. This is equivalent to:
// N = mantissa * 256^(exponent-3)
var bn *big.Int
if exponent <= 3 {
mantissa >>= 8 * (3 - exponent)
bn = big.NewInt(int64(mantissa))
} else {
bn = big.NewInt(int64(mantissa))
bn.Lsh(bn, 8*(exponent-3))
}

// Make it negative if the sign bit is set.
if isNegative {
bn = bn.Neg(bn)
}

return bn
}
Loading

0 comments on commit 01280b3

Please sign in to comment.