Firmador de documentos PDF para Firma Digital de Costa Rica

En artículos anteriores había publicado cómo firmar documentos utilizando un firmador de escritorio usando software libre (dss-standalone). La herramienta era relativamente difícil de configurar, así que he creado mi propia herramienta y la publico también bajo una licencia de software libre. El nombre no es muy original, se llama firmador-pdf.

Esta herramienta cubre una necesidad personal pero también espero que resulte útil para todas las personas interesadas en firmar documentos PDF que cumplan la Política de Formatos Oficiales de los Documentos Electrónicos Firmados Digitalmente publicada por el MICITT.

Las ventajas con respecto a la anterior herramienta es que obtiene la cadena de certificados automáticamente y supera la validación en herramientas de terceros como LibreFirma. La descarga es relativamente ligera (13 MB) y requiere tener instalado Java 8 y los controladores de Soporte Firma Digital.

La firma generada cumple con el perfil estándar PAdES Baseline LTA (equivalente a PAdES-LTV).


El código fuente se puede obtener en mi repositorio de firmador-pdf en GitHub. La licencia del programa es GPL-3.0.

Para ejecutarlo en Windows hay que hacer doble clic en el archivo jar. Para ejecutarlo en GNU/Linux hay que teclear en una terminal java -jar Descargas/firmador.jar. Para ejecutarlo en macOS hay que hacer desde la carpeta de descargas en el Finder sobre el icono clic derecho -> abrir la primera vez, las siguientes ya se puede hacer doble clic sobre el archivo.

On crowdsales and multiple inheritance

On 2017 we saw a mind-blowing number of crowdsales and ICOs running in the Ethereum blockchain. They have proven to be a powerful tool to collect the funds required to start a project, and they are one of the most common uses for smart contracts right now. The Zeppelin team has been very involved in this topic, auditing many crowdsale contracts, and supporting OpenZeppelin, the most popular framework to build these crowdsales. What we found was a lot of contracts repeating the same basic concepts, with very similar code, and common vulnerabilities.

Earlier this year, part of the team took the task to redesign our base Crowdsale contract in order to support a lot of new crowdsale flavors out of the box and to make the experience of building and publishing your own crowdsale more clear and secure. The idea is to make audits less necessary, with an architecture that is modular, that encourages reuse and that will be collectively reviewed. These new contracts were added to OpenZeppelin version 1.7.0, and since the release they have been widely used by our community with great success. So, this is a good moment to show off :)

Let's start with a dive into the source code of the Crowdsale base contract. The first thing you will notice is a lot of comments, guiding you through the details of the OpenZeppelin crowdsale architecture. They explain that some functions are the core of the architecture and should not be overriden, like buyTokens. Some others like _preValidatePurchase can be overriden to implement the requirements of your crowdsale, but that extra behavior should be concatenated with the one of the parent by calling super, to preserve the validations from the base contract. Some functions like _postValidatePurchase can be just added as hooks in other parts of the crowdsale's lifecycle.

Building on top of this base, we now provide some contracts for common crowdsale scenarios involving distribution, emission, price, and validation.

So, let's say that you want to set a goal for your crowdsale and if it's not met by the time the sale finishes, you want to refund all your investors. For that, you can use the RefundableCrowdsale contract, which overrides the base _forwardFunds behavior to send the funds to a fancy RefundVault while the crowdsale is in progress, instead of sending them directly to the wallet of the crowdsale owner.

Another common scenario is when you want the tokens to be minted when they are purchased. For that, take a look at the MintedCrowdsale contract, which overrides the simple _deliverTokens behavior of the base class to call instead the mint function of an ERC20 Mintable token.

What if we want to do something more interesting with the price of the tokens? The base Crowdsale contract defines a constant rate between tokens and wei, but if we override _getTokenAmount, we could do something like increasing the price as the closing time of the crowdsale approaches. That's exactly what the IncreasingPriceCrowdsale contract does.

To get started developing and deploying a crowdsale using the OpenZeppelin framework, Gustavo Guimaraes published a nice guide where you will see in action a crowdsale that is timed and minted.

These are just a few examples. I invite you to explore the OpenZeppelin crowdsale contracts to see all the new flavors that you can easily use to fund your cool idea; and take a look at the SampleCrowdsale contract, a more complex scenario that comes with full test coverage. If you like OpenZeppelin, remember that you are welcome into our vibrating community to help us adding new contracts or improving the existing ones.

On the repo you will also find that all these contracts are very well tested. And as we have seen, the new architecture is clearer and safer, with each contract explaining the functions that you can or can't override, and how to do it. However, you should be extra careful when combining them. It's not the same to have three contracts with one condition than to have one contract with three conditions. The combination increases the attack surface, so you need to have a good idea of what you want to achieve, and know the implementation details of the tools you are using.

Before you go ahead and deploy a complex crowdsale that combines some of our contracts, I would like to spend some time going deep into how Solidity works when you combine contracts through multiple inheritance, like Gustavo did in his guide to make a contract that inherits from TimedCrowdsale and from MintedCrowdsale.

Multiple inheritance is hard, and it can get very confusing if you abuse it. Some languages don't support it at all; but it is becoming a very common pattern on Solidity.

The problem, from the point of view of the programmer, is to understand the order of the calls when a contract has multiple parents. Let's say we have a base contract A, with a function named f:

contract A {
  function f() {

Then we have two contracts B and C, both inherit from A and override f:

contract B is A {
  function f() {

contract C is A {
  function f() {

And finaly we have a contract D, which inherits from B and C, and overrides f too:

contract D is B, C {
  function f() {

What happens when you call D.f?

This is called the diamond problem, because we end up with a diamond-shaped inheritance diagram:

Diamond inheritance problem

(the image is from wikipedia)

To solve it, Solidity uses C3 linearization, also called Method Resolution Order (MRO). This means that it will linearize the inheritance graph. If D is defined as:

contract D is B, C {}

then D.f will call:


When D inherits from B, C, the linearization results in D→ C→ B→ A. This means that super on D calls C. And you might be a little surprised by the fact that calling super on C will result on a call to B instead of A, even though C doesn't inherit from B. Finally, super on B will call A.

If D is instead defined as:

contract D is C, B {}

then D.f will call:


When D inherits from C, B, the linearization results in D→ B→ C→ A.

Notice here that the order in which you declare the parent contracts matters a lot. If the inheritance graph is not too complex, it will be easy to see that the order of calls will follow the order in which the parents were declared, right to left. If your inheritance is more complicated than this and the hierarchy is not very clear, you should probably stop using multiple inheritance and look for an alternate solution.

I recommend you to read the wikipedia pages of Multiple inheritance and C3 linearization, and the Solidity docs about multiple inheritance. You will find in there a complete explanation of the C3 algorithm, and an example with a more complicated inheritance graph.

To better understand how this can impact your crowdsales, take a look at the case that Philip Daian brilliantly explains on his blog post Solidity anti-patterns: Fun with inheritance DAG abuse. There, he presents a Crowdsale contract that needs "to have a whitelist pool of preferred investors able to buy in the pre-sale [...], along with a hard cap of the number of [...] tokens that can be distributed." On his first (deliberately) faulty implementation, he ends up with a crowdsale that checks:

((withinPeriod && nonZeroPurchase) && withinCap) || (whitelist[msg.sender] && !hasEnded())

Pay close attention to the resulting condition, and notice that if a whitelisted investor buys tokens before the crowdsale has ended, she will be able to bypass the hard cap, and buy as many tokens as she wants.

By just inverting the order on which the parents are defined, he fixes the contract which will now check for:

(withinPeriod && nonZeroPurchase || (whitelist[msg.sender] && !hasEnded())) && withinCap

Now, if the purchase attempt goes above the cap, the transaction will be reverted even if the sender is whitelisted.

There are some simple cases, where the order of the conditions is not important. By now, you should have started to suspect that the contract above became complicated because of the || (or) condition. Things are a lot easier when all our conditions are merged with && (and), because in that case the order of the conditions doesn't alter the result.

Our new architecture was crafted for using require instead of returning booleans, which works nicely to combine and conditions, and to revert the transaction when one fails. Let's say that we have a crowdsale that should only allow whitelisted investors to buy tokens while the sale is open and the cap has not been reached. In this case, the condition to check would be (in pseudocode):

require(hasStarted() && !hasEnded())

Our contract would be as simple as:

pragma solidity ^0.4.18;

import "zeppelin-solidity/contracts/crowdsale/validation/TimedCrowdsale.sol";
import "zeppelin-solidity/contracts/crowdsale/validation/WhitelistedCrowdsale.sol";
import "zeppelin-solidity/contracts/crowdsale/validation/CappedCrowdsale.sol";

contract WhitelistedTimedCappedCrowdsale is TimedCrowdsale, WhitelistedCrowdsale, CappedCrowdsale {

  function WhitelistedTimedCappedCrowdsale(
    uint256 _rate,
    address _wallet,
    ERC20 _token,
    uint256 _openingTime,
    uint256 _closingTime,
    uint256 _cap
    Crowdsale(_rate, _wallet, _token)
    TimedCrowdsale(_openingTime, _closingTime)


It doesn't matter how you order the parents, all the conditions will be checked always. But take this with a grain of salt. All the conditions will be checked, but the order will be different. This can have different side-effects on Solidity, as some paths will execute statements that other paths won't, and it could lead an attacker to find a specific path that is vulnerable. Also, if your parent contracts are not super clear, they might be hiding an || condition in a few hard-to-read code statements.

It's very easy to think that the parent contracts will just be magically merged into something that will make sense for our use case, or to make a mistake when we linearize them in our mind. Every use case will be different, so our framework can't save you from the work of organizing your contracts' hierarchy. You must analyze the linearization of the inheritance graph to get a clear view of the functions that will be called and their order, and always always add a full suite of automated tests for your final crowdsale contract to make sure that it enforces all the conditions.

To finish, I wanted to show you my repo where I will be doing experiments with crowdsales and their tests:

Take a look at my PreSaleWithCapCrowdsale contract. You will see that I preferred to be explicit about the conditions instead of using super:

function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal {
  require(_beneficiary != address(0));
  require(_weiAmount != 0);
  require(block.timestamp >= openingTime || whitelist[_beneficiary]);
  require(block.timestamp <= closingTime);
  require(weiRaised.add(_weiAmount) <= cap);

I encourage you to join me on these experiments, to try different combinations of crowdsales and to play switching the order of the inheritance graph. We will learn more about the resulting code that Solidity compiles, we will improve our mental picture of the execution stack, and we will practice writing tests that fully cover all the possible paths. If you have questions, find errors on the implementations in OpenZeppelin, or find an alternative implementation that will make it easier to develop crowdsales on top of our contracts, please let us know by sending a message to the slack channel. I am elopio in there.

A relevant experiment here is to use composition over inheritance. In OpenZeppelin we couldn't implement an architecture based on composition because of the high gas costs implied, especially during deployment. That is one of the reasons we are now hard at work on zeppelin_os, which minimizes deployment costs by helping you use libraries that are already on-chain. Expect exciting news very soon!

Thanks a lot to Ale and Alejo, who worked on these new contracts and helped me to understand them. <3

Cambiar el mundo ¿Es posible?

Si ustedes han estado conmigo los últimos meses creo que se han dado cuenta de que este ha sido un a;o diferente para mi en algunas cosas demasiado mal, pero el 90% del tiempo ha ido demasiado bien, la Comunidad Arduino Guatemala esta creciendo, he estado ayudando a mas personas con sus problemas tecnológicos (extra;o porque hace un par de a;os era otro tipo de ayuda la que normalmente ofrecía, pero bueno esos son otros 20 pesos :V), realmente ha sido un inicio de a;o espectacular trabajo aquí, trabajo allá, conferencia aquí, conferencia allá, ahora el que ha cambiado de cierta manera he sido yo, ya no comparto tanto en lo digital pero si me he vuelto mas allegado a las personas reales mas a un grupo de amigos de CCCGT.
No se, creo que crecer ya casi cumplo los 24 me ha hecho mas reservado con que cosas comparto en internet y cuales no, pero heme aqui compartiendo con todos ustedes algo que a día de hoy 27 de febrero, no puedo no compartirlo ( en eso me desahogo y tal vez llore, pero eso no lo sabrán porque están del otro lado de la pantalla, pero se que mis lectores van a intuirlo por las palabras que utilizare), Gracias a Luis Olivet por recomendarme compartirlo.
Pero bueno muy pocos saben que formo parte de un emprendimiento llamado Antigua Makerspace, el cual a groso modo esta orientado a utilizar STEEM (science, technology, engineering, entreprenuership and Math) ya un año desde que iniciamos este viaje y si ustedes piensan que es publicidad pues pueden cerrar la ventanita y seguir con sus vidas. No suelo contar mis proyectos personales por la misma razon que muchas personas lo suelen tomar a mal, pero en realidad hay muchas veces que no se como hacerlo. Bueno dejaremos mis problemas mentales para otra publicación jajaja.
Todo empezó con un mensaje, el cual decía que si podíamos apoyar para dar un taller de Robótica  a una escuela de niños con discapacidad visual( total, parcial, visión baja) la cual unas horas antes de llegar supe que era la escuela Santa Lucia( como el pueblo donde vivo ) del Comité pro ciegos y sordos. Si por aquí va la cosa, pero créanme no es el típico post donde uno se sorprende de que los niños con discapacidad tienen las mismas capacidades cognitivas que los demás, mi publicación va un poco mas allá.
Y como siempre digo que si, me gustan los retos aparte de que nunca digo que no, asi que allí empezaba el reto, el mensaje decía son 25 niños ciegos y yo en mi cabeza “Mierda, si me cuesta con niños ahora con niños ciegos me va a costar mucho mas(algo de lo cual me iba a sorprender mas adelante)”, pero como siempre he dicho la mejor manera de aprender es enseñar así que me puse en contacto con mi llamada de emergencia, para que me hiciera ganas para poder ir a la capital en la madrugada. Un par de tropiezos en nuestra logística para movernos la noche del 26 pero estábamos seguros de que de cualquier manera íbamos a ir.
Estuve bastante emocionado, durante la noche que no podía dormir ( aparte de que tenia un dolor del cuello tan feo que no podía voltear a ver a ningún lado, aun lo tengo pero ya es menos), yo y mis planes perfectos donde todo sale bien decían irse a dormir a las 10 para despertarse a las 4 bañarse e irse, la realidad fue otra, dormir a las 12:30 despertar a las 4:30 y levantarse a las 5:10, aún no estaba seguro el transporte hasta que veo el mensaje, ya voy para allá. Como buen trabajador me fui sin desayunar, madrugando por una cosa estúpida llamada trafico, yo soy del pensamiento de madrugar para viajar 300kms es una buena idea; madrugar para viajar 30-40 kms es una estupidez.
Llegando a la escuela que de fuera parece algo súper chiquito, del tamaño de unos cuantos cuartos y dos niveles, pensé: “tengo hambre”, así que fuimos a un canasto de comida que estaba en la esquina a “desayunar” con mi amigo MDR, con la pena de que de verdad yo no tenia idea de como iba a ser la cosa, fui y hable con el guardia que no tenia ni idea de que le estaba diciendo no porque no entendiera, sino que no estaba enterado de la actividad así que decidimos esperar, una hora para ser exactos, los que me  han intentado contactar saben que yo sin acceso a internet no funciono así que estaba pidiendo wifi del cel de MDR, nos acabamos 60 megas en 20 minutos ya solo nos quedaban 40 para el resto del día jaja eran las 8 am.
Ya cansado y con sueño decidí ir con el guardia e inventarle la casaca de que quería hablar con la directora aunque no sabia si era hombre o mujer el encargado, asi que le dije: “poli, mire no esta la directora es que ya es tarde”, pura casualidad fue que si era mujer xD le conté que eramos los de robótica y pues nos dejo pasar.
Era un centro educativo bastante grande, muy limpio y acogedor realmente amplio y hasta tenia parqueo en la parte de atrás. (adiós prejuicio 1), ah olvide mencionar que este proyecto es conjunto con la secretaria nacional de ciencia y tecnología, SENACYT. Realmente esperaba una mejor bienvenida de parte de uno de sus miembros, pero creo que se molesto al ver que tenia el pelo verde e iba con playera, pantalón y tenis, mientras el iba con su traje super presentable. Cuestión de gustos y comodidad supongo, que bueno que me quite los aretes del labio y no tengo tatuajes, sino ni me deja participar. No es la primera vez que me topo con personas así, así que lo mejor es ser profesionales y hacer bien tu trabajo que para eso fue que te llamaron, cuando entro en modo trabajo me irrito fácilmente cuando se meten con lo que hago, no hubo nunca discusiones ni nada solo se sentía la mala vibra. ( aclaro de una vez que de ninguna manera me insulto ni hizo ningún desprecio, fue mas como subestimacion hacia mi persona), empezamos a sacar los robots y yo no tenia ni idea de como hacerle para explicar a los niños, se me acerco el encargado y nos empezó a explicar como había sido el taller anterior ( si este era el segundo y yo aun trataba de descifrar como le hicieron el anterior).
Empezaron a llegar los niños se sentaron, empezaron a tocar la mesa cuando objeto robotico no identificado aparece. IMG_1266
Al igual que todo niño con juguete nuevo no querían soltarlo ni compartir con nadie, pero hubieron algunos veteranos ( que ya habían tomado el curso) que dijeron a su compañero mas cercano, “mira estos son los ojos” y le ponían las manos al compañero en el sensor de proximidad. Eso fue lo mejor de la mañana, compartir lo que saben y enseñar. Dejamos que los niños jugaran y como buenos niños hacían bulla y no ponían atención y aquí mi miedo porque no sabia si iban a entender o no lo que les iba a decir.
Hubo un acto protocolario pequeño y fugaz agradeciendo a todos y bueno empezamos
“Hola niños mi nombre es Yeffri soy su profesor de robótica y tengo el pelo verde”, instantáneamente las risas de los niños en ese momento se fue mi miedo y empezó mi seguridad, seguía sin saber que hacer pero estaba seguro de que iba a funcionar.
Les dimos los des armadores sin instrucciones de como desarmarlos y pum.
Entre tanto y tanto empezaron a desarmar los robots, había de todo niños que se les caían los tornillos, niños que los guardaban en el pantalón y luego estaban ellos. Aunque he de admitir que normalmente se pierden 8-10 tornillos en este caso de todos los robots se perdio 1 tornillo.


Me estoy centrando en cosas que me sorprendieron, la mayoría buenas pero no todo es bueno mas adelante vamos a verlo.

Al final pues la idea era primero memorizar o conocer el robot, desarmarlo y luego volver a armarlo. y que creen Si es posible armarlo sin ver nada. Yo no puedo hacerlo.

Hubieron algunas cosas como el hecho de que no conocían que el desarmado tenia dos cabezas una de cruz y la otra hexagonal así que perdieron bastante tiempo intentando atornillar con la cabeza equivocada.


Armados ya los robots pos vamoaprogramar


y he aquí el mayor de los problemas de la inclusión de personas con discapacidad que si bien es cierto que estamos tan avanzados que ya podemos hablar con una cosa para decirle que compre papel de baño por amazon, no hemos podido crear un software lo suficientemente bueno para personas ciegas, aquí hay un gran problema y es que muy pocos estamos trabajando en ello, digo trabajando por Sonar.

Normalmente para programar a los Mbots se utiliza mblock que es un software basado en Scratch y S4A, bastante bueno e intuitivo, estos de makeblock le han metido bastante dinero a esto. mblock, no funciona con el text to speech de windows XP, baaaaaad. Entonces lo que hicimos fue abrir el bloc de notas y con el text to speech de la computadora pudimos esquivar algunas cosas, pero de nuevo mas problemas el código esta en ingles con funciones, programación pura que los niños jamas en la vida habían escuchado. Vomo pude y con ayuda de los profesores pudimos liarla y logramos programar el Mbot para utilizarlo como detector de obstáculos. Nuestras mentes de adultos siempre intentando meterles conocimiento mientras ellos lo único que quieren es jugar( quédense con esta frase).

Todo esta bien hasta que te empiezas a fijar bien en los detalles, como toda sociedad hay personas que no se incluyen aquí también, pues los niños con ceguera total se mantenían sentados y callados mientras los demás gritaban y jugaban en el piso con los robots.

Esta nena estuvo parada por 5 minutos esperando a que alguien le hablara.


Les enseñamos algunas cosas que imprimimos en 3D como una etiqueta braille que podían leer perfectamente.

Bueno ya terminado el curso era hora de los diplomas, yeei


Asi que gracias a SENACYT los niños pudieron tener un robot en sus manos.

Y los niños ya pudieron volver a ser niños y jugar con los robots el resto del día.

Teniendo en cuenta que no se dispone de herramientas necesarias para que la inclusion de personas con discapacidad en muchos ámbitos aparte de las laborales, estamos hablando de cosas sociales, lúdicas y sobre todo de aprendizaje, cuando esas barreras se rompan o podamos saltarlas por cualquier medio estoy seguro que vamos a poder cambiar el mundo, es una necesidad del mundo. Yo por mi parte seguiré luchando intentando hacer el mundo un poco mas humano y mas accesible para todas las personas, con proyectos como estos con sonar, la matriz braille etc.

Realmente hay muchos proyectos que tienen el potencial de cambiar el mundo, luego están los proyectos que gracias a SENACYT buscan cambiar el mundo de los olvidados, de los despreciados dándoles herramientas para que dentro de algunos años puedan hacer algo con este conocimiento. El proximo taller es con niños con discapacidad auditiva, seguire informando que tal nos va.

Si ustedes me preguntan a dia de hoy y con esta experiencia si es posible cambiar el mundo yo puedo decir abiertamente SI juntos podemos lograrlo.

Asi que los dejo con esta  hermosa sonrisa para que puedan sentirse mejor.


PD: al terminar de redactar esta publicación estaba lleno de lágrimas, malditas injusticias, los niños no tienen porque nacer asi. Pero estamos hablando de que cerca del 10% de la población guatemalteca tiene alguna discapacidad y la mayoría es discapacidad visual producida por falta de control pre y post natal, pero es que como quieren que las personas lleguen a comprar prenatales y pastillas a las farmacias caminando kilómetros si no tienen ni para comer, realmente esto es una cadena y me siento bien basura al pensar que no puedo realmente ayudar de una mejor manera, asi que lo mejor que puedo hacer es seguir compartiendo el conocimiento que tengo y tratar de nunca despreciar a nadie. Sé que es un placebo para que mi corazón no sufra, pero aun no se como ayudarlos.

Error programadora ESP8266-01: Solucion.

Houston tenemos un problema. La programadora barata que encontramos en ebay no funciona 😦


Holi, Bienvenidos de nuevo a the microfcontrol, ha sido un año bastante bueno en Guatemala con respecto a tecnología e internet de las cosas y especialmente para la comunidad que mañana 24 de febrero tendrá su primera reunion, Asi que nos vemos mañana en Teclab, edificio Tec, 4 grados norte, Junto con Giangoolabs Yeii.

Entrando en materia, hace un año aproximadamente que Oscar Leche (Fundador de la comunidad) me dijo, mira esta programadora para el ESP esta cool, yo ya llevaba cerca de un año de estar jugando con los ESP8266 (ay, como pasa el tiempo… Gracias al sensei kestler por haberme iniciado en el camino del IoT ), la cosa es que no pude hacer nada con ella, ya que no había ni nombre de la placa ni información, pero gracias a internet y un poco de research, logré dar con la solución.

Bueno antes vamos a poner un poco en contexto que es el ESP8266, bueno ya llevamos poco mas de dos años desde que empezamos a hablar sobre IoT en Guatemala con la comunidad y con los miembros, yendo y viniendo de departamento en departamento incentivando a jóvenes (algunos universitarios, otros como yo que no lo son) a que inventen y se unan a la ola de internet de las cosas. Pueden leer un poco mas en esta publicación. Dos años después de verdad ha dado frutos, he recibido solicitudes de ayuda de 4-5 tesis, he visto proyectos de IoT materializarse y realmente es gratificante como esta cambiando el movimiento.

Bueno y toda esta casaca para que es, pues los primeros dispositivos de IoT que empezamos a usar fueron efectivamente los ESP8266, del cual pueden ver que casi todo lo que hay en este blog con WiFi esta hecho con ello, es uno de los favoritos a nivel mundial por la comunidad maker por el costo principalmente, pero tiene sus ventajas grandes.

De fabrica vienen para programarse o interactuar con LUA, que tambien tengo una guia lo interesante es que los makers vieron su potencial asi que ahora ya podemos utilizar micropython o arduino para poder utilizarlo, haciendo un Flasheo a la memoria e insertando un binario nuevo con el código, en micropython solo se flashea una vez para que se pueda ejecutar el interprete y luego por comunicación serial o por medio de wifi para poder reprogramarlo necesitamos que el pin0 cuando se enciende el ESP8266 reciba un pulso de GND, esto lo podemos hacer bastante bien con un jumper o un pedazo de alambre, lo mejor es usar un boton para ponerlo en modo programming.

El problema

El problema con la programadora es que sirve unicamente para poder comunicarnos via serial a el esp8266, cuando queremos usarla para reprogramar no funciona porque no esta en modo programar.


Poner un jumper entre el pin 0 y GND, para así poder ponerlo en modo programar el problema con soldar directamente un cable entre gnd y pin0 es que cada vez que conectemos nuestro esp8266 va a entrar en modo programación y no sabremos si nuestro codigo funciona o no hasta que recarguemos el Binario.

Solución de la solución


Un botón, un pin conectado a GND y el otro a pin0 y se acabo el problema.

Quedaría algo así:


Instagram Photo

y pues bueno realmente no es la gran solución, pero es La solución así que cualquier cosa por favor háganla saber.

Nos vemos en la siguiente publicación.

Y recuerden solo necesitan una excusa para cambiar el mundo ❤

PD: se me olvido mencionar que este modelo de ESP8266 es el mas barato de todos asi que es mejor que compren una sola quemadora y luego tengan una placa especificamente hecha para poder usarlo despues, les va a salir mas barato que un nodemcu y aprovechan mejor el hardware.


Quipus incas y cifrado con factorización de números primos

Dibujo de un inca y su quipu

Según este sabio [Nordenskiöld], los indios colocaban en las tumbas únicamente quipus con números que a sus ojos tenían valor mágico, expresándolos no en forma directa sino mediante otros que los incluyen o que son sus múltiplos y tratando de hacerlos coincidir con la numeración resultante de cálculos obtenidos de la consulta de los astros. [...]. La finalidad que llevó a los indios a tal práctica fue entretener, mediante estos complicados «rebus», a los espíritus malignos, quienes se esforzarían para desanudar las cuerdas y encontrar esta numeración mágica [...].

en Estudio sobre los quipus, de Carlos Radicati di Primeglio.

Y así fue como lxs incas inventaron el cifrado con factorización de números primos, la base de todas las comunicaciones seguras y criptomonedas de hoy. :D

Domotica con Telegram: Como utilizar un chatbot para controlar cosas conectadas a internet

Holi, lo sé no es el mejor titulo del mundo, pero mi compañero (me robo la idea ). Pero bueno a dejar de llorar y empezar a crear,

El dia de hoy vamos a crear un chatbot para poder controlar nuestras cosas conectadas a internet por medio de MQTT y python. WUJU

Lo que necesitamos:

  • Telegram app
  • Python3
  • Mosquitto
  • Arduino IDE
  • ESP8266
  • Ledes
  • DHT11

Vamos a empezar por lo que ya conocemos y es la parte electronica, hay una serie de complicaciones con respecto a nuestro querido ESP8266, es que no es tolerante a 5v, por lo que tendremos que tener bastante cuidado a la hora de meterle sensores analogicos o señales de 5v, podemos utilizar un divisor de voltaje o un nivelador de voltaje.


Necesitamos Algunas librerias, a estas alturas del partido no creo que sea necesario que tener que explicar como instalar las librerias, pero si eres nuevo en mi blog te recomiendo que leas la serie de IOT de mi blog y estés en lo ultimo a nivel mundial como nosotros.( ja, ja, ja( asi se escribe la risa xD))

La logica del codigo es la siguiente, por medio de WIFI, nos conectamos a un broker ( en este caso local) pero puede ser en la nube aunque uno local funciona muy bien, se suscribe a los topicos para controlar las salidas en este caso /salida1, hasta la /salida4 recibiendo un CHAR o Caracter 0 o 1 para apagar y encender respectivamente. luego veran que la luz de su esp8266 azul se queda parpadeando, eso quiere decir que funciona correctamente el codigo, cada 5 segundos envia el estado del sensore humedad y temperatura respectivamente.


 Este sketch es para controlar 4 salidas digitales distintas
 tambien tiene un dht11 para enviar la temperatura y un bluetooth para conectarse de manera local.

 NodeMCU v1.0
 Modulo Blutooth hc-06 o hc-05
 4 modulos de relay o relays
 led amarillo, rojo.
 resistencia 10k para el dht11

Yeffri J. Salazar
 Hackerspace Xibalba y Comunidad Arduino Guatemala

 ** Librerias **
#include "DHT.h"
 ** constantes **
#define salida1 D5
#define salida2 D6
#define salida3 D7
#define salida4 D8
#define ledAmarillo D4
#define ledRojo D1
#define pinDHT 9
#define puertoMqtt 1883
 ** objetos y variables **
DHT dht;
WiFiClient clienteWifi;//este cliente se encarga de la comunicacion con el wifi
PubSubClient clienteMQTT(clienteWifi);//este utiliza el cliente anterior para hacer poder crear la conexion mqtt
//si pasan por el hackerspace Xibalba pues ya tienen la clave
const char * ssid = "Hackerspace";
const char * claveWifi = "IOT12345";
const char * brokerMqtt = "";// ip del broker sin http ni nada solo los numeros
uint32_t ultimoIntentoReconexion;
uint32_t timerEnvioDatos;
uint32_t cambioEstado = 0;
uint8_t estadoLed = 0;
uint16_t tiempoParpadeo = 350;
float humedad, temperatura;

void parpadeo(uint8_t led) {
 //Serial.println(millis() - cambioEstado);
 if ( millis() - cambioEstado > tiempoParpadeo) {
 // Serial.println(pasoSemaforo);
 cambioEstado = millis();
 estadoLed = !estadoLed;
 digitalWrite(led, estadoLed);

void conectarAlWifi() {

WiFi.begin(ssid, claveWifi);
 Serial.print("conectando a");
 while (WiFi.status() != WL_CONNECTED) {

 Serial.println("Wifi Conectado ");
 Serial.println("direccion IP: ");

void callback(char* topic, byte* mensaje, unsigned int length) {
 String topico = topic;
 Serial.print("Mensaje Recibido del topico: ");
 Serial.print("mensaje : ");
 for (uint8_t i = 0; i < length; i++) {  Serial.print(mensaje[i]);  }  //le restamos -48 para que el valor sea 0 o 1  //restamos el valor ascii para hacerlo un entero if (topico == "/salida1") {  digitalWrite(salida1, mensaje[0] - 48);  Serial.println("Salida 1 ");  }  else if (topico == "/salida2") {  digitalWrite(salida2, mensaje[0] - 48);  Serial.println("Salida 2 ");  }  else if (topico == "/salida3") {  digitalWrite(salida3, mensaje[0] - 48);  Serial.println("Salida 3 ");  }  else if (topico == "/salida4") {  digitalWrite(salida4, mensaje[0] - 48);  Serial.println("Salida 4 ");  }  else if (topico == "/temperatura") {  Serial.println(temperatura);  char msg[3];  snprintf (msg, 3, "%ld", (int)temperatura);  clienteMQTT.publish("/respuestaTemperatura", msg);  }  else if (topico == "/humedad") {  Serial.println(humedad);  char msg[3];  snprintf (msg, 3, "%ld", (int)temperatura);  clienteMQTT.publish("/respuestaTemperatura", msg);  }  else {  Serial.println("error de mensaje");  } } boolean reconexion() {  Serial.print("Conectando al broker mqtt");  //intentando conectar al broker  if (clienteMQTT.connect("ESP8266Client")) {  Serial.println("Conectado");  //publicamos que estamos conectados  clienteMQTT.publish("/conexion", "Conectado");  //nos suscribimos a los topicos para controlar los ledes  clienteMQTT.subscribe("/salida1");  clienteMQTT.subscribe("/salida2");  clienteMQTT.subscribe("/salida3");  clienteMQTT.subscribe("/salida4");  clienteMQTT.subscribe("/temperatura");  clienteMQTT.subscribe("/humedad");  } else {  Serial.print("falló, rc=");  Serial.print(clienteMQTT.state());  }  return clienteMQTT.connected(); } void entradaSerial() {  if (Serial.available()) {  char dato =;  if (dato == 'a') {  digitalWrite(salida1, HIGH);  Serial.println("Salida 1 ");  }  else if (dato == 's') {  digitalWrite(salida2, HIGH);  Serial.println("Salida 2 ");  }  else if (dato == 'd') {  digitalWrite(salida3, HIGH);  Serial.println("Salida 3 ");  }  else if (dato == 'f') {  digitalWrite(salida4, HIGH);  Serial.println("Salida 4 ");  }  else if (dato == 'q') {  digitalWrite(salida1, LOW);  Serial.println("Salida 1 ");  }  else if (dato == 'w') {  digitalWrite(salida2, LOW);  Serial.println("Salida 2 ");  }  else if (dato == 'e') {  digitalWrite(salida3, LOW);  Serial.println("Salida 3 ");  }  else if (dato == 'r') {  digitalWrite(salida4, LOW);  Serial.println("Salida 4 ");  }  else if (dato == ' ') {  digitalWrite(salida4, LOW);  digitalWrite(salida3, LOW);  digitalWrite(salida2, LOW);  digitalWrite(salida1, LOW);  Serial.println("apagando todo");  }  else if (dato == '1') {  digitalWrite(salida4, HIGH);  digitalWrite(salida3, HIGH);  digitalWrite(salida2, HIGH);  digitalWrite(salida1, HIGH);  Serial.println("encendiendo todo");  } else {  Serial.println("error de mensaje");  }  } } void setup() {  Serial.begin(9600);  Serial.println("iniciando programa Holi mami");  dht.setup(pinDHT); // dht PIN  pinMode(salida1, OUTPUT);  pinMode(salida2, OUTPUT);  pinMode(salida3, OUTPUT);  pinMode(salida4, OUTPUT);  pinMode(ledAmarillo, OUTPUT);  pinMode(ledRojo, OUTPUT);  digitalWrite(ledAmarillo, HIGH);  digitalWrite(ledRojo, HIGH);  conectarAlWifi();  clienteMQTT.setServer(brokerMqtt, puertoMqtt); //le decimos cual es el servidor y el puerto al que se debe conectar  clienteMQTT.setCallback(callback);//le decimos como se llama la funcion de callback } void loop() {  entradaSerial();  if (!clienteMQTT.connected()) {  if (millis() - ultimoIntentoReconexion > 5000) {
 ultimoIntentoReconexion = millis();
 // Attempt to reconnect
 if (reconexion()) {
 ultimoIntentoReconexion = 0;
 } else {
 //cliente conectado
 if (millis() - timerEnvioDatos > 5000) {
 timerEnvioDatos = millis();
 //a falta de sensores enviamos valores aleatorios
 char msg[3];
 humedad = dht.getHumidity();
 temperatura = dht.getTemperature();
 snprintf (msg, 3, "%ld", (int)temperatura);
 clienteMQTT.publish("/temperatura", msg);
 snprintf (msg, 3, "%ld", (int)humedad);
 clienteMQTT.publish("/humedad", msg);

He tratado la manera de hacer el codigo lo mejor legible posible asi que si no le entienden no duden en comentar abajo 🙂

Esto al final es algo que ya hemos visto anteriormente solo un poco tuneado, para que funcione para nuestros propositos la verdadera magia al final de todo esto la hará python ( ay python :3 ), eso quiere decir que necesitamos una computadora o un servidor para que corra nuestro script.


Antes de empezar a programar y configurar nuestro servidor o maquina local, vamos a crear de manera super sencilla nuestro bot en telegram. Para quien no sepa que es telegram, pues es una aplicacion de mensajeria instantanea que funciona en web, escritorio y movil. yo suelo utilizarla mucho por su versatilidad y que aparte es open source gracias a ello tiene demasiadas features muy buenas que poco a poco la competencia como whatsapp ha ido implementando(copiando).

El papa de todos los bots.

Lo primero que debemos hacer es hablar con el padre de todos los bots el @BotFather, es super sencillo crear nuestro bot, al final nos dara un TOKEN  el cual usaremos para poder acceder a los servicios de la API de telegram. Este token es super secreto asi que tengan cuidado donde lo almacenan, por favor usen keypassxc (promocion gracias a que me ha ayudado mucho a guardar las 123873102937 contraseñas que utilizo ).

Asi que vamos a lo nuestro. Con esta serie de capturas de pantalla  veremos lo facil que es configurar el bot.  Pero si no quieres ver estos son los comandos

En el chat con BotFather,

Nombre del bot
usuario del bot
Haga click para ver el pase de diapositivas.

Servidor o maquina local

Como mencionaba en el parrafo anterior lo que necesitamos es una computadora para correr nuestro script de python3, pero bien puede ser una rpi como cerebro de nuestra casa.


Sudo apt-get install mosquitto
sudo apt-get install mosquitto-clients


La ultima vez que pregunte si era mejor usar python 2 o python 3 me regañaron demasiado fuerte asi que de ahora en adelante todo sera python 3
Utilizaremos esta muy bonita y facil de implementar libreria, tambien una libreria que nos ayudara en la comunicacion con el broker MQTT #telegram api

Asi que la mejor manera de instalarla es como dice en el repositorio:

pip3 install pyTelegramBotAPI pip3 install paho-mqtt

Vamos entonces a probar un poco de codigo seguirmos las instrucciones del repositorio, pero traducido para mejor entendimiento. Cualquier duda consultar en el repositorio o escribir en los comentarios jovenes 😀

import telebot

bot = telebot.TeleBot("TOKEN") #la cadena que nos dio nuestro botfather

@bot.message_handler(commands=['start', 'help'])
def bienvenida(message):
	bot.reply_to(message, "Holi, bienvenido a themicrofcontrol")

@bot.message_handler(func=lambda message: True)
def repetirTodo(message):
	bot.reply_to(message, message.text)


Algo que deben de saber es que las funciones handler se ejecutan en el orden en el que fueron declaradas asi que cuidado.

Ahora vamos a hacer un pequeño script que publica por medio de mqtt al broker que deseamos.

Primero ejecutamos el siguiente comando en la terminal para habilitar nuestro broker local

mosquitto -d

ahora ya podemos probar nuestro codigo en python3

import paho.mqtt.publish as publish
from time import sleep
while True:

Para verificar si estamos enviando correctamente los datos utilizaremos mosquitto_sub

mosquitto_sub -h localhost -t /TopicoPrueba


Una vez hecho esto cada segundo estaremos recibiendo informacion en este caso Holi.

Si bien es cierto que no soy un experto programador quise hacer este codigo lo mejor legible posible, se puede mejorar mucho, como todo en la vida.

La idea es la siguiente
Hablamos con nuestro chatbot corriendo este script en python para que pueda ejecutar las ordenes que le damos. El comando /start inicia la conversacion y nos dice los comandos (aun debo trabajar en la parte de la interfaz pero poco a poco), las ordenes son sencillas y descriptivas EncenderSalida1 enciende la salida 1 ja, ja, ApagarSalida1 la apaga y es lo mismo con las 4 salidas o las que queramos poner solo agregamos mas elif en la funcion.

Bueno sin mas les dejo el codigo y el repositorio:

Por alguna extraña razon,  no me aparece el codigo indentado, pero de igual manera se los dejo, por favor copienlo del repositorio.

 import paho.mqtt.publish as publish
 import telebot
 bot = telebot.TeleBot(token)
 @bot.message_handler(commands=['start', 'help'])
 def send_welcome(message):
 bot.reply_to(message, "Holi, bienvenido al chatbot mas kulz <3")
 bot.reply_to(message, "Comandos: \n EncenderSalida1, EncenderSalida2, EncenderSalida3, EncenderSalida4, EncenderSalida4, EncenderTodo.\n ApagarSalida1, ApagarSalida2, ApagarSalida3, ApagarSalida4, ApagarTodo\n Gracias")
 @bot.message_handler(func=lambda message: True)
 def echo_all(message):
 bot.reply_to(message, message.text)
 if(message.text == 'EncenderSalida1'):
 print("Salida 1 encendida.")
 bot.reply_to(message, "Salida 1 encendida.")
 elif(message.text == 'ApagarSalida1'):
 print("Salida 1 apagada")
 bot.reply_to(message, "Salida 1 apagada.")
 elif(message.text == 'EncenderSalida2'):
 print("Salida 2 encendida.")
 bot.reply_to(message, "Salida 2 encendida.")
 elif(message.text == 'ApagarSalida2'):
 print("Salida 2 apagada.")
 bot.reply_to(message, "Salida 2 apagada.")
 elif(message.text == 'EncenderSalida3'):
 print("Salida 3 encendida.")
 bot.reply_to(message, "Salida 3 encendida.")
 elif(message.text == 'ApagarSalida3'):
 print("Salida 3 apagada.") 
 bot.reply_to(message, "Salida 3 apagada.")
 elif(message.text == 'EncenderSalida4'):
 print("Salida 4 encendida.")
 bot.reply_to(message, "Salida 4 encendida.")
 elif(message.text == 'ApagarSalida4'):
 print("Salida 4 apagada.") 
 bot.reply_to(message, "Salida 4 apagada."
 elif(message.text == 'ApagarTodo'):
 bot.reply_to(message, "Todo encendido.")
 print("apagando todo.") 
 elif(message.text == 'EncenderTodo'):
 print("apagando todo.") 
 bot.reply_to(message, "Todo apagado")
 print("iniciando programa")


Bueno y con eso concluye el tutorial del dia de hoy no olviden comentar y compartir.

y recuerden, Solo necesitan una excusa para cambiar el mundo.

San José participará en #CompletetheMap, para mejorar el mapa de la ciudad entre todes

Mapillary por primera vez ha lanzado un reto global de captura de imágenes. Desde el 11 de diciembre hasta el 31 de enero, San José estará participando en #CompletetheMap para completar su mapa capturando fotos con las herramientas de Mapillary, junto a ciudades, pueblos y lugares remotos de todo el mundo.

Mapillary es una plataforma colaborativa que permite visualizar el mundo con fotos a nivel de la calle. Las fotos son contribuidas por una amplia gama de fuentes, incluyendo personas, gobiernos, agencias humanitarias y empresas de mapas. Las fotos luego son procesadas por Mapillary para extraer datos geográficos como límites de velocidad, giros prohibidos, ciclovías y la cantidad de vegetación en un lugar. Por estas razones, se ha convertido en una herramienta popular en la comunidad de OpenStreetMap, un proyecto de código abierto que se basa en personas editoras voluntarias para crear el mapa del mundo.

Algunos usos prácticos de estos datos incluyen el análisis de la infraestructura para bicicletas a lo largo de una ciudad, reducción de riesgos antes y después de desastres, movilidad urbana, y puntos de reunión. #CompletetheMap viene a impulsar este estilo rápido de recolección de datos en un área específica. La idea de #CompletetheMap es simple. El área seleccionada se divide en zonas, y personas miembros de la comunidad local colaboran para capturar imágenes en cada zona. Conforme el porcentaje de fotos de calles y caminos aumenta, la zona cambia de color de rojo a naranja, y de naranja a verde.

El reto #CompletetheMap empezó en mayo de este año y ya se ha realizado en ciudades como Brasilia, Moscú, Berlín y Ottawa.

Cada una de estas ciudades ha respondido en su propia forma, reuniendo a la comunidad y mostrando la gran cantidad de datos que incluso un pequeño grupo de personas puede recolectar. Brasilia se ha concentrado en características de calles y puntos de interés. Moscú se reunió para capturar fotos de algunas de las carreteras más nuevas alrededor del centro de la ciudad. Berlín, la primera en participar en el reto de #CompletetheMap, ayudó a prepararlo recolectando muchas de las calles más pequeñas y rutas peatonales. Luego está Ottawa, un #CompletetheMap centrado en infraestructura para bicicletas. En este reto, 20 personas lograron recolectar medio millón de imágenes y casi 2000 km de cobertura nueva.

El reto global le permite a cualquier persona seguir su progreso relativo a otras alrededor del mundo, recolectando fotos en un área de 50 km2. Las participantes pueden ganar aumentando los km de nuevas rutas que capturan, la cantidad de imágenes que toman, y el número de participantes que se unen para ayudarles.


Actualmente, 23 ciudades de 17 países se han registrado para el #CompletetheMap global.

Todo lo que se necesita para colaborar es un teléfono celular. Participe en el reto descargando la aplicación de Mapillary y tomando fotos de las calles por las que viaja. Una vez que se conecte a una red wifi, suba las imágenes y véalas aparecer en

Puede unirse a la comunidad de maperos y maperas de Costa Rica en

Cómo instalar Firma Digital de Costa Rica en GNU/Linux Fedora 27

Esta guía documenta cómo instalar el controlador de la tarjeta de Firma Digital de Costa Rica y la jerarquía de certificados del Banco Central (SINPE) y del MICITT en el sistema operativo Fedora de arquitectura Intel de 64 bits (x86_64).

El motivo de esta nueva guía de instalación tenía los siguientes propósitos:

  • Configurar de la forma más sencilla y adecuada el sistema para que funcione con la mayor cantidad de programas.
  • Lograr que funcione para todos los usuarios del sistema, incluyendo los nuevos usuarios creados tras las instalación.
  • Funcionar con servicios obsoletos como el de la CCSS (con applet Java) en el navegador Icecat.

Instalación de las dependencias

  • Instalar el soporte CCID de PC/SC para que reconozca el lector de tarjetas y el plugin NPAPI IcedTea-Web para poder cargar el applet Java que permite firmar desde el navegador Icecat (Firefox ya no soporta applets Java) y OpenJFX si pretende instalarse el firmador del Banco Central:
# dnf -y install pcsc-lite-ccid icedtea-web icecat java-1.8.0-openjdk-openjfx

# systemctl start pcscd.socket

Descarga del “instalador”

  • Descargar el “instalador” en el desplegable llamado “Usuarios Linux” en la página de descarga de instaladores del sitio web de Soporte Firma Digital de Costa Rica, introduciendo el número de serie de la tarjeta y el captcha.

Desempaquetado del “instalador”

  • Descomprimir el archivo zip descargado con unzip, en el momento de escribir esta documentación se llama Se creará una carpeta llamada Firma Digital. Se asume que el archivo zip se ha descargado en la carpeta Descargas:
$ cd ~/Descargas

$ unzip

Instalación de los certificados

Es necesario agregar a la lista de confianza la jerarquía de certificados del SINPE y del MICITT. Para ello, un par de comandos:

  • Copiar los certificados:
# cp ~/Descargas/Firma\ Digital/Certificados/* /usr/share/pki/ca-trust-source/anchors/
  • Regenerar los archivos de certificados para todas las aplicaciones:
# update-ca-trust

Instalación del módulo PKCS#11

Aunque hay un módulo en el directorio Librerías, no es la versión más reciente y tiene varios defectos de enlazado. La versión distribuida en el paquete PinTool es más reciente y funciona correctamente en todos los programas probados. En el siguiente proceso se extrae y se instala conservando la fecha original de la librería y con los permisos correctos de usuario y de SELinux.

  • Instalar el módulo PKCS#11 propietario en /usr/lib64/pkcs11:
$ cd ~/Descargas/Firma\ Digital/PinTool/IDProtect\ PINTool\ 6.41.01/RPM

$ rpm2cpio idprotectclient-641.01-0.x86_64.rpm | cpio -dim ./usr/lib/x64-athena/
# mv usr/lib/x64-athena/ /usr/lib64/pkcs11/

# chown root:root /usr/lib64/pkcs11/

# chmod 755 /usr/lib64/pkcs11/

# chcon system_u:object_r:lib_t:s0 /usr/lib64/pkcs11/
  • Crear los siguientes enlaces simbólicos (necesarios para que funcionen algunos programas y applets):
# ln -s /usr/lib64/pkcs11/ /usr/lib64/

# ln -s /usr/lib64/pkcs11/ /usr/lib/

# mkdir -p /usr/lib/x64-athena/

# ln -s /usr/lib64/pkcs11/ /usr/lib/x64-athena/
  • Si se va a trabajar con el applet de la CCSS se puede realizar el siguiente paso opcional:
# mkdir -p /Firma_Digital/LIBRERIAS/

# ln -s /usr/lib64/pkcs11/ /Firma_Digital/LIBRERIAS/

# ln -s /usr/share/pki/ca-trust-source/anchors/ /Firma_Digital/CERTIFICADOS
  • Crear el fichero /etc/Athena/IDPClientDB.xml y abrirlo para edición:
# mkdir /etc/Athena/

# gedit /etc/Athena/IDPClientDB.xml
  • En la ventana del editor de textos gedit, pegar el siguiente texto, guardar y cerrar el editor:
<?xml version="1.0" encoding="utf-8" ?>
     <ATR type='hexBinary'>3BDC00FF8091FE1FC38073C821106600000000000000</ATR>
     <ATRMask type='hexBinary'>FFFF00FFF0FFFFFFFFFFFFFFFFF0FF00000000000000</ATRMask>
  • Crear un fichero llamado /etc/pkcs11/modules/firmadigital.module y abrirlo para edición:
# gedit /etc/pkcs11/modules/firmadigital.module
  • En la ventana del editor de textos gedit, pegar el siguiente texto, guardar y cerrar el editor:
  • Ejecutar el siguiente comando para reemplazar el enlace simbólico a libnssckbi para que haga uso de p11-kit-proxy de forma prioritaria:
# alternatives --install /usr/lib64/ /usr/lib64/ 50

Eso es todo. Es necesario reiniciar Firefox, Evolution y cualquier otra aplicación que use certificados para que se apliquen los cambios. Si se ha insertado el lector y la tarjeta al lector, estas aplicaciones preguntarán por el PIN, lo que indicará que se la instalación ha sido exitosa.

Si el componente de firma del Banco Central está instalado debería funcionar para poder realizar la prueba de firma. El sitio web de Soporte Firma Digital podría usar todavía la prueba de Java y en este caso solamente funciona con el navegador Icecat. Icecat incluye varias extensiones que bloquean JavaScript y hay que desactivarlas para poder navegar en la mayoría de sitios y para poder firmar. En la página de prueba de firma con Java, si a la hora de firmar aparece en el navegador que se quiere ejecutar “IcedTea-Web”, hay que permitirlo. Si el navegador hace preguntas sobre el applet responder afirmativamente y aceptar a todos los cuadros de mensaje que aparezcan e ingresar el PIN cuando lo solicite.

HolaMundo con ethereum

Lista de herramientas

  • testRPC
  • nvm
  • web3
  • geth
  • solc
  • web3



Los siguientes pasos muestras como instalar las herramientas necesarias en Ubuntu 17.04

curl -o- | bash
nvm ls-remote
nvm install <la ultima LTS>
npm install -g ethereumjs-testrpc
npm install solc
npm install web3


Para instalar nvm es necesario tener brew

brew install nvm
nvm ls-remote
nvm install <la ultima LTS>
npm install -g ethereumjs-testrpc
npm install solc
npm install web3


Con el editor preferido, escribir el contrato, para esto se va a utilizar el lenguage solidity, sin embargo existen otras opciones como serpent.

Para compilar el contrato vamos a utilizar el comando solc --bin --optimize <archivo.sol>

Escribit el siguiente contrato en un archivo llamado Voting.sol

pragma solidity ^0.4.11;
// We have to specify what version of compiler this code will compile with

contract Voting {
  /* mapping field below is equivalent to an associative array or hash.
  The key of the mapping is candidate name stored as type bytes32 and value is
  an unsigned integer to store the vote count

  mapping (bytes32 => uint8) public votesReceived;

  /* Solidity doesn't let you pass in an array of strings in the constructor (yet).
  We will use an array of bytes32 instead to store the list of candidates

  bytes32[] public candidateList;

  /* This is the constructor which will be called once when you
  deploy the contract to the blockchain. When we deploy the contract,
  we will pass an array of candidates who will be contesting in the election
  function Voting(bytes32[] candidateNames) {
    candidateList = candidateNames;

  // This function returns the total votes a candidate has received so far
  function totalVotesFor(bytes32 candidate) returns (uint8) {
    if (validCandidate(candidate) == false) throw;
    return votesReceived[candidate];

  // This function increments the vote count for the specified candidate. This
  // is equivalent to casting a vote
  function voteForCandidate(bytes32 candidate) {
    if (validCandidate(candidate) == false) throw;
    votesReceived[candidate] += 1;

  function validCandidate(bytes32 candidate) returns (bool) {
    for(uint i = 0; i < candidateList.length; i++) {
      if (candidateList[i] == candidate) {
        return true;
    return false;

Pasos para desplegar el contrato

Ejecutar node

Mientras se ejecutan los comandos, se puede ver su salida y analizarla.

Web3 = require('web3')
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

Listar las cuentas existentes en la red


Compilar el código

code = fs.readFileSync('Voting.sol').toString()
solc = require('solc')
compiledCode = solc.compile(code)
abiDefinition = JSON.parse(compiledCode.contracts[':Voting'].interface)
VotingContract = web3.eth.contract(abiDefinition)
byteCode = compiledCode.contracts[':Voting'].bytecode
deployedContract =['Rama','Nick','Jose'],{data: byteCode, from: web3.eth.accounts[0], gas: 4700000})
contractInstance =

{ [String: '0'] s: 1, e: 0, c: [ 0 ] }

> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})


> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})


> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})




Opcodes de la EVM

0s: Stop and Arithmetic Operations

0x00    STOP        Halts execution
0x01    ADD         Addition operation
0x02    MUL         Multiplication operation
0x03    SUB         Subtraction operation
0x04    DIV         Integer division operation
0x05    SDIV        Signed integer
0x06    MOD         Modulo
0x07    SMOD        Signed modulo
0x08    ADDMOD      Modulo
0x09    MULMOD      Modulo
0x0a    EXP         Exponential operation
0x0b    SIGNEXTEND  Extend length of two's complement signed integer

10s: Comparison & Bitwise Logic Operations

0x10    LT      Lesser-than comparison
0x11    GT      Greater-than comparison
0x12    SLT     Signed less-than comparison
0x13    SGT     Signed greater-than comparison
0x14    EQ      Equality  comparison
0x15    ISZERO  Simple not operator
0x16    AND     Bitwise AND operation
0x17    OR      Bitwise OR operation
0x18    XOR     Bitwise XOR operation
0x19    NOT     Bitwise NOT operation
0x1a    BYTE    Retrieve single byte from word

20s: SHA3

0x20    SHA3    Compute Keccak-256 hash

30s: Environmental Information

0x30    ADDRESS         Get address of currently executing account
0x31    BALANCE         Get balance of the given account
0x32    ORIGIN          Get execution origination address
0x33    CALLER          Get caller address. This is the address of the account that is directly responsible for this execution
0x34    CALLVALUE       Get deposited value by the instruction/transaction responsible for this execution
0x35    CALLDATALOAD    Get input data of current environment
0x36    CALLDATASIZE    Get size of input data in current environment
0x37    CALLDATACOPY    Copy input data in current environment to memory This pertains to the input data passed with the message call instruction or transaction
0x38    CODESIZE        Get size of code running in current environment
0x39    CODECOPY        Copy code running in current environment to memory
0x3a    GASPRICE        Get price of gas in current environment
0x3b    EXTCODESIZE     Get size of an account's code
0x3c    EXTCODECOPY     Copy an account's code to memory

40s: Block Information

0x40    BLOCKHASH   Get the hash of one of the 256 most recent complete blocks
0x41    COINBASE    Get the block's beneficiary address
0x42    TIMESTAMP   Get the block's timestamp
0x43    NUMBER      Get the block's number
0x44    DIFFICULTY  Get the block's difficulty
0x45    GASLIMIT    Get the block's gas limit

50s Stack, Memory, Storage and Flow Operations

0x50    POP         Remove item from stack
0x51    MLOAD       Load word from memory
0x52    MSTORE      Save word to memory
0x53    MSTORE8     Save byte to memory
0x54    SLOAD       Load word from storage
0x55    SSTORE      Save word to storage
0x56    JUMP        Alter the program counter
0x57    JUMPI       Conditionally alter the program counter
0x58    PC          Get the value of the program counter prior to the increment
0x59    MSIZE       Get the size of active memory in bytes
0x5a    GAS         Get the amount of available gas, including the corresponding reduction
0x5b    JUMPDEST    Mark a valid destination for jumps

60s & 70s: Push Operations

0x60    PUSH1   Place 1 byte item on stack
0x61    PUSH2   Place 2-byte item on stack
0x7f    PUSH32  Place 32-byte (full word) item on stack

80s: Duplication Operations

0x80    DUP1    Duplicate 1st stack item
0x81    DUP2    Duplicate 2nd stack item
0x8f    DUP16   Duplicate 16th stack item

90s: Exchange Operations

0x90    SWAP1   Exchange 1st and 2nd stack items
0x91    SWAP2   Exchange 1st and 3rd stack items
…   …
0x9f    SWAP16  Exchange 1st and 17th stack items

a0s: Logging Operations

0xa0    LOG0    Append log record with no topics
0xa1    LOG1    Append log record with one topic
…   …
0xa4    LOG4    Append log record with four topics

f0s: System operations

0xf0    CREATE          Create a new account with associated code
0xf1    CALL            Message-call into an account
0xf2    CALLCODE        Message-call into this account with alternative account's code
0xf3    RETURN          Halt execution returning output data
0xf4    DELEGATECALL    Message-call into this account with an alternative account's code, but persisting the current values for `sender` and `value`

Halt Execution, Mark for deletion

0xff    SELFDESTRUCT    Halt execution and register account for later deletion




testrpc -n5

An errbot snap for simplified chatops

I'm a Quality Assurance Engineer. A big part of my job is to find problems, then make sure that they are fixed and automated so they don't regress. If I do my job well, then our process will identify new and potential problems early without manual intervention from anybody in the team. It's like trying to automate myself, everyday, until I'm no longer needed and have to jump to another project.

However, as we work in the project, it's unavoidable that many small manual tasks accumulate on my hands. This happens because I set up the continuous integration infrastructure, so I'm the one who knows more about it and have easier access, or because I'm the one who requested access to the build farm so I'm the one with the password, or because I configured the staging environment and I'm the only one who knows the details. This is a great way to achieve job security, but it doesn't lead us to higher quality. It's a job half done, and it's terribly boring to be a bottleneck and a silo of information about testing and the release process. All of these tasks should be shared by the whole team, as with all the other tasks in the project.

There are two problems. First, most of these tasks involve delicate credentials that shouldn't be freely shared with everybody. Second, even if the task itself is simple and quick to execute, it's not very simple to document how to set up the environment to be able to execute them, nor how to make sure that the right task is executed in the right moment.

Chatops is how I like to solve all of this. The idea is that every task that requires manual intervention is implemented in a script that can be executed by a bot. This bot joins the communication channel where the entire team is present, and it will execute the tasks and report about their results as a response to external events that happen somewhere in the project infrastructure, or as a response to the direct request of a team member in the channel. The credentials are kept safe, they only have to be shared with the bot and the permissions can be handled with access control lists or membership to the channel. And the operative knowledge is shared with all the team, because they are all listening in the same channel with the bot. This means that anybody can execute the tasks, and the bot assists them to make it simple.

In snapcraft we started writing our bot not so long ago. It's called snappy-m-o (Microbe Obliterator), and it's written in python with errbot. We, of course, packaged it as a snap so we have automated delivery every time we change it's source code, and the bot is also autoupdated in the server, so in the chat we are always interacting with the latest and greatest.

Let me show you how we started it, in case you want to get your own. But let's call this one Baymax, and let's make a virtual environment with errbot, to experiment.

drawing of the Baymax bot

$ mkdir -p ~/workspace/baymax
$ cd ~/workspace/baymax
$ sudo apt install python3-venv
$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install errbot
$ errbot --init

The last command will initialize this bot with a super simple plugin, and will configure it to work in text mode. This means that the bot won't be listening on any channel, you can just interact with it through the command line (the ops, without the chat). Let's try it:

$ errbot
>>> !help
All commands
!tryme - Execute to check if Errbot responds to command.
>>> !tryme
It works !
>>> !shutdown --confirm

tryme is the command provided by the example plugin that errbot --init created. Take a look at the file plugins/err-example/, errbot is just lovely. In order to define your own plugin you will just need a class that inherits from errbot.BotPlugin, and the commands are methods decorated with @errbot.botcmd. I won't dig into how to write plugins, because they have an amazing documentation about Plugin development. You can also read the plugins we have in our snappy-m-o, one for triggering autopkgtests on GitHub pull requests, and the other for subscribing to the results of the pull requests tests.

Let's change the config of Baymax to put it in an IRC chat:

$ pip install irc

And in the file, set the following values:

    'nickname' : 'baymax-elopio',  # Nicknames need to be unique, so append your own.
                                   # Remember to replace 'elopio' with your nick everywhere
                                   # from now on.
    'server' : '',
CHATROOM_PRESENCE = ('#snappy',)

Run it again with the errbot command, but this time join the #snappy channel in, and write in there !tryme. It works ! :)

screenshot of errbot on IRC

So, this is very simple, but let's package it now to start with the good practice of continuous delivery before it gets more complicated. As usual, it just requires a snapcraft.yaml file with all the packaging info and metadata:

name: baymax-elopio
version: '0.1-dev'
summary: A test bot with errbot.
description: Chat ops bot for my team.
grade: stable
confinement: strict

    command: env LC_ALL=C.UTF-8 errbot -c $SNAP/
    plugs: [home, network, network-bind]

    plugin: python
    python-packages: [errbot, irc]
    source: .
    plugin: dump
      - plugins
    after: [errbot]

And we need to change a few more values in to make sure that the bot is relocatable, that we can run it in the isolated snap environment, and that we can add plugins after it has been installed:

import os

BOT_DATA_DIR = os.environ.get('SNAP_USER_DATA')
BOT_EXTRA_PLUGIN_DIR = os.path.join(os.environ.get('SNAP'), 'plugins')
BOT_LOG_FILE = BOT_DATA_DIR + '/err.log'

One final try, this time from the snap:

$ sudo apt install snapcraft
$ snapcraft
$ sudo snap install baymax*.snap --dangerous
$ baymax-elopio

And go back to IRC to check.

Last thing would be to push the source code we have just written to a GitHub repo, and enable the continuous delivery in Go to your server and install the bot with sudo snap install baymax-elopio --edge. Now everytime somebody from your team makes a change in the master repo in GitHub, the bot in your server will be automatically updated to get those changes within a few hours without any work from your side.

If you are into chatops, make sure that every time you do a manual task, you also plan for some time to turn that task into a script that can be executed by your bot. And get ready to enjoy tons and tons of free time, or just keep going through those 400 open bugs, whichever you prefer :)