diff --git a/blockchain/block.go b/blockchain/block.go index 969d25f..6ca7178 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -50,13 +50,16 @@ func (b *Block) Hash() string { } func (b *Block) AddTransactionToTheBlock(txn *Transaction) { - isValid := txn.VerifyTransaction() + if txn.Status == constants.STATUS_PENDING { + isValid := txn.VerifyTransaction() - if isValid { - txn.Status = constants.STATUS_SUCCESS - } else { - txn.Status = constants.STATUS_FAILED - } + if isValid { + txn.Status = constants.STATUS_SUCCESS + } else { + txn.Status = constants.STATUS_FAILED + } + + b.Transactions = append(b.Transactions, txn) - b.Transactions = append(b.Transactions, txn) + } } diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index bda00fa..d0e6e04 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -11,6 +11,12 @@ import ( type Blockchain struct { TransactionPool []*Transaction `json:"transaction_pool"` Blocks []*Block `json:"block_chain"` + WalletIndex *WalletIndex +} + +func (bc *Blockchain) Airdrop(address string) { + txn := NewTransaction(constants.BLOCKCHAIN_AIRDROP_ADDRESS, address, constants.AIRDROP_AMOUNT, []byte{}) + bc.AddTransactionToTransactionPool(txn) } func NewBlockchain(genesisBlock *Block) *Blockchain { @@ -19,14 +25,20 @@ func NewBlockchain(genesisBlock *Block) *Blockchain { if err != nil { bc = new(Blockchain) + bc.TransactionPool = []*Transaction{} + bc.Blocks = append(bc.Blocks, genesisBlock) + bc.WalletIndex = NewWalletIndex() + err := PutIntoDb(bc) + + if err != nil { + log.Panicf(err.Error()) + } + } else { log.Println("Found existing blockchain state, persisting state from datastore") bc = &state } - bc.TransactionPool = []*Transaction{} - bc.Blocks = append(bc.Blocks, genesisBlock) - return bc } @@ -34,7 +46,7 @@ func (bc *Blockchain) ToJSON() string { bbc, err := json.Marshal(bc) if err != nil { - panic("Something went wrong while serializing blockchain object") + log.Panic(err.Error()) } return string(bbc) @@ -43,12 +55,23 @@ func (bc *Blockchain) ToJSON() string { func (bc *Blockchain) AddTransactionToTransactionPool(txn *Transaction) { txn.Status = constants.STATUS_PENDING bc.TransactionPool = append(bc.TransactionPool, txn) + err := PutIntoDb(bc) + if err != nil { + log.Default().Panicf(err.Error()) + } } func (bc *Blockchain) AddBlock(b *Block) { m := map[string]bool{} - for _, txn := range b.Transactions { + + nextBlockHeight := len(bc.Blocks) + + for index, txn := range b.Transactions { m[txn.TransactioHash] = true + balance := bc.WalletIndex.CalculateBalance(txn.From) + log.Printf("\n\nsender balance -> %d \n\n", balance) + bc.WalletIndex.AddTransaction(txn.From, nextBlockHeight, index, txn) + bc.WalletIndex.AddTransaction(txn.To, nextBlockHeight, index, txn) } for idx, txn := range bc.TransactionPool { @@ -64,36 +87,48 @@ func (bc *Blockchain) AddBlock(b *Block) { err := PutIntoDb(bc) if err != nil { - log.Panicf("Something went wrong while saving state to database, halting entire process %s", err) + log.Panic(err.Error()) } } -func (bc *Blockchain) ProofOfWorkMining(minerAddress string) { +func (bc *Blockchain) LastBlock() *Block { + return bc.Blocks[len(bc.Blocks)-1] +} + +func (bc *Blockchain) CopyTransactionPool() []*Transaction { + t := make([]*Transaction, 0) + for _, txn := range bc.TransactionPool { + t = append(t, NewTransaction(txn.From, txn.To, txn.Value, txn.Data)) + } + return t +} + +func (bc *Blockchain) ValidProof(nonce int, previousHash string, transactions []*Transaction, difficulty int) bool { + zeroes := strings.Repeat("0", difficulty) + guessBlock := &Block{PrevHash: previousHash, Timestamp: 0, Nonce: nonce, Transactions: transactions} + guessHash := guessBlock.Hash() + return zeroes == guessHash[2:2+constants.MINING_DIFFICULTY] +} + +func (bc *Blockchain) ProofOfWork() (int, []*Transaction) { + t := bc.CopyTransactionPool() + previousHash := bc.LastBlock().Hash() nonce := 0 - log.Println("Starting Proof of Work") - for { - prevHash := bc.Blocks[len(bc.Blocks)-1].Hash() - guessBlock := NewBlock(prevHash, nonce) - - for _, txn := range bc.TransactionPool { - tx := NewTransaction(txn.From, txn.To, txn.Value, txn.Data) - guessBlock.AddTransactionToTheBlock(tx) - } - zeroes := strings.Repeat("0", constants.MINING_DIFFICULTY) - guessHash := guessBlock.Hash() - - if zeroes == guessHash[2:2+constants.MINING_DIFFICULTY] { - log.Println("Found solution") - log.Printf("Mining Difficulty is %d", constants.MINING_DIFFICULTY) - log.Printf("Hash Solution: %s", guessHash) - rewardTxn := NewTransaction(constants.BLOCKCHAIN_REWARD_ADDRESS, minerAddress, constants.MINING_REWARD, []byte{}) - rewardTxn.Status = constants.STATUS_SUCCESS - guessBlock.Transactions = append(guessBlock.Transactions, rewardTxn) - bc.AddBlock(guessBlock) - log.Printf("%s \n\n", bc.ToJSON()) - nonce = 0 - continue - } + for !bc.ValidProof(nonce, previousHash, t, constants.MINING_DIFFICULTY) { + nonce += 1 } + + return nonce, t +} + +func (bc *Blockchain) Mining() bool { + log.Println("Start proof of work") + nonce, txns := bc.ProofOfWork() + previousHash := bc.LastBlock().Hash() + block := NewBlock(previousHash, nonce) + block.Transactions = txns + bc.AddBlock(block) + log.Println("Found solution") + return true } diff --git a/constants/constants.go b/constants/constants.go index 7cca032..83be1a4 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -6,11 +6,15 @@ const ( STATUS_SUCCESS = "success" STATUS_FAILED = "failed" STATUS_PENDING = "pending" - MINING_DIFFICULTY = 5 + MINING_DIFFICULTY = 3 MINING_REWARD = 1200 * DECIMAL CURRENCY_NAME = "gopher" DECIMAL = 100 BLOCKCHAIN_REWARD_ADDRESS = "Gopher_Reward_Pool" DB_PATH = "./data" DB_KEY = "blockchain_data" + //temporary airdrop distribution for sender wallet since tx will fail because balance is 0 for sender wallets + AIRDROP_AMOUNT = 100 + AIRDROP_ROUND = 1 + BLOCKCHAIN_AIRDROP_ADDRESS = "Gopher_Airdrop_Pool" ) diff --git a/main.go b/main.go index 2b5d51a..700226e 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,6 @@ package main import ( "log" - "sync" - "time" "github.com/stinkymonkeyph/gopher-blocks/blockchain" "github.com/stinkymonkeyph/gopher-blocks/constants" @@ -14,17 +12,16 @@ func init() { } func main() { - - var wg sync.WaitGroup - block := blockchain.NewBlock("0x0", 0) - transaction1 := blockchain.NewTransaction("0x1", "0x2", 12, []byte{}) bc := blockchain.NewBlockchain(block) + bc.Airdrop("0x1") + bc.Mining() log.Print(bc.ToJSON()) - wg.Add(1) - go bc.ProofOfWorkMining("alice") - time.Sleep(2000) + transaction1 := blockchain.NewTransaction("0x1", "0x2", 12, []byte{}) bc.AddTransactionToTransactionPool(transaction1) - wg.Wait() + bc.Mining() + log.Print(bc.ToJSON()) + senderBalance := bc.WalletIndex.CalculateBalance("0x1") + log.Printf("\n\n\nSender Balance: %d \n", senderBalance) }