Armando una Blockchain (6)

Published on Author lopezLeave a comment

Anterior Post
Siguiente Post

Esta semana pasada, agregué proceso de transacciones a mi proyecto personal de blockchain:

https://github.com/ajlopez/BlockchainSharp

En el anterior post, describí el Trie inmutable que estuve armando. Ahora voy a usarlo para guardar el AccountState por dirección de cuenta:

public class AccountState
{
    private BigInteger balance;

    public AccountState(BigInteger balance)
    {
        if (BigInteger.Compare(BigInteger.Zero, balance) > 0)
            throw new InvalidOperationException("Invalid balance");

        this.balance = balance;
    }

    public BigInteger Balance { get { return this.balance; } }

    public AccountState AddToBalance(BigInteger amount)
    {
        return new AccountState(BigInteger.Add(this.balance, amount));
    }

    public AccountState SubtractFromBalance(BigInteger amount)
    {
        return new AccountState(BigInteger.Subtract(this.balance, amount));
    }
}

Decidí implementar cuentas como en Ethereum: tener una cuenta con saldo en luegar de inputs y outputs. Por ahora, la única propiedad es el Balance, pero iré agregando más datos.  Vean arriba que los saldos negativos son rechazadas. He agregado un  TransactionProcessor:

public class TransactionProcessor
{
    private Trie<AccountState> states;

    public TransactionProcessor(Trie<AccountState> states)
    {
        this.states = states;
    }

    public Trie<AccountState> States { get { return this.states; } }

    public bool ExecuteTransaction(Transaction transaction)
    {
        var states = this.states;

        try
        {
            foreach (var av in transaction.Inputs)
            {
                var addr = av.Address.ToString();
                var state = states.Get(addr);
                var newstate = state.SubtractFromBalance(av.Value);
                states = states.Put(addr, newstate);
            }

            foreach (var av in transaction.Outputs)
            {
                var addr = av.Address.ToString();
                var state = states.Get(addr);
                var newstate = state.AddToBalance(av.Value);
                states = states.Put(addr, newstate);
            }

            this.states = states;

            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }
}

Que parte de un estado de cuentas y va construyedo otro. Si la transacción es procesado, un nuevo trie es generado, y ExecuteTransaction retorna true. Si la transacción es rechazada (la causa podría ser que al aplicarla resulte un saldo negativo), el trie inicial se mantiene. Un test típico que escribí:

[TestMethod]
public void ExecuteTransaction()
{
    var transaction = CreateTransaction(100);

    var addr1 = transaction.Inputs.First().Address;
    var addr2 = transaction.Outputs.First().Address;

    var states = new Trie<AccountState>(new AccountState(BigInteger.Zero));

    states = states.Put(addr1.ToString(), new AccountState(new BigInteger(200)));

    var processor = new TransactionProcessor(states);

    Assert.IsTrue(processor.ExecuteTransaction(transaction));

    var newstates = processor.States;

    Assert.IsNotNull(newstates);
    Assert.AreNotSame(states, newstates);

    Assert.AreEqual(new BigInteger(200), states.Get(addr1.ToString()).Balance);
    Assert.AreEqual(BigInteger.Zero, states.Get(addr2.ToString()).Balance);

    Assert.AreEqual(new BigInteger(100), newstates.Get(addr1.ToString()).Balance);
    Assert.AreEqual(new BigInteger(100), newstates.Get(addr2.ToString()).Balance);
}

El método auxiliar CreateTransaction crea una transacción con un monton, y dos direcciones creadas al azar.

Estoy pensando en tener solamente una cuenta sender y una cuenta receiver por transacción, como en Ethereum. De hecho, ya ayer lo reimplementé así, haciendo rediseño y refactor, ayudado por toda la batería de tests que ya me daba TDD. El cambio fue fácil y apenas tomó unos minutos.

Próximos temas: ejecutar bloques con transacciones, guardar el estado resultante en un almacén persistente, implementación de la máquina virtual y su ejecución de bytecodes, el compilador simple que armé, etc…

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Leave a Reply

Your email address will not be published. Required fields are marked *