So you’ve created your own blockchain, but how do you reward miner’s for mining blocks?
In this post, we’ll discuss the miner’s reward for mined blocks, which we’ll refer to as the block reward. To support the block reward, you’ll need to update some existing classes. Moreover, to support wallets or accounts, you’ll create some new classes.
We’ll only talk about accounts in this chapter, since they are the simplest form of wallets and every wallet represents an account.
Assigning Accounts to Miners
Create the SignatureHelper utility class in the utils/SignatureHelper.java file. Right now, the name might not be the best fit, but the class can be used and extended for additional cryptographic methods.
Wallets and Accounts: In online resources, the terms wallet and account are often used synonymously, but there are subtle differences (e.g., in the Ethereum blockchain, an account is a primitive wallet). The account consists only of a public key and a private key, whereas a wallet is represented by a smart contract. The smart contract of the wallet offers advanced functionalities that are not required by standard users. The advanced functionalities include but are not limited to granting power of attorney or allowances, and owners of a wallet can authorize others to spend Ether on their behalf. These powers of attorney can also be limited to a maximum amount of Ether, or they can expire.
First, you’ll implement a method to generate a key pair. To do so, use the functions provided in the Bouncy Castle library. See http://www.bouncycastle.org/wiki/display/JA1/Elliptic+Curve+Key+Pair+Generation+and+Key+Factories for helpful information on using the library.
The code below shows an implementation to generate key pairs. You can, of course, use any elliptic curve, but we recommend the secp256k1 curve because it’s the common curve for cryptocurrencies and is therefore supported by both Java and the JavaScript library. The KeyPairGenerator expects two parameters: the name of the algorithm and the name of the provider. Use the two parameters given: ECDSA is the acronym of the Elliptic Curve Digital Signature Algorithm (ECDSA), and the BC provider represents Bouncy Castle.
public static KeyPair generateKeyPair() {
Security.addProvider(new BouncyCastleProvider());
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA","BC");
g.initialize(ecSpec, new SecureRandom());
return g.generateKeyPair();
}
Storing Accounts Persistently
Now that you can generate key pairs or accounts, add an attribute of the KeyPair type to the Miner class. In the constructor, you’ll generate a key pair using the helper method. This is what the miners will use to claim their reward, and to ensure that the owner of a node can also access these rewards, you should save the account to disk. Therefore, create the AccountPersistence class in the persistence/AccountPersistence.java file.
Next, define a path where the accounts should be stored, like the persistent storage of the blockchain. We use the accounts folder for simplicity, and you can simply store the key pair as a JavaScript Object Notation (JSON) object. The code below shows this process. Give your miner an ID that you can use as a filename. You can do this by simply having Java generate a UUID when the constructor of the Miner class is called.
public void saveKeyPair(KeyPair keyPair, String minerId) {
File file = new File(path + minerId + ".json");
byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded();
try (OutputStreamWriter outputStream = ↩
new OutputStreamWriter(new FileOutputStream(file), encoding)) {
outputStream.write("{\"publicKey\":\"");
outputStream.write(SHA3Helper.digestToHex(publicKey);
outputStream.write("\",\n\"privateKey\":\"");
outputStream.write(SHA3Helper.digestToHex(privateKey));
outputStream.write("\"}");
outputStream.flush();
}
catch (IOException e) {
e.printStackTrace();
}
}
If you store the two keys as shown, you’ll run into problems later because Java encodes the keys in Data Encryption Standard (DES) format. This contains metadata in addition to the respective keys, but you can work around this by storing the raw keys. You can easily extract the public key by removing the first 23 elements from the encoded byte array; alternatively, you can truncate the first 46 characters from the hex string.
Isolating the private key is also not difficult at code level, but you’ll need a little more understanding of ECC. The private key is based on the secret number used for the ECC calculations, so you need to cast the private key to BCECPrivateKey. Now, you can access the secret number D and store the key in the JSON file. Use the following code in the AccountPersistence class:
((BCECPrivateKey))keyPair.getPrivate()).getD().toByteArray()
If you didn’t extract the two keys, the DES format of the private key wouldn’t only contain the secret number D but would also contain the meta information for defining the elliptic curve as well as the public key. This would make the private key longer than the public key, so you can check whether you were able to isolate the keys correctly in the JSON file by comparing the length of the two keys. The public key should be almost twice as long as the private key—to be exact, the public key should be 130 characters and the private key should be 66 characters long.
Storing Accounts Securely: Note that in a production system, to prevent unauthorized access, accounts should not be stored in plain text. Instead, you can encrypt the file with a password.
Assigning Miners to Blocks
For the miners to receive the block reward, each block requires information about its miner. A unique assignment can be made via the public key of the account. For this purpose, each block should have a coinbase attribute, which is of the byte array type and contains the address of the miner. Add this attribute to the Block class and modify the BlockAdapter class accordingly. Remember to use the @JsonConverter annotation, since it’s a byte array.
If a miner has mined a new block, it must add its own address to the block as coinbase to claim its reward. This can be done in the blockMined method of the Miner class. If the block is accepted by the rest of the network and added to the longest chain, this claim is considered approved.
The final step is to customize your block explorer. Add the coinbase to the webapp/blocks.html file and include it in the server’s response when reading the JSON object.
Editor’s note: This post has been adapted from a section of the book Blockchain: The Comprehensive Guide to Blockchain Development, Ethereum, Solidity, and Smart Contracts
by Tobias Fertig and Andreas Schütz.
Comments