DEV Community

Cover image for Blockchain e struttura dei dati
Venerito Gianmarco
Venerito Gianmarco

Posted on

Blockchain e struttura dei dati

In questo post impareremo a conoscere i blocchi, gli hash e il modo in cui i blocchi sono collegati tra loro per creare una struttura blockchain crittograficamente sicura.

L'implementazione della blockchain risultante avrà un aspetto simile a questo:

Rappresentazione semplificativa dei blocchi

Ogni blocco contiene un Hash precedente che rappresenta il contenuto del suo predecessore, e i dati (nello specifico, il payload) che sono le informazioni memorizzate nel blocco.


DEFINIZIONE

Blockchain è effettivamente un nome appropriato: si tratta infatti di una catena di blocchi. Ogni blocco contiene dati transazionali, alcuni metadati che descrivono il blocco stesso e un collegamento al blocco precedente. Questi componenti vengono inseriti in una funzione di hash per creare una sequenza unica di bit che rappresenta il blocco.

Il primo blocco di una blockchain viene spesso chiamato blocco genesi. Il blocco ha solitamente un indice pari a 0 - questa è l'informatica, dove sappiamo ormai che tutto è indicizzato a 0 :)

L'aggiunta dell'hash del blocco precedente (previousHash) al nuovo blocco creerà un collegamento tra i due.

FUNZIONI DI HASH

Le funzioni hash sono utilizzate per prendere in input dati di qualsiasi dimensione e restituire una serie unica di bit di dimensioni specifiche che rappresentano i dati originali.

Una funzione hash crittografica ideale può, dato un qualsiasi input, restituire un output coerente ma apparentemente casuale.

È importante che l'output sia coerente, in modo da poter contare sul fatto di inserire gli stessi input e ricevere lo stesso output.

È anche importante che la casualità sia abbastanza forte da rendere impossibile ricostruire l'input dall'output. In questo modo, sappiamo che è a prova di manomissione.

Ad esempio, l'algoritmo SHA256 accetta un input come "Music" e restituisce un output coerente:

const hash = SHA256("Music");
console.log( hash.toString() ); // b12595…1cbe7e abbreviato per comodita'

Enter fullscreen mode Exit fullscreen mode

Il log è abbreviato, in realtà è lungo 64 caratteri esadecimali. SHA256 produce 256 bit. Poiché un carattere esadecimale richiede 4 bit, ci sono 64 caratteri esadecimali in un hash SHA256.

Se invece il mio input fosse "music" in minuscolo, il risultato sarebbe completamente diverso:

const hash = SHA256("music");
console.log( hash.toString() ); // ec4f2d...56f1cb
Enter fullscreen mode Exit fullscreen mode

Questi risultati hash sono apparentemente casuali rispetto ai loro input: "Music" e "music". Sono anche coerenti: inserendo questi input si ottengono sempre gli stessi output. Per questi motivi sha256 è una funzione hash crittografica ideale e viene spesso utilizzata nei programmi crittografici.


Crypto-JS

La libreria crypto-js ci fornisce diverse utilità crittografiche da poter implementare scrivendo codice in Javascript. In particolare, il metodo SHA256 di questa libreria è un'implementazione dell'algoritmo SHA256 progettato dall'NSA.

Questa funzione accetta come argomento qualsiasi stringa, indipendentemente dalla sua dimensione, e la sottopone ad hash in un array di 256 bit. Se chiamiamo toString() sull'oggetto restituito, riceveremo una stringa esadecimale di 64 caratteri.

Esadecimale
Si noterà che gli output mostrati consistono in un insieme di caratteri che vanno da a a f e da 0 a 9. Si tratta di caratteri esadecimali: è ormai consuetudine utilizzare l'esadecimale quando si visualizza un hash.

Spesso si incontra anche un hash con un prefisso 0x davanti. Questo prefisso significa che viene utilizzata la notazione esadecimale. Quindi, se si vede una stringa di caratteri "0x123abc", lo "0x" indica l'uso di esadecimali e il valore della stringa è in realtà solo la parte successiva, ovvero "123abc".

Per il file di prova che troverete qui sotto, si noterà che l'hash del blocco viene testato dall'espressione regolare (regex) /^[0-9A-F]{64}$/i. L'espressioneSta semplicemente verificando che si tratti di un output esadecimale di 64 caratteri.

Le espressioni regolari possono aiutare a definire un modello di ricerca per i dati in ingresso. Potete trovare maggiori informazioni sulle espressioni regolari su MDN

Perché 64 caratteri esadecimali?

Un bit può rappresentare due valori: 0 e 1. Due bit possono rappresentare quattro valori: 00, 01, 10 e 11. Quattro bit possono rappresentare 16 valori da 0000 a 1111. Possiamo mappare ciascuno di questi valori con un carattere dell'alfabeto esadecimale, poiché contiene 16 caratteri :)

Poiché SHA256 produce 256 bit, dividiamo questo valore per il numero di bit che rappresentano un carattere esadecimale (4) per ottenere 64 caratteri.

Guardiamo ora come implementare un modello semplificativo tramite JS.


In un file che chiameremo Block.js, abbiamo una classe Block. Utilizzando la funzione SHA256 della libreria Crypto JS, otteniamo un hash valido nella funzione toHash.

Iniziamo con l'aggiungere dati alla funzione di hash:
In questo modo si assicura che l'hash del blocco sia legato al suo contenuto.

Quando si crea un nuovo blocco, i dati vengono passati al suo costruttore:

const block = new Block("Alice ha inviato a Bob 1 BTC");

console.log( block.data ); // Alice ha inviato a Bob 1 BTC

Enter fullscreen mode Exit fullscreen mode

Come mostrato sopra, aggiungiamo una proprietà data al blocco.

Successivamente, aggiungiamo un costruttore alla nostra classe Block che prenda un parametro data e lo assegni a this.data
Una volta aggiunti i dati al blocco, li utilizziamo per calcolare l'hash del blocco nella funzione toHash.

const SHA256 = require("crypto-js/sha256");

// 
class Block {
  constructor(data) {
    this.data = data;
  }

  // Ritorniamo l'hash di data
  toHash() {
    return SHA256(this.data + this.previousHash);
  }
}

Enter fullscreen mode Exit fullscreen mode

Ora creiamo un nuovo file: Blockchain.js :)

Il file si concentrerà sull'aggiunta del primo blocco alla nostra nuova classe Blockchain. Il primo blocco viene spesso chiamato blocco Genesi.

Il file Blockchain.js contiene la classe Blockchain che al suo interno contiene un array chiamato chain. Aggiungiamo il blocco Genesi a questo array.

const Block = require('./Block');

class Blockchain {
    constructor() {
        this.chain = [new Block()];
    }
}

module.exports = Blockchain;
Enter fullscreen mode Exit fullscreen mode

Creiamo quindi un nuovo blocco nel costruttore di Blockchain e aggiungiamo l'array chain.


Funzione AddBlock

Creiamo una funzione addBlock nella nostra classe Blockchain.

Questa funzione accetta un nuovo blocco e lo aggiunge all'array chain:

addBlock(block) {
    // Aggiunge il previousHash al nuovo blocco, collegandoli.

    block.previousHash = this.chain[this.chain.length - 1].toHash();
    this.chain.push(block);
  }

Enter fullscreen mode Exit fullscreen mode

Ora dovremmo avere sia il blocco genesi che il nuovo blocco.


Funzione di Linking dei blocchi

È il momento di aggiungere un altro input cruciale al calcolo dell'hash del nostro blocco: l'hash del blocco precedente nella catena.
In questo modo si crea una catena in cui qualsiasi modifica ai dati di un blocco precedente si ripercuote su ogni blocco successivo.

Linking example

Per collegare i blocchi occorre fare due cose:

1) Aggiungere una proprietà previousHash a ogni blocco. Il valore di questa proprietà deve essere l'hash del blocco precedente nella catena.
2) Utilizzare questa proprietà previousHash nel calcolo dell'hash del blocco.

const Block = require("./Block");
const SHA256 = require("crypto-js/sha256");

class Blockchain {
    constructor() {
        const block = new Block("Music");
        this.chain = [block]; // genisis block
    }
    addBlock(newBlock) {
        newBlock.previousHash = this.chain[this.chain.length - 1].toHash();
        newBlock.data = "Altri dati";
        this.chain.push(newBlock);
    }
}
const blockchain = new Blockchain();
const block = new Block();

blockchain.addBlock(block);

module.exports = Blockchain;
Enter fullscreen mode Exit fullscreen mode

La Chain Validation
Le blockchain sono gestite da una rete di nodi, come dicevamo. Quando un nodo trova un nuovo blocco, trasmette la nuova versione della blockchain a tutti i suoi pari. In qualsiasi momento possono esistere più versioni della blockchain. Tuttavia, la blockchain valida più a lungo è quella accettata.

Creiamo ora una funziona per validare i blocchi.

Creare una funzione chiamata isValid sulla nostra Blockchain che restituisca true o false se un blocco è rispettivamente valido o non valido.
isValid deve controllare l'integrità di ogni blocco nella sua catena, guardando il campo previousHash di ogni blocco e assicurandosi che sia uguale all'hash del blocco precedente.


isValid() {
    let bool = true;

    for (let i = 0; i < this.chain.length - 2; i++) {
      const hashPrev = this.chain[i].toHash();
      const prevHash = this.chain[i + 1].previousHash;

      if (hashPrev.toString() != prevHash.toString()) {
        bool = false;
        break;
      }
    }

    return bool;
  }

Enter fullscreen mode Exit fullscreen mode

Il blocco Genesi e il Blockchain Explorer

È un buon momento per parlare del blocco genesi, (Genesis Block) e di Bitcoin.

Guardiamo il blocco genesi di Bitcoin dall'explorer.

Blocco genesi, visionabile su blockchain.com


Il numero di conferme è il numero di blocchi dal blocco genesi. Poiché il blocco genesi è il primo blocco, questo è anche l'altezza del blocco della blockchain!

È interessante notare che se si guarda al nonce del blocco genesi, esso è 2.083.236.893. Se si dà un'occhiata a un blocco più recente, ad esempio il 632900, si vedrà che il nonce è in realtà molto più basso. Perché? La difficoltà non dovrebbe aumentare con la crescita della rete?

Scopriamo così che il nonce del blocco è in realtà un campo di 32 bit e 2^32 fa 4.294.967.296, quindi questa è la dimensione massima di un nonce. Cosa succede quando il miner raggiunge questo punto? Può cambiare qualsiasi altra cosa nell'intestazione del blocco per aumentare la casualità. Altre proprietà includono:

  • Versione del software - Traccia gli aggiornamenti del software Bitcoin.
  • Hash del blocco precedente - Hash del blocco precedente a questo
  • Merkle Root - Non l'abbiamo ancora analizzato, è un hash che rappresenta tutte le transazioni!
  • Timestamp - Ora approssimativa (meno di due ore nel futuro secondo le regole del consenso)
  • Target - Obiettivo di difficoltà che stabilisce quanto deve essere piccola la Proof Of Work.

Top comments (0)