- Оригинальный адрес:20 января Создание вашего первого блокчейна с помощью Java, часть 2 — Транзакции
- Оригинальный автор:Kass
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:IllllllIIl
- Корректор:jaymz1439,NeoyeElf
Создание вашего первого блокчейна на Java. Часть 2 — Транзакции
Цель этой серии учебных пособий — помочь вам составить общее представление о технологии разработки блокчейна. Учебные пособия можно найти здесь.первая часть.
Во второй части урока мы:
- Создайте простой кошелек.
- Отправляйте подписанные транзакции, используя нашу цепочку блоков.
- нарциссизм.
Все вышеперечисленное в конечном итоге создаст нашу собственную криптовалюту (что-то в этом роде)!
Не волнуйтесь, эта статья просто пустая болтовня, в ней есть нечто большее, чем в предыдущем уроке! Если вы не читаете текст, вы можете напрямую прочитать исходный кодGithub.
Предыдущий урокМы сказали, что у нас есть базовый проверяемый блокчейн. Но теперь наш блокчейн может хранить только бесполезную информацию. Сегодня мы собираемся заменить эти бесполезные данные данными транзакций (наши блоки смогут хранить несколько транзакций), чтобы мы могли создать очень простую криптовалюту. Мы называем эту новую валюту: «валюта новичка» (оригинал на английском языке: noobcoin).
- В этом руководстве предполагается, что вы уже прочитали другойруководство.
- Зависимость: вам нужно импортироватьbounceycastle(Это простое руководство)а такжеGSON.
1. Подготовьте кошелек
В криптовалютах денежная собственность передается в блокчейне в транзакциях, где участники владеют адресами отправителя и получателя средств.Если это только базовая форма кошелька, кошелек может хранить только эту адресную информацию. Однако большинство кошельков также способны генерировать новые транзакции на программном уровне.
Не беспокойтесь о знании торговой части, мы скоро объясним это.
давайте создадимWalletкласс для хранения информации о наших открытых и закрытых ключах:
package noobchain;
import java.security.*;
public class Wallet {
public PrivateKey privateKey;
public PublicKey publicKey;
}
Убедитесь, что вы импортируете пакеты java.security.*!
Для чего используются эти открытые и закрытые ключи?
Для нашей «монеты-новичка» открытый ключ — это наш адрес. Вы можете поделиться своим открытым ключом с другими, чтобы получать платежи. И наш закрытый ключ используется для подписи наших транзакций, так что никто, кроме владельца закрытого ключа, не может украсть наши монеты-новички.Пользователи должны хранить свои закрытые ключи в безопасности!Мы также отправляем наш открытый ключ во время транзакции, и открытый ключ также можно использовать для проверки того, является ли наша подпись законной и были ли данные подделаны.
Закрытый ключ используется для подписи наших данных и предотвращения их подделки. Открытый ключ используется для проверки этой подписи.
Мы берем паруKeyPair的形式生成私钥和公钥。 мы будем использоватьКриптография на эллиптических кривыхдля создания нашегоKeyPairs. Добавляем класс WalletgenerateKeyPair()метод и вызвать его в конструкторе:
package noobchain;
import java.security.*;
public class Wallet {
public PrivateKey privateKey;
public PublicKey publicKey;
public Wallet(){
generateKeyPair();
}
public void generateKeyPair() {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA","BC");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime192v1");
// 初始化 KeyGenerator 并且生成一对 KeyPair
keyGen.initialize(ecSpec, random); //256 字节大小是可接受的安全等级
KeyPair keyPair = keyGen.generateKeyPair();
// 从 KeyPair中获取公钥和私钥
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
}catch(Exception e) {
throw new RuntimeException(e);
}
}
}
Все, что вам нужно знать об этом методе, это то, что он использует Java.security.KeyPairGenerator для создания пары ключей, которая применяет криптографию на основе эллиптических кривых. Этот метод генерирует открытые и закрытые ключи и назначает их соответствующим объектам открытых и закрытых ключей. Это практично.
Теперь, когда у нас есть общее представление о классе Wallet, давайте взглянем на транзакционную часть.
2. Транзакции и подписи
Каждая транзакция содержит данные определенного размера:
- Публичный ключ (адрес) отправителя средств.
- Открытый ключ (адрес) получателя средств.
- Сумма денежных средств для перевода.
- Ввод, который является ссылкой на последнюю транзакцию, доказывает, что у отправителя есть средства для отправки.
- Выход - это сумма, полученная получателем в транзакции. (Эти выходные данные также будут рассматриваться как входные данные в новых транзакциях)
- Зашифрованная подпись, которая доказывает, что владельцем адреса является тот, кто отправил транзакцию, и что отправленные данные не были подделаны. (например, чтобы третьи лица не могли изменить отправленную сумму)
Напишем новый класс Transaction:
import java.security.*;
import java.util.ArrayList;
public class Transaction {
public String transactionId; // 这个也是交易的哈希值
public PublicKey sender; // 发送方地址/公钥
public PublicKey reciepient; // 接受方地址/公钥
public float value;
public byte[] signature; // 用来防止他人盗用我们钱包里的资金
public ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>();
public ArrayList<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
private static int sequence = 0; // 对已生成交易个数的粗略计算
// 构造方法:
public Transaction(PublicKey from, PublicKey to, float value, ArrayList<TransactionInput> inputs) {
this.sender = from;
this.reciepient = to;
this.value = value;
this.inputs = inputs;
}
// 用来计算交易的哈希值(可作为交易的 id)
private String calulateHash() {
sequence++; //increase the sequence to avoid 2 identical transactions having the same hash
return StringUtil.applySha256(
StringUtil.getStringFromKey(sender) +
StringUtil.getStringFromKey(reciepient) +
Float.toString(value) + sequence
);
}
}
Мы также должны написать пустойTransactionInputкласс иTransactionOutputклассы, мы добавим их позже.
Наш класс транзакций также содержит методы для создания/проверки подписи и проверки транзакций.
Но подождите минутку. . .
Какова цель и как работают эти подписи?
подписатьв нашем блокчейнедваВажными задачами являются: во-первых, они позволяют владельцам тратить свои деньги, а во-вторых, они не позволяют другим вмешиваться в отправленные ими транзакции до того, как будет добыт новый блок (во всей цепочке блоков).
Закрытый ключ используется для подписи данных, а открытый ключ используется для проверки их легитимности.
**Пример:**Боб хочет дать Салли две монеты-новичка, поэтому клиент их кошелька генерирует эту транзакцию и отправляет ее майнеру, чтобы сделать ее частью следующего блока. Майнер попытался изменить получателя этих двух монет на Джона. Однако, к счастью, Боб подписал данные транзакции своим закрытым ключом, и любой, кто использует открытый ключ Боба, может убедиться, что данные транзакции не были изменены (открытые ключи других людей не могут подтвердить эту транзакцию).
(из предыдущего кода) мы видим, что наша подпись будет содержать много байтов информации, поэтому мы создаем метод, который генерирует эту информацию. Сначала мыStringUtilНапишите в классе несколько вспомогательных методов:
//采用 ECDSA 签名并返回结果(以字节形式)
public static byte[] applyECDSASig(PrivateKey privateKey, String input) {
Signature dsa;
byte[] output = new byte[0];
try {
dsa = Signature.getInstance("ECDSA", "BC");
dsa.initSign(privateKey);
byte[] strByte = input.getBytes();
dsa.update(strByte);
byte[] realSig = dsa.sign();
output = realSig;
} catch (Exception e) {
throw new RuntimeException(e);
}
return output;
}
//验证一个字符串签名
public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) {
try {
Signature ecdsaVerify = Signature.getInstance("ECDSA", "BC");
ecdsaVerify.initVerify(publicKey);
ecdsaVerify.update(data.getBytes());
return ecdsaVerify.verify(signature);
}catch(Exception e) {
throw new RuntimeException(e);
}
}
public static String getStringFromKey(Key key) {
return Base64.getEncoder().encodeToString(key.getEncoded());
}
Не заходите слишком далеко, чтобы понять, как работают эти методы. Что вам действительно нужно знать, так это то, что метод applyECDSASig принимает закрытый ключ отправителя и входные строки, подписывает их и возвращает массив байтов. Метод verifyECDSASig принимает подпись, открытый ключ и строку и возвращает значение true или false в зависимости от достоверности подписи. Метод getStringFromKey принимает любой закрытый ключ и возвращает зашифрованную строку.
теперь мыTransactionЧтобы использовать эти методы, связанные с подписью, в классе, добавьтеgenerateSignature()а такжеverifiySignature()метод.
//对所有我们不想被篡改的数据进行签名
public void generateSignature(PrivateKey privateKey) {
String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient) + Float.toString(value) ;
signature = StringUtil.applyECDSASig(privateKey,data);
}
//验证我们已签名的数据
public boolean verifiySignature() {
String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient) + Float.toString(value) ;
return StringUtil.verifyECDSASig(sender, data, signature);
}
На самом деле, вы можете захотеть подписать больше информации, например вывод/ввод или метки времени (но пока мы хотим подписать только самую основную информацию).
Подписи могут быть проверены майнерами так же, как новая транзакция проверяется и добавляется в блок.
При проверке легитимности блокчейна мы также можем проверить подпись.
3. Тестовый кошелек и подпись:
Теперь, когда мы почти на полпути, давайте проверим это. существуетNoobChainкласс, добавьте несколько новых переменных и замените ихmainСоответствующее содержимое в методе:
import java.security.Security;
import java.util.ArrayList;
import java.util.Base64;
import com.google.gson.GsonBuilder;
public class NoobChain {
public static ArrayList<Block> blockchain = new ArrayList<Block>();
public static int difficulty = 5;
public static Wallet walletA;
public static Wallet walletB;
public static void main(String[] args) {
//设置 Bouncey castle 作为 Security Provider
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
//创建新的钱包
walletA = new Wallet();
walletB = new Wallet();
//测试公钥和私钥
System.out.println("Private and public keys:");
System.out.println(StringUtil.getStringFromKey(walletA.privateKey));
System.out.println(StringUtil.getStringFromKey(walletA.publicKey));
//生成从 WalletA 到 walletB 的测试交易
Transaction transaction = new Transaction(walletA.publicKey, walletB.publicKey, 5, null);
transaction.signature = transaction.generateSignature(walletA.privateKey);
//验证签名是否起作用并结合公钥验证
System.out.println("Is signature verified");
System.out.println(transaction.verifiySignature());
}
Обязательно добавьте бонси-замок в качестве провайдера безопасности.
Мы создаем два кошелька, walletA и walletB, и распечатываем закрытый и открытый ключи walletA. Транзакция генерируется и подписывается открытым ключом walletA. Тогда просто надеюсь, что все работает нормально.
Ваш вывод должен выглядеть так:
Подпись должна быть проверена как истинная, как и ожидалось.Вам следует немного похвалить себя. Теперь мы просто создаем/проверяем выходы и входы и сохраняем транзакцию в блокчейне.
4. Ввод и вывод 1. Как вы храните криптовалюты
Если вы хотите иметь биткойн, вы должны сначала получить биткойн. Счет за транзакцию фактически не добавляет один биткойн вам и не вычитает один биткойн у отправителя. Отправитель имеет идентификацию, чтобы доказать, что он/она уже получил биткойн, а затем генерируется вывод транзакции, показывающий, что биткойн был отправлен на ваш адрес (ввод в транзакции получен из вывода предыдущей транзакции). ).
Баланс вашего кошелька — это все ваши неизрасходованные транзакции.
На этом этапе мы будем ссылаться на неизрасходованный вывод транзакции, как это делает биткойн:UTXO.
Давайте напишем ещеTransactionInputДобрый:
public class TransactionInput {
public String transactionOutputId; //把 TransactionOutputs 标识为对应的transactionId
public TransactionOutput UTXO; //包括了所有未花费的交易输出
public TransactionInput(String transactionOutputId) {
this.transactionOutputId = transactionOutputId;
}
}
Этот класс будет использоваться как ссылка на неизрасходованные TransactionOutputs. transactionOutputId используется для поиска связанного TransactionOutput, что позволяет майнерам проверить ваше право собственности.
а такжеTransactionOutputsДобрый:
import java.security.PublicKey;
public class TransactionOutput {
public String id;
public PublicKey reciepient; //这些币的新持有者
public float value; //他们持有币的总额
public String parentTransactionId; //生成这个输出的之前交易的 id
//构造方法
public TransactionOutput(PublicKey reciepient, float value, String parentTransactionId) {
this.reciepient = reciepient;
this.value = value;
this.parentTransactionId = parentTransactionId;
this.id = StringUtil.applySha256(StringUtil.getStringFromKey(reciepient)+Float.toString(value)+parentTransactionId);
}
//检查币是否属于你
public boolean isMine(PublicKey publicKey) {
return (publicKey == reciepient);
}
}
Выходные данные транзакции показывают сумму, которая в конечном итоге была отправлена каждому получателю. Эти выходные данные используются в качестве входных данных в новых транзакциях, как доказательство того, что у вас есть средства для отправки.
5. Вход и выход 2: обработка транзакций
Блоки могут получать много транзакций, а длина цепочки блоков может быть очень большой, что может занять очень много времени для обработки новой транзакции, потому что ее входные данные необходимо искать и проверять. Чтобы справиться с этим, мы собираемся написать еще один набор неизрасходованных транзакций, которые можно использовать в качестве выходных данных. существуетNoobChainкласс, добавьUTXOsсобирать:
public class NoobChain {
public static ArrayList<Block> blockchain = new ArrayList<Block>();
public static HashMap<String,TransactionOutputs> UTXOs = new HashMap<String,TransactionOutputs>(); //未花费交易的 list
public static int difficulty = 5;
public static Wallet walletA;
public static Wallet walletB;
public static void main(String[] args) {
HashMaps находит значение по ключу, но вам нужно импортировать java.util.HashMap.
Хорошо, теперь самое главное.
Поместите метод processTransaction, который обрабатывает транзакции, вTransactionВнутри класса:
//如果新交易可以生成,返回 true
public boolean processTransaction() {
if(verifiySignature() == false) {
System.out.println("#Transaction Signature failed to verify");
return false;
}
//整合所有交易输入(确保是未花费的)
for(TransactionInput i : inputs) {
i.UTXO = NoobChain.UTXOs.get(i.transactionOutputId);
}
//检查交易是否合法
if(getInputsValue() < NoobChain.minimumTransaction) {
System.out.println("#Transaction Inputs to small: " + getInputsValue());
return false;
}
//生成交易输出
float leftOver = getInputsValue() - value; //获取剩余的零钱
transactionId = calulateHash();
outputs.add(new TransactionOutput( this.reciepient, value,transactionId)); //send value to recipient
outputs.add(new TransactionOutput( this.sender, leftOver,transactionId)); //把剩下的“零钱“发回给发送方
//添加输出到未花费的 list 中
for(TransactionOutput o : outputs) {
NoobChain.UTXOs.put(o.id , o);
}
//从 UTXO list里面移除已花费的交易输出
for(TransactionInput i : inputs) {
if(i.UTXO == null) continue; //if Transaction can't be found skip it
NoobChain.UTXOs.remove(i.UTXO.id);
}
return true;
}
//返回输入(UTXOs) 值的总额
public float getInputsValue() {
float total = 0;
for(TransactionInput i : inputs) {
if(i.UTXO == null) continue; //if Transaction can't be found skip it
total += i.UTXO.value;
}
return total;
}
//返回输出总额
public float getOutputsValue() {
float total = 0;
for(TransactionOutput o : outputs) {
total += o.value;
}
return total;
}
Также добавьте метод getInputsValue.
Этот метод выполняет некоторые проверки для подтверждения легитимности транзакции, затем интегрирует входные данные и генерирует выходные данные (для ясности см. комментарии в коде).
Важно отметить, что в конце мы меняем Inputs сUTXOИсключено из списка, что указывает навывод транзакцииМожет использоваться только один раз в качестве входа. Следовательно, введенное общее значение должно быть потрачено так, чтобы у отправителя осталась «сдача», чтобы получить ее обратно.
Красная стрелка — это выход. Обратите внимание, что зеленый вход поступает из предыдущего выхода.
Наконец, обновите наш кошелек:
- Соберите наш баланс (просматривая список UTXO и проверяя, является ли вывод транзакции собственной монетой)
- Генерируйте транзакции для нас
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class Wallet {
public PrivateKey privateKey;
public PublicKey publicKey;
public HashMap<String,TransactionOutput> UTXOs = new HashMap<String,TransactionOutput>(); //只是这个钱包拥有的 UTXO
public Wallet() {...
public void generateKeyPair() {...
//返回余额并存储这个钱包的 UTXO
public float getBalance() {
float total = 0;
for (Map.Entry<String, TransactionOutput> item: NoobChain.UTXOs.entrySet()){
TransactionOutput UTXO = item.getValue();
if(UTXO.isMine(publicKey)) { //if output belongs to me ( if coins belong to me )
UTXOs.put(UTXO.id,UTXO); //add it to our list of unspent transactions.
total += UTXO.value ;
}
}
return total;
}
//从这个钱包生成并返回一个新的交易
public Transaction sendFunds(PublicKey _recipient,float value ) {
if(getBalance() < value) { //gather balance and check funds.
System.out.println("#Not Enough funds to send transaction. Transaction Discarded.");
return null;
}
//生成输入的 ArrayList
ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>();
float total = 0;
for (Map.Entry<String, TransactionOutput> item: UTXOs.entrySet()){
TransactionOutput UTXO = item.getValue();
total += UTXO.value;
inputs.add(new TransactionInput(UTXO.id));
if(total > value) break;
}
Transaction newTransaction = new Transaction(publicKey, _recipient , value, inputs);
newTransaction.generateSignature(privateKey);
for(TransactionInput input: inputs){
UTXOs.remove(input.transactionOutputId);
}
return newTransaction;
}
}
При желании вы можете добавить в кошелек другие функции, например запись истории транзакций.
6. Добавляем транзакцию в наш блок:
Теперь, когда у нас есть работающая система транзакций, нам нужно интегрировать ее в блокчейн. Мы должны заменить бесполезные данные, которые ранее были заняты в блоке, на ArrayList транзакций. Однако в блоке может быть 1000 транзакций, что больше, чем может обработать наш расчет хэша. Но не бойтесь, мы можем использовать корень Меркла транзакции для обработки (вы вскоре прочтете о деревьях Меркла).
Добавьте в StringUtils метод для создания merkleroot:
//Tacks in array of transactions and returns a merkle root.
public static String getMerkleRoot(ArrayList<Transaction> transactions) {
int count = transactions.size();
ArrayList<String> previousTreeLayer = new ArrayList<String>();
for(Transaction transaction : transactions) {
previousTreeLayer.add(transaction.transactionId);
}
ArrayList<String> treeLayer = previousTreeLayer;
while(count > 1) {
treeLayer = new ArrayList<String>();
for(int i=1; i < previousTreeLayer.size(); i++) {
treeLayer.add(applySha256(previousTreeLayer.get(i-1) + previousTreeLayer.get(i)));
}
count = treeLayer.size();
previousTreeLayer = treeLayer;
}
String merkleRoot = (treeLayer.size() == 1) ? treeLayer.get(0) : "";
return merkleRoot;
}
*Скоро я заменю текущий метод на метод, который возвращает настоящий merkleroot, но пока этот метод будет заменен.
сделай это сейчасBlockМеста, которые необходимо изменить в классе:
import java.util.ArrayList;
import java.util.Date;
public class Block {
public String hash;
public String previousHash;
public String merkleRoot;
public ArrayList<Transaction> transactions = new ArrayList<Transaction>(); //我们的数据就是一个简单的信息
public long timeStamp; //从1970/1/1到现在经过的毫秒时间
public int nonce;
//构造方法
public Block(String previousHash ) {
this.previousHash = previousHash;
this.timeStamp = new Date().getTime();
this.hash = calculateHash(); //确保设置了其它值之后再计算哈希值
}
//基于区块内容计算新的哈希值
public String calculateHash() {
String calculatedhash = StringUtil.applySha256(
previousHash +
Long.toString(timeStamp) +
Integer.toString(nonce) +
merkleRoot
);
return calculatedhash;
}
//哈希目标达成的话,增加 nonce 值
public void mineBlock(int difficulty) {
merkleRoot = StringUtil.getMerkleRoot(transactions);
String target = StringUtil.getDificultyString(difficulty); //Create a string with difficulty * "0"
while(!hash.substring( 0, difficulty).equals(target)) {
nonce ++;
hash = calculateHash();
}
System.out.println("Block Mined!!! : " + hash);
}
//添加交易到区块
public boolean addTransaction(Transaction transaction) {
//process transaction and check if valid, unless block is genesis block then ignore.
if(transaction == null) return false;
if((previousHash != "0")) {
if((transaction.processTransaction() != true)) {
System.out.println("Transaction failed to process. Discarded.");
return false;
}
}
transactions.add(transaction);
System.out.println("Transaction Successfully added to Block");
return true;
}
}
Мы также обновили конструктор Block, поскольку нам не нужно передавать строку, и добавили корень Меркла в хеш-метод.
Метод addTransaction добавляет транзакцию и возвращает значение true только в том случае, если транзакция была успешно добавлена.
Ха-ха! Мы построили все, что хотели, и теперь мы можем совершать транзакции на нашем блокчейне!
7. Подводя итог (вначале только монеты-новички):
Пришло время протестировать отправку монет-новичков из кошелька или получение монет-новичков через кошелек, а также обновить проверки легитимности блокчейна. Но сначала мы должны выяснить, как интегрировать недавно добытые монеты-новички в систему.Существует много способов генерировать новые монеты.В качестве примера возьмем блокчейн Биткойн: майнеры могут совершать транзакцию как часть себя, как район. вознаграждение, когда блок будет добыт. Теперь мы просто выпускаем определенное количество монет в первом блоке (блок генезиса), чтобы удовлетворить потребности нашего проекта. Как и в биткойнах, мы бы жестко закодировали блок генезиса, записав фиксированное значение.
Полностью обновим класс NoobChain:
- В учредительном блоке 100 монет-новичков были отправлены на кошелек А.
- В связи с добавлением транзакционной части была обновлена проверка легальности блокчейна.
- Некоторые тестовые транзакции для проверки правильности работы.
public class NoobChain {
public static ArrayList<Block> blockchain = new ArrayList<Block>();
public static HashMap<String,TransactionOutput> UTXOs = new HashMap<String,TransactionOutput>();
public static int difficulty = 3;
public static float minimumTransaction = 0.1f;
public static Wallet walletA;
public static Wallet walletB;
public static Transaction genesisTransaction;
public static void main(String[] args) {
//添加我们的区块到区块链 ArrayList中
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); //设置 Bouncey castle 为 Security Provider
//生成钱包
walletA = new Wallet();
walletB = new Wallet();
Wallet coinbase = new Wallet();
//生成创始交易,内容是发送100个菜鸟币到 walletA
genesisTransaction = new Transaction(coinbase.publicKey, walletA.publicKey, 100f, null);
genesisTransaction.generateSignature(coinbase.privateKey); //手动对创始交易签名
genesisTransaction.transactionId = "0"; //手动设置交易 id
genesisTransaction.outputs.add(new TransactionOutput(genesisTransaction.reciepient, genesisTransaction.value, genesisTransaction.transactionId)); //手动添加交易输出
UTXOs.put(genesisTransaction.outputs.get(0).id, genesisTransaction.outputs.get(0)); //在 UTXO list 里面保存第一个交易很重要
System.out.println("Creating and Mining Genesis block... ");
Block genesis = new Block("0");
genesis.addTransaction(genesisTransaction);
addBlock(genesis);
//测试
Block block1 = new Block(genesis.hash);
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("\nWalletA is Attempting to send funds (40) to WalletB...");
block1.addTransaction(walletA.sendFunds(walletB.publicKey, 40f));
addBlock(block1);
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("WalletB's balance is: " + walletB.getBalance());
Block block2 = new Block(block1.hash);
System.out.println("\nWalletA Attempting to send more funds (1000) than it has...");
block2.addTransaction(walletA.sendFunds(walletB.publicKey, 1000f));
addBlock(block2);
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("WalletB's balance is: " + walletB.getBalance());
Block block3 = new Block(block2.hash);
System.out.println("\nWalletB is Attempting to send funds (20) to WalletA...");
block3.addTransaction(walletB.sendFunds( walletA.publicKey, 20));
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("WalletB's balance is: " + walletB.getBalance());
isChainValid();
}
public static Boolean isChainValid() {
Block currentBlock;
Block previousBlock;
String hashTarget = new String(new char[difficulty]).replace('\0', '0');
HashMap<String,TransactionOutput> tempUTXOs = new HashMap<String,TransactionOutput>(); //对给定的区块状态,一个临时的未花费交易输出list
tempUTXOs.put(genesisTransaction.outputs.get(0).id, genesisTransaction.outputs.get(0));
//循环区块链去检查哈希值
for(int i=1; i < blockchain.size(); i++) {
currentBlock = blockchain.get(i);
previousBlock = blockchain.get(i-1);
//比较当前区块存储的哈希值和计算得出的哈希值
if(!currentBlock.hash.equals(currentBlock.calculateHash()) ){
System.out.println("#Current Hashes not equal");
return false;
}
//比较前一个区块的哈希值和当前区块中存储的上一个区块哈希值
if(!previousBlock.hash.equals(currentBlock.previousHash) ) {
System.out.println("#Previous Hashes not equal");
return false;
}
//检查哈希值是否解出来了
if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) {
System.out.println("#This block hasn't been mined");
return false;
}
//循环区块链交易
TransactionOutput tempOutput;
for(int t=0; t <currentBlock.transactions.size(); t++) {
Transaction currentTransaction = currentBlock.transactions.get(t);
if(!currentTransaction.verifiySignature()) {
System.out.println("#Signature on Transaction(" + t + ") is Invalid");
return false;
}
if(currentTransaction.getInputsValue() != currentTransaction.getOutputsValue()) {
System.out.println("#Inputs are note equal to outputs on Transaction(" + t + ")");
return false;
}
for(TransactionInput input: currentTransaction.inputs) {
tempOutput = tempUTXOs.get(input.transactionOutputId);
if(tempOutput == null) {
System.out.println("#Referenced input on Transaction(" + t + ") is Missing");
return false;
}
if(input.UTXO.value != tempOutput.value) {
System.out.println("#Referenced input Transaction(" + t + ") value is Invalid");
return false;
}
tempUTXOs.remove(input.transactionOutputId);
}
for(TransactionOutput output: currentTransaction.outputs) {
tempUTXOs.put(output.id, output);
}
if( currentTransaction.outputs.get(0).reciepient != currentTransaction.reciepient) {
System.out.println("#Transaction(" + t + ") output reciepient is not who it should be");
return false;
}
if( currentTransaction.outputs.get(1).reciepient != currentTransaction.sender) {
System.out.println("#Transaction(" + t + ") output 'change' is not sender.");
return false;
}
}
}
System.out.println("Blockchain is valid");
return true;
}
public static void addBlock(Block newBlock) {
newBlock.mineBlock(difficulty);
blockchain.add(newBlock);
}
}
Это более длинные методы. . .
Наш вывод должен выглядеть так:
Теперь кошельки могут безопасно отправлять средства на ваш блокчейн, если, конечно, у вас есть деньги. Это означает, что у вас уже есть собственная локализованная криптовалюта.
Теперь вы реализовали транзакционную часть своего блокчейна!
Вы успешно создали собственную криптовалюту (частично). Ваш текущий блокчейн может:
- Позволяет пользователям создавать кошельки с помощью new Wallet().
- Предоставляет кошелек, который использует шифрование на основе эллиптических кривых для шифрования открытых и закрытых ключей.
- Докажите право собственности на средства с помощью алгоритма цифровой подписи для защиты перевода средств.
- Наконец, разрешите пользователям инициировать транзакции в вашей цепочке блоков с помощью Block.addTransaction(walletA.sendFunds(walletB.publicKey, 20)).
ты сможешьGithubЗагрузите этот проект выше.
Ты сможешьПодписывайтесь на меня, так что когда будет опубликовано следующее руководство или другая статья о разработке блокчейнаполучить уведомление. Мы ценим любую обратную связь от вас. Благодарю.
Реализуйте свой первый блокчейн на Java. третья часть:
Далее мы поговорим о той части сети P2P,алгоритм консенсуса,Блочное хранилище и база данных. (скоро выйдет)
Свяжитесь со мной: kassCrypto@gmail.comобмен проблемами:discord.gg/ZsyQqyk(Я состою в клубе блокчейн-разработчиков на дискорде)
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из ИнтернетаНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,товар,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.