Solana es un proyecto blockchain que nace en 2017, y en 2018 se publica su whitepaper, en donde se describe como este blockchain funciona, introduciendo un concepto novedoso llamado proof of history , el cual es básicamente un método criptográfico para verificar la valides de un hash output de un nodo anterior, además aprovecha los beneficios de las GPU para realizar este proceso en paralelo, como se ve en la Figura 1, haciendo checkpoint del ledger y coordinando el consensus en un tiempo increíblemente corto, dando como resultado procesar hasta 50.000 TPS y transacciones con una duración de 400 ms, esto contra las 20 TPS de Ethereum y las 4 TPS de bitcoin, sin contar también el precio del gas que como sabemos en Ethereum puede llegar a ser un problema, en este contexto, solana ha desarrollado un ecosistema sobre su clúster descentralizado, y es el tema principal del cual hablaremos hoy en este post
Programs
En solana los smart contract se llaman program, estos también son muy populares porque, de nuevo, aprovecha el uso de lo GPU y ejecutan estos programa en paralelo, haciendo que su ejecución sea muy rápida, por esto mismo los programas funcionan un poco diferente a los smart contract de Ethereum, como se ve en la Figura 2, estos escriben en rust, c o c++ y son compilados a un BPF byte code, después de este proceso son cargados y ejecutados dentro de la blockchain en un runtime por BPF VM, todo esto funciona sobre el kernel, pero la mayor diferencia radica en que los programs son stateless, esto quiere decir que el programa no guarda información, esta se guarda en las accounts, es decir, los accounts no solo tienen información de wallet, sino que almacenan información de los programs
por este how to work, es que solana logra esos rendimientos, claramente eso hace que escribir un smart contract requiera gran trabajo, aunque si ya vienes de programar en rust o c++ seguro será más fácil, aquí podemos ver un hello world en rust para solana
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
};
/// Define the type of state stored in accounts
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
/// number of greetings
pub counter: u32,
}
// Declare and export the program's entrypoint
entrypoint!(process_instruction);
// Program entrypoint's implementation
pub fn process_instruction(
program_id: &Pubkey, // Public key of the account the hello world program was loaded into
accounts: &[AccountInfo], // The account to say hello to
_instruction_data: &[u8], // Ignored, all helloworld instructions are hellos
) -> ProgramResult {
msg!("Hello World Rust program entrypoint");
// Iterating accounts is safer than indexing
let accounts_iter = &mut accounts.iter();
// Get the account to say hello to
let account = next_account_info(accounts_iter)?;
// The account must be owned by the program in order to modify its data
if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
// Increment and store the number of times the account has been greeted
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
greeting_account.counter += 1;
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
msg!("Greeted {} time(s)!", greeting_account.counter);
Ok(())
}
// Sanity tests
#[cfg(test)]
mod test {
use super::*;
use solana_program::clock::Epoch;
use std::mem;
#[test]
fn test_sanity() {
let program_id = Pubkey::default();
let key = Pubkey::default();
let mut lamports = 0;
let mut data = vec![0; mem::size_of::<u32>()];
let owner = Pubkey::default();
let account = AccountInfo::new(
&key,
false,
true,
&mut lamports,
&mut data,
&owner,
false,
Epoch::default(),
);
let instruction_data: Vec<u8> = Vec::new();
let accounts = vec![account];
assert_eq!(
GreetingAccount::try_from_slice(&accounts[0].data.borrow())
.unwrap()
.counter,
0
);
process_instruction(&program_id, &accounts, &instruction_data).unwrap();
assert_eq!(
GreetingAccount::try_from_slice(&accounts[0].data.borrow())
.unwrap()
.counter,
1
);
process_instruction(&program_id, &accounts, &instruction_data).unwrap();
assert_eq!(
GreetingAccount::try_from_slice(&accounts[0].data.borrow())
.unwrap()
.counter,
2
);
}
}
Solang y metaplex
Con lo descrito anteriormente, surgen varias necesidades, entre ellas la principal es acortar el camino que toma escribir un program dentro de solana, teniendo como referencia rust, que es donde está la mayor cantidad de ejemplos, hay algunas alternativas que solucionan este problema y que llamaron mi atención, donde podría decirse que seriamos early adopters
Solang
Es una herramienta que permite convertir código de solidty a un BPF byte code que es el código que interpreta la blockchain de solana, eso quiere decir que podemos compilar código, que ya está escrito en solidity al intérprete de progams en solana, hay varias cosas que me preocupan con la herramienta, esencialmente el tema del almacenamiento de tokens, sin embargo, a largo plazo hay un roadmap en donde se está teniendo muy en cuenta todo lo relacionado con solidity, por eso creo es buena opción darle una oportunidad veamos un ejemplo sencillo
para instalar solang usamos docker, reemplazamos local/path por la ubicación donde se tenga los archivos solidity
docker run --rm -it -v /local/path:/sources ghcr.io/hyperledger-labs/solang -o /sources /sources/Migration.sol
yo usare de ejemplo el siguiente smart contract
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract flipper {
bool private value;
/// Constructor that initializes the `bool` value to the given `init_value`.
constructor(bool initvalue) {
value = initvalue;
}
/// A message that can be called on instantiated contracts.
/// This one flips the value of the stored `bool` from `true`
/// to `false` and vice versa.
function flip() public {
value = !value;
}
/// Simply returns the current value of our `bool`.
function get() public view returns (bool) {
return value;
}
}
ejecutamos el comando de docker y nos creara en la carpeta que pasamos como parametro un archivo llamado bundle.so, el cual es el program que se desplegara en solana
Metaplex
Esta es una herramienta no code para crear programs dentro de solana, el programa que crea lo llaman candy machine, el cual su configuración se realiza por medio de un archivo de configuración con este estilo
{
"price": 1.0,
"number": 10,
"gatekeeper": null,
"solTreasuryAccount": "<YOUR WALLET ADDRESS>",
"splTokenAccount": null,
"splToken": null,
"goLiveDate": "25 Dec 2021 00:00:00 GMT",
"endSettings": null,
"whitelistMintSettings": null,
"hiddenSettings": null,
"storage": "arweave-sol",
"ipfsInfuraProjectId": null,
"ipfsInfuraSecret": null,
"nftStorageKey": null,
"awsS3Bucket": null,
"noRetainAuthority": false,
"noMutable": false
}
y luego se suben las imágenes con su metadata y se crea el program en solana
ts-node ~/metaplex/js/packages/cli/src/candy-machine-v2-cli.ts upload \
-e devnet \
-k ~/.config/solana/devnet.json \
-cp config.json \
-c example \
./assets
como ven con esta herramienta no se debe programar, ya que esta genera el código a partir de una metadata, es muy buena opción siempre y cuando no se desea trabajar reglas de negocio a profundidad, porque esto sería un limitante porque es una herramienta de alto nivel
solana pay
Es un framework que desarrollaron dentro de solana para potenciar los pagos wallet to wallet, la idea es interesante, y es bastante sencilla de entender, esta se puede apreciar en la Figura 3, el objetivo principal de este flujo es quitar la intermediación, para dar un poco de contexto, actualmente la mayoría de pagos viajan por los rieles de tarjeta de crédito, aquí interfieren muchos actores, generalmente es conocido como pago a 4 esquinas porque hay dos entidades financieras, el emisor de la tarjeta y la entidad que gestiona el pago del merchant (acquirer), adicional están el cardholder y el merchant, ese modelo busca ser reemplazado por este modelo más sencillo, sin embargo, en la práctica tiene su challenge para su implementación
en el modelo de solana pay hay solamente tres actores, el merchant, el costumer (con su wallet) y la blockchain de solana, el objetivo es que el merchant genere un QR siguiendo el estándar de especificación de solana que contiene toda la información para realizar un pago, la estructura se aprecia a continuación
solana:<recipient>
?amount=<amount>
&spl-token=<spl-token>
&reference=<reference>
&label=<label>
&message=<message>
&memo=<memo>
este es basado en el estándar BIP 21 un estándar similar que se propuso para bitcoin, luego de generado esta url , la wallet tiene que tener una integración especial, ya existe una libreria desarrollada para wallets, la cual permite leer y entender ese url encodeado, al ser aceptado por el dueño de wallet, se hace la transferencia, y al final el merchant busca esa transacción realizada en solana, y con la signature, el monto y la referencia, válida que efectivamente fue el pago de ese usuario, a continuación dejo un ejemplo de código para la implementación desde el punto de un mechant
Conclusiones
Solana es un proyecto bastante sólido, que está generando constátenteme productos innovadores, todos girando alrededor de su blockchain, creo que en temas de smart contract aún van a mejorar, el ecosistema está creciendo y esto va a hacer eventualmente que se generan frameworksy librerías que van a potencializar al desarrollador/a para crear sus productos sobre esta blockchain