Skip to content

Commit

Permalink
add : execution features
Browse files Browse the repository at this point in the history
  • Loading branch information
0xsharma committed Jun 24, 2023
1 parent 33df2e2 commit d28aa4c
Show file tree
Hide file tree
Showing 15 changed files with 297 additions and 76 deletions.
10 changes: 6 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/0xsharma/compact-chain/config"
"github.com/0xsharma/compact-chain/core"
"github.com/0xsharma/compact-chain/types"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -57,20 +58,21 @@ func demoBlockchain() {
StateDBDir: stateDbPath,
MinFee: big.NewInt(100),
RPCPort: ":6999",
BalanceAlloc: map[string]*big.Int{},
}

chain := core.NewBlockchain(config)
if chain.LastBlock.Number.Int64() == 0 {
fmt.Println("Number : ", chain.LastBlock.Number, "Hash : ", chain.LastBlock.Hash.String())
fmt.Println("Number : ", chain.LastBlock.Number, "Hash : ", chain.LastBlock.DeriveHash().String())
} else {
fmt.Println("LastNumber : ", chain.LastBlock.Number, "LastHash : ", chain.LastBlock.Hash.String())
fmt.Println("LastNumber : ", chain.LastBlock.Number, "LastHash : ", chain.LastBlock.DeriveHash().String())
}

lastNumber := chain.LastBlock.Number

for i := lastNumber.Int64() + 1; i <= lastNumber.Int64()+10; i++ {
time.Sleep(2 * time.Second)
chain.AddBlock([]byte(fmt.Sprintf("Block %d", i)))
fmt.Println("Number : ", chain.LastBlock.Number, "Hash : ", chain.LastBlock.Hash.String())
chain.AddBlock([]byte(fmt.Sprintf("Block %d", i)), []*types.Transaction{})
fmt.Println("Number : ", chain.LastBlock.Number, "Hash : ", chain.LastBlock.DeriveHash().String())
}
}
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Config struct {
RPCPort string
SignerPrivateKey *ecdsa.PrivateKey
Mine bool
BalanceAlloc map[string]*big.Int
}

func DefaultConfig() *Config {
Expand Down
28 changes: 24 additions & 4 deletions consensus/pow/pow.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package pow

import (
"fmt"
"math/big"

"github.com/0xsharma/compact-chain/executer"
"github.com/0xsharma/compact-chain/types"
)

type POW struct {
difficulty *big.Int
difficulty *big.Int
TxProcessor *executer.TxProcessor
}

// NewPOW creates a new proof of work consensus.
func NewPOW(difficulty int) *POW {
func NewPOW(difficulty int, txProcessor *executer.TxProcessor) *POW {
return &POW{
difficulty: big.NewInt(int64(difficulty)),
difficulty: big.NewInt(int64(difficulty)),
TxProcessor: txProcessor,
}
}

Expand All @@ -37,6 +41,23 @@ func (c *POW) GetTarget() *big.Int {
func (c *POW) Mine(b *types.Block) *types.Block {
nonce := big.NewInt(0)

validTxs := []*types.Transaction{}

for _, tx := range b.Transactions {
if c.TxProcessor.IsValid(tx) {
err := c.TxProcessor.ProcessTx(tx)
if err == nil {
validTxs = append(validTxs, tx)
} else {
fmt.Println("Failed to execute Tx :", "tx :", tx, "error", err)
}
} else {
fmt.Println("Invalid Tx :", "tx :", tx)
}
}

b.Transactions = validTxs

for {
b.SetNonce(nonce)
hash := b.DeriveHash()
Expand All @@ -45,7 +66,6 @@ func (c *POW) Mine(b *types.Block) *types.Block {
hashBig := new(big.Int).SetBytes(hashBytes)

if hashBig.Cmp(c.GetTarget()) < 0 {
b.SetHash(hash)
break
}

Expand Down
59 changes: 38 additions & 21 deletions core/blockchain.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package core

import (
"fmt"
"math/big"
"sync"

Expand Down Expand Up @@ -47,8 +48,8 @@ func NewBlockchain(c *config.Config) *Blockchain {

lastBlockBytes, err := db.Get(dbstore.LastHashKey)
if err != nil {
genesis = CreateGenesisBlock()
lastHash := genesis.Hash
genesis = CreateGenesisBlock(c.BalanceAlloc, stateDB)
lastHash := genesis.DeriveHash()

dbBatch := db.NewBatch()

Expand All @@ -73,14 +74,21 @@ func NewBlockchain(c *config.Config) *Blockchain {
lastBlock = types.DeserializeBlock(lastBlockBytes)
}

var txProcessor *executer.TxProcessor

if c.Mine && c.SignerPrivateKey != nil {
p := c.SignerPrivateKey.PublicKey
txProcessor = executer.NewTxProcessor(stateDB, c.MinFee, util.PublicKeyToAddress(&p))
}

var consensus consensus.Consensus

switch c.ConsensusName {
case "pow":
if c.ConsensusDifficulty > 0 {
consensus = pow.NewPOW(c.ConsensusDifficulty)
consensus = pow.NewPOW(c.ConsensusDifficulty, txProcessor)
} else {
consensus = pow.NewPOW(defaultConsensusDifficulty)
consensus = pow.NewPOW(defaultConsensusDifficulty, txProcessor)
}
default:
panic("Invalid consensus algorithm")
Expand All @@ -93,36 +101,31 @@ func NewBlockchain(c *config.Config) *Blockchain {
}
rpcServer := rpc.NewRPCServer(c.RPCPort, rpcDomains)

var txProcessor *executer.TxProcessor

if c.Mine && c.SignerPrivateKey != nil {
p := c.SignerPrivateKey.PublicKey
txProcessor = executer.NewTxProcessor(stateDB, c.MinFee, util.PublicKeyToAddress(&p))
}

bc := &Blockchain{LastBlock: lastBlock, Consensus: consensus, Mutex: new(sync.RWMutex), Db: db, LastHash: lastBlock.Hash, StateDB: stateDB, Txpool: bc_txpool, TxProcessor: txProcessor, RPCServer: rpcServer}
bc := &Blockchain{LastBlock: lastBlock, Consensus: consensus, Mutex: new(sync.RWMutex), Db: db, LastHash: lastBlock.DeriveHash(), StateDB: stateDB, Txpool: bc_txpool, TxProcessor: txProcessor, RPCServer: rpcServer}

return bc
}

// AddBlock mines and adds a new block to the blockchain.
func (bc *Blockchain) AddBlock(data []byte) {
func (bc *Blockchain) AddBlock(data []byte, txs []*types.Transaction) {
bc.Mutex.Lock()
defer bc.Mutex.Unlock()

prevBlock := bc.LastBlock
blockNumber := big.NewInt(0).Add(prevBlock.Number, big.NewInt(1))
block := types.NewBlock(blockNumber, prevBlock.Hash, data)
block := types.NewBlock(blockNumber, prevBlock.DeriveHash(), data)

block.Transactions = txs

// Mine block
minedBlock := bc.Consensus.Mine(block)

dbBatch := bc.Db.NewBatch()

// Batch write to db
dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.HashesKey, minedBlock.Hash.String())), minedBlock.Serialize())
dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.BlockNumberKey, minedBlock.Number.String())), minedBlock.Hash.Bytes())
dbBatch.Put([]byte(dbstore.LastHashKey), minedBlock.Hash.Bytes())
dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.HashesKey, minedBlock.DeriveHash().String())), minedBlock.Serialize())
dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.BlockNumberKey, minedBlock.Number.String())), minedBlock.DeriveHash().Bytes())
dbBatch.Put([]byte(dbstore.LastHashKey), minedBlock.DeriveHash().Bytes())

// Commit batch to db
err := bc.Db.WriteBatch(dbBatch)
Expand All @@ -133,9 +136,23 @@ func (bc *Blockchain) AddBlock(data []byte) {
bc.LastBlock = minedBlock
}

// Mine the genesis block
func CreateGenesisBlock() *types.Block {
genesis := types.NewBlock(big.NewInt(0), util.NewHash([]byte("0x0")), []byte("Genesis Block"))
// Mine the genesis block and do initial balance allocation.
func CreateGenesisBlock(balanceAlloc map[string]*big.Int, db *dbstore.DB) *types.Block {
genesis := types.NewBlock(big.NewInt(0), util.HashData([]byte("0x0")), []byte("Genesis Block"))

dbBatch := db.NewBatch()

for address, balance := range balanceAlloc {
fmt.Println("Allocating", balance, "to", address)
dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.BalanceKey, address)), balance.Bytes())
}

// Commit batch to db
err := db.WriteBatch(dbBatch)
if err != nil {
panic(err)
}

return genesis
}

Expand All @@ -154,7 +171,7 @@ func (bc *Blockchain) GetBlockByNumber(b *big.Int) (*types.Block, error) {
return nil, err
}

hash := util.NewHash(hashBytes)
hash := util.HashData(hashBytes)
block, err := bc.GetBlockByHash(hash)

if err != nil {
Expand Down
116 changes: 116 additions & 0 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package core

import (
"fmt"
"math/big"
"testing"
"time"

"github.com/0xsharma/compact-chain/config"
"github.com/0xsharma/compact-chain/dbstore"
"github.com/0xsharma/compact-chain/types"
"github.com/0xsharma/compact-chain/util"
"github.com/stretchr/testify/assert"
)

func TestBlockchainStateBalance(t *testing.T) {
t.Parallel()

to1 := util.BytesToAddress([]byte{0x01})
to2 := util.BytesToAddress([]byte{0x02})

config := &config.Config{
ConsensusDifficulty: 16,
ConsensusName: "pow",
DBDir: t.TempDir(),
StateDBDir: t.TempDir(),
MinFee: big.NewInt(100),
RPCPort: ":6999",
BalanceAlloc: map[string]*big.Int{
"0xa52c981eee8687b5e4afd69aa5006548c24d7685": big.NewInt(1000000000000000000), // Allocating funds to 0xa52c981eee8687b5e4afd69aa5006548c24d7685
},
Mine: true,
SignerPrivateKey: util.HexToPrivateKey("e3ddd0f483e2ef1f8a0b4db676bce3eaebd7d9afc68e1e7e28ca8738a6"), // Address = 0x93a63fc45341fc02ac9cce62cc5aeb5c5799403e
}

chain := NewBlockchain(config)
if chain.LastBlock.Number.Int64() == 0 {
fmt.Println("Number : ", chain.LastBlock.Number, "Hash : ", chain.LastBlock.DeriveHash().String())
} else {
fmt.Println("LastNumber : ", chain.LastBlock.Number, "LastHash : ", chain.LastBlock.DeriveHash().String())
}

pkey := util.HexToPrivateKey("c3fc038a9abc0f483e2e1f8a0b4db676bce3eaebd7d9afc68e1e7e28ca8738a6") // Address = 0xa52c981eee8687b5e4afd69aa5006548c24d7685
ua := util.NewUnlockedAccount(pkey)

// tx1
tx1 := newTransaction(t, ua.Address().Bytes(), to1.Bytes(), "hello", 200, 1000, 0)
tx1.Sign(ua)

// tx2
tx2 := newTransaction(t, ua.Address().Bytes(), to2.Bytes(), "hello", 100, 2000, 1)
tx2.Sign(ua)

// Add tx1 and tx2 to txpool
chain.Txpool.AddTxs([]*types.Transaction{tx1, tx2})

// Add block 1
time.Sleep(2 * time.Second)
chain.AddBlock([]byte(fmt.Sprintf("Block %d", chain.LastBlock.Number.Int64()+1)), []*types.Transaction{})

fmt.Println("Number : ", chain.LastBlock.Number, "Hash : ", chain.LastBlock.DeriveHash().String(), "TxCount", len(chain.LastBlock.Transactions))

//Add block 2
time.Sleep(2 * time.Second)
chain.AddBlock([]byte(fmt.Sprintf("Block %d", chain.LastBlock.Number.Int64()+1)), chain.Txpool.GetTxs())

fmt.Println("Number : ", chain.LastBlock.Number, "Hash : ", chain.LastBlock.DeriveHash().String(), "TxCount", len(chain.LastBlock.Transactions))

// Assertions
balanceSender, err := chain.StateDB.Get(dbstore.PrefixKey(dbstore.BalanceKey, ua.Address().String()))
if err != nil {
t.Fatal(err)
}

balanceSenderBig := new(big.Int).SetBytes(balanceSender)
assert.Equal(t, big.NewInt(999999999999997000), balanceSenderBig)

balanceTo1, err := chain.StateDB.Get(dbstore.PrefixKey(dbstore.BalanceKey, to1.String()))
if err != nil {
t.Fatal(err)
}

balanceTo1Big := new(big.Int).SetBytes(balanceTo1)
assert.Equal(t, big.NewInt(1000), balanceTo1Big)

balanceTo2, err := chain.StateDB.Get(dbstore.PrefixKey(dbstore.BalanceKey, to2.String()))
if err != nil {
t.Fatal(err)
}

balanceTo2Big := new(big.Int).SetBytes(balanceTo2)
assert.Equal(t, big.NewInt(2000), balanceTo2Big)

addressMiner := util.NewUnlockedAccount(config.SignerPrivateKey)

balanceMiner, err := chain.StateDB.Get(dbstore.PrefixKey(dbstore.BalanceKey, addressMiner.Address().String()))
if err != nil {
t.Fatal(err)
}

balanceMinerBig := new(big.Int).SetBytes(balanceMiner)
assert.Equal(t, big.NewInt(300), balanceMinerBig)
}

func newTransaction(t *testing.T, from, to []byte, msg string, fee, value int64, nonce int64) *types.Transaction {
t.Helper()

return &types.Transaction{
From: *util.BytesToAddress(from),
To: *util.BytesToAddress(to),
Msg: []byte(msg),
Fee: big.NewInt(fee),
Value: big.NewInt(value),
Nonce: big.NewInt(nonce),
}
}
Loading

0 comments on commit d28aa4c

Please sign in to comment.