owned this note
owned this note
Published
Linked with GitHub
---
tags: Projects
---
# Coin
:::info
**Released:** March 3rd, 11:59pm EST
**Due:** March 21st, 11:59pm EST
:::
Clone the stencil [here](https://classroom.github.com/a/t0X7NUuh)!
## Introduction
In Chain, we learned how a cryptocurrency might implement a blockchain for block storage and validation. Now it's time to flesh out the rest of the system! You've heard about nodes, miners, and wallets in class (or will soon). Now's your chance to dive into the details of how these parts come together.
This project builds on the blockchain that you implemented for your first project. Don't worry if you didn't pass all of our tests on Chain -- we've provided our solution for the blockchain.
## Components
Coin consists of several main components. Here's a quick overview:
1. **Blockchain**
- See [Chain](https://hackmd.io/@KOnWTzqNQeG_fcTmpWZdTw/r1RPZNSDke).
2. **Miner**
- The Miner handles our proof of work (POW) consensus mechanism. It's in charge of finding a winning nonce for the blocks it forms. The miner keeps track of a Transaction Pool, which contains transactions passed to it by the node. Off to the races!
3. **Node**
- The node handles all top level logic. Among its various duties, it validates incoming blocks (using our blockchain), broadcasts transaction requests from the wallet, and tells the miner (if it has one) when to stop and start mining. Whenever it sees a valid block that appends to its chain, the node should tell its miner to get busy on a new block.
4. **Wallet**
- The wallet keeps track of our coins and creates transactions, which it passes along to its node. The node can then broadcast these transactions to the network. Miners of nodes that hear about these transactions will add them to their transaction pools. Given a large enough fee (or enough space on a block), the miner will include the transaction in their block.
5. **Server**
- If we wanted to, we could start a server and run our very own cryptocurrency out of Brown. We're skeptical this would be a good idea, since there are plenty of bugs lurking around in the code base. But we still could! **You don't need to worry about servers for this assignment**, but we still wanted to mention it here.
## Assignment
For this assignment, you will implement the functions listed below. We recommend that you tackle them in the following order:
1. `CalculateNonce`
2. `GenerateCoinbaseTransaction`
3. `Mine`
4. `HandleTransactions`
5. `RequestTransaction`
6. `BroadcastTransaction`
7. `HandleMinerBlock`
To see how difficult we think each function is, check out our [grading breakdown](##Grading).
### (1) mine.go
The infamous miners. Greedy miners compile the transactions that give them the highest reward into blocks, which they then race to find a winning nonce (number only used once) for. As soon as they hear about another block, they have to start from scratch. It's a rat race.
Not only does the miner get to keep all transaction fees, they also get the minting reward for each block. Recall that minting rewards halve every 10,000 blocks. Our halvings occur more frequently.
**Here's an overview of the relevant `Miner` fields:**
:::info
* **`Config`**: our miner’s configuration options. For this project, you'll only really have to worry about `Config.NonceLimit`
* **`TxPool`** all of the transactions that the miner can pull from to create their block. `TxPool` is an enhanced priority queue that also keeps track of total priority, to know whether or not the miner should mine at all.
* **`MiningPool`**: the transactions that the miner is actively mining in the current block. This is a subset of the `TxPool`.
* **`PreviousHash`**: the hash of the current last block on the main chain, so that our miner knows where to point to when constructing its new block.
* **`ChainLength`**: used by the miner to calculate the current minting reward.
* **`SendBlock`**: the channel that the node monitors to know when the miner has successfully mined a block. See `node.Start` for how this is used.
* **`PoolUpdated`**: the channel used to send information about TxPool updates to the miner.
* **`DifficultyTarget`**: the level of difficulty that a winning nonce's resulting hash must beat.
:::
#### Uhm... what's`context.Context`?
* We use `context` to send cancellation signals to goroutines, which take on lives of their own once spawned.
* If you're still a bit fuzzy on context, here is the [documentation](https://pkg.go.dev/context) and some [example code](https://gobyexample.com/context).
#### Why are we using `atomic` variables?
* Since our miner runs concurrently using goroutines, we must be mindful of any potential synchronization issues that could arise.
* The `atomic` package provides low-level atomic memory primitives useful for implementing synchronization algorithms -- it basically allows us to read/write to shared memory without having to deal with mutexes.
* Ask us on Ed if you have questions about concurrency or synchronization (especially if you haven't taken a systems course like cs0300 or cs0330).
* **Fun fact:** Multi-processor synchronization is one of Dr. Herlihy's specialties, so if you geek about this kind of stuff, go talk to him about it at his office hours!
:::info
**Important! A note on `select`:**
* Go's `select` statement allows you to wait for results from multiple channels. It's really just a fancy version of the classic switch statement, where your cases are channel results.
* For examples of how the select statement might be used, check out **[this post](https://gobyexample.com/select)** and `node.Start`. We usually combine select statements with infinite loops, so that a process just monitors relevant channels until it's terminated.
:::
#### Implement:
```go!
// CalculateNonce finds a winning nonce for a block. It uses
// context to know whether it should quit before it finds a nonce
// (if another block was found). ASICSs are optimized for this task.
func (m *Miner) CalculateNonce(ctx context.Context, b *block.Block) bool
```
##### Overview:
* This is what miners spend the majority of their time doing: they update the block header's Nonce until they find a hash smaller than the difficulty target.
* **Note:** our hashes are not technically hex, so we'll be comparing byte values.
* Don't forget about `context`! How can we use it to monitor whether our nonce calculations are still a worthwhile pursuit?
* Returns `true` if we find a valid hash, `false` otherwise.
##### Some helpful functions:
* context's `Done() <-chan struct{}`.
#### Implement:
```go!
// GenerateCoinbaseTransaction generates a coinbase
// transaction based off the transactions in the mining pool.
// It does this by adding the fee reward to the minting reward.
func (m *Miner) GenerateCoinbaseTransaction(txs []*block.Transaction) *block.Transaction
```
##### Overview:
* The miner works so hard in hopes of securing a coinbase transaction, in which it receives transaction fees and the minting reward.
* Transaction fees are implicit: they're the sum of the inputs minus the sum of the outputs.
* You should aggregate all of the fees for the transactions, get the minting reward, and make sure this transaction is addressed to you! How can we get that from our `Id`?
##### Some helpful functions:
* `func CalculateMintingReward(c *Config, chainLength uint32) uint32`: we need to know what the reward is for this chain length!
* `func (m *Miner) getInputSums(txs []*block.Transaction) ([]uint32, error)` : our miner uses its `GetInputSums` channel to request this information from the node. Once it receives the request, the node will get the input sums for each transaction from the blockchain and return it to the miner via the `InputSums` channel
#### Implement:
```go!
// When asked to mine, the miner selects the transactions
// with the highest priority to add to the mining pool.
func (m *Miner) Mine() *block.Block
```
##### Overview:
* In our cryptocurrency, a miner needs to be sure it's even worth mining a block. After all, CPU usage can get pricey. Thus, there better be enough transactions worth mining. Hint: check out the `TxPool`'s functions.
* If we're set to mine, then we should set our `Mining` field to true.
* Now, our miner should select the very best transactions to mine. We suggest you look at the `MiningPool`.
* Once we figure out which transactions we want to mine, it's time to construct the block.
* Then, we do the dirty work: we try and find a winning nonce for our block, so that our node can broadcast it to rest of our network and get our sweet reward.
* After successfully(or unsuccessfully finding our nonce), we can safely set our `Mining` field to false. We're done.
* If we were successful in finding a winning nonce, we should send the block so that our node can hear about it, then we should handle the block ourselves (in order to remove the block's transactions from our `TxPool`)
##### Some helpful functions:
* atomic's `func (x *Bool) Store(v bool)`: this is how we update an atomic boolean.
* context's`WithTimeout(context.Background(), 2*time.Second)` to create a context that times out after 2 seconds. This will give your program enough time to find a hash value while timing out in case of a bug.
* You should be able to figure out which other functions to use based on the overview above!
:::danger
Note: the `TestMine` tests requires a valid `HandleMinerBlock` implementation as well!
:::
### (2) wallet.go
The vast majority of cryptocurrency holders trust a third-party to take care of their coins (e.g. services like [Coinbase](https://coinbase.com) and [FTX](https://claims.ftx.com/welcome)). Even investment management companies like Blackrock and Fidelity are now offering custodial crypto [ETFs](https://www.blackrock.com/us/financial-professionals/investments/products/bitcoin-investing). Nodes, on the other hand, keep track of their own holdings. You'll learn more about wallets in a future lecture.
**Here's an overview of the relevant `Wallet` fields:**
:::info
* **`Config`**: our wallet's configuration options. For this project, you only need to worry about `Config.SafeBlockAmount`.
* **`Id`** : our wallet's `SimpleID` keeps track of the node's public and private keys. More complicated configurations--such as **[hierarachical deterministic wallets](https://en.bitcoin.it/wiki/Deterministic_wallet)**--are both more secure and more complex. We opted for keeping things simple: our wallet can only process transactions sent to our sole `PublicKey`.
* **`TransactionRequests`**: the channel that our wallet uses to send transaction requests to the `Node`, which can then broadcast this transaction to its peers.
* **`CoinCollection`**: this mapping contains all of the owner's coins that have been confirmed enough times (see `config.SafeBlockAmount`).
* **`UnseenSpentCoins`**: this mapping contains all of the coins that we've spent (by requesting a transaction) but haven't yet seen in a Block. Until we see our transaction actually included in a Block on the Chain, it hasn't happened!
* **`UnconfirmedSpentCoins`**: Once we've seen one of our spent coins on the chain, we should move it from `UnseenSpentCoins` to `UnconfirmedSpentCoins`. Even though we've seen our coin included in a block, we still have to wait until enough confirmations (new blocks added to the chain) have occurred until we can safely spend our coin.
* **`UnconfirmedReceivedCoins`**: Since we're an active cryto-user, we need a way to handle coins sent to us from our friends and peers, as well as the change from our own transactions! Like with `UnconfirmedSpentCoins`, we'll have to wait until enough confirmations have occured until we can spend any Coin that we receive.
:::
Ok, I think I understand how wallets work. But what are `CoinInfos`?
`CoinInfos` are convenient structs that hold the information about TransactionOutputs necessary for turning them into TransactionInputs. You'll see them as either keys or values in all of the wallet's mappings.
#### Implement:
```go!
// HandleTransactions handles the transactions of a new block.
// It updates the wallet's fields when applicable.
func (w *Wallet) HandleTransactions(txs []*block.Transaction)
```
##### Overview:
* Simply put, `HandleTransactions` moves coins (via coinInfos) between our various mappings. This function looks at all of the transactions in a block and sees if any of them involve the wallet's owner.
* What fields of `Wallet` should we update?
#### Implement:
```go!
// RequestTransaction allows the wallet to send a transaction to the node,
// which will propagate the transaction along the P2P network.
func (w *Wallet) RequestTransaction(amount uint32, fee uint32, recipientPK []byte) *block.Transaction
```
#### Overview
* What should we check before sending a transaction?
* What fields of `Wallet` should we update?
* Make sure that you actually send your finalized transaction to the `TransactionRequests` channel. Otherwise, the node won't be able to broadcast it!
* Lastly, update your balance. We temporarily reduce our balance by the total of the inputs when we have a pending transaction. This means we'll have to wait until we've seen our transaction in the Chain (with enough confirmations) to get our change back!
##### Helpful functions:
* `func (txo *TransactionOutput) MakeSignature(id id.ID) (string, error)`
* `func (id *SimpleID) GetPublicKeyString() string`
* While we did not provide our implementations of `generateTransactionInputs` and `generateTransactionOutputs`, we did leave you the function headers as a guide to how you might want to fill in and use these helpers.
### (3) node.go
Nodes are the workhouses of cryptocurrencies like Bitcoin. They send information to their peers, run miners, and generally do a lot of validation.
Our node should take advantage of non-blocking **goroutines** to delegate work to our miner/wallet/blockchain and continue on with its duties. That way, the nodes won't have to wait for the miner/wallet/blockchain to finish before we can continue. Head back to [Lab 1](https://hackmd.io/@KOnWTzqNQeG_fcTmpWZdTw/H1NKYBHuyl) for a refresher on goroutines.
**Here are `Node` fields that you have to be aware of for this project:**
:::info
* `Config`: the node's configuration. You'll want to know whether this node has a miner
* `Blockchain`, `Wallet`, and `Miner`: self-explanatory
* `SeenTransactions`: the transactions that our node has either (1) heard about from other nodes or (2) broadcast itself
* `SeenBlocks`: same as `SeenTransactions`, but for blocks
* `PeerDb`: the database of peers that the Node is currently connected to.
:::
#### Implement:
```go!
// BroadcastTransaction broadcasts transactions created by the wallet
// to other peers in the network.
func (n *Node) BroadcastTransaction(tx *block.Transaction)
```
##### Overview:
* Our wallet has just requested that we broadcast one of its transactions to our peers, who will then broadcast to their peers, etc...
* What field(s) of `Node` should we update?
* If we have a miner, it should handle the tx in the `TXPool` somehow, since it can now include this transaction in one of its own blocks.
* We should send this transaction to every peer in our `PeerDb`. Make sure to wrap your RPC call in a goroutine!
##### Helpful functions:
* `func (m *Miner) HandleTransaction(t *block.Transaction)`
* `func (a *Address) ForwardTransactionRPC(request *pro.Transaction) (*pro.Empty, error)`: this function allows us to forward a transaction to other nodes.
#### Implement:
```go!
// HandleMinerBlock handles a block
// that was just made by the miner. It does this
// by sending the block to the chain so that it can be
// added, to the wallet, and to the network to be
// broadcast.
func (n *Node) HandleMinerBlock(b *block.Block)
```
##### Overview:
* Woo! Our miner has succesfully mined a block!
* What field(s) of `Node` should we update?
* What function can we use to update the blockchain?
* If we have a wallet, how can we update its state of coins?
* We need to broadcast this block to all the peers in our network.
##### Helpful functions:
* `func (a *Address) ForwardBlockRPC(request *pro.Block) (*pro.Empty, error)`: this function allows us to forward a block to other nodes.
---
## Testing
This assignment is autograded, and you are able to run our test suite as many time as you like.
In addition, you should write your own tests when things aren't working! We have provided several helper functions in `test/mocking_utils.go` and `test/testing_utils.go` and some starter tests as well.
If you have written your own tests, you can `cd` into `test` and run `go test`. This will let you know which tests are failing, and why. It will likely be more convenient to **run individual tests**, which you can do using the GoLand UI.
## Debugging
Since many parts of this project make use of concurrency, you may find it a bit tricky to debug. Luckily, the GoLand debugger has support for debugging goroutines. You can read all about it [here](https://blog.jetbrains.com/go/2020/03/03/how-to-find-goroutines-during-debugging/).
## Install
1. Clone the [stencil repo](https://classroom.github.com/a/t0X7NUuh).
2. In your local Coin directory, run `go get ./...` to install the project's dependencies.
3. Get after it! Good luck :smiley:
## HandIn
1. Log into [Gradescope](https://www.gradescope.com/).
2. Submit your code using **GitHub**.
## Grading
This assignment carries a total of **100 points**. This will be determined by passing the following test functions:
### Miner
| Test Function | Points |
|:--------------------------------- |:------:|
| `TestCalculateNonce` | 5 |
| `TestCalculateNonceContext` | 5 |
| `TestGenerateCoinbaseTransaction` | 5 |
| `TestMine` | 15 |
| **Total** | **30** |
### Wallet
| Test Function | Points |
|:--------------------------------- |:-------:|
| `TestHandleTransactions` | 15 |
| `TestBasicTransactionRequest` | 10 |
| `TestMultipleTransactionRequests` | 15 |
| `TestRequestTransactionFails` | 10 |
| **Total** | **50** |
### Node
| Test Function | Points |
|:-------------------------- |:------:|
| `TestBroadcastTransaction` | 10 |
| `TestHandleMinerBlock` | 10 |
| **Total** | **20** |