Skip to content

Commit

Permalink
init : executer, signing, state
Browse files Browse the repository at this point in the history
  • Loading branch information
0xsharma committed Jun 18, 2023
1 parent cb7660a commit 73a7fcd
Show file tree
Hide file tree
Showing 10 changed files with 337 additions and 19 deletions.
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"crypto/ecdsa"
"math/big"
"os"
)
Expand All @@ -19,6 +20,8 @@ type Config struct {
StateDBDir string
MinFee *big.Int
RPCPort string
SignerPrivateKey *ecdsa.PrivateKey
Mine bool
}

func DefaultConfig() *Config {
Expand Down
34 changes: 21 additions & 13 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ import (
"github.com/0xsharma/compact-chain/consensus"
"github.com/0xsharma/compact-chain/consensus/pow"
"github.com/0xsharma/compact-chain/dbstore"
"github.com/0xsharma/compact-chain/executer"
"github.com/0xsharma/compact-chain/rpc"
"github.com/0xsharma/compact-chain/txpool"
"github.com/0xsharma/compact-chain/types"
"github.com/0xsharma/compact-chain/util"
)

type Blockchain struct {
LastBlock *types.Block
Consensus consensus.Consensus
Mutex *sync.RWMutex
LastHash *util.Hash
Db *dbstore.DB
StateDB *dbstore.DB
Txpool *txpool.TxPool
RPCServer *rpc.RPCServer
LastBlock *types.Block
Consensus consensus.Consensus
Mutex *sync.RWMutex
LastHash *util.Hash
Db *dbstore.DB
StateDB *dbstore.DB
Txpool *txpool.TxPool
RPCServer *rpc.RPCServer
TxProcessor *executer.TxProcessor
Signer *util.Address
}

// defaultConsensusDifficulty is the default difficulty for the proof of work consensus.
Expand Down Expand Up @@ -83,15 +86,20 @@ func NewBlockchain(c *config.Config) *Blockchain {
panic("Invalid consensus algorithm")
}

bc_txpool := txpool.NewTxPool(c.MinFee)

bc := &Blockchain{LastBlock: lastBlock, Consensus: consensus, Mutex: new(sync.RWMutex), Db: db, LastHash: lastBlock.Hash, StateDB: stateDB, Txpool: bc_txpool}
bc_txpool := txpool.NewTxPool(c.MinFee, stateDB)

rpcDomains := &rpc.RPCDomains{
TxPool: bc.Txpool,
TxPool: bc_txpool,
}
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.RPCServer = rpc.NewRPCServer(c.RPCPort, rpcDomains)
bc := &Blockchain{LastBlock: lastBlock, Consensus: consensus, Mutex: new(sync.RWMutex), Db: db, LastHash: lastBlock.Hash, StateDB: stateDB, Txpool: bc_txpool, TxProcessor: txProcessor, RPCServer: rpcServer}

return bc
}
Expand Down
2 changes: 2 additions & 0 deletions dbstore/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const (
LastHashKey = "lh" // Last hash key ( lastHash -> hash)
HashesKey = "hs" // Hashes key (hash->block)
BlockNumberKey = "bn" // Block number key (blockNumber -> hash)
BalanceKey = "bl" // Balance key (address -> balance)
NonceKey = "nc" // Nonce key (address -> nonce)
)

// PrefixKey prefixes a string with another string.
Expand Down
114 changes: 114 additions & 0 deletions executer/tx_processor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package executer

import (
"errors"
"math/big"

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

var (
ErrInvalidTransaction = errors.New("invalid transaction")
)

type TxProcessor struct {
MinFee *big.Int
State *dbstore.DB
Signer *util.Address
}

func NewTxProcessor(state *dbstore.DB, minFee *big.Int, signer *util.Address) *TxProcessor {
return &TxProcessor{
MinFee: minFee,
State: state,
Signer: signer,
}
}

func (txp *TxProcessor) IsValid(tx *types.Transaction) bool {
from := tx.From
balance, err := txp.State.Get(dbstore.PrefixKey(dbstore.BalanceKey, from.String()))
if err != nil {
return false
}

balanceBig := new(big.Int).SetBytes(balance)

// Add Fee to Value
totalValue := big.NewInt(0).Add(tx.Value, tx.Fee)

if balanceBig.Cmp(totalValue) < 0 {
return false
}

var nonceBig *big.Int
nonce, err := txp.State.Get(dbstore.PrefixKey(dbstore.NonceKey, from.String()))
if err != nil {
nonceBig = big.NewInt(0)
} else {
nonceBig = new(big.Int).SetBytes(nonce)
}

if big.NewInt(0).Sub(tx.Nonce, nonceBig).Cmp(big.NewInt(1)) != 0 {
return false
}

return true
}

// ProcessTx processes a transaction and returns the transaction fee.
func (txp *TxProcessor) ProcessTx(tx *types.Transaction) error {
if !txp.IsValid(tx) {
return ErrInvalidTransaction
}

from := tx.From
to := tx.To
value := tx.Value

dbBatch := txp.State.NewBatch()

// Get sender balance.
senderBalance, err := txp.State.Get(dbstore.PrefixKey(dbstore.BalanceKey, from.String()))
if err != nil {
return err
}
sendBalanceBig := new(big.Int).SetBytes(senderBalance)

// Get receiver balance.
receiverBalance, err := txp.State.Get(dbstore.PrefixKey(dbstore.BalanceKey, to.String()))
if err != nil {
return err
}
receiverBalanceBig := new(big.Int).SetBytes(receiverBalance)

// Update sender balance.
sendBalanceBig.Sub(sendBalanceBig, value)
dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.BalanceKey, from.String())), sendBalanceBig.Bytes())

// Update receiver balance.
receiverBalanceBig.Add(receiverBalanceBig, value)
dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.BalanceKey, to.String())), receiverBalanceBig.Bytes())

// Update sender nonce.
nonce, err := txp.State.Get(dbstore.PrefixKey(dbstore.NonceKey, from.String()))
if err != nil {
return err
}
nonceBig := new(big.Int).SetBytes(nonce)
nonceBig.Add(nonceBig, big.NewInt(1))
dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.NonceKey, from.String())), nonceBig.Bytes())

// Update Miner Fee.
dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.BalanceKey, txp.Signer.String())), tx.Fee.Bytes())

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

return nil
}
2 changes: 1 addition & 1 deletion rpc/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestTxpoolRPC(t *testing.T) {

rpcPort := ":1711"

txpool := txpool.NewTxPool(config.DefaultConfig().MinFee)
txpool := txpool.NewTxPool(config.DefaultConfig().MinFee, nil)
NewRPCServer(rpcPort, &RPCDomains{TxPool: txpool})
time.Sleep(2 * time.Second)

Expand Down
73 changes: 70 additions & 3 deletions txpool/txpool.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,84 @@
package txpool

import (
"errors"
"fmt"
"math/big"
"sort"

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

var (
ErrInvalidTransaction = errors.New("invalid transaction")
)

type TxPool struct {
MinFee *big.Int
State *dbstore.DB
Transactions []*types.Transaction
}

func NewTxPool(minFee *big.Int) *TxPool {
return &TxPool{}
func NewTxPool(minFee *big.Int, db *dbstore.DB) *TxPool {
if db == nil {
fmt.Println("DB is nil, running in mock mode for tests")
}

return &TxPool{
MinFee: minFee,
State: db,
}
}

func intToBool(n int) bool {
return n >= 0
}

func (txp *TxPool) IsValid(tx *types.Transaction) bool {
if txp.State == nil {
return true
}

if tx.Fee.Cmp(txp.MinFee) < 0 {
return false
}

from := tx.From
balance, err := txp.State.Get(dbstore.PrefixKey(dbstore.BalanceKey, from.String()))
if err != nil {
return false
}

balanceBig := new(big.Int).SetBytes(balance)

// Add Fee to Value
totalValue := big.NewInt(0).Add(tx.Value, tx.Fee)

if balanceBig.Cmp(totalValue) < 0 {
return false
}

var nonceBig *big.Int
nonce, err := txp.State.Get(dbstore.PrefixKey(dbstore.NonceKey, from.String()))
if err != nil {
nonceBig = big.NewInt(0)
} else {
nonceBig = new(big.Int).SetBytes(nonce)
}

if big.NewInt(0).Sub(tx.Nonce, nonceBig).Cmp(big.NewInt(1)) != 0 {
return false
}

return true
}

func (tp *TxPool) AddTx(tx *types.Transaction) {
if !tp.IsValid(tx) {
return
}

txs := append(tp.Transactions, tx)
sort.Slice(txs, func(i, j int) bool {
return intToBool(txs[i].Fee.Cmp(txs[j].Fee))
Expand All @@ -29,7 +88,15 @@ func (tp *TxPool) AddTx(tx *types.Transaction) {
}

func (tp *TxPool) AddTxs(txs []*types.Transaction) {
txpoolTxs := append(tp.Transactions, txs...)
validTxs := make([]*types.Transaction, len(txs))

for _, tx := range txs {
if tp.IsValid(tx) {
validTxs = append(validTxs, tx)
}
}

txpoolTxs := append(tp.Transactions, validTxs...)
sort.Slice(txpoolTxs, func(i, j int) bool {
return intToBool(txpoolTxs[i].Fee.Cmp(txpoolTxs[j].Fee))
})
Expand Down
2 changes: 1 addition & 1 deletion txpool/txpool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func NewRandomTx(t *testing.T) *types.Transaction {
func TestTxpoolAdd(t *testing.T) {
t.Parallel()

txpool := NewTxPool(big.NewInt(0))
txpool := NewTxPool(big.NewInt(0), nil)

for i := 0; i < 100; i++ {
txpool.AddTx(NewRandomTx(t))
Expand Down
42 changes: 41 additions & 1 deletion types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"bytes"
"encoding/gob"
"math/big"

"github.com/0xsharma/compact-chain/util"
Expand All @@ -19,10 +20,49 @@ type Transaction struct {
Value *big.Int
Msg []byte
Fee *big.Int
Nonce *big.Int
R *big.Int
S *big.Int
}

func (tx *Transaction) Hash() *util.Hash {
txHash := bytes.Join([][]byte{tx.From.Bytes(), tx.To.Bytes(), tx.Value.Bytes(), tx.Msg}, []byte{})
txHash := bytes.Join([][]byte{tx.From.Bytes(), tx.To.Bytes(), tx.Value.Bytes(), tx.Msg, tx.Fee.Bytes(), tx.Nonce.Bytes()}, []byte{})

return util.NewHash(txHash)
}

func (tx *Transaction) Serialize() []byte {
var res bytes.Buffer
encoder := gob.NewEncoder(&res)

err := encoder.Encode(tx)

if err != nil {
panic(err)
}

return res.Bytes()
}

func DeserializeTransaction(data []byte) *Transaction {
var tx Transaction

decoder := gob.NewDecoder(bytes.NewReader(data))

err := decoder.Decode(&tx)
if err != nil {
panic(err)
}

return &tx
}

func (tx *Transaction) Sign(ua *util.UnlockedAccount) {
r, s, err := ua.Sign(tx.Hash().Bytes())
if err != nil {
panic(err)
}

tx.R = r
tx.S = s
}
Loading

0 comments on commit 73a7fcd

Please sign in to comment.