From d28aa4c850128d665a4ee85911221e56ec7b5dfe Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Sat, 24 Jun 2023 15:38:07 +0530 Subject: [PATCH] add : execution features --- cmd/root.go | 10 ++-- config/config.go | 1 + consensus/pow/pow.go | 28 ++++++++-- core/blockchain.go | 59 +++++++++++++------- core/blockchain_test.go | 116 +++++++++++++++++++++++++++++++++++++++ executer/tx_processor.go | 32 ++++++++--- go.mod | 1 + go.sum | 2 + rpc/rpc_test.go | 14 +++-- txpool/txpool.go | 25 +++++---- types/block.go | 47 ++++++++++------ types/transaction.go | 23 +++++++- util/hash.go | 8 +-- util/signature.go | 6 +- util/signature_test.go | 1 + 15 files changed, 297 insertions(+), 76 deletions(-) create mode 100644 core/blockchain_test.go diff --git a/cmd/root.go b/cmd/root.go index e7d873d..e5444d2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -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" ) @@ -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()) } } diff --git a/config/config.go b/config/config.go index 7df1973..699f78d 100644 --- a/config/config.go +++ b/config/config.go @@ -22,6 +22,7 @@ type Config struct { RPCPort string SignerPrivateKey *ecdsa.PrivateKey Mine bool + BalanceAlloc map[string]*big.Int } func DefaultConfig() *Config { diff --git a/consensus/pow/pow.go b/consensus/pow/pow.go index 141b41a..3edbc34 100644 --- a/consensus/pow/pow.go +++ b/consensus/pow/pow.go @@ -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, } } @@ -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() @@ -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 } diff --git a/core/blockchain.go b/core/blockchain.go index 98cd627..7936f0d 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1,6 +1,7 @@ package core import ( + "fmt" "math/big" "sync" @@ -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() @@ -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") @@ -93,26 +101,21 @@ 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) @@ -120,9 +123,9 @@ func (bc *Blockchain) AddBlock(data []byte) { 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) @@ -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 } @@ -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 { diff --git a/core/blockchain_test.go b/core/blockchain_test.go new file mode 100644 index 0000000..c3d399d --- /dev/null +++ b/core/blockchain_test.go @@ -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), + } +} diff --git a/executer/tx_processor.go b/executer/tx_processor.go index 33c6bf5..2bc6b6a 100644 --- a/executer/tx_processor.go +++ b/executer/tx_processor.go @@ -48,7 +48,7 @@ func (txp *TxProcessor) IsValid(tx *types.Transaction) bool { nonce, err := txp.State.Get(dbstore.PrefixKey(dbstore.NonceKey, from.String())) if err != nil { - nonceBig = big.NewInt(0) + nonceBig = big.NewInt(-1) } else { nonceBig = new(big.Int).SetBytes(nonce) } @@ -81,12 +81,15 @@ func (txp *TxProcessor) ProcessTx(tx *types.Transaction) error { sendBalanceBig := new(big.Int).SetBytes(senderBalance) // Get receiver balance. + var receiverBalanceBig *big.Int + receiverBalance, err := txp.State.Get(dbstore.PrefixKey(dbstore.BalanceKey, to.String())) if err != nil { - return err - } + receiverBalanceBig = big.NewInt(0) + } else { + receiverBalanceBig = new(big.Int).SetBytes(receiverBalance) - receiverBalanceBig := new(big.Int).SetBytes(receiverBalance) + } // Update sender balance. sendBalanceBig.Sub(sendBalanceBig, value) @@ -97,17 +100,32 @@ func (txp *TxProcessor) ProcessTx(tx *types.Transaction) error { dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.BalanceKey, to.String())), receiverBalanceBig.Bytes()) // Update sender nonce. + var nonceBig *big.Int + nonce, err := txp.State.Get(dbstore.PrefixKey(dbstore.NonceKey, from.String())) if err != nil { - return err + nonceBig = big.NewInt(-1) + } else { + nonceBig = new(big.Int).SetBytes(nonce) } - nonceBig := new(big.Int).SetBytes(nonce) nonceBig.Add(nonceBig, big.NewInt(1)) dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.NonceKey, from.String())), nonceBig.Bytes()) + // Get Miner balance. + var minerBalanceBig *big.Int + + minerBalance, err := txp.State.Get(dbstore.PrefixKey(dbstore.BalanceKey, txp.Signer.String())) + if err != nil { + minerBalanceBig = big.NewInt(0) + } else { + minerBalanceBig = new(big.Int).SetBytes(minerBalance) + + } + // Update Miner Fee. - dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.BalanceKey, txp.Signer.String())), tx.Fee.Bytes()) + minerBalanceBig.Add(minerBalanceBig, tx.Fee) + dbBatch.Put([]byte(dbstore.PrefixKey(dbstore.BalanceKey, txp.Signer.String())), minerBalanceBig.Bytes()) // Commit batch to db err = txp.State.WriteBatch(dbBatch) diff --git a/go.mod b/go.mod index 0818769..cab6b6b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/0xsharma/compact-chain go 1.19 require ( + github.com/cbergoon/merkletree v0.2.0 github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.2 github.com/syndtr/goleveldb v1.0.0 diff --git a/go.sum b/go.sum index 98fd3d0..3a01140 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/cbergoon/merkletree v0.2.0 h1:Bttqr3OuoiZEo4ed1L7fTasHka9II+BF9fhBfbNEEoQ= +github.com/cbergoon/merkletree v0.2.0/go.mod h1:5c15eckUgiucMGDOCanvalj/yJnD+KAZj1qyJtRW5aM= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/rpc/rpc_test.go b/rpc/rpc_test.go index 1e15b07..27ec99b 100644 --- a/rpc/rpc_test.go +++ b/rpc/rpc_test.go @@ -20,13 +20,16 @@ func TestTxpoolRPC(t *testing.T) { t.Parallel() rpcPort := ":1711" + pkey := util.HexToPrivateKey("c3fc038a9abc0f483e2e1f8a0b4db676bce3eaebd7d9afc68e1e7e28ca8738a6") + ua := util.NewUnlockedAccount(pkey) txpool := txpool.NewTxPool(config.DefaultConfig().MinFee, nil) NewRPCServer(rpcPort, &RPCDomains{TxPool: txpool}) time.Sleep(2 * time.Second) // Send add Transacation request 1 - tx1 := newTransaction(t, []byte{0x01}, []byte{0x02}, "hello", 100, 100) + tx1 := newTransaction(t, []byte{0x01}, []byte{0x02}, "hello", 100, 100, 0) + tx1.Sign(ua) res, err := SendRpcRequest(t, "TxPool.AddTx_RPC", tx1, rpcPort) if err != nil { @@ -38,7 +41,7 @@ func TestTxpoolRPC(t *testing.T) { } // Send add Transacation request 2 - tx2 := newTransaction(t, []byte{0x02}, []byte{0x03}, "hello1", 101, 101) + tx2 := newTransaction(t, []byte{0x02}, []byte{0x03}, "hello1", 101, 101, 1) res, err = SendRpcRequest(t, "TxPool.AddTx_RPC", tx2, rpcPort) if err != nil { @@ -92,14 +95,15 @@ func SendRpcRequest(t *testing.T, method string, params interface{}, addr string return reply, nil } -func newTransaction(t *testing.T, from, to []byte, msg string, fee, value int64) *types.Transaction { +func newTransaction(t *testing.T, from, to []byte, msg string, fee, value int64, nonce int64) *types.Transaction { t.Helper() return &types.Transaction{ - From: *util.NewAddress(from), - To: *util.NewAddress(to), + From: *util.BytesToAddress(from), + To: *util.BytesToAddress(to), Msg: []byte(msg), Fee: big.NewInt(fee), Value: big.NewInt(value), + Nonce: big.NewInt(nonce), } } diff --git a/txpool/txpool.go b/txpool/txpool.go index 8b70d27..afa8c06 100644 --- a/txpool/txpool.go +++ b/txpool/txpool.go @@ -45,8 +45,8 @@ func (txp *TxPool) IsValid(tx *types.Transaction) bool { } from := tx.From - balance, err := txp.State.Get(dbstore.PrefixKey(dbstore.BalanceKey, from.String())) + balance, err := txp.State.Get(dbstore.PrefixKey(dbstore.BalanceKey, from.String())) if err != nil { return false } @@ -56,22 +56,25 @@ func (txp *TxPool) IsValid(tx *types.Transaction) bool { // Add Fee to Value totalValue := big.NewInt(0).Add(tx.Value, tx.Fee) + // nolint : gosimple if balanceBig.Cmp(totalValue) < 0 { return false } - var nonceBig *big.Int + // TODO : Write nonce logic in txpool and enable this check - 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) - } + // var nonceBig *big.Int - if big.NewInt(0).Sub(tx.Nonce, nonceBig).Cmp(big.NewInt(1)) != 0 { - return false - } + // nonce, err := txp.State.Get(dbstore.PrefixKey(dbstore.NonceKey, from.String())) + // if err != nil { + // nonceBig = big.NewInt(-1) + // } else { + // nonceBig = new(big.Int).SetBytes(nonce) + // } + + // if big.NewInt(0).Sub(tx.Nonce, nonceBig).Cmp(big.NewInt(1)) != 0 { + // return false + // } return true } diff --git a/types/block.go b/types/block.go index 0360cfc..f70a80e 100644 --- a/types/block.go +++ b/types/block.go @@ -6,15 +6,17 @@ import ( "math/big" "github.com/0xsharma/compact-chain/util" + "github.com/cbergoon/merkletree" ) // Block is the basic unit of the blockchain. type Block struct { - Number *big.Int - Hash *util.Hash - ParentHash *util.Hash - Data []byte - Nonce *big.Int + Number *big.Int + ParentHash *util.Hash + ExtraData []byte + Nonce *big.Int + Transactions []*Transaction + TxRoot *util.Hash } // NewBlock creates a new block and sets the hash. @@ -22,29 +24,45 @@ func NewBlock(number *big.Int, parentHash *util.Hash, data []byte) *Block { block := &Block{ Number: number, ParentHash: parentHash, - Data: data, + ExtraData: data, Nonce: big.NewInt(0), } - block.Hash = block.DeriveHash() - return block } // Clone returns a duplicate block from the source block. func (dst *Block) Clone(src *Block) { dst.Number = src.Number - dst.Hash = src.Hash dst.ParentHash = src.ParentHash - dst.Data = src.Data + dst.ExtraData = src.ExtraData dst.Nonce = src.Nonce } // DeriveHash derives the hash of the block. func (b *Block) DeriveHash() *util.Hash { - blockHash := bytes.Join([][]byte{b.Number.Bytes(), b.ParentHash.Bytes(), b.Data, b.Nonce.Bytes()}, []byte{}) + blockHash := bytes.Join([][]byte{b.Number.Bytes(), b.ParentHash.Bytes(), b.ExtraData, b.Nonce.Bytes(), b.TxRootHash().Bytes()}, []byte{}) + + return util.HashData(blockHash) +} - return util.NewHash(blockHash) +func (b *Block) TxRootHash() *util.Hash { + if len(b.Transactions) == 0 { + return util.HashData([]byte{}) + } else { + var list []merkletree.Content + for _, tx := range b.Transactions { + list = append(list, tx) + } + + //Create a new Merkle Tree from the list of Content + t, err := merkletree.NewTree(list) + if err != nil { + panic(err) + } + mr := t.MerkleRoot() + return util.ByteToHash(mr) + } } // SetNonce sets the nonce of the block. @@ -52,11 +70,6 @@ func (b *Block) SetNonce(n *big.Int) { b.Nonce = n } -// SetHash sets the hash of the block. -func (b *Block) SetHash(h *util.Hash) { - b.Hash = h -} - // Serialize serializes the block object into bytes. func (b *Block) Serialize() []byte { var res bytes.Buffer diff --git a/types/transaction.go b/types/transaction.go index 36c15fe..591f3b2 100644 --- a/types/transaction.go +++ b/types/transaction.go @@ -6,6 +6,7 @@ import ( "math/big" "github.com/0xsharma/compact-chain/util" + "github.com/cbergoon/merkletree" ) type Transactions []*Transaction @@ -28,7 +29,27 @@ type Transaction struct { func (tx *Transaction) Hash() *util.Hash { 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) + return util.HashData(txHash) +} + +func (tx *Transaction) CalculateHash() ([]byte, error) { + return tx.Hash().Bytes(), nil +} + +// Equals tests for equality of two Contents +func (tx *Transaction) Equals(other merkletree.Content) (bool, error) { + OtherFrom := other.(*Transaction).From + OtherTo := other.(*Transaction).To + OtherValue := other.(*Transaction).Value.Bytes() + OtherMsg := other.(*Transaction).Msg + OtherFee := other.(*Transaction).Fee.Bytes() + OtherNonce := other.(*Transaction).Nonce.Bytes() + OtherR := other.(*Transaction).R.Bytes() + OtherS := other.(*Transaction).S.Bytes() + + out := tx.From == OtherFrom && tx.To == OtherTo && bytes.Equal(tx.Value.Bytes(), OtherValue) && bytes.Equal(tx.Msg, OtherMsg) && bytes.Equal(tx.Fee.Bytes(), OtherFee) && bytes.Equal(tx.Nonce.Bytes(), OtherNonce) && bytes.Equal(tx.R.Bytes(), OtherR) && bytes.Equal(tx.S.Bytes(), OtherS) + + return out, nil } func (tx *Transaction) Serialize() []byte { diff --git a/util/hash.go b/util/hash.go index 6eaf9e3..5f17ade 100644 --- a/util/hash.go +++ b/util/hash.go @@ -47,7 +47,7 @@ func ByteToHash(b []byte) *Hash { } // NewHash creates a new sha256 hash from the given byte array. -func NewHash(b []byte) *Hash { +func HashData(b []byte) *Hash { sum := sha256.Sum256(b) hash := Hash{} @@ -57,11 +57,9 @@ func NewHash(b []byte) *Hash { } // NewAddress creates a new address from the given byte array. -func NewAddress(b []byte) *Address { - sum := sha256.Sum256(b) - +func BytesToAddress(b []byte) *Address { address := Address{} - copy(address[:], sum[:]) + copy(address[:], b[:]) return &address } diff --git a/util/signature.go b/util/signature.go index 839cefa..0c30586 100644 --- a/util/signature.go +++ b/util/signature.go @@ -24,7 +24,11 @@ func (ua *UnlockedAccount) PublicKey() *ecdsa.PublicKey { func PublicKeyToAddress(pubkey *ecdsa.PublicKey) *Address { data := elliptic.Marshal(pubkey, pubkey.X, pubkey.Y) - return NewAddress(data) + + address := Address{} + copy(address[:], data[2:]) + + return &address } func (ua *UnlockedAccount) Address() *Address { diff --git a/util/signature_test.go b/util/signature_test.go index 2938722..e043d3c 100644 --- a/util/signature_test.go +++ b/util/signature_test.go @@ -7,6 +7,7 @@ import ( func TestSigning(t *testing.T) { t.Parallel() + // Address = 0xa52c981eee8687b5e4afd69aa5006548c24d7685 pkey := HexToPrivateKey("c3fc038a9abc0f483e2e1f8a0b4db676bce3eaebd7d9afc68e1e7e28ca8738a6") ua := NewUnlockedAccount(pkey)