diff --git a/.gitignore b/.gitignore index 0566492..d4bc9b9 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ go.work node_modules/ .DS_Store -chain_storage/ +chain_storage*/ diff --git a/README.md b/README.md index 9713743..83cd111 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ go run cmd/blockchain/main.go -address localhost:8080 -http localhost:8090 -stor go run cmd/blockchain/main.go -address localhost:8081 -peers localhost:8080 -http localhost:8091 -storage chain_storage_2 -go run cmd/blockchain/main.goo -address localhost:8082 -peers localhost:8080,localhost:8081 -http localhost:8092 -storage chain_storage_3 +go run cmd/blockchain/main.go -address localhost:8082 -peers localhost:8080,localhost:8081 -http localhost:8092 -storage chain_storage_3 ``` ### Generate private key for testing diff --git a/api/api.go b/api/api.go index 756da1b..583fc14 100644 --- a/api/api.go +++ b/api/api.go @@ -78,7 +78,7 @@ func (h *Handler) MineBlock(w http.ResponseWriter, r *http.Request) { h.StatusesRWLock.Lock() h.MiningStatuses[id] = MineStatusResponse{Status: StatusPending} h.StatusesRWLock.Unlock() - err := h.Blockchain.MinePendingTransactions("") + block, err := h.Blockchain.MinePendingTransactions("") if err != nil { h.StatusesRWLock.Lock() h.MiningStatuses[id] = MineStatusResponse{ @@ -86,9 +86,11 @@ func (h *Handler) MineBlock(w http.ResponseWriter, r *http.Request) { Details: fmt.Sprintf("Error: %v", err), } h.StatusesRWLock.Unlock() + return } h.StatusesRWLock.Lock() h.MiningStatuses[id] = MineStatusResponse{Status: StatusSuccessful} + go h.Node.BroadcastBlock(block) h.StatusesRWLock.Unlock() }() err := json.NewEncoder(w).Encode(MineResponse{Id: id.String()}) diff --git a/chain/chain.go b/chain/chain.go index fc7567f..fcfbd59 100644 --- a/chain/chain.go +++ b/chain/chain.go @@ -273,16 +273,28 @@ func (chain *Blockchain) IsValid() bool { return true } -func (chain *Blockchain) MinePendingTransactions(minerAddress string) error { +func (chain *Blockchain) MinePendingTransactions(minerAddress string) (Block, error) { currentPoolSize := len(chain.PendingTransactions) + if currentPoolSize == 0 { + return Block{}, errors.New("Transaction pool is empty") + } + var transactions []Transaction if currentPoolSize < chain.MaxBlockSize { transactions = chain.PendingTransactions[0:currentPoolSize] chain.PendingTransactions = chain.PendingTransactions[currentPoolSize:] + err := chain.Storage.DequeueTransactions(currentPoolSize) + if err != nil { + fmt.Println("Could not dequeue transactions from storage") + } } else { transactions = chain.PendingTransactions[0 : chain.MaxBlockSize-1] chain.PendingTransactions = chain.PendingTransactions[chain.MaxBlockSize-1:] + err := chain.Storage.DequeueTransactions(chain.MaxBlockSize - 1) + if err != nil { + fmt.Println("Could not dequeue transactions from storage") + } } rewardTx := Transaction{ @@ -302,13 +314,15 @@ func (chain *Blockchain) MinePendingTransactions(minerAddress string) error { } block.Hash = block.CalculateHash() + // TODO: sync transaction removal between nodes + // TODO: recover if mining failed block.MineBlock(chain.Difficulty) chain.AddBlock(block) err := chain.Storage.AddBlock(block) if err != nil { - return err + return Block{}, err } - return nil + return block, nil } type Storage interface { @@ -316,6 +330,7 @@ type Storage interface { AddBlock(b Block) error AddTransaction(t Transaction) error Reset(chain *Blockchain) error + DequeueTransactions(n int) error } func InitBlockchain(difficulty, maxBlockSize int, miningReward float64, s Storage) *Blockchain { diff --git a/cmd/private_key_generator/main.go b/cmd/private_key_generator/main.go index 8330a67..2f29305 100644 --- a/cmd/private_key_generator/main.go +++ b/cmd/private_key_generator/main.go @@ -9,6 +9,7 @@ func main() { wallet := chain.Wallet{} wallet.KeyGen() fmt.Println(wallet.PrivateKey) + fmt.Println(wallet.PublicKey) t := chain.Transaction{} err := t.Sign(wallet.PrivateKey) if err != nil { diff --git a/frontend/src/AddTransactionModalButton.tsx b/frontend/src/AddTransactionModalButton.tsx index ce87017..9adff1e 100644 --- a/frontend/src/AddTransactionModalButton.tsx +++ b/frontend/src/AddTransactionModalButton.tsx @@ -12,14 +12,23 @@ import { import AddTransactionForm from './AddTransactionForm' -export default function AddTransactionsModalButton() { - const { isOpen, onOpen, onClose } = useDisclosure() +interface AddTransactionsModalButtonProps { + onClose: () => void; + } + +export default function AddTransactionsModalButton({ onClose }: AddTransactionsModalButtonProps) { + const { isOpen, onOpen, onClose: closeModal } = useDisclosure() + + const handleClose = () => { + closeModal(); + onClose(); + }; return ( <> - + @@ -39,7 +48,7 @@ export default function AddTransactionsModalButton() { - diff --git a/frontend/src/TransactionsPage.tsx b/frontend/src/TransactionsPage.tsx index e0629ad..bdd538d 100644 --- a/frontend/src/TransactionsPage.tsx +++ b/frontend/src/TransactionsPage.tsx @@ -12,17 +12,21 @@ async function fetchTransactions(): Promise { } export default function TransactionsPage({ caption }: { caption: string }) { - const { isPending, error, data } = useQuery({ + const { isPending, error, data, refetch } = useQuery({ queryKey: ["transactions"], queryFn: fetchTransactions, }); + const handleRefetch = () => { + refetch(); + }; + if (!data || !data.length) { return ( <> <>No transactions yet. - + ); @@ -34,7 +38,7 @@ export default function TransactionsPage({ caption }: { caption: string }) { <> - + ); diff --git a/storage/badger.go b/storage/badger.go index 946f0de..61b64f8 100644 --- a/storage/badger.go +++ b/storage/badger.go @@ -158,6 +158,33 @@ func (bs *Storage) AddTransaction(t chain.Transaction) error { return err } +func (bs *Storage) DequeueTransactions(n int) error { + return bs.db.Update(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + opts.PrefetchValues = false + it := txn.NewIterator(opts) + defer it.Close() + + prefix := []byte(transactionPrefix) + count := 0 + + for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { + if count >= n { + break + } + + key := it.Item().Key() + if err := txn.Delete(key); err != nil { + return err + } + + count++ + } + + return nil + }) +} + func (storage *Storage) deleteByPrefix(prefix []byte) error { deleteKeys := func(keysForDelete [][]byte) error { if err := storage.db.Update(func(txn *badger.Txn) error {