Un contratto è un accordo legale tra due o più parti con la caratteristica di essere privo di ambiguità. In Informatica uno smart contract è invece un software con un comportamento deterministico la cui semantica sia chiara e imposta da un sistema di consenso.
Le Blockchain rappresentano l'ambiente ideale per gli smart contract grazie alla loro natura non centralizzata che riduce il rischio che una singola parte possa cambiare le regole contrattuali, fornendo ad esempio una semantica non standard per il codice degli smart contract.
Sebbene la definizione data di smart contract lo associ ad un codice software, esiste un particolare tipo di smart contract, l'externally owned account, che gestisce denaro senza avere alcun codice associato. In quest'ultimo caso il contratto è controllato da un un agente esterno (wallet o essere umano).
Takamaka implementa entrambe queste tipologie di smart contract attraverso istanze della classe io.takamaka.code.lang.Contract
. Essa può essere memorizzata in una Blockchain grazie all'estensione della classe io.takamaka.code.lang.Storage
.
Un semplice contratto: Investimento Ponzi
In questo articolo descriveremo l'implementazione del contratto che definisce lo schema Ponzi dove ogni investitore ottiene un guadagno di almeno il 10% dall'investitore precedente man mano che nuovi investitori si aggiungono.
In altre parole ogni investitore viene pagato dall'investitore che lo segue immediatamente nella catena dei pagamenti. L'ultimo non riceverà nulla non essendoci un investitore successivo
in grado di fornirgli un guadagno.
Creiamo un progetto maven all'interno della cartella tutorial
del progetto HotMoka utilizzando lo schema del progetto family
come riferimento. Nel nuovo progetto vuoto a livello di classi e package andiamo a creare il package takamaka.ponzi
con al suo interno la seguente implementazione smart contract dello schema Ponzi:
package takamaka.ponzi;
import static io.takamaka.code.lang.Takamaka.require;
import java.math.BigInteger;
import io.takamaka.code.lang.Contract;
import io.takamaka.code.lang.FromContract;
import io.takamaka.code.lang.Payable;
import io.takamaka.code.lang.PayableContract;
import io.takamaka.code.util.StorageLinkedList;
import io.takamaka.code.util.StorageList;
public class PonziContract extends Contract {
public final BigInteger MINIMUM_INVESTMENT = BigInteger.valueOf(1_000L);
private final StorageList investors = new StorageLinkedList();
public @FromContract(PayableContract.class) PonziContract() {
investors.add((PayableContract) caller());
}
public @Payable @FromContract(PayableContract.class) void invest(BigInteger amount) {
require(amount.compareTo(MINIMUM_INVESTMENT) >= 0,
() -> "you must invest at least " + MINIMUM_INVESTMENT);
BigInteger eachInvestorGets = amount.divide(BigInteger.valueOf(investors.size()));
investors.stream().forEachOrdered(investor -> investor.receive(eachInvestorGets));
investors.add((PayableContract) caller());
}
}
Quando su un oggetto del contratto si invoca il metodo invest()
il contratto sarà caricato automaticamente con la quantità di moneta virtuale necessaria. Questo significa avere moneta automaticamente trasferita sul bilancio dell'istanza PonziContract
che riceve la chiamata.
L'invocazione del metodo invest()
richiede un account utente che abbia sufficiente credito per poter effettuare la chiamata. Se il credito è insufficiente verrà lanciata un'eccezione del tipo:
io.hotmoka.beans.TransactionRejectedException: the payer has not enough funds to buy ... units of gas
Evidenziamo inoltre la necessità per l'account utente di essere in possesso di una quantità di moneta virtuale in grado di consentire sia il pagamento per il gas amount che l'esecuzione del metodo invest()
.L'annotazione @Payable
può essere applicata ad un costruttore o metodo annotato con @FromContract
. L'annotation fa in modo che ci sia un trasferimento di denaro per poter richiamare il metodo.
L'annotation @FromContract
consente l'invocazione del metodo solo ad oggetti di un contratto o external wallet che sono in grado di pagare per la transazione legata alla chiamata del metodo. Ad esempio, il metodo invest()
non è richiamabile da un blocco di codice che non rappresenta un contratto.
All'interno del codice mostrato, il metodo caller()
restituisce il contratto chiamante.
Storage Lists
In Java abbiamo numerose classi che rappresentano il tipo lista attraverso l'implementazione dell'interfaccia java.util.List
.
Queste classi possono essere utilizzate anche in Takamaka ma non come campi di classi da installare su un nodo Blockchain.
Takamaka fornisce implementazioni di liste basate sull'interfaccia io.takamaka.code.util.StorageLinkedList
. Oggetti di queste liste rappresentano degli storage objects e possono essere quindi campi di classi installate su un nodo HotMoka. Le liste Takamaka forniscono un tempo di accesso costante e la possibilità di aggiunta ad inizio e fine.
Conclusione
Nella lezione successiva vedremo come installare il contratto su un nodo Blockchain, creare un oggetto PonziContract
e invocare su di esso il metodo invest()
attraverso differenti accounts.