Hoy cubriremos los análogos de todas las variables de bloque de Solidity. No todas tienen análogos 1 a 1. En Solidity, tenemos las siguientes variables de bloque de uso común:
- block.timestamp
- block.number
- blockhash()
Y las menos conocidas:
- block.coinbase
- block.basefee
- block.chainid
- block.difficulty / block.prevrandao
Asumimos que ya sabes qué hacen, pero están explicadas en la documentación de variables globales de Solidity si necesitas un repaso.
block.timestamp en Solana
Al utilizar el campo unix_timestamp dentro del sysvar Clock, podemos acceder al timestamp del bloque en Solana.
Primero inicializamos un nuevo proyecto de Anchor:
anchor init sysvar
Reemplaza la función initialize con esto:
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let clock: Clock = Clock::get()?;
msg!(
"Block timestamp: {}",
// Get block.timestamp
clock.unix_timestamp,
);
Ok(())
}
El módulo prelude de Anchor contiene el struct Clock, que se importa automáticamente por defecto:
use anchor_lang::prelude::*;
De manera un poco confusa, el tipo devuelto por unix_timestamp es un i64, no un u64, lo que significa que soporta números negativos aunque el tiempo no pueda ser negativo. Los deltas de tiempo, sin embargo, sí pueden ser negativos.
Obteniendo el día de la semana
Ahora vamos a crear un programa que nos diga el día actual de la semana usando el unix_timestamp del sysvar Clock.
El crate chrono proporciona funcionalidad para operaciones con fechas y horas en Rust.
Agrega el crate chrono como una dependencia en nuestro archivo Cargo.toml en el directorio del programa ./sysvar/Cargo.toml:
[dependencies]
chrono = "0.4.31"
Importa el crate chrono dentro del módulo sysvar:
// ...other code
#[program]
pub mod sysvar {
use super::*;
use chrono::*; // new line here
// ...
}
Ahora, agregamos la siguiente función a nuestro programa:
pub fn get_day_of_the_week(
_ctx: Context<Initialize>) -> Result<()> {
let clock = Clock::get()?;
let time_stamp = clock.unix_timestamp; // current timestamp
let date_time = chrono::NaiveDateTime::from_timestamp_opt(time_stamp, 0).unwrap();
let day_of_the_week = date_time.weekday();
msg!("Week day is: {}", day_of_the_week);
Ok(())
}
Pasamos el timestamp de unix actual que obtenemos del sysvar Clock como argumento a la función from_timestamp_opt, la cual devuelve un struct NaiveDateTime que contiene una fecha y una hora. Luego, llamamos al método weekday para obtener el día de la semana actual basado en el timestamp que pasamos.
Y actualizamos nuestra prueba:
it("Get day of the week", async () => {
const tx = await program.methods.getDayOfTheWeek().rpc();
console.log("Your transaction signature", tx);
});
Ejecuta la prueba nuevamente y obtén los siguientes registros:
Transaction executed in slot 36:
Signature: 5HVAjmo85Yi3yeQX5t6fNorU1da4H1zvgcJN7BaiPGnRwQhjbKd5YHsVE8bppU9Bg2toF4iVBvhbwkAtMo4NJm7V
Status: Ok
Log Messages:
Program H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy invoke [1]
Program log: Instruction: GetDayOfTheWeek
Program log: Week day is: Wed
Program H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy consumed 1597 of 200000 compute units
Nota el registro “Week day is: Wed”.
block.number en Solana
Solana tiene la noción de un “número de slot” que está muy relacionado con el “número de bloque” pero no es lo mismo. La distinción entre estos se cubrirá en el siguiente tutorial, por lo que posponemos una discusión completa sobre cómo obtener el “número de bloque” hasta entonces.
block.coinbase
En Ethereum, el “Block Coinbase” representa la dirección del minero que ha minado con éxito un bloque en Proof of Work (PoW). Por otro lado, Solana utiliza un mecanismo de consenso basado en líderes que es una combinación tanto de Proof of History (PoH) como de Proof of Stake (PoS), eliminando el concepto de minería. En su lugar, se designa a un líder de bloque o slot para validar transacciones y proponer bloques durante ciertos intervalos, bajo un sistema conocido como el calendario de líderes (leader schedule). Este calendario determina quién será el productor de bloques en un momento dado.
Sin embargo, en la actualidad, no hay una forma específica de acceder a la dirección del líder del bloque en los programas de Solana.
blockhash
Incluimos esta sección para mayor exhaustividad, pero pronto quedará obsoleta (deprecated).
Esta sección puede ser omitida sin consecuencias para el lector que no esté interesado.
Solana tiene un sysvar RecentBlockhashes que almacena los block hashes recientes activos, así como sus calculadoras de tarifas asociadas. Sin embargo, este sysvar ha sido obsoleto (deprecated) y no será compatible en futuras versiones de Solana. El sysvar RecentBlockhashes no proporciona un método get como lo hace el sysvar Clock. Sin embargo, los sysvars que carecen de este método se pueden acceder utilizando sysvar_name::from_account_info.
También introduciremos algo de sintaxis nueva que se explicará más adelante. Por ahora, trátala como código repetitivo (boilerplate):
#[derive(Accounts)]
pub struct Initialize<'info> {
/// CHECK: readonly
pub recent_blockhashes: AccountInfo<'info>,
}
Así es como obtenemos el block hash más reciente en Solana:
use anchor_lang::{prelude::*, solana_program::sysvar::recent_blockhashes::RecentBlockhashes};
// replace program id
declare_id!("H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy");
#[program]
pub mod sysvar {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
// RECENT BLOCK HASHES
let arr = [ctx.accounts.recent_blockhashes.clone()];
let accounts_iter = &mut arr.iter();
let sh_sysvar_info = next_account_info(accounts_iter)?;
let recent_blockhashes = RecentBlockhashes::from_account_info(sh_sysvar_info)?;
let data = recent_blockhashes.last().unwrap();
msg!("The recent block hash is: {:?}", data.blockhash);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
/// CHECK: readonly
pub recent_blockhashes: AccountInfo<'info>,
}
El archivo de prueba:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Sysvar } from "../target/types/sysvar";
describe("sysvar", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Sysvar as Program<Sysvar>;
it("Is initialized!", async () => {
// Add your test here.
const tx = await program.methods
.initialize()
.accounts({
recentBlockhashes: anchor.web3.SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
})
.rpc();
console.log("Transaction hash:", tx);
});
});
Ejecutamos la prueba y obtenemos el siguiente registro:
Log Messages:
Program H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy invoke [1]
Program log: Instruction: Initialize
Program log: The recent block hash is: erVQHJdJ11oL4igkQwDnv7oPZoxt88j7u8DCwHkVFnC
Program H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy consumed 46181 of 200000 compute units
Program H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy success
Podemos ver el block hash más reciente. Ten en cuenta que, debido a que estamos implementando en nuestro nodo local, el block hash que obtenemos es el de nuestro nodo local y no el de la Mainnet de Solana.
En términos de estructuración del tiempo, Solana opera en una línea de tiempo fija dividida en slots, donde cada slot es la porción de tiempo asignada a un líder para proponer un bloque. Estos slots se organizan a su vez en epochs, que son períodos predefinidos durante los cuales el calendario de líderes permanece sin cambios.
block.gaslimit
Solana tiene un límite de 48 millones de compute units por bloque. Cada transacción está limitada por defecto a 200,000 compute units, aunque puede aumentarse a 1.4 millones de compute units (discutiremos esto en un tutorial posterior, aunque puedes ver un ejemplo aquí).
No es posible acceder a este límite desde un programa de Rust.
block.basefee
En Ethereum, el basefee es dinámico según el EIP-1559; es una función de la utilización del bloque anterior. En Solana, el precio base de una transacción es estático, por lo que no hay necesidad de una variable como esta.
block.difficulty
La dificultad de bloque es un concepto asociado a las blockchains de Proof of Work (PoW). Solana, por el contrario, opera con un mecanismo de consenso Proof of History (PoH) combinado con Proof of Stake (PoS), que no involucra el concepto de dificultad de bloque.
block.chainid
Solana no tiene un chain id porque no es una blockchain compatible con la EVM. El block.chainid es la forma en que los contratos inteligentes de Solidity saben si están en una testnet, L2, mainnet o alguna otra cadena compatible con la EVM.
Solana ejecuta clústeres separados para Devnet, Testnet y Mainnet, pero los programas no tienen un mecanismo para saber en cuál se encuentran. Sin embargo, puedes ajustar tu código de forma programática en el momento de la implementación usando la funcionalidad cfg de Rust para tener diferentes características dependiendo del clúster en el que se implemente. Aquí tienes un ejemplo de cómo cambiar el program id dependiendo del clúster.
Aprende más
Este tutorial es parte de nuestro curso de Solana gratuito.
Publicado originalmente el 18 de febrero de 2024