Passer au contenu principal




Le but de cette série de tutoriels est de vous aider à construire une image de la façon dont la technologie blockchain pourrait être développée. Vous pouvez trouver la partie 1 ici: Construire votre propre Blockchain en JavaScript - Partie 1.

Dans ce deuxième tutoriel, nous allons ..:

  • Créez un portefeuille simple.
  • Envoyez des transactions signées en utilisant notre blockchain.

Tout ce qui précède aboutira à notre propre crypto-monnaie.

En continuant avec le dernier tutoriel, nous avons une Blockchain de base vérifiable. Mais actuellement, notre chaîne ne stocke que des messages assez inutiles. Aujourd'hui, nous allons remplacer ces données par des transactions ("Notre bloc peut effectuer plusieurs transactions"), ce qui nous permettra de créer un crypto-monnaie très simple. Nous appellerons notre nouvelle monnaie "NoobCoin".

Ce didacticiel suppose que vous avez suivi l'autre didacticiel.

Dépendances: vous devrez importer rebondir y GSON.

Préparez un portefeuille

Dans les crypto-monnaies, la propriété de la devise est transférée sur la blockchain au fur et à mesure des transactions, les participants ont une adresse à laquelle les fonds peuvent être envoyés. Dans leur forme de base, les portefeuilles peuvent simplement stocker ces adresses, mais la plupart des portefeuilles sont également Logiciel capable d'effectuer de nouvelles transactions dans le Blockchain.

Alors créons une classe de portefeuille pour conserver notre clé publique et privée:

package noobchain;
import java.security.*;

public class Wallet {
public PrivateKey privateKey;
public PublicKey publicKey;
}

À quoi servent les clés publiques et privées?

Pour notre «noobcoin» la clé publique agira comme notre adresse. Il est normal de partager cette clé publique avec d'autres pour être payé. Notre clé privée est utilisée pour signer nos transactions, afin que personne ne puisse dépenser notre noobcoins plutôt que le propriétaire de la clé privée. Les utilisateurs devront garder leur clé privée secrète. Nous envoyons également notre clé publique avec la transaction et elle peut être utilisée pour vérifier que notre signature est valide et que les données n'ont pas été modifiées.

Nous générons nos clés privées et publiques dans un KeyPair. Nous utiliserons la cryptographie à courbe elliptique pour générer notre KeyPairs. Ajoutons une méthode generateKeyPair () à notre classe Portefeuille et appelons-le dans le constructeur:

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");
// Inicializar el generador de claves y generar un KeyPair
keyGen.initialize(ecSpec, random); //256 bytes provides an acceptable security level
KeyPair keyPair = keyGen.generateKeyPair();
// Configurar las claves pública y privada desde el keyPair
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
}catch(Exception e) {
throw new RuntimeException(e);
}
}

}

Maintenant que nous avons les contours de notre classe de portefeuille, jetons un coup d'œil aux transactions.

Transactions et signatures

Chaque transaction contiendra une certaine quantité de données:

  • La clé publique (adresse) de l'expéditeur des fonds.
  • La (les) clé (s) publique (s) du destinataire des fonds.
  • La valeur / le montant des fonds à transférer.
  • Les entrées, qui sont des références à des transactions précédentes qui prouvent que l'expéditeur a des fonds à envoyer.
  • Sorties, qui montre le nombre d'adresses pertinentes reçues dans l'opération. (Ces sorties sont appelées entrées sur les nouvelles transactions)
  • Une signature cryptographique, qui prouve que le propriétaire de l'adresse est celui qui envoie la transaction et que les données n'ont pas été modifiées. (par exemple: empêcher un tiers de modifier le montant envoyé)

Créons cette nouvelle classe de transaction:

import java.security.*;
import java.util.ArrayList;

public class Transaction {

public String transactionId; // este es también el hash de la transacción.
public PublicKey sender; // dirección del remitente/llave pública.
public PublicKey reciepient; // Dirección del destinatario/clave pública.
public float value;
public byte[] signature; // Esto es para evitar que nadie más gaste fondos en nuestra billetera.

public ArrayList inputs = new ArrayList();
public ArrayList outputs = new ArrayList();

private static int sequence = 0; // un recuento aproximado de cuántas transacciones se han generado.

// Constructor:
public Transaction(PublicKey from, PublicKey to, float value, ArrayList inputs) {
this.sender = from;
this.reciepient = to;
this.value = value;
this.inputs = inputs;
}

// Calcula el hash de la transacción (que se utilizará como su Id)
private String calulateHash() {
sequence++; // aumentar la secuencia para evitar 2 transacciones idénticas con el mismo hash
return StringUtil.applySha256(
StringUtil.getStringFromKey(sender) +
StringUtil.getStringFromKey(reciepient) +
Float.toString(value) + sequence
);
}
}

Il faut aussi créer des classes vides TransactionInput y TransactionOutput, ne vous inquiétez pas, nous pourrons les compléter plus tard.

Notre classe de transaction contiendra également des méthodes pertinentes pour générer / vérifier la signature et vérifier la transaction.

À quoi servent les signatures et comment fonctionnent-elles?

Les entreprises effectuent deux tâches très importantes sur notre blockchain: Premièrement, elles permettent uniquement au propriétaire de dépenser ses pièces; deuxièmement, ils empêchent les autres de manipuler la transaction présentée avant qu'un nouveau bloc ne soit extrait (au point d'entrée).

La clé privée est utilisée pour signer les données et la clé publique peut être utilisée pour vérifier son intégrité.

Par exemple: James veut envoyer 2 NoobCoins à Allie, donc son logiciel de portefeuille génère cette transaction et l'envoie aux mineurs pour être inclus dans le bloc suivant. Un mineur tente de changer le destinataire des 2 pièces en Juan. Heureusement, cependant, James avait signé les données de transaction avec sa clé privée, permettant à quiconque de vérifier si les données de transaction ont été modifiées à l'aide de la clé publique de James (car aucune autre personne ne pourra vérifier la transaction).

Nous pouvons voir (à partir du bloc de code ci-dessus) que notre signature sera un tas d'octets, alors créons une méthode pour les générer. La première chose dont nous aurons besoin est de quelques fonctions d'assistance dans la classe StringUtil :

//Aplica la firma ECDSA y devuelve el resultado ( en bytes).
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;
}

//Verifica una firma de cadena
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());
}

Nous allons maintenant utiliser ces méthodes de signature dans notre classe Transaction, ajout des méthodes generateSignature () y verifiySignature ():

//Firma todos los datos que no queremos que sean alterados.
public void generateSignature(PrivateKey privateKey) {
String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient) + Float.toString(value) ;
signature = StringUtil.applyECDSASig(privateKey,data);
}
//Verifica que los datos que firmamos no hayan sido alterados
public boolean verifiySignature() {
String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient) + Float.toString(value) ;
return StringUtil.verifyECDSASig(sender, data, signature);
}

Les signatures seront vérifiées par les mineurs lorsqu'une nouvelle transaction est ajoutée à un bloc.

Preuve de portefeuilles et de signatures

Maintenant, nous sommes presque à mi-chemin, essayons des choses qui fonctionnent. Dans la classe NoobChain Ajoutons de nouvelles variables et remplaçons le contenu de notre méthode principale:

import java.security.Security;
import java.util.ArrayList;
import java.util.Base64;
import com.google.gson.GsonBuilder;

public class NoobChain {

public static ArrayList blockchain = new ArrayList();
public static int difficulty = 5;
public static Wallet walletA;
public static Wallet walletB;

public static void main(String[] args) {
//Configurar el castillo de Bouncey como un proveedor de seguridad
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
//Crear las nuevas carteras
walletA = new Wallet();
walletB = new Wallet();
//Probar claves públicas y privadas
System.out.println("Claves privadas y públicas:");
System.out.println(StringUtil.getStringFromKey(walletA.privateKey));
System.out.println(StringUtil.getStringFromKey(walletA.publicKey));
//Crear una transacción de prueba de WalletA a WalletB
Transaction transaction = new Transaction(walletA.publicKey, walletB.publicKey, 5, null);
transaction.generateSignature(walletA.privateKey);
//Verificar el funcionamiento de la firma y verificarla desde la clave pública
System.out.println("Se verifica la firma");
System.out.println(transaction.verifiySignature());

}

Entrées et sorties 1: Comment posséder la monnaie cryptographique….

Pour que vous ayez un (1) bitcoin, vous devez recevoir 1 Bitcoin. Le grand livre ne lui ajoute pas réellement de pièce Bitcoin et moins une pièce Bitcoin de l'expéditeur, l'expéditeur fait référence au fait qu'il a déjà reçu une pièce Bitcoin, puis une sortie de transaction a été créée montrant que 1 Bitcoin a été envoyé à votre adresse. (Les entrées de transaction sont des références aux sorties de transaction précédentes.)

Le solde de votre portefeuille est la somme de toutes les sorties de transaction inutilisées qui vous sont adressées.

À partir de ce moment, nous suivrons la convention Bitcoin et appellerons les sorties de transaction inutilisées: UTXO.

Créons donc une classe d'entrée de transaction:

public class TransactionInput {
public String transactionOutputId; // Referencia a TransactionOutputs -> transactionId
public TransactionOutput UTXO; // Contiene la salida de la transacción no utilizada

public TransactionInput(String transactionOutputId) {
this.transactionOutputId = transactionOutputId;
}
}

Et une classe TransactionOutputs:

import java.security.PublicKey;

public class TransactionOutput {
public String id;
public PublicKey reciepient; // también conocido como el nuevo dueño de estas monedas.
public float value; // la cantidad de monedas que poseen
public String parentTransactionId; // el identificador de la transacción en la que se creó esta salida

//Constructor
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);
}

//Comprueba si la moneda le pertenece
public boolean isMine(PublicKey publicKey) {
return (publicKey == reciepient);
}

}

Les résultats de la transaction montreront le montant final envoyé à chaque partie de la transaction. Celles-ci, lorsqu'elles sont mentionnées comme entrées dans de nouvelles transactions, servent de preuve que vous avez des pièces à envoyer.

Entrées et sorties 2: Traitement de la transaction….

Les blocs de la chaîne peuvent recevoir de nombreuses transactions et la chaîne de blocs peut être très, très longue, cela peut prendre des éons pour traiter une nouvelle transaction car nous devons trouver et vérifier ses entrées. Pour éviter cela, nous conserverons une collection supplémentaire de toutes les transactions non dépensées qui peuvent être utilisées comme entrées. Dans notre classe NoobChain ajouter cette collection de tous UTXO:

public class NoobChain {

public static ArrayList blockchain = new ArrayList();
public static HashMap<String,TransactionOutputs> UTXOs = new HashMap<String,TransactionOutputs>(); //lista de todas las transacciones no gastadas.
public static int difficulty = 5;
public static Wallet walletA;
public static Wallet walletB;

public static void main(String[] args) {

Nous allons maintenant tout rassembler pour traiter la transaction avec une méthode booléenne processusTransaction dans notre classe Transaction:

//Devuelve true si se pudiera crear una nueva transacción.
public boolean processTransaction() {

if(verifiySignature() == false) {
System.out.println("#la Firma de la transacción no se verificó");
return false;
}

//Recopilar los datos de las transacciones (asegúrese de que no se hayan gastado):
for(TransactionInput i : inputs) {
i.UTXO = NoobChain.UTXOs.get(i.transactionOutputId);
}

//verificar si la transacción es válida:
if(getInputsValue() < NoobChain.minimumTransaction) {
System.out.println("#Insumos de transacciones a pequeños: " + getInputsValue());
return false;
}

//generar resultados de transacciones:
float leftOver = getInputsValue() - value; //get value of inputs then the left over change:
transactionId = calulateHash();
outputs.add(new TransactionOutput( this.reciepient, value,transactionId)); //enviar valor al destinatario
outputs.add(new TransactionOutput( this.sender, leftOver,transactionId)); // enviar la parte de atrás 'cambiar' de nuevo al remitente

//añadir salidas a la lista de productos no utilizados
for(TransactionOutput o : outputs) {
NoobChain.UTXOs.put(o.id , o);
}

//Eliminar las entradas de transacción de las listas UTXO como gastadas:
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;
}

//devuelve la suma de los valores de las entradas (UTXOs)
public float getInputsValue() {
float total = 0;
for(TransactionInput i : inputs) {
if(i.UTXO == null) continue; //Si no se encuentra la transacción, sáltela.
total += i.UTXO.value;
}
return total;
}

//returns sum of outputs:
public float getOutputsValue() {
float total = 0;
for(TransactionOutput o : outputs) {
total += o.value;
}
return total;
}

… Avec cette méthode, nous faisons quelques vérifications pour nous assurer que la transaction est valide, puis nous collectons des entrées et générons des sorties. (Voir les lignes commentées dans le code pour plus d'informations).

Surtout, vers la fin, nous supprimons les entrées de notre liste UTXO, ce qui signifie qu'une sortie de transaction ne peut être utilisée qu'une seule fois comme entrée ... Par conséquent, la valeur totale des entrées doit être utilisée, de sorte que l'expéditeur renvoie le " changer "à lui-même.

Enfin, nous allons mettre à jour notre portefeuille pour:

Rassemblez notre solde (en parcourant la liste des UTXO et en vérifiant si la sortie d'une transaction est Mine ())
Et générer des transactions pour nous….

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>(); //only UTXOs owned by this wallet.

public Wallet() {...

public void generateKeyPair() {...

//devuelve el saldo y almacena los UTXO's propiedad de esta cartera en este.UTXOs
public float getBalance() {
float total = 0;
for (Map.Entry<String, TransactionOutput> item: NoobChain.UTXOs.entrySet()){
TransactionOutput UTXO = item.getValue();
if(UTXO.isMine(publicKey)) { // si la salida me pertenece ( si las monedas me pertenecen)
UTXOs.put(UTXO.id,UTXO); //añádalo a nuestra lista de transacciones no gastadas.
total += UTXO.value ;
}
}
return total;
}
//Genera y devuelve una nueva transacción desde esta cartera.
public Transaction sendFunds(PublicKey _recipient,float value ) {
if(getBalance() < value) { //reunir el saldo y verificar los fondos.
System.out.println("#No hay fondos suficientes para enviar la transacción. Transacción descartada.");
return null;
}
//create array list of inputs
ArrayList inputs = new ArrayList();

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;
}

}

Ajouter des transactions à nos blocs:

Maintenant que nous avons un système de transaction fonctionnel, nous devons l'implémenter sur notre blockchain. Nous devrions remplacer les données inutiles que nous avions dans nos blocs par une ArrayList de transactions. Cependant, il peut y avoir des milliers de transactions dans un seul bloc, trop nombreuses pour être incluses dans notre calcul de hachage ... mais ne vous inquiétez pas, nous pouvons utiliser la racine merkle des transactions (vous pouvez rapidement lire sur les arbres merkle ici * bientôt *).

Ajoutons une méthode d'assistance pour générer le merkleroot dans StringUtils:

//Realiza una serie de transacciones y devuelve una raíz de merkle.
public static String getMerkleRoot(ArrayList transactions) {
int count = transactions.size();
ArrayList previousTreeLayer = new ArrayList();
for(Transaction transaction : transactions) {
previousTreeLayer.add(transaction.transactionId);
}
ArrayList treeLayer = previousTreeLayer;
while(count > 1) {
treeLayer = new ArrayList();
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;
}

Nous allons maintenant implémenter nos modifications dans la classe de bloc:

import java.util.ArrayList;
import java.util.Date;

public class Block {

public String hash;
public String previousHash;
public String merkleRoot;
public ArrayList transactions = new ArrayList(); //our data will be a simple message.
public long timeStamp; //como número de milisegundos desde 1/1/1970.
public int nonce;

//Block Constructor.
public Block(String previousHash ) {
this.previousHash = previousHash;
this.timeStamp = new Date().getTime();

this.hash = calculateHash(); //Making sure we do this after we set the other values.
}

//Calcular nuevo hash basado en el contenido de los bloques
public String calculateHash() {
String calculatedhash = StringUtil.applySha256(
previousHash +
Long.toString(timeStamp) +
Integer.toString(nonce) +
merkleRoot
);
return calculatedhash;
}

//Aumenta el valor de nonce hasta que se alcanza el objetivo de hash.
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("Bloque minado!!! : " + hash);
}

//Añadir transacciones a este bloque

public boolean addTransaction(Transaction transaction) {
//procesar la transacción y verificar si es válida, a menos que el bloque sea un bloque de génesis y luego ignorar.
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("Transacción añadida con éxito al Bloque");
return true;
}

}

Notez que nous mettons également à jour notre constructeur de blocs car nous n'avons pas besoin de transmettre les données de chaîne et d'inclure la racine de merkle dans la méthode de calcul de hachage.

Notre méthode booléenne addTransaction il ajoutera les transactions et ne retournera vrai que si la transaction a été ajoutée avec succès.

La grande finale (au début, il n'y avait pas de pièces):

Nous devrions tester l'envoi de pièces vers et depuis les portefeuilles et mettre à jour notre vérification de validité de la blockchain. Mais d'abord, nous avons besoin d'un moyen d'introduire de nouvelles pièces dans le mélange. Il existe de nombreuses façons de créer de nouvelles pièces, sur la blockchain Bitcoin par exemple: les mineurs peuvent inclure une transaction pour eux-mêmes en récompense pour chaque bloc extrait. Pour l'instant, cependant, nous ne publierons que toutes les pièces que nous souhaitons avoir, dans le premier bloc (le bloc de genèse). Comme Bitcoin, nous coderons le bloc de genèse.

Nous allons mettre à jour notre classe NoobChain avec tout ce dont elle a besoin:

  • Un bloc Genesis qui libère 100 Noobcoins dans le portefeuille.
  • Un contrôle de validité de chaîne mis à jour qui prend en compte les opérations.
  • Quelques transactions de test pour voir que tout fonctionne.
public class NoobChain {

public static ArrayList blockchain = new ArrayList();
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) {
//añadir nuestros bloques a la lista ArrayList de bloques:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); //Setup Bouncey castle as a Security Provider

//Crear billeteras
walletA = new Wallet();
walletB = new Wallet();
Wallet coinbase = new Wallet();

//crear una transacción de génesis, que envía 100 NoobCoin a la billeteraA:
genesisTransaction = new Transaction(coinbase.publicKey, walletA.publicKey, 100f, null);
genesisTransaction.generateSignature(coinbase.privateKey); //firmar manualmente la transacción de génesis
genesisTransaction.transactionId = "0"; //configurar manualmente el ID de la transacción
genesisTransaction.outputs.add(new TransactionOutput(genesisTransaction.reciepient, genesisTransaction.value, genesisTransaction.transactionId)); //añadir manualmente la salida de transacciones
UTXOs.put(genesisTransaction.outputs.get(0).id, genesisTransaction.outputs.get(0)); // es importante almacenar nuestra primera transacción en la lista de UTXOs.

System.out.println("Creación y explotación del bloque Génesis.... ");
Block genesis = new Block("0");
genesis.addTransaction(genesisTransaction);
addBlock(genesis);

//prueba
Block block1 = new Block(genesis.hash);
System.out.println("nEl balance de WalletA es: " + walletA.getBalance());
System.out.println("nWalletA está intentando enviar fondos (40) a WalletB...");
block1.addTransaction(walletA.sendFunds(walletB.publicKey, 40f));
addBlock(block1);
System.out.println("n el balance de WalletA es: " + walletA.getBalance());
System.out.println("El balance de WalletB es: " + walletB.getBalance());

Block block2 = new Block(block1.hash);
System.out.println("nWalletA Intentando enviar más fondos (1000) de los que tiene...");
block2.addTransaction(walletA.sendFunds(walletB.publicKey, 1000f));
addBlock(block2);
System.out.println("nel balance de WalletA es: " + walletA.getBalance());
System.out.println("el balance de WalletB es: " + walletB.getBalance());

Block block3 = new Block(block2.hash);
System.out.println("nWalletB está intentando enviar fondos (20) a WalletA...");
block3.addTransaction(walletB.sendFunds( walletA.publicKey, 20));
System.out.println("nel balance de WalletA es: " + walletA.getBalance());
System.out.println("el balance de WalletB es: " + walletB.getBalance());

isChainValid();

}

public static Boolean isChainValid() {
Block currentBlock;
Block previousBlock;
String hashTarget = new String(new char[difficulty]).replace('', '0');
HashMap<String,TransactionOutput> tempUTXOs = new HashMap<String,TransactionOutput>(); //a temporary working list of unspent transactions at a given block state.
tempUTXOs.put(genesisTransaction.outputs.get(0).id, genesisTransaction.outputs.get(0));

//a través de la cadena de bloques para comprobar los hashes:
for(int i=1; i < blockchain.size(); i++) {

currentBlock = blockchain.get(i);
previousBlock = blockchain.get(i-1);
//comparar el hash registrado y el hash calculado:
if(!currentBlock.hash.equals(currentBlock.calculateHash()) ){
System.out.println("#Hashes actuales no son iguales");
return false;
}
//comparar el hash anterior y el hash anterior registrado
if(!previousBlock.hash.equals(currentBlock.previousHash) ) {
System.out.println("#Hashes anteriores no son iguales");
return false;
}
//comprobar si se ha resuelto el problema del hash
if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) {
System.out.println("#Este bloque no ha sido minado");
return false;
}

//a través de las transacciones de bloqueo:
TransactionOutput tempOutput;
for(int t=0; t <currentBlock.transactions.size(); t++) {
Transaction currentTransaction = currentBlock.transactions.get(t);

if(!currentTransaction.verifiySignature()) {
System.out.println("#La firma en la transacción (" + t + ") es inválida");
return false;
}
if(currentTransaction.getInputsValue() != currentTransaction.getOutputsValue()) {
System.out.println("#Las entradas son iguales a las salidas en la transacción(" + t + ")");
return false;
}

for(TransactionInput input: currentTransaction.inputs) {
tempOutput = tempUTXOs.get(input.transactionOutputId);

if(tempOutput == null) {
System.out.println("#Falta la entrada referenciada en Transacción(" + t + ")");
return false;
}

if(input.UTXO.value != tempOutput.value) {
System.out.println("#El valor de la entrada referenciada Transacción(" + t + ") es inválido");
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("#El destinatario de la salida de la transacción (" + t + ") no es quien debería ser");
return false;
}
if( currentTransaction.outputs.get(1).reciepient != currentTransaction.sender) {
System.out.println("#Transacción(" + t + ") salida 'cambio' no es el remitente.");
return false;
}

}

}
System.out.println("La Blockchain es válida");
return true;
}

public static void addBlock(Block newBlock) {
newBlock.mineBlock(difficulty);
blockchain.add(newBlock);
}
}

Les portefeuilles peuvent désormais envoyer des fonds en toute sécurité sur votre blockchain, uniquement s'ils ont des fonds pour les envoyer. Cela signifie que vous avez votre propre crypto-monnaie locale.

Vous avez terminé les transactions sur votre blockchain!

Vous avez créé avec succès votre propre crypto-monnaie. Votre Bockchain maintenant:

  • Permet aux utilisateurs de créer des portefeuilles avec `new Portfolio (); '.
  • Fournit des portefeuilles de clés publics et privés utilisant la cryptographie à courbe elliptique.
  • Sécurisez le transfert de fonds, en utilisant un algorithme de signature numérique pour prouver la propriété.
  • Et enfin permettre aux utilisateurs d'effectuer des transactions sur leur blockchain avec 'Block.addTransaction (walletA.sendFunds (walletB.publicKey, 20));'; »Block.addTransaction (walletA.sendFunds (walletB.publicKey, 20)); '.

Juegos Play-to-earn

La industria de los videojuegos está pasando por una fase de crecimiento intenso, y el sistema «jugar para ganar» abre nuevas posibilidades para el juego con blockchain.

Los jugadores de este modelo de negocio crean valor para los desarrolladores del juego y otros jugadores al participar en la economía del juego. Como recompensa por su participación, reciben activos del juego. Estos activos van desde recursos del juego como herramientas de juego, armas o criptomonedas, hasta otros activos del juego que pueden ser tokenizados en la blockchain e incluso vendidos como NFT. Por ello, el modelo de negocio «play-to-earn» ha tenido éxito cuando se ha utilizado con juegos de blockchain. Los ingresos totales del juego «play-to-earn», Axie Infinity, se acercaron a los USD 120 millones en julio.

R Marketing Numérique