Archive for the 'Test-Driven Development' Category

Resoluciones del Nuevo Mes: Abril 2017

Friday, April 7th, 2017

Un nuevo comienza, ya pasó el verano, estamos en otoño por estos lares. Tiempo de escribir mis resoluciones mensuales, pero primero un repaso de las del mes anterior:

– Continuar RskSharp [complete] ver repo
– Continuar SimpleBlockchain [pending]
– Continuar Solidity Compiler [complete] ver repo
– Continuar ChineseP [pending]
– Continuar TensorSharp [complete] ver repo

Además estuve trabajando en:

– Mejorar SimpleProlog [completo] ver repo
– Continuar RSharp [completo] ver repo
– Mejorar AjDrawJS [completo] ver repo
– Mejorar SimpleForth [completo] ver repo

Mis resoluciones del nuevo mes son:

– Continuar RskSharp
– Continuar SimpleBlockchain
– Continuar Solidity Compiler
– Continuar ChineseP
– Continuar TensorSharp
– Continuar RSharp
– Continuar SimpleForth

Nos leemos!

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

El Programador Profesional, The Clean Coder (1)

Monday, April 3rd, 2017

Cada tanto vuelvo a leer algunos libros clásicos de programación, como The Pragmatic Programmer, Programming Pearls, The C Programming Language, Code Complete, Clean Code… En estos días me reencuentro con el excelente The Clean Coder del bueno de @unclebobmartin (ver el blog http://blog.cleancoder.com/ y el sitio asociado https://cleancoders.com/). Me gustó darme cuenta que en estos años estuve mejorando en varios de los puntos que menciona para un programador profesional (en otros todavía estoy en deuda). Quisiera comentar en esta nueva serie de posts las cualidades y actitudes que menciona como importantes.

Primero, Martin se refiere a programadores PROFESIONALES: todo lo que expone es lo que él ve como deseable para un programador que quiera llamarse profesional, no solamente porque trabaja programando sino por el respeto y la posición que alcanza entre sus colegas y clientes.

La primera gran regla que da se basa en el juramento hipocrático:

First, Do Not Harm

Es decir, lo primero, al programar, es no hacer daño. Esto se traduce en NO INTRODUCIR ERRORES (bugs que le dicen). Cualquier software exitoso necesita ser modificado en el tiempo, y es signo de profesionalismo en un programador el no introducir errores en las modificaciones que haga. Uno podría decir: la programación es una actividad muy complicada, tanto que como seres humanos no podemos evitar introducir bugs. Pero Martin retruca: un médico también puede equivocarse, pero el juramento hipocrático lo lleva a esforzarse, a hacer todo lo posible que esté a su alcance y bajo su entendimiento para evitar lastimar a sus pacientes. Eso mismo tenemos que hacer nosotros como programadores: esforzarnos por no introducir bugs cuando corregimos alguno o agregamos nueva funcionalidad. Martin da algunos ejemplos de su propia historia profesional sobre las consecuencias nefastas que puede provocar el agregar bugs sin darnos cuenta.

Pienso que estamos todos de acuerdo en que necesitamos hacer ese esfuerzo, pero ¿cómo lograrlo? Una estrategia, desaconsejable, es hacer que los clientes finales sean los que reporten bugs. ¡Qué bueno! Entreguemos algo rápido, así nos destacamos en nuestro cumplimiento de los plazos y luego, reparemos lo que ellos detectaron, también rápido. Pero no: no es un camino a seguir en los tiempos que corren. Otra aproximación: hacer que el equipo de QA (Quality Assurance), se encargue de detectar los problemas. De nuevo, así podemos entregar algo rápido, avanzar, total, ya alguien va a reportar los errores. Tampoco este es el camino esperado para un programador profesional: malgastar el tiempo de otros cuando uno debería directamente entregar código que funciona, no es profesional, es chapucero.

Entonces ¿cómo hacemos que lo que codificamos no tenga errores? Una es aplicar las mejores prácticas, todos los patrones del mundo, y confiar en nuestra capacidad de escribir bien, de “compilar” los resultados de una función que escribimos en nuestra cabeza, sin nunca probarla. Esta estrategia sigue siendo tan común que me asombra. Aún cuando seamos los “grandes” programadores que toman todas sus precauciones para no introducir errores, los errores siguen apareciendo. ¿Qué propone Martin (y es agua para mi molino, si ya conocen mi postura sobre el tema)? Pues bien:

How can you know your code works? That’s easy. Test it. Test it again. Test it up. Test it down. Test it seven ways to Sunday!

Siempre probar el código. Siempre. ¿Sugiere entonces Martin tener un 100% de cubrimiento de código? No, no lo sugiere, lo DEMANDA. Cada línea de producción que uno escriba debe ser puesta a prueba.

Every single line of code that you write should be tested. Period.

Parece no realista. Pero no lo es. Cada línea que uno escribe es para ser ejecutada alguna vez, entonces, DEBEMOS asegurarnos que funciona como esperamos. No basta con leer el código y “compilar” el resultado en nuestros sesos, decir simplemente: “esto hace lo que se espera”. Martin (y permítanme que yo me sume) DEMANDA que sea probada con un test.

Una estrategia que veo seguir, entonces, es escribir las pruebas DESPUES de escribir el código. He escuchado cosas como: “con escribir código DESPUES, me aseguro que funcione, y el código que YA escribí tiene buena calidad”. Bueno, pocas veces he visto que se cumpliera. Lo que sucede muchas veces es que el código a probar a posteriori, es un código enrevesado, o al menos, difícil de ser probado. Es por eso que Martin escribe:

The solution to that is to design your code to be easy to test. And the best way to do that is to write your tests first, before you write the code that passes them.

¡¡SI!! Es tan simple como eso: escribir las pruebas ANTES del código. Esto nos lleva a TDD (Test-Driven Development), flujo de trabajo del que Martin es un “big supporter” (y yo también).

Por supuesto que esto no quita que probemos nuestro software con pruebas automáticas de integración, o con pruebas manuales de QA. Siempre probar, probar, probar… Hacer todo el esfuerzo como profesionales para quedar seguros de que nuestro software funciona como se espera. Pero el paso inicial es usar TDD: me temo que no encuentro todavía una disciplina o flujo de trabajo que reemplace a TDD para conseguir el “First, Do Not Harm”.

Alguno podrá decir que practicar TDD lleva “más tiempo”. Pues yo todavía tengo que ver eso en el contexto de un proyecto con lógica no trivial: el aparente más tiempo que involucra escribir los tests antes del código, y el trabajo de refactor, que permite hacer que el código evolucione, en vez de “escribirlo bien en la primera vez”, es menor que el tiempo tradicional si contamos el tiempo de depurar, los días y horas gastados en identificar las causas de errores, y el tiempo que se tarda en corregirlos.

Puedo dar como evidencia experiencia propia, lamentablemente sin poder mostrar código concreto por haber sidos proyectos privados. El cliente final fue el mismo, el dominio fue parecido en complejidad y alcance, y los equipos de programadores de muy buen y similar nivel.

Proyecto A: escrito sin TDD, con pruebas de QA, pruebas manuales, pruebas de usuarios en ambiente de test, y luego en stage, antes de llegar a producción.

Proyecto B: escrito con TDD, con tests automáticos de integración, pruebas de QA, pruebas manuales, pruebas de usuarios en ambiente de test y stage, antes de llegar a producción.

El segundo proyecto, escrito con TDD, se implementó evolutivamente, con TRES implementaciones esencialmente diferentes, en el TIEMPO que se había estimado hacerlo sin TDD: Luego, fue más fácil en este proyecto introducir pruebas automáticas de integración, incluso directamente en el código. Y cada vez que modificábamos código o agregamos algo nuevo, al quedar las pruebas de código en “verde” luego las pruebas de integración y manuales de QA eran “un trámite”: muchas muchas veces pasaban, al habernos asegurado el cumplimiento de las pruebas escritas con TDD.

El primer proyecto también tenia pruebas de código, con un cubrimiento de código mayor de ochenta por ciento, escritas DESPUES de escribir el código. Pero aún cuando esas pruebas pasaran a “verde”, eso NO ASEGURABA el buen funcionamiento del sistema final. Y había que dar muchas vueltas para probar un código que no había sido escrito como para ser probado fácilmente. Muchas veces las pruebas de QA fallaban. Y cada vez que se introducía un cambio o se arreglaba un bug conocido, aparecía algún otro problema detectado en QA. Aún dos semanas antes de la entrega final, se introdujo un problema que sólo fue detectado en producción: dos clicks en el browser, y el sistema explotaba en cuatro continentes.

Del segundo proyecto, no escuché nada por más de un año, luego de su entrega. Pregunto un día ¿lo están usando? Sí, lo estaba usando. Ah ¿lo están usando para probarlo cada tanto? No, lo usaban cada día YA EN producción. Se le había entregado el mantenimiento del sistema a otro equipo, lejano. Entonces pregunto; ¿cómo? ¿lo están usando desde hace meses? ¿cómo es que no nos llegan reportes de bugs? La respuesta: porque NO LOS ENCONTRARON (o por lo menos hasta ese momento). Todo había sido escrito ordenadamente, aplicando “baby steps”, programación evolutiva, escribiendo explícitamente los casos de uso esperados, tanto de funcionamiento correcto como de conducta ante entradas inválidas. Estaba armado de tal forma que, al tener ese tipo de pruebas, y código que fuera fácilmente probable, las modificaciones se podían implementar y enterarse inmediatamente si algo anterior se rompía o no. Porque eso es también nos da TDD: el facilitar la profesión de los que vengan después a modificar el software que estamos construyendo.

Por supuesto que TDD solo no asegura la AUSENCIA TOTAL de bugs. Pero su aplicación a conciencia, junto con refactor casi continuo, la aplicación CONCIENZUDA de patrones y otros conceptos (no solamente por que sí), el diseño evolutivo (que permite ir construyendo algo que no se aparta de la simplicidad sino hasta la llegada de un caso de uso que haga necesario agregar algo), todo eso, que llamo “TDD y cabeza”, es lo que permitió el resultado exitoso del proyecto B.

Como programadores que aspiramos a ser profesionales tenemos que poner todo nuestro esfuerzo en este tema, en escribir código sin introducir errores. Y para eso, veo como esencial el escribir las pruebas antes del código, y mantener el sistema en un estado simple, claro, entendible y mantenible. Más sobre el tema en próximo post.

Nos leemos!

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

Resoluciones del Nuevo Mes: Julio 2016

Tuesday, July 5th, 2016

Llegó el dia de escribir mis nuevas resoluciones y repasar las del mes pasado:

– Mejorar WangTiles [pendiente]
– Mejorar WangTilesJS [completo] ver repo
– Mejorar CrysSharp  [completo] ver repo
– Mejorara GoSharp [pendiente]
– Mejorar SimpleForth [completo] ver repo
– Mejorar BlockchainSharp [completo] ver repo
– Mejorar SimpleBlockchain [completo] ver repo

Además, estuve trabajando en:

– Mejorar CrysJS [completo] ver repo

Mis nuevas resoluciones:

– Mejorar WangTiles
– Mejorar WangTilesJS
– Mejorar CrysSharp
– Mejorar CrysJS
– Mejorar SimpleForth
– Mejorar BlockchainSharp
– Mejorar SimpleBlockchain

Nos leemos!

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

Armando una Blockchain (11)

Wednesday, June 1st, 2016

Anterior Post
Siguiente Post

En estos dias estuve refactorizando la implementación de la blockchain de mi proyecto personal en JavaScript/NodeJS:

https://github.com/ajlopez/SimpleBlockchain

En el anterior post había mostrado algunos de los tests que escribí siguiendo el flujo de TDD  (Test-Driven Development). Hoy, quiero mostrar la implementación actual, que es la evolución de la anterior.

La implementación está en:

https://github.com/ajlopez/SimpleBlockchain/blob/master/lib/blockchains.js

Hay ahí una “clase” JavaScript, implementada como función:

function Blockchain(block) {
    var self = this;
    var blocks = [block];
    var blockstore = stores.blockstore();
    blockstore.save(block);
    
    this.bestBlock = function () { return blocks[blocks.length - 1]; }

En mi nueva implementación, estoy usando un almacén de bloques (por ahora en memoria). Este objeto puede registrar/salvar un bloque, recuperar un bloque por hash, recuperar bloques del mismo número de altura. Pero ahora le agregué recuperar bloques que tengan en mismo bloque padre, por hash del bloque padre. De esta forma, puedo recuperar los hijos inmediatos de un bloque. y también sus descendientes. Ahora puedo reconstruir desde ese almacén/store todo el árbol de bloques partiendo de uno inicial.

Vean que el estado de la blockchain tiene una implementación ingenua, sencilla: solo es un arreglo JavaScript, con índice el número de bloque, comenzando con el bloque génesis, que tiene número 0. Mi plan es refactorizar esta implementación para soportar muchos bloques, usando una lista grabada en disco. Pero por ahora, usando esta implementación ingenua, puedo armar la conducta esperada de la aplicación que estoy escribiendo.

Un método que exponso hacia afuera es agregar un bloque:

this.add = function (block) {
    if (blockstore.hasBlockHash(block.hash))
        return;
        
    blockstore.save(block);
    
    if (getUnknownAncestor(block) != null)
        return;
    
    tryAdd(block);
}

Si este bloque es conocido, entonces implica que ya fue procesado, y se sale de la función.

Si no, el bloque es agregado al almacén en memoria, y se calcula el primer ancestro no conocido, pues bien puede que todavía no haya llegado al nodo. Si hay algún ancestro desconocido, no se puede formar todavía una cadena con este bloque, y salimos de la función.

Si el bloque tiene una cadena de ancestros que se termina conectando con el bloque génesis, tratamos de agregarlo a la cadena principal:

function tryAdd(block) {
    if (isBestBlockChild(block))
        blocks.push(block);
    else if (isBetterBestBlock(block))
        tryFork(block);
        
    tryChildren(block);
}

Si es un hijo directo del mejor bloque conocido, lo agregamos a la cadena. Si no, pero es un mejor bloque (por ejemplo, porque tiene una mejor altura que la cadena principal conocida), se intenta armar un fork de la cadena, posiblemente cambiando a otra cadena mejor. En todo caso, se procesan sus hijos inmediatos: tal vez la aparición del bloque permite completar cadenas que estaban pendientes de proceso.

Estos son los predicados que uso:

function isBestBlockChild(block) {
    var bblock = self.bestBlock();
    
    return bblock.hash === block.parentHash && bblock.number === block.number - 1;
}

function isBetterBestBlock(block) {
    var bblock = self.bestBlock();
    
    return bblock.number < block.number;
}

Este es el proceso de los hijos:

function tryChildren(block) {
    var children = blockstore.getChildren(block.hash);

    for (var n in children)
        tryAdd(children[n]);
}

Noten que es una implementación ingenua que implica recursión. Puedo refactorizar el código para no usar recursión.

Dado un bloque, esta función calcula la cadena de ancestros que se une en alguna parte a la cadena principal, y cambia a esa lista como blockchain actual:

function tryFork(block) {
    var newbranch = [block];
    var parentHash = block.parentHash;
    
    while (parentHash && blockstore.hasBlockHash(parentHash)) {
        var parent = blockstore.getByHash(parentHash);
        
        if (parent.hash === blocks[parent.number].hash)
            return switchToFork(newbranch);
            
        newbranch.push(parent);
        
        parentHash = parent.parentHash;
    }
}

Ubicando el ancestro no conocido de un bloque:

function getUnknownAncestor(block) {
    var parentHash = block.parentHash;

    while (parentHash && blockstore.hasBlockHash(parentHash)) {
        var parent = blockstore.getByHash(parentHash);
        
        if (parent.hash === blocks[parent.number].hash)
            return null;
        
        parentHash = parent.parentHash;
    }
    
    return parentHash;
}

Cambiando la cadena actual a una nueva cadena:

function switchToFork(newbranch) {
    for (var n = newbranch.length; n-- > 0😉 {
        var block = newbranch[n];
        blocks[block.number] = block;
    }
}

Todos los tests pasan. Como mencioné, mi plan es ir mejorando la implementación. Pero en mi flujo de trabajo con TDD prefiero ir dando pasos pequeños (“baby steps”), y luego “make it works” para recién ahí pasar a “make it right”, “make it fast”. He tenido buenas experiencias usando este manera de ir escribiendo código, obteniendo implementaciones simples, sólidas, probadas, y fácilmente cambiables y mejorables, donde cada artefacto se agrega ante un caso de uso/test específico, en lugar de armar “arquitecturas de astronauta”.

Animado por este resultado, también refactoricé mi implementación de C#:

https://github.com/ajlopez/BlockchainSharp

Comencé a usar un almacén de bloques que usa GetByParentHash en mi código de BlockChain:

https://github.com/ajlopez/BlockchainSharp/blob/master/Src/BlockchainSharp/Core/BlockChain.cs

Escribí sobre mi anterior implementación en este post.

Nos leemos!

Angel “Java” Lopez

@ajlopez

Armando una Blockchain (10)

Thursday, May 26th, 2016

Anterior Post
Siguiente Post

Esta semana pasado, estuve agregando más lógica a mi proyecto personal de blockchain en JavaScript:

https://github.com/ajlopez/SimpleBlockchain

Como es habitual, usando el flujo de trabajo de TDD (Test-Driven Development). Un primer test creando una blockchain con un bloque genesis:

var blockchains = require('../lib/blockchains');
var blocks = require('../lib/blocks');

exports['create blockchain'] = function (test) {
    var genesis = blocks.block();
    var bc = blockchains.blockchain(genesis);
    
    test.ok(bc);
    test.equal(typeof bc, 'object');
    test.equal(bc.bestBlock(), genesis);
};

Luego, un test agregando un bloque:

exports['add block'] = function (test) {
    var genesis = blocks.block();
    var block = blocks.block(genesis);
    var bc = blockchains.blockchain(genesis);
    
    bc.add(block);
    
    test.equal(bc.bestBlock(), block);
};

El rechazo de un bloque que tiene la misma altura que la blockchain actual:

exports['add block same height'] = function (test) {
    var genesis = blocks.block();
    var block = blocks.block(genesis);
    var block2 = blocks.block(genesis);
    
    var bc = blockchains.blockchain(genesis);
    
    bc.add(block);
    bc.add(block2);
    
    test.equal(bc.bestBlock(), block);
};

Agregar un mejor bloque, y luego, el rechazo de un mejor bloque pero que no deciende del mejor bloque actual:

exports['add block with next height'] = function (test) {
    var genesis = blocks.block();
    var block = blocks.block(genesis);
    var block2 = blocks.block(block);
    
    var bc = blockchains.blockchain(genesis);
    
    bc.add(block);
    bc.add(block2);
    
    test.equal(bc.bestBlock(), block2);
};

exports['add block next height but another parent block'] = function (test) {
    var genesis = blocks.block();
    var block = blocks.block(genesis);
    var block2 = blocks.block(genesis);
    var block3 = blocks.block(block2);
    
    var bc = blockchains.blockchain(genesis);
    
    bc.add(block);
    bc.add(block3);
    
    test.equal(bc.bestBlock(), block);
};

Estos tests fueron escritos uno por uno, y cada test fue seguido de la implementación más simple que pudo hacer pasar el test. Esto es parte del espíritu de TDD: pasos de bebé. implementaciones simples, descripción explícita de la conducta esperada.

Próximos posts: implementando estados de cuentas, estados inmutables, comunicación entre nodos.

Nos leemos!

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

Armando una Blockchain (8)

Wednesday, May 11th, 2016

Anterior Post
Siguiente Post

En el anterior post, describí parte de la virtual machine que estoy incorporando a mi proyecto personal de blockchain, escrito en C#, usando el flujo de trabajo de TDD:

https://github.com/ajlopez/SimpleBlockchain

Agregué un simple compilador de bytecodes, para simplificar algunos tests. Esta clase no es necesaria en producción, pero aún así quedó en el proyecto core:

public class BytecodeCompiler
{
    private IList<byte> bytes = new List<byte>();

    public void Stop()
    {
        this.Compile(Bytecodes.Stop);
    }

    public void Add()
    {
        this.Compile(Bytecodes.Add);
    }

    public void Subtract()
    {
        this.Compile(Bytecodes.Subtract);
    }
    
    // more methods
    
    public byte[] ToBytes()
    {
        return this.bytes.ToArray();
    }
}

Lo que hace es coleccionar una serie de bytecodes, con argumentos opcionales. Y tiene un método que devuelve esa serie como un arreglo de bytes. Un simple test mostrando su uso:

[TestMethod]
public void LessThan()
{
    BytecodeCompiler compiler = new BytecodeCompiler();

    compiler.Push(2);
    compiler.Push(2);
    compiler.LessThan();
    compiler.Push(0);
    compiler.Push(1);
    compiler.LessThan();
    compiler.Push(1);
    compiler.Push(0);
    compiler.LessThan();

    Machine machine = new Machine();

    machine.Execute(compiler.ToBytes());

    var stack = machine.Stack;

    Assert.IsNotNull(stack);
    Assert.AreEqual(3, stack.Size);
    Assert.AreEqual(DataWord.Zero, stack.ElementAt(2));
    Assert.AreEqual(DataWord.Zero, stack.ElementAt(1));
    Assert.AreEqual(DataWord.One, stack.ElementAt(0));
}

También escribí un simple compilador de líneas, llamado SimpleCompiler. Lo puedo usar así en mis tests:

[TestMethod]
public void IsZeroUsingSimpleCompiler()
{
    string program = "push 2\n" +
        "iszero\n" +
        "push 0\n" +
        "iszero";

    SimpleCompiler compiler = new SimpleCompiler(program);

    Machine machine = new Machine();

    machine.Execute(compiler.Compile());

    var stack = machine.Stack;

    Assert.IsNotNull(stack);
    Assert.AreEqual(2, stack.Size);
    Assert.AreEqual(DataWord.Zero, stack.ElementAt(1));
    Assert.AreEqual(DataWord.One, stack.ElementAt(0));
}

Estas son clases auxiliares, pero me simplificaron la escritura de algunos tests. Es parte de mi estrategia, el que la escritura de tests sea facilitada con el tiempo. Al principio escribo tests que involucran mayor trabajo, pero luego, en algún momento, ya apoyado en esos tests de base, los próximos se pueden escribir con más confianza usando estas clases auxiliares.

En el próximo post, una sorpresa: nuevo proyecto de blockchain, pero esta vez en mi querido NodeJS y JavaScript.

Nos leemos!

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

Armando una Blockchain (7)

Tuesday, May 3rd, 2016

Anterior Post
Siguiente Post

En mi proyecto personal de blockchain:

https://github.com/ajlopez/BlockchainSharp

quiero tener la capacidad de ejecutar código, lo que se llama codechain, o smart contracts. He adoptado las ideas de la máquina virtual de Ethereum (ver el Yellow Paper de Ethereum). Algunas clases:

Un DataWord representa un número usando 32 bytes. He implementado la aritmética de esos números usando internamente como base a System.Numerics.BigInteger. Puedo crear DataWord desde enteros y arreglos de bytes.

El Stack es una pila de DataWords, manipulada por la máquina virtual en Machine. Hay una enumeración de los bytecodes implementados, llamada justamente Bytecodes, que se ejecutan en la Machine. Un programa consiste en una serie de bytecodes, contenidos en un arreglo de butes. El método Machine.Execute es donde esos bytecodes son recorridos y ejecutados, modificando una pila, y próximamente, el Storage del contrato, y la Memory asociada en el momento de ejecución. Fragmento del código:

public void Execute(byte[] bytecodes)
{
    int pc = 0;
    int pl = bytecodes.Length;

    while (pc < pl)
    {
        byte bytecode = bytecodes[pc++];

        switch (bytecode)
        {
            case (byte)Bytecodes.Stop:
                return;
            case (byte)Bytecodes.Add:
                this.stack.Push(this.stack.Pop().Add(this.stack.Pop()));
                break;
            case (byte)Bytecodes.Multiply:
                this.stack.Push(this.stack.Pop().Multiply(this.stack.Pop()));
                break;
            case (byte)Bytecodes.Subtract:
                this.stack.Push(this.stack.Pop().Subtract(this.stack.Pop()));
                break;
                
            // more operations
        }
    }
}

El Storage estará asociada a la cuenta del contrato en ejecución. Cada contrato es una cuenta, como otras, con una dirección, balance, código inmutable, y almacenamiento. La Memory es transitoria, y solo se aloca para la ejecución de un contrato, y no persiste más allá de la ejecución. En cambio, el Storage sí vive más allá de cada ejecución, y es parte del estado del mundo que cada nodo de la red debe mantener por cuenta..

Tengo también un simple compilador de bytecodes, y un compilado de comandos por línea, para facilitar la creación de programas, y todo escrito usando el flujo de trabajo de TDD (Test-Driven Development).

Próximos posts: descripción del Storage, Memory, compiladores, ejecución de bloques y transacciones, y almacenamiento persistente.

Nos leemos!

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

Armando una Blockchain (6)

Tuesday, April 26th, 2016

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

Armando una Blockchain (5)

Monday, April 18th, 2016

Anterior Post
Siguiente Post

En esta semana pasada estuve trabajando bastante en mi proyecto personal:

https://github.com/ajlopez/BlockchainSharp

implementando una blockchain en C#, usando como es habitual el flujo de trabajo de TDD (Test-Driven Development). Un elemento que necesito implementar es el almacenamiento (store) de los estados de cuentas (en principio, voy a guardar sus balances). El balance de una cuenta debe poder ser recuperado por el id de la cuenta (un hash). Pero en muchos casos de uso, necesito conocer los balances de algunas cuentas a UN INSTANTE dado de tiempo. No es suficiente tener los balances de las cuentas al tiempo actual, solamente. Para cumplir con esos casos de uso, implementé un trie pero inmutable.

Un trie es un árbol donde los nodos no terminales almacenan parte de la clave:

En el ejemplo de la figura de arriba, el valor V1 está asociado con la clave AAA, y el valor V5 está asociado con la clave ACA. CUando cambio el valor asociado con la clave ABC desde V4 a V7, entonces, un nuevo trie es creado, manteniéndose el anterior sin modificarlo:

Puedo acceder el par original clave/valor usando a partir de la “vieja raíz” del trie, y cuando necesito valores más actuales, cambio a usar la “nueva raíz” del nuevo trie. Si algún nodo o porción del árbol queda inalcanzable, el recolector de basura liberará la memoria asociada.

La implementación del trie que tengo ahora es simple, y está basada en haber escrito los test e implementado el código que hace que los test pases. Un ejemplo de test:

[TestMethod]
public void PutAndGetKeyValue()
{
    Trie<string> trie = new Trie<string>();

    var trie2 = trie.Put("012", "foo");

    Assert.IsNotNull(trie2);
    Assert.AreNotSame(trie2, trie);
    Assert.IsNull(trie.Get("012"));
    Assert.AreEqual("foo", trie2.Get("012"));
}

Otro ejemplo que muestra que los tries mantienen sus valores, aún cuando se crean nuevos tries en cada actualización:

[TestMethod]
public void ReplaceValue()
{
    Trie<string> trie = new Trie<string>();

    var trie2 = trie.Put("012", "foo");
    var trie3 = trie2.Put("012", "bar");

    Assert.IsNotNull(trie2);
    Assert.AreNotSame(trie2, trie);
    Assert.IsNull(trie.Get("012"));
    Assert.AreEqual("foo", trie2.Get("012"));

    Assert.IsNotNull(trie3);
    Assert.AreNotSame(trie3, trie2);
    Assert.AreEqual("bar", trie3.Get("012"));
}

Mi idea es usar Trie<AccountState> como un almacén de los estados de cuenta. Al final del proceso de cada bloque y de cada transacción, habrá un trie de estado de cuentas. Y al final del siguiente bloque, habrá otro trie, con los balances actualizados. En cualquier momento, puedo recuperar los estados de las cuentas al final de bloque 1, o al final del bloque 2, partiendo de los respectivos tries.

Nos leemos!

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

Armando una Blockchain (3)

Wednesday, April 6th, 2016

Anterior post
Siguiente post

Agregué bastante código en estos días a mi simple implementación de una blockchain, escrita en C#:

https://github.com/ajlopez/BlockchainSharp

Como es usual, seguí el flujo de trabajo de TDD (Test-Driven Development), persiguiendo también la simplicidad en cada paso, haciendo baby steps, avanzando de a poco pero firme. En el anterior post mencioné el uso de un DSL (Domain Specific Language) para especificar algunos procesos de bloques que tienen un preparado (setup) largo.

Inicialmente, escribí test como:

[TestMethod]
public void ProcessTwoBlocksAndTwoUncles()
{
    Block genesis = new Block(0, null);
    Block block = new Block(1, genesis.Hash);
    Block uncle1 = new Block(1, genesis.Hash);
    Block uncle2 = new Block(2, uncle1.Hash);

    BlockProcessor processor = new BlockProcessor();

    processor.Process(genesis);
    processor.Process(block);
    processor.Process(uncle1);
    processor.Process(uncle2);

    Assert.IsNotNull(processor.BlockChain);
    Assert.AreEqual(2, processor.BlockChain.BestBlockNumber);
    Assert.AreEqual(genesis, processor.BlockChain.GetBlock(0));
    Assert.AreEqual(uncle1, processor.BlockChain.GetBlock(1));
    Assert.AreEqual(uncle2, processor.BlockChain.GetBlock(2));
}

La idea es:

– Crear algunos bloques
– Enviar los bloques al procesador de bloques
– Revisar los bloques que entonces quedaron en la blockchain

Los bloques creados están vinculados por relaciones de padre-hijo. Algunas veces, un bloque competitivo es creado, y el procesador de bloques debe manejar la existencia de ramas alternativas, que compiten por llegar a ser la nueva blockchain.

El preparado, setup del código puede ser largo. Así que en unos minutos pude escribir un DSL que me permite escribir tests como:

[TestMethod]
public void SendTwoBlocksAndTwoUncles()
{
    var processor = new BlockProcessor();
    var dsl = new BlockProcessorDsl(processor);

    dsl.Run(new string[] 
    {
        "chain g0 b1 b2",
        "chain b1 c2 c3",
        "send b1 b2",
        "send c2 c3",
        "top c3"
    });
}

Cada comando del DSL es un texto, con verbo y argumentos. El bloque g0 es el bloque génesis. El verbo “chain” enumera una lista de bloques a ser creados, cada uno es bloque hijo del anterior. El verbo “send” envía los bloques creados en memoria al procesador de bloques. El verbo “top” verifica que el bloque especificado sea el mejor de la blockchain actual.

El resultado no debe depender del orden de llegada de los bloques, por ejemplo:

[TestMethod]
public void SendTwoBlocksInReversedOrder()
{
    var processor = new BlockProcessor();
    var dsl = new BlockProcessorDsl(processor);

    dsl.Run(new string[] 
    {
        "chain g0 b1 b2",
        "send b2 b1",
        "top b2"
    });
}

Podría agregar texto en archivos, cada uno representado un test, y ejecutarlos todos desde código. Por ahora, sigo con estos tests explícitos en código.

En los próximos posts: las implementaciones esenciales del problema (bloque, transacción, estado…) que son necesarios para armar  una block chain, serialización (a bytes), e implementaciones como tries inmutables para almacenar los estados mutables.

Nos leemos!

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