NEAR Protocol y Solana

10 min read
Near Protocol and Solana
To Share and +4 nLEARNs

Hoy le echaremos un ojo a dos de las plataformas de contratos inteligentes más prometedoras del mundo Near Protocol y Solana. Aunque actualmente Ethereum domina el mercado, es una realidad que su falta de escalabilidad y altas tarifas han obligado a la mayoría de los desarrolladores a buscar alternativas viables. Near y Solana han emergido como las principales opciones.

¿Qué es Solana?

Solana fue fundada en 2017 por Anatoly Yakovenko, quien anteriormente había trabajado en DropBox. Yakovenko, junto con Eric Williams y el CTO Greg Fritzgerald, crearon Solana para resolver los problemas existentes en Bitcoin y Ethereum. El proyecto ha atraído inversiones de Multicoin Capital, Foundation Capital, SLOW Capital, CMCC Global, Abstract Ventures y más.

NEAR Solana

Características de la Blockchain de Solana

  • 50,000 transacciones por segundo y 0,4 segundos por bloque
  • El sistema puede procesar 28,4 millones de transacciones por segundo en una red de 40 gigabits
  • Solana utiliza el algoritmo de consenso Proof-of-History (Prueba de Historia).

¿Cómo funciona Proof-of-History (PoH)?

El consenso es esencial, especialmente en una red descentralizada que se extiende sobre un espacio inmenso . Bitcoin utiliza el consenso de prueba de trabajo (PoW) para realizar el consenso. Si bien el método es muy seguro, es difícil no ignorar su problema más importante – la falta de escalabilidad. Bitcoin solo puede procesar 7 transacciones por segundo..

Solana utiliza la prueba de historia (PoH), en la que crea registros históricos para demostrar que un evento ocurre durante un momento específico en el tiempo. Aquí hay algunos puntos que se deben tener en cuenta:

  • El algoritmo utiliza una función de retardo verificable de alta frecuencia que requiere un cierto número de pasos secuenciales para finalizar.
  • Las transacciones o eventos cronometrados dentro de la red son designados con un hash único un recuento, que se puede verificar públicamente.
  • El recuento permite a la red saber exactamente cuándo ocurrió la transacción o el evento.
  • Cada nodo tiene un reloj criptográfico que ayuda a rastrear la hora de la red y el orden de los eventos.

Debido a PoH, la red Solana admite 50.000 transacciones por segundo mientras se ejecuta con GPU. 

¿Qué es un Clúster en Solana?

Un clúster es un conjunto de computadoras de propiedad independiente que trabajan juntas y pueden verse como un sistema singular. Las principales características del Clúster son las siguientes:

  • Ayudan a verificar el resultado de programas enviados por usuarios que no son de confianza
  • Mantiene un registro de cualquier transacción o evento que realice un usuario.
  • Realiza un seguimiento de las computadoras que realizaron un trabajo significativo para mantener la red en funcionamiento.
  • Realiza un seguimiento de la posesión de activos del mundo real.

¿Cuál de los siguientes utiliza Sharding?

Correct! Wrong!

Programando en Solana

Los Contratos Inteligentes en Solana son escritos en Rust o C y compilados en el código de bytes de Berkeley Packet Filter (BPF). Dado que hay más herramientas disponibles, se recomienda que codifique en Rust. Los principiantes deben codificar sus programas utilizando el Anchor framework, que simplifica la ejecución.

Solana has a unique account model that is similar to files in the Linux operating system. They can hold any arbitrary data and also contain metadata about how they can be accessed. Keep in mind though, that the accounts have a fixed size and cannot be resized. 

Solana’s current programming model may force you to move application logic off-chain or modify the functionality to be inefficient and limited by the fixed account size.

¿Cuál de los siguientes es una parte integral de Solana?

Correct! Wrong!

Ejemplo de contrato

#![feature(proc_macro_hygiene)]

use anchor_lang::prelude::*;
use anchor_spl::token::{self, TokenAccount, Transfer};

#[program]
pub mod plutocratic_hosting {
    use super::*;

    /// Initialize a new contract with initialized content.
    #[access_control(Initialize::validate(&ctx, nonce))]
    pub fn initialize(
        ctx: Context<Initialize>,
        price: u64,
        content: String,
        nonce: u8,
    ) -> ProgramResult {

        // Transfer funds to the contract vault.
        let cpi_accounts = Transfer {
            from: ctx.accounts.from.to_account_info().clone(),
            to: ctx.accounts.vault.to_account_info().clone(),
            authority: ctx.accounts.owner.clone(),
        };
        let cpi_program = ctx.accounts.token_program.clone();
        let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
        token::transfer(cpi_ctx, price)?;

        // Initialize the content data.
        let content_record = &mut ctx.accounts.content;
        content_record.price = price;
        content_record.content = content;
        content_record.nonce = nonce;
        content_record.owner = *ctx.accounts.owner.to_account_info().key;
        content_record.vault = *ctx.accounts.vault.to_account_info().key;
        Ok(())

    }

    /// Purchase content address for new price, if transferring more tokens.
    #[access_control(check_funds(&ctx.accounts.content, price))]
    pub fn purchase(ctx: Context<Purchase>, price: u64, content: String) -> ProgramResult {
        // Transfer funds from contract back to owner.
        let seeds = &[
            ctx.accounts.content.to_account_info().key.as_ref(),
            &[ctx.accounts.content.nonce],
        ];
        let signer = &[&seeds[..]];
        let cpi_accounts = Transfer {
            from: ctx.accounts.vault.to_account_info().clone(),
            to: ctx.accounts.owner_token.to_account_info().clone(),
            authority: ctx.accounts.contract_signer.clone(),
        };
        let cpi_program = ctx.accounts.token_program.clone();
        let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
        token::transfer(cpi_ctx, ctx.accounts.content.price)?;

        // Transfer funds from new owner to contract.
        let cpi_accounts = Transfer {
            from: ctx.accounts.new_owner_token.to_account_info().clone(),
            to: ctx.accounts.vault.to_account_info().clone(),
            authority: ctx.accounts.new_owner.clone(),
        };
        let cpi_program = ctx.accounts.token_program.clone();
        let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
        token::transfer(cpi_ctx, price)?;

        // Overwrite content
        let content_record = &mut ctx.accounts.content;
        content_record.price = price;
        content_record.content = content;
        content_record.owner = *ctx.accounts.new_owner.to_account_info().key;

        Ok(())
    }
}

#[account]
pub struct ContentRecord {
    /// Price at which the current content is owned.
    pub price: u64,
    /// Content Data.
    pub content: String,
    /// Public key of current owner of the content.
    pub owner: Pubkey,
    /// Address for token program of funds locked in contract.
    pub vault: Pubkey,
    /// Nonce for the content, to create valid program derived addresses.
    pub nonce: u8,
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init)]
    content: ProgramAccount<'info, ContentRecord>,
    #[account(mut, "&vault.owner == contract_signer.key")]
    vault: CpiAccount<'info, TokenAccount>,
    /// Program derived address for the contract.
    contract_signer: AccountInfo<'info>,
    /// Token account the contract is made from.
    #[account(mut, has_one = owner)]
    from: CpiAccount<'info, TokenAccount>,
    /// Owner of the `from` token account.
    owner: AccountInfo<'info>,
    token_program: AccountInfo<'info>,
    rent: Sysvar<'info, Rent>,
}


impl<'info> Initialize<'info> {
    pub fn validate(ctx: &Context<Self>, nonce: u8) -> Result<()> {
        let signer = Pubkey::create_program_address(
            &[
                ctx.accounts.content.to_account_info().key.as_ref(),
                &[nonce],
            ],
            ctx.program_id,
        )
        .map_err(|_| ErrorCode::InvalidNonce)?;
        if &signer != ctx.accounts.contract_signer.to_account_info().key {
            return Err(ErrorCode::InvalidSigner.into());
        }
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Purchase<'info> {
    #[account(mut, has_one = vault)]
    content: ProgramAccount<'info, ContentRecord>,
    #[account(mut)]
    vault: CpiAccount<'info, TokenAccount>,
    #[account(seeds = [
        content.to_account_info().key.as_ref(),
        &[content.nonce],
    ])]
    contract_signer: AccountInfo<'info>,
    #[account(mut, has_one = owner)]
    owner_token: CpiAccount<'info, TokenAccount>,
    #[account(mut)]
    new_owner_token: CpiAccount<'info, TokenAccount>,
    #[account(signer)]
    new_owner: AccountInfo<'info>,
    owner: AccountInfo<'info>,
    token_program: AccountInfo<'info>,
}

fn check_funds(check: &ContentRecord, new_price: u64) -> Result<()> {
    if check.price >= new_price {
        return Err(ErrorCode::InsufficientFunds.into());
    }

    Ok(())
}

#[error]
pub enum ErrorCode {
    #[msg("The given nonce does not create a valid program derived address.")]
    InvalidNonce,
    #[msg("The derived signer does not match that which was given.")]
    InvalidSigner,
    #[msg("Insufficient funds provided to purchase route.")]
    InsufficientFunds,
}

¿Qué está pasando en el contrato?

  • Todas las cuentas a ser accesadas son anotadas en estructura para cada llamada con # [derive(Accounts)]. 
  • La función ayuda a inicializar la data de la cuenta para el dueño inicial y la compra. Esto permite que cualquiera pueda comprarla por más tokens. 
  • Data temporal es transferida a los parámetros de la función. Esto también incluye el contexto que la cuenta requiere para la transacción.
  • El estado de contrato luego se localiza en la estructura de ContentRecord. Ésto es luego anotado con #[account] para indicar que representa la capa de data de una cuenta.

¿Qué es Protocolo NEAR?

Llegando a existir en verano del 2018, el protocolo fue diseñado para crear el ambiente perfecto para aplicaciones descentralizadas proporcionando velocidades más altas, un mayor rendimiento y una mejor compatibilidad con otras cadenas. NEAR tiene una técnica de fragmentación o “Sharding” e introduce un mecanismo de generación de bloques llamado “Doomslug” propuesto en 2019. Doomslug permite finalidad práctica o finalidad “doomslug”, asegurando que los bloques reciban finalidad en segundos.

El protocolo NEAR es desarrollado por el colectivo NEAR, una comunidad de desarrolladores e investigadores colaborando en construir el proyecto. Algunas características críticas son:

  • NEAR es un sistema fragmentado que permite escalabilidad infinita
  • Un protocolo fácil de usar, NEAR permite a los desarrolladores construir aplicaciones fácil y rápido.
  • NEAR no es una cadena paralela sino un protocolo de primera capa.
  •  Las dApps creadas usando NEAR corren encima de la capa de NEAR subyacente.

¿Qué es el colectivo NEAR?

El colectivo NEAR incluye organizaciones independientes y otros contribuyentes que están constantemente trabajando en mejorar el protocolo NEAR. El colectivo trabaja en proyectos tales como escribir el código inicial y la implementación de la red NEAR. Es completamente descentralizado, operando independientemente, y no puede ser apagado o manipulado incluso por aquellos quienes lo crearon.

Es una organización sin fines de lucro enfocada en crear un ecosistema vibrante alrededor de la blockchain de NEAR. Ayuda en la coordinación de actividades de gobierno y desarrollo. El colectivo NEAR tiene varios proyectos, con la blockchain de NEAR uno de cada tantos proyectos está bajo la tutela del colectivo.

Mecanismo de consenso de NEAR

El mecanismo de consenso implementado en NEAR se llama Nightshade. Nightshade modela el sistema como una única blockchain. La lista de todas las transacciones en cada bloque se divide en pedazos físicos. Un pedazo por fragmento. Todos los pedazos se acumulan en un bloque. Nótese que los pedazos pueden ser validados solo por nodos que mantienen el estado de los fragmentos.

Hablando de validación, un componente clave de NEAR son los validadores. Éstos validadores son responsables por mantener el consenso dentro del protocolo. Los validadores son nodos especializados que mantienen sus servidores en línea el 100% del tiempo mientras mantienen sus sistemas continuamente actualizados.

A continuación veremos algunos puntos que debe tener en mente sobre los validadores de la red.

  • NEAR verifica los validadores de su red en cada nuevo epoch, eligiendolos en base a su participación.
  • Los validadores elegidos son re-inscritos por una re-participación automática de sus token más las recompensas precisas.
  • Los validadores potenciales deben mantener su participación por encima de un nivel determinado de forma dinámica.
  • Existen dos metodos que los validadores pueden usar para fortalecer su participación. Comprando los tokens por sí mismos o prestarlos a través de delegación de tokens
  • La recompensa que recibe es directamente proporcional a su participación, mientras más tokens tenga mayor será la recompensa.

El consenso se basa en el consenso más pesado de la cadena. Esto significa que una vez que un productor de bloques publica un bloque, ellos recolectan las firmas de los nodos validadores. El peso del bloque es entonces una participación acumulativa de todos los participantes que firman dentro del bloque. El peso de la cadena es entonces la suma del peso de todos los bloques. Adicionalmente, el consenso utiliza un dispositivo de finalidad que produce condiciones de cortes adicionales para mayor seguridad de la cadena.

¿Doomslug es un mecanismo generador de bloque de qué protocolo?

Correct! Wrong!

Aurora y el protocolo NEAR

Aurora también fue lanzada en el protocolo NEAR, proveyendo una experiencia de segunda capa en Ethereum. Algunas de las formas como aurora mejora a NEAR son:

  • Ayuda a aumentar el rendimiento a miles de transacciones por segundo.
  • Una finalidad de bloque de 2 segundos.
  • Crecimiento del ecosistema garantizada a futuro.
  • Bajas comisiones de transacción, que son 1000x veces menores que en Ethereum.
  • Compatibilidad sin riesgos con ethereum.

Ejemplo de contrato

use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::collections::LookupMap;
use near_sdk::{env, near_bindgen, AccountId, Balance, Promise};

#[global_allocator]
static ALLOC: near_sdk::wee_alloc::WeeAlloc = near_sdk::wee_alloc::WeeAlloc::INIT;

#[derive(BorshDeserialize, BorshSerialize)]
pub struct ContentRecord {
    pub price: Balance,
    pub content: String,
    pub owner: AccountId,
}

#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct ContentTracker {
    values: LookupMap<String, ContentRecord>,
    contract_owner: AccountId,
}

impl Default for ContentTracker {
    fn default() -> Self {
        let contract_owner = env::predecessor_account_id();
        Self {
            values: LookupMap::new(b"v".to_vec()),
            contract_owner,
        }
    }
}

#[near_bindgen]
impl ContentTracker {
    /// Gets content at a given route.
    pub fn get_route(&self, route: String) -> Option<String> {
        self.values.get(&route).map(|v| v.content)
    }

    /// Purchases a route based on funds sent to the contract.
    #[payable]
    pub fn purchase(&mut self, route: String, content: String) {
        let deposit = env::attached_deposit();
        assert!(deposit > 0);
        if let Some(entry) = self.values.get(&route) {
            assert!(
                deposit > entry.price,
                "Not enough deposit to purchase route, price: {} deposit: {}",
                entry.price,
                deposit
            );


            // Refund purchase to existing owner
            Promise::new(entry.owner).transfer(entry.price);
        }


        // Update record for the contract state.
        self.values.insert(
            &route,
            &ContentRecord {
                price: deposit,
                content,
                owner: env::predecessor_account_id(),
            },
        );
    }


    /// Allows owner of the contract withdraw funds.
    pub fn withdraw(&mut self) {
        assert_eq!(env::predecessor_account_id(), self.contract_owner);


        // Send the contract funds to the contract owner
        Promise::new(self.contract_owner.clone()).transfer(env::account_balance());
    }
}

¿Qué está ocurriendo en el contrato?

Entonces, ¿qué está ocurriendo en el contrato? tomemos un vistazo más cercano

  • El contrato es definido por #[near_bindgen] en el rastreador de contenido, es similar a un constructor en Solidity y se llama o ejecuta cuando el contrato es desplegado.
  • La funcion de compra es anotada con #[payable].
  • El contrato incluye llamadas asíncronas en lineas cuyo formato es Promise::new(…).transfer(…); 
  • La estructura de datos LookupMap<String, ContentRecord> maneja la busqueda de los valores de llaves, los cuales acceden al almacenamiento. Esto es igual al “mapping” o mapeo en Solidity.

Conclusión

Tanto Solana como NEAR son plataformas brillantes de contratos inteligentes que han construido una comunidad altamente activa. Ambos nos brindan caracteristicas especiales que ayudan a combatir el problema más grande que está plagando la velocidad del mundo descentralizado. Ambas plataformas están aún creciendo y tienen mucho que prometer.

107
Ir arriba