Proseguiamo con la creazione del nostro progetto definendo un package di nome runs con al suo interno una classe denominata Runs
. In essa inseriamo le prime righe di codice per l'inizializzazione della chiamata alla Blockchain.
Connessione alla Blockchain
import static java.math.BigInteger.ONE;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyPair;
import io.hotmoka.crypto.SignatureAlgorithm;
import io.hotmoka.beans.references.TransactionReference;
import io.hotmoka.beans.requests.InstanceMethodCallTransactionRequest;
import io.hotmoka.beans.requests.JarStoreTransactionRequest;
import io.hotmoka.beans.requests.SignedTransactionRequest;
import io.hotmoka.beans.requests.SignedTransactionRequest.Signer;
import io.hotmoka.beans.signatures.CodeSignature;
import io.hotmoka.beans.values.BigIntegerValue;
import io.hotmoka.beans.values.StorageReference;
import io.hotmoka.beans.values.StringValue;
import io.hotmoka.crypto.SignatureAlgorithmForTransactionRequests;
import io.hotmoka.views.GasHelper;
import io.hotmoka.nodes.Node;
import io.hotmoka.remote.RemoteNode;
import io.hotmoka.remote.RemoteNodeConfig;
public class Runs {
private final static String ADDRESS = "1049b887421e7f3f91dec79d8e46d62fab4141bcf48504d626aa0fbd6f3a706c#0";
public static void main(String[] args) throws Exception {
Path familyPath = Paths.get("../family/target/family-0.0.1-SNAPSHOT.jar");
RemoteNodeConfig config = new RemoteNodeConfig.Builder().setURL("192.168.126.137:8080").build();
try (Node node = RemoteNode.of(config)) {}
}
private static KeyPair loadKeys(String account) throws Exception {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("../" + account + ".keys"))) {
return (KeyPair) ois.readObject();
}
}
}
La costante ADDRESS
contiene il codice account recuperato durante la creazione dell'account (moka create-account
). Vengono quindi definiti il path del file jar del progetto da installare sulla Blockchain e la configurazione del nodo al quale connettersi.
Nel nostro caso abbiamo una macchina virtuale con indirizzo IP 192.168.126.137
che esegue una MemoryBlockchain contattabile sulla porta 8080
.
Continuiamo la scrittura del codice nel blocco try
. Abbiamo bisogno di una transazione, recuperiamo quindi un riferimento di questo tipo attraverso le API TakamakaCode
e cifriamo il riferimento dell'account utilizzando la chiave privata dell'account stesso. Per recuperare il codice dell'algoritmo di cifratura è sufficiente eseguire da browser una richiesta simile alla seguente:
http://192.168.126.137:8080/get/nameOfSignatureAlgorithmForRequests.
Si otterà una struttura JSON dalla quale estrarlo facilmente. Una chiamata al metodo nonce()
dell'account conclude questa parte.
Il nonce è un contatore progressivo del numero di transazioni eseguite con l'account in uso. Il suo valore iniziale è 0. Dobbiamo ricordare però che il nostro account è già stato oggetto di transazioni attraverso i comandi moka, per questa ragione andiamo preliminarmente a richiedere il nonce al nodo Blockchain.
La transazione che andiamo ad eseguire richiama un metodo annotato con annotation @View
. Tutte le chiamate a metodi con essa hanno la caratteristica di essere free, nessuno paga per la loro esecuzione. In virtù di questa caratteristica non dobbiamo cifrare la transazione fornire un nonce corretto o un gas price. Le chiamate a metodi @View
sono di sola lettura e hanno la limitazione di transazioni associate non soggette al consensus della rete.
Questo significa che dobbiamo fidarci del nodo richiamato.
TransactionReference takamakaCode = node.getTakamakaCode();
SignatureAlgorithm signature = SignatureAlgorithmForTransactionRequests
.mk("ed25519");
StorageReference account = new StorageReference(ADDRESS);
KeyPair keys = loadKeys(ADDRESS);
Signer signer = Signer.with(signature, keys.getPrivate());
BigInteger nonce = ((BigIntegerValue) node
.runInstanceMethodCallTransaction(new InstanceMethodCallTransactionRequest(account,
BigInteger.valueOf(50_000),
takamakaCode,
CodeSignature.NONCE,
account)))
.value;
Recupero del chain identifier
Completiamo il codice del progetto, recuperando il chain identifier della rete (operazione anche questa in modalità free essendo il metodo invocato annotato con annotation @View
) e richiededendo l'installazione del jar.
Quest'ultima operazione non è free, questa volta la transazione richiede un payer, il nostro account. Il signer associato cifrerà la transazione. Il nounce, cosi come il gas price, sono adesso rilevanti e devono essere in corrispondenza con quelli della rete.
L'esecuzione del metodo addJarStoreTransaction()
esegue infine la transazione per l'installazione del jar sul nodo Blockchain.
String chainId = ((StringValue) node
.runInstanceMethodCallTransaction(new InstanceMethodCallTransactionRequest(account,
BigInteger.valueOf(50_000),
takamakaCode,
CodeSignature.GET_CHAIN_ID,
node.getManifest())))
.value;
GasHelper gasHelper = new GasHelper(node);
TransactionReference family = node.addJarStoreTransaction(new JarStoreTransactionRequest(signer,
account,
nonce,
chainId,
BigInteger.valueOf(300_000),
gasHelper.getSafeGasPrice(),
takamakaCode,
Files.readAllBytes(familyPath),
takamakaCode));
nonce = nonce.add(ONE);
System.out.println("family-0.0.1.jar installed at: " + family);
Se tutto è stato eseguito correttamente, dovremmo ottenere un risultato simile al seguente: