HolaMundo con ethereum

Lista de herramientas

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

Instalación

Linux

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

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
nvm ls-remote
nvm install <la ultima LTS>
npm install -g ethereumjs-testrpc
npm install solc
npm install web3

Mac

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

Desarrollo

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

web3.eth.accounts

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 = VotingContract.new(['Rama','Nick','Jose'],{data: byteCode, from: web3.eth.accounts[0], gas: 4700000})
deployedContract.address
contractInstance = VotingContract.at(deployedContract.address)
> contractInstance.totalVotesFor.call('Rama')

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

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

'0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53'

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

'0x02c054d238038d68b65d55770fabfca592a5cf6590229ab91bbe7cd72da46de9'

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

'0x3da069a09577514f2baaa11bc3015a16edf26aad28dffbcd126bde2e71f2b76f'

> contractInstance.totalVotesFor.call('Rama').toLocaleString()

'3'

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

Referencias

  • https://github.com/creationix/nvm
  • https://github.com/ethereumjs/testrpc
  • https://en.wikipedia.org/wiki/Remote_procedure_call
  • http://ethdocs.org/en/latest/network/test-networks.html
  • https://medium.com/@doart3/ethereum-dapps-without-truffle-compile-deploy-use-it-e6daeefcf919
  • https://medium.com/@mvmurthy/full-stack-hello-world-voting-ethereum-dapp-tutorial-part-1-40d2d0d807c2
  • https://github.com/ethereum/yellowpaper

Notas

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/example.py, 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 config.py file, set the following values:

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

Run it again with the errbot command, but this time join the #snappy channel in irc.freenode.net, 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

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

parts:
  errbot:
    plugin: python
    python-packages: [errbot, irc]
  baymax:
    source: .
    plugin: dump
    stage:
      - config.py
      - plugins
    after: [errbot]

And we need to change a few more values in config.py 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 build.snapcraft.io. 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 :)

Deploy to all SBCs with Gobot and a single snap package

I love playing with my prototyping boards. Here at Ubuntu we are designing the core operating system to support every single-board computer, and keep it safe, updated and simple. I've learned a lot about physical computing, but I always have a big problem when my prototype is done, and I want to deploy it. I am working with a Raspberry Pi, a DragonBoard, and a BeagleBone. They are all very different, with different architectures, different pins, onboard capabilities and peripherals, and they can have different operating systems. When I started learning about this, I had to write 3 programs that were very different, if I wanted to try my prototype in all my boards.

picture of the three different SBCs

Then I found Gobot, a framework for robotics and IoT that supports my three boards, and many more. With the added benefit that you can write all the software in the lovely and clean Go language. The Ubuntu store supports all their architectures too, and packaging Go projects with snapcraft is super simple. So we can combine all of this to make a single snap package that with the help of Gobot will work on every board, and deploy it to all the users of these boards through the snaps store.

Let's dig into the code with a very simple example to blink an LED, first for the Raspberry PI only.

package main

import (
  "time"

  "gobot.io/x/gobot"
  "gobot.io/x/gobot/drivers/gpio"
  "gobot.io/x/gobot/platforms/raspi"
)

func main() {
  adaptor := raspi.NewAdaptor()
  led := gpio.NewLedDriver(adaptor, "7")

  work := func() {
    gobot.Every(1*time.Second, func() {
      led.Toggle()
    })
  }

  robot := gobot.NewRobot("snapbot",
    []gobot.Connection{adaptor},
    []gobot.Device{led},
    work,
  )

  robot.Start()
}

In there you will see some of the Gobot concepts. There's an adaptor for the board, a driver for the specific device (in this case the LED), and a robot to control everything. In this program, there are only two things specific to the Raspberry Pi: the adaptor and the name of the GPIO pin ("7").

picture of the Raspberry Pi prototype

It works nicely in one of the boards, but let's extend the code a little to support the other two.

package main

import (
  "log"
  "os/exec"
  "strings"
  "time"

  "gobot.io/x/gobot"
  "gobot.io/x/gobot/drivers/gpio"
  "gobot.io/x/gobot/platforms/beaglebone"
  "gobot.io/x/gobot/platforms/dragonboard"
  "gobot.io/x/gobot/platforms/raspi"
)

func main() {
  out, err := exec.Command("uname", "-r").Output()
  if err != nil {
    log.Fatal(err)
  }
  var adaptor gobot.Adaptor
  var pin string
  kernelRelease := string(out)
  if strings.Contains(kernelRelease, "raspi2") {
    adaptor = raspi.NewAdaptor()
    pin = "7"
  } else if strings.Contains(kernelRelease, "snapdragon") {
    adaptor = dragonboard.NewAdaptor()
    pin = "GPIO_A"
  } else {
    adaptor = beaglebone.NewAdaptor()
    pin = "P8_7"
  }
  digitalWriter, ok := adaptor.(gpio.DigitalWriter)
  if !ok {
    log.Fatal("Invalid adaptor")
  }
  led := gpio.NewLedDriver(digitalWriter, pin)

  work := func() {
    gobot.Every(1*time.Second, func() {
      led.Toggle()
    })
  }

  robot := gobot.NewRobot("snapbot",
    []gobot.Connection{adaptor},
    []gobot.Device{led},
    work,
  )

  robot.Start()
}

We are basically adding in there a block to select the right adaptor and pin, depending on which board the code is running. Now we can compile this program, throw the binary in the board, and give it a try.

picture of the Dragonboard prototype

But we can do better. If we package this in a snap, anybody with one of the boards and an operating system that supports snaps can easily install it. We also open the door to continuous delivery and crowd testing. And as I said before, super simple, just put this in the snapcraft.yaml file:

name: gobot-blink-elopio
version: master
summary:  Blink snap for the Raspberry Pi with Gobot
description: |
  This is a simple example to blink an LED in the Raspberry Pi
  using the Gobot framework.

confinement: devmode

apps:
  gobot-blink-elopio:
    command: gobot-blink

parts:
  gobot-blink:
    source: .
    plugin: go
    go-importpath: github.com/elopio/gobot-blink

To build the snap, here is a cool trick thanks to the work that kalikiana recently added to snapcraft. I'm writing this code in my development machine, which is amd64. But the raspberry pi and beaglebone are armhf, and the dragonboard is arm64; so I need to cross-compile the code to get binaries for all the architectures:

snapcraft --target-arch=armhf
snapcraft clean
snapcraft --target-arch=arm64

That will leave two .snap files in my working directory that then I can upload to the store with snapcraft push. Or I can just push the code to GitHub and let build.snapcraft.io to take care of building and pushing for me.

Here is the source code for this simple example: https://github.com/elopio/gobot-blink

Of course, Gobot supports many more devices that will let you build complex robots. Just take a look at the documentation in the Gobot site, and at the guide about deployable packages with Gobot and snapcraft.

picture of the BeagleBone prototype

If you have one of the boards I'm using here to play, give it a try:

sudo snap install gobot-blink-elopio --edge --devmode
sudo gobot-blink-elopio

Now my experiments will be to try make the snap more secure, with strict confinement. If you have any questions or want to help, we have a topic in the forum.

#Maperespeis - Costa Rica

Desde hace 2 años, que me uní al Jaquerespeis, tuve la oportunidad de aprender acerca de mapas abiertos, OpenStreetMaps y Mapillary. Uno de las fortalezas del Jaquerespeis es el hecho de que es un grupo con un gran interés por los datos abiertos, información libre y que tiene mucha pasión por los proyectos que realizan. Otra fortaleza es el hecho de que Leo Arias (el embajador de Mapillary de Costa Rica) moviliza mucho las actividades y gracias a su contacto con Mapillary hemos conseguido productos para ayudar el mapeo del país. Un ejemplo de esto es la cámara 360.

El #Maperespeis es una oportunidad para reunirnos con mayor frecuencia, de realizar actividades, analizar los datos que se recolectaron , hacer un análisis de los mapas abiertos que existen y crear planes de trabajo para mejorar el estado en el cual se encuentran.

Descripción de los programas\herramientas que se utilizan:

Mapillary: Es una aplicación celular y web que permite recrear el entorno en 360 grados. Esta aplicación usa como base el OpenStreetMap, es gratis y se puede mapear en una gran variedad de celulares. Como a las grandes empresas no les interesa un mapeo de paises más pequeños, esta aplicación es una oportunidad que se puede aprovechar en cualquier país.

OpenStreetMap: Este es un programa de mapas libre que se lanzo a la web en el 2004. Este mapa es creado por voluntarios que colaboran para documentar el espacio donde se encuentran. Una ventaja muy grande de este mapa es que se puede descargar la base del mapa a un Sistema de Informacion Geográfico. Algunas iniciativas que trabajan con esto son: MapaNica, SiCultura (Costa Rica), OSMTasking Manager

Cámara 360 : Este tipo de camara permite grabar en 360 con cada imagen que guarda. Generalmente tiene dos lentes con gran alcance hacia las periferias. Estas imágenes luego se pueden subir a Mapillary.

Las reuniones se realizan de manera esporádica. Todo se notifica por el Facebook.

Vamos a mapear!!

User acceptance testing of snaps, with Travis

Travis CI offers a great continuous integration service for the projects hosted on GitHub. With it, you can run tests, deliver artifacts and deploy applications every time you push a commit, on pull requests, after they are merged, or with some other frequency.

Last week Travis CI updated the Ubuntu 14.04 (Trusty) machines that run your tests and deployment steps. This update came with a nice surprise for everybody working to deliver software to Linux users, because it is now possible to install snaps in Travis!

I've been excited all week telling people about all the doors that this opens; but if you have been following my adventures in the Ubuntu world, by now you can probably guess that I'm mostly thinking about all the potential this has for automated testing. For the automation of user acceptance tests.

User acceptance tests are executed from the point of view of the user, with your software presented as a black box to them. The tests can only interact with the software through the entry points you define for your users. If it's a CLI application, then the tests will call commands and subcommands and check the outputs. If it's a website or a desktop application, the tests will click things, enter text and check the changes on this GUI. If it's a service with an HTTP API, the tests will make requests and check the responses. On these tests, the closer you can get to simulate the environment and behaviour of your real users, the better.

Snaps are great for the automation of user acceptance tests because they are immutable and they bundle all their dependencies. With this we can make sure that your snap will work the same on any of the operating systems and architectures that support snaps. The snapd service takes care of hiding the differences and presenting a consistent execution environment for the snap. So, getting a green execution of these tests in the Trusty machine of Travis is a pretty good indication that it will work on all the active releases of Ubuntu, Debian, Fedora and even on a Raspberry Pi.

Let me show you an example of what I'm talking about, obviously using my favourite snap called IPFS. There is more information about IPFS in my previous post.

Check below the packaging metadata for the IPFS snap, a single snapcraft.yaml file:

name: ipfs
version: master
summary: global, versioned, peer-to-peer filesystem
description: |
  IPFS combines good ideas from Git, BitTorrent, Kademlia, SFS, and the Web.
  It is like a single bittorrent swarm, exchanging git objects. IPFS provides
  an interface as simple as the HTTP web, but with permanence built in. You
  can also mount the world at /ipfs.
confinement: strict

apps:
  ipfs:
    command: ipfs
    plugs: [home, network, network-bind]

parts:
  ipfs:
    source: https://github.com/ipfs/go-ipfs.git
    plugin: nil
    build-packages: [make, wget]
    prepare: |
      mkdir -p ../go/src/github.com/ipfs/go-ipfs
      cp -R . ../go/src/github.com/ipfs/go-ipfs
    build: |
      env GOPATH=$(pwd)/../go make -C ../go/src/github.com/ipfs/go-ipfs install
    install: |
      mkdir $SNAPCRAFT_PART_INSTALL/bin
      mv ../go/bin/ipfs $SNAPCRAFT_PART_INSTALL/bin/
    after: [go]
  go:
    source-tag: go1.7.5

It's not the most simple snap because they use their own build tool to get the go dependencies and compile; but it's also not too complex. If you are new to snaps and want to understand every detail of this file, or you want to package your own project, the tutorial to create your first snap is a good place to start.

What's important here is that if you run snapcraft using the snapcraft.yaml file above, you will get the IPFS snap. If you install that snap, then you can test it from the point of view of the user. And if the tests work well, you can push it to the edge channel of the Ubuntu store to start the crowdtesting with your community.

We can automate all of this with Travis. The snapcraft.yaml for the project must be already in the GitHub repository, and we will add there a .travis.yml file. They have good docs to prepare your Travis account. First, let's see what's required to build the snap:

sudo: required
services: [docker]

script:
  - docker run -v $(pwd):$(pwd) -w $(pwd) snapcore/snapcraft sh -c "apt update && snapcraft"

For now, we build the snap in a docker container to keep things simple. We have work in progress to be able to install snapcraft in Trusty as a snap, so soon this will be even nicer running everything directly in the Travis machine.

This previous step will leave the packaged .snap file in the current directory. So we can install it adding a few more steps to the Travis script:

[...]

script:
  - docker [...]
  - sudo apt install --yes snapd
  - sudo snap install *.snap --dangerous

And once the snap is installed, we can run it and check that it works as expected. Those checks are our automated user acceptance test. IPFS has a CLI client, so we can just run commands and verify outputs with grep. Or we can get fancier using shunit2 or bats. But the basic idea would be to add to the Travis script something like this:

[...]

script:
  [...]
  - /snap/bin/ipfs init
  - /snap/bin/ipfs cat /ipfs/QmVLDAhCY3X9P2uRudKAryuQFPM5zqA3Yij1dY8FpGbL7T/readme | grep -z "^Hello and Welcome to IPFS!.*$"
  - [...]

If one of those checks fail, Travis will mark the execution as failed and stop our release process until we fix them. If instead, all of the checks pass, then this version is good enough to put into the store, where people can take it and run exploratory tests to try to find problems caused by weird scenarios that we missed in the automation. To help with that we have the snapcraft enable-ci travis command, and a tutorial to guide you step by step setting up the continuous delivery from Travis CI.

For the IPFS snap we had for a long time a manual smoke suite, that our amazing community of testers have been executing over and over again, every time we want to publish a new release. I've turned it into a simple bash script that from now on will be executed frequently by Travis, and will tell us if there's something wrong before anybody gives it a try manually. With this our community of testers will have more time to run new and interesting scenarios, trying to break the application in clever ways, instead of running the same repetitive steps many times.

Thanks to Travis and snapcraft we no longer have to worry about a big part of or release process. Continuous integration and delivery can be fully automated, and we will have to take a look only when something breaks.

As for IPFS, it will keep being my guinea pig to guide new features for snapcraft and showcase them when ready. It has many more commands that have to be added to the automated test suite, and it also has a web UI and an HTTP API. Lots of things to play with! If you would like to help, and on the way learn about snaps, automation and the decentralized web, please let me know. You can take a look on my IPFS snap repo for more details about testing snaps in Travis, and other tricks for the build and deployment.

screenshot of the IPFS smoke test running in travis

#maperespeis6

El 17 de junio del 2017, nos reunimos en el restaurante Manos en la Masa. Si no lo conocen lo recomiendo, comida rica, un espacio amplio para trabajar, internet rápido y un grupo de profesionales que atiende super atenta.

El propósito de esta reunión fue el de organizar la información recolectada en sesiones anteriores de Mapillary, llevadas a cabo en diferentes puntos del país. Estas actividades fueron desarrolladas por integrantes del Jaquerespeis y posteriormente, y de manera específica, por personas pertenecientes al Maperespeis.

Las actividades que recordamos haber realizado, hasta la fecha, y que trabajamos en ese día son las siguientes:

  • Mapeo del parque la LibertadBares de Heredia
  • Bares de San José
  • Volcán Poás
  • Coronado
  • Bosque del niño

Las acciones que se realizaron fueron: la revisión de las imágenes para agregar datos concretos que se ven en las imágenes pero que no salen en OpenStreetMap, la corrección de caminos en bosque y la digitalización de aspectos que aparecen en las imágenes aéreas y satelitales que no salen en OpenStreetMap.

De todas las tareas propuestas, se logró actualizar los mapas según los datos que se recolectaron en las sesiones de campo con el Mapillary. Algunas de las conversaciones de grupo, llevaron a analizar diferentes factores organizativos con el fin de mejorar el espacio en el cual trabajamos, como por ejemplo, celebrar reuniones periódicamente y de manera regular con el fin de asegurar que la información no se olvide; establecer cuál es el mejor medio para convocar a actividades (redes sociales o Telegram); y/o mejorar la sistematización del trabajo que se va desarrollando.

En total, logramos actualizar todos los datos de las salidas anteriores. En relación con el change set #maperespeis6 hay más de 805 cambios asociados.

Enlaces Útiles:

Drupal 8: Pseudo fields, templates and rendering images with links.

Illustration by Lucy Sánchez

Lea este artículo en español: Drupal 8: Pseudocampos, templates y render de imágenes con links.

A few days ago I started the development of a new Drupal 8 site. This site has come with a large set of interesting challenges because a lot of the required functionality is still not easy to implement. As of this moment, there are a lot of contrib modules that are in development, one of them is group.

Our client requires that every authenticated user can see the groups it belongs to directly from its profile. This feature is required to be done in such a way that you can see the group’s logo, and the logo has to be linked to that group page on the site. I researched alternatives with views and other contrib modules, but it didn’t bear fruit.

I opted for the custom approach: to create a pseudo field for the users. Such field would use a template in order to print the groups the user belongs to.

Shoulders to the wheel!

First we need to create a pseudo field. Let’s remember that pseudo fields are elements that, just like fields, we can manage from the admin interface of each entity, but the difference lies in that the element has only one function: to display information. Contrary to that, a field will store and show information entered by the users.

In Drupal 8 -like Drupal 7- we need to use 2 hooks to create our pseudo field for an entity: hook_entity_extra_field_info() and hook_[ENTITY NAME]_view().

hook_entity_extra_field_info().

It tells the entity that there will be a new element available to display. For our case, we’ll implement it like so:

/**
* Implements hook_entity_extra_field_info().
*/
function my_module_entity_extra_field_info() {
$extra = [];
$extra['user']['user']['display']['my_mudule_active_subsites'] = array(
'label' => new TranslatableMarkup('Active subsites'),
'description' => new TranslatableMarkup('Show the current subscribed sites by one user'),
);
return $extra;
}

hook_[ENTITY NAME]_view()

In this case, we’re interested in using hook_user_view() because we’re working with the user type entity. hook_[ENTITY NAME]_view() basically handles the display of each one of the elements of an entity. In this case, we’ll use it to format our pseudo field my_mudule_active_subsites.

For Drupal 8, the implementation of this hook varies a bit in the parameters it receives, we can find more documentation here:

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_view/8.2.x

/**
* Implements hook_user_view().
*/
function my_module_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode) {
if ($view_mode === 'full') {
if ($display->getComponent('my_module_active_subsites')) {
$groups = my_module_user_current_user_groups($entity);
$groups_to_render = my_module_render_groups_images($groups);
$build['my_module_user_active_subsites'] = [
'#theme' => 'my_module_active_subsites',
'#groups' => $groups_to_render,
];
}
}
}

Like the example, we’re using the variable build that we get from the reference, to assign the value to show with our pseudo field. Notice that we created a new index inside the build array, which is a render array. Inside our index we’ll use #theme to indicate that we’re going to use a template, which we’ll define further down the road, and we’ll also use a variable called #groups, which is a variable inside our template.

Now, an important detail: like you can see in our example, we use 2 functions to obtain both the the groups of the user and the preprocessed data ready to be printed by Drupal. The function my_module_user_current_user_groups() basically returns an array of the entity type group, in which we wont delve. On the contrary, we’ll see the details of my_module_render_groups_images().

my_module_render_groups_images()

This function was born because of our need to wrap the images in a link, specifically a link to the entity type group. We’ll solve that this way:

/**
* Helper function to render the images with a specific image style.
*
* @param array $groups
* The entity groups with the images to render.
*/
function my_module_render_groups_images(array $groups) {
$groups_to_render = [];
foreach ($groups as $group) {
$group_item = new Stdclass();
$image_id = $group->field_site_logo->target_id;
if (!empty($image_id)) {
$image_render_array = [
'#theme' => 'image_style',
'#width' => $group->field_site_logo->width,
'#height' => $group->field_site_logo->height,
'#style_name' => 'site_logo_user_profile',
'#uri' => $group->field_site_logo->entity->uri->value,
];
$renderer = \Drupal::service('renderer');
$renderer->addCacheableDependency($image_render_array, $group->field_site_logo->entity);
$rendered_image = $renderer->render($image_render_array);
$link_render_array = [
'#type' => 'link',
'#url' => Url::fromUri('entity:group/' . $group->id()),
'#title' => $rendered_image,
];
$group_item->logo = $link_render_array;
}
$groups_to_render[] = $group_item;
}
return $groups_to_render;
}

As you can see in the example, what we do to print our image with a link is: first, prepare a render array to use the image_style template. After that, we use the service renderer from Drupal 8 (instead of Drupal_render from Drupal 7) to obtain an render type object, which will be assigned as a value for the title of our link.

Defining our twig template

Like Drupal 7, if we want to use our own templates inside a module, we need to define it, that is, make Drupal aware that such template exists. For that we’ll use hook_theme().

/**
* Implements hook_theme().
*/
function my_module_user_theme($existing, $type, $theme, $path) {
return [
'my_module_user_active_subsites' => [
'variables' => [
'groups' => '',
],
],
];
}

Creating our template

For our final step, we’ll proceed to create our twig template. In order to be recognized, our templates have to be placed in a specific location, with a specific naming convention.

Our templates have to be inside the folder called “templates” in the root of our module and like as I mentioned, have to follow the naming convention: template-name.html.twig. We’ll create the file my-module-active-subsites.html.twig.

This template will handle the format and will print the images belonging to our groups:

{#
/**
* @file
* Template file my_module_active_subsites field
*/
#}
<div class="container user-groups-container">
{% if groups is not empty %}
{% for group in groups %}
<div class="container user-groups-element" >
{{ group.logo }}
</div>
{% endfor %}
{% endif %}
</div>

As you can see, Twig is very different from what we know before with PHP templates, but this does not mean is complicated. In my case, I used this documentation: https://www.drupal.org/docs/8/theming/twig/comparison-of-phptemplate-and-twig-theming-paradigms, which is a comparative between what we used to do in PHP template and how we do it with Twig.

Pseudo fields in Drupal 8 -like in Drupal 7- are still a great resource that allows flexibility in the display of our entities. If we add templates in the mix, we can comply with any display requirement for our content. Just be aware that it depends on the type of display we need to use for our content.

Happy coding!

Manatí is a web consultancy firm from Costa Rica, where we make websites strategically designed to help organization achieve goals.

Don’t miss out on projects, news and ideas from Manatí. Learn about what we do, follow us on medium, twitter and facebook.


Drupal 8: Pseudo fields, templates and rendering images with links. was originally published in MANATI | Agencia Web on Medium, where people are continuing the conversation by highlighting and responding to this story.

Drupal 8: Pseudocampos, templates y render de imágenes con links.

Ilustración de Lucy Sánchez

Read this article in english: Drupal 8: Pseudo fields, templates and rendering images with links.

Hace unos días atrás inicie el desarrollo de un nuevo sitio en Drupal 8. Este sitio ha llegado con una gran cantidad de retos interesantes puesto que muchas de las funcionalidades requeridas aún no son sencillas de implementar. En este momento hay muchos módulos contrib que aun se encuentran en desarrollo, uno de estos módulos es: group.

Nuestro cliente requiere que cada usuario autenticado pueda ver los grupos a los que pertenece directamente desde su perfil. Esta funcionalidad tiene que realizase de manera tal que se pudiera ver solamente el logo del grupo, y este logo debe estar enlazado a la página del sitio. Investigué para buscar alternativas con views u otros módulos contrib, pero la investigación no dio muchos frutos.

Opté por el desarrollo de algo custom: elegí desarrollar un pseudocampo para los usuarios. Dicho campo utilizará un template para imprimir dichos grupos.

Manos a la obra.

Primero necesitamos crear un pseudocampo. Recordemos que los pseudocampos son elementos que, al igual que un campo, podemos administrar desde la interfaz de despliegue de cada entidad, pero la diferencia radica en que este elemento solo tiene una función: mostrar información, a diferencia de un campo normal que muestra y almacena información brindada por los usuarios.

En drupal 8- como en drupal 7- necesitamos hacer uso de 2 hooks para hacer disponible nuestro pseudocampo para una entidad, estos son: hook_entity_extra_field_info() y hook_[ENTITY NAME]_view().

hook_entity_extra_field_info().

Indica a la entidad que ahora va a existir un nuevo elemento disponible para su despliegue. Para nuestro caso lo implementamos de la siguiente manera:

/**
* Implements hook_entity_extra_field_info().
*/
function my_module_entity_extra_field_info() {
$extra = [];
$extra['user']['user']['display']['my_mudule_active_subsites'] = array(
'label' => new TranslatableMarkup('Active subsites'),
'description' => new TranslatableMarkup('Show the current subscribed sites by one user'),
);
return $extra;
}

hook_[ENTITY NAME]_view()

En este caso nos interesa usar hook_user_view() porque que estamos trabajando con la entidad de tipo usuario. hook_[ENTITY NAME]_view() básicamente se encarga de manejar el despliegue de cada uno de los elementos de una entidad dada. En este caso lo vamos a utilizar para dar forma a nuestro pseudocampo my_mudule_active_subsites.

Para drupal 8, la implementación de este hook varía un poco en los parámetros que recibe, podemos encontrar más documentación aquí: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_view/8.2.x

/**
* Implements hook_user_view().
*/
function my_module_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode) {
if ($view_mode === 'full') {
if ($display->getComponent('my_module_active_subsites')) {
$groups = my_module_user_current_user_groups($entity);
$groups_to_render = my_module_render_groups_images($groups);
$build['my_module_user_active_subsites'] = [
'#theme' => 'my_module_active_subsites',
'#groups' => $groups_to_render,
];
}
}
}

Como podemos ver en el ejemplo, estamos usando la variable build que nos llega por referencia para asignar a nuestro pseudocampo el valor a mostrar. Nótese que creamos un nuevo índice dentro del arreglo de build que es un arreglo de render. Dentro de nuestro índice usamos #theme para indicar que vamos a hacer uso de un template, el cual definiremos más adelante y también hacemos uso de #groups que es una variable propia de nuestro template.

Ahora, un detalle importante: como se puede apreciar en nuestro ejemplo, se hace uso de 2 funciones para obtener tanto los grupos del usuario como los datos preprocesados listos para ser impresos por drupal. La función my_module_user_current_user_groups() básicamente retorna un arreglo de entidades de tipo group, por lo cual no ahondaremos en ella. Por el contrario, sí entraremos en detalles con my_module_render_groups_images().

my_module_render_groups_images()

Esta función nació por la necesidad de que nuestras imágenes estén envueltas en un link, específicamente, un enlace a la entidad de tipo grupo. Lo resolvemos de la siguiente manera:

/**
* Helper function to render the images with a specific image style.
*
* @param array $groups
* The entity groups with the images to render.
*/
function my_module_render_groups_images(array $groups) {
$groups_to_render = [];
foreach ($groups as $group) {
$group_item = new Stdclass();
$image_id = $group->field_site_logo->target_id;
if (!empty($image_id)) {
$image_render_array = [
'#theme' => 'image_style',
'#width' => $group->field_site_logo->width,
'#height' => $group->field_site_logo->height,
'#style_name' => 'site_logo_user_profile',
'#uri' => $group->field_site_logo->entity->uri->value,
];
$renderer = \Drupal::service('renderer');
$renderer->addCacheableDependency($image_render_array, $group->field_site_logo->entity);
$rendered_image = $renderer->render($image_render_array);
$link_render_array = [
'#type' => 'link',
'#url' => Url::fromUri('entity:group/' . $group->id()),
'#title' => $rendered_image,
];
$group_item->logo = $link_render_array;
}
$groups_to_render[] = $group_item;
}
return $groups_to_render;
}

Como se puede observar en el ejemplo, lo que realizamos para imprimir nuestra imagen con link, fue: primero preparar un arreglo de render para hacer uso del template de image_style. Luego usamos el servicio renderer de drupal 8 (remplazo de drupal_render en drupal 7) para obtener un objeto de tipo renderer, el cual posteriormente será asignado como valor para el title de nuestro link.

Definiendo nuestro twig template.

Al igual que en drupal 7, si queremos hacer uso de nuestro propio template en un módulo, debemos hacer la definición del mismo, osea, hacerle saber a Drupal que este template existe. Para esto hacemos uso del hook_theme().

/**
* Implements hook_theme().
*/
function my_module_user_theme($existing, $type, $theme, $path) {
return [
'my_module_user_active_subsites' => [
'variables' => [
'groups' => '',
],
],
];
}

Creando nuestro template.

Ahora, como paso final, procederemos a crear nuestro twig template. Para que nuestros templates sean reconocidos debemos colocarlos en una ubicación específica, siguiendo un formato de nombre específico.

Nuestros templates deben estar en una carpeta llamada “templates” en la raíz de nuestro módulo y como mencionamos, deben seguir el siguiente formato de nombre: nombre-template.html.twig. Para nuestro caso creamos el archivo: my-module-active-subsites.html.twig.

Este template se encargará de formatear e imprimir las imágenes pertenecientes a nuestros grupos:

{#
/**
* @file
* Template file my_module_active_subsites field
*/
#}
<div class="container user-groups-container">
{% if groups is not empty %}
{% for group in groups %}
<div class="container user-groups-element" >
{{ group.logo }}
</div>
{% endfor %}
{% endif %}
</div>

Como podemos observar twig es muy distinto de lo que conocíamos antes en los PHP templates, pero esto no significa que sea complicado. En mi caso me apoyé en esta documentación: https://www.drupal.org/docs/8/theming/twig/comparison-of-phptemplate-and-twig-theming-paradigms, esta documentación da una comparación entre lo que antes hacíamos con PHP templates y cómo se hace ahora con twig.

Como pudimos ver, los pseudocampos de Drupal 8 al igual que en Drupal 7, siguen siendo un gran recurso que permite dar flexibilidad a nuestros despliegues de entidades. Si a esto le unimos templates, podemos cumplir con cualquier requerimiento de despliegue de nuestro contenido. Solo se debe recordar que el uso de los mismos depende del tipo del despliegue que queramos usar para nuestro contenido.

¡Feliz codificación!

Manatí es una firma consultora web de origen costarricense, donde hacemos sitios estratégicamente diseñados para llevar organizaciones a lograr objetivos.

No se pierdan los proyectos, noticias e ideas de nosotros en Manatí. Conozca más sobre lo que hacemos, síganos en medium, twitter y facebook.


Drupal 8: Pseudocampos, templates y render de imágenes con links. was originally published in MANATI | Agencia Web on Medium, where people are continuing the conversation by highlighting and responding to this story.

“Mapillaryando” en cantinas de San José

-- por Marcia Ugarte y Joaquín Lizano

Un sábado por la tarde resultó ser el momento perfecto para que un grupo de personas se juntara en San José para iniciar la labor de contribuir al mapeo libre esta vez de cantinas del centro de la capital. Cantina en Costa Rica hace referencia a un bar popular, que posiblemente tenga sus añitos, sin aires snobs y que tiene alcohol y comida a buen precio, o solo alcohol.

Este grupo sacrificado trazó un plan de visita y mínimas normas para el proceso de mapeo: tomar máximo una cerveza en cada sitio y comer donde se pudiera ojalá una boca conocida del bar correspondiente. La ruta inició en El Gran Vicio, cantina de toda la vida dentro del Mercado Central; continuó al Ballestero, la única cantina que queda en una de las cuatro esquinas de entrada al San José de antaño y que dicen tiene uno de los mejores chifrijos; luego pasamos a La Embajada, bar chirrión con una barra larga larga y famoso por el gallo de chorizo; después le tocó al turno a El Faro, cantina de 3 pisos con hora feliz de cerveza a menos de mil colones y buena costilla; el siguiente fue La Bohemia, después Wongs y lxs últimxs valientes terminaron de madrugada en Area City.

Detallando el recorrido, el punto de inicio, “El Gran Vicio”, es probablemente una de las cantinas más viejas de San José. Ubicado en el Mercado Central de la ciudad, abrió sus puertas en 1880. Podríamos decir que es tan viejo que pareciera que es solo para hombres. El orinal está en una esquina del bar y la puerta no cierra ni abre, está como medio puesta y no resguarda aquella privacidad que se espera de un servicio sanitario. ¿Baño de mujeres? no hay. La pared opuesta a la barra de este espacio, que funciona como un pasillo debido a su estrechez, está llena de firmas, mensajes y memorias gráficas.

  • clientela que se conoce entre sí, algunos con sus uniformes de trabajo del mercado
  • el cantinero no era muy amable con los extraños (nosotros…)
  • es un bar de paso (paso a tomarme una cerveza y/o un trago y me voy)
  • interesante experiencia

De ahí partimos al “Ballestero”. Pocas cantinas tienen plantas naturales a la entrada. Seña que vamos por una experiencia diferente. Está situada en una de las esquinas del cierre de la calle ancha que da entrada a la ciudad capital desde el Norte. Desde la mesa de la esquina consumimos felices las bocas (dar fe que los patacones con frijoles son de lo mejor de la ciudad) mientras admiramos la bola disco de espejitos en el centro del techo (sin luces dirigidas, sin mecanismos para que gire), un antojo de los dueños para dar simbolismos fiesteros al lugar. Tal vez no combina la bola con la colección de vasos y las fotos familiares en la paredes, tal vez ese es justo el estilo que andaban buscando.

  • música texmex
  • chilera de la casa
  • tarjetas de crédito no bienvenidas (pero aceptadas si se insiste mucho)
  • mejor llevar cash

Debería haber también mención a los trayectos. Las caminatas de unxs jóvenes (y otros no tanto) caminando con sus celulares en posición horizontal y más arriba de sus cabezas, grabando el camino, siguiendo a su “líder” que camina con un báculo tecnológico con un ojo en las alturas. Digamos que no pasamos desapercibidxs por el público josefino. Posiblemente si unx nos viera pasar así de la nada… seguramente que tampoco sabría qué pensar… turistas, extraterrestres, geeks haciendo una peli/docu de chepe, buscando pokemones…?

El siguiente bar fue “La Embajada” que terminó siendo denominada la nueva embajada de Mapillary en Costa Rica. Su principal característica es la barra enorme que abarca gran parte del espacio y que da la impresión de que si nos animamos a llegar al final nos comerá la oscuridad, pero no. El fondo está repleto de mesas y hay suficiente espacio para todo el grupo y un mariachi que se acomoda al final de la barra. Realmente sorprende que exista un espacio tan grande y que fácilmente unx pase por fuera sin darse cuenta de lo que hay dentro.

  • la birra a 900 parece que era una publicidad vieja que no han eliminado. Costaba 1000
  • barra muy larga
  • los gallos de chorizo o salchichón vienen sin tortilla
  • mariachi compite con “música de cabina”
  • no se separan cuentas
  • muy concurrido

Seguimos el trayecto en un atardecer que afectaba, dada la cada vez menos luz, la posibilidad de mapear al caminar; pero “sacrificadamente” hicimos todo lo que estaba en nuestras manos para no frenar el mapeo. Caminamos por el puro centro de Chepe y llegamos al Faro. Una vieja edificación de tres pisos con vista al sur de San José. Ya cuan lejos logre ver unx desde este faro dependerá, entre otras cosas, de cuanto se enfieste en el lugar. Cada piso es un ambiente; de hecho, el tercer piso es para fiestas y no está abierto normalmente. Abajo estaba lleno por lo que fuimos al segundo piso, que además tenía un rock/pop ochentero apreciado por la mayoría del grupo. Las ventanas abiertas generaban un nivel de ventolero que, de querer enviajarse, podria referir al faro y ambientarse unx quien sabe donde.

  • buena atención
  • separan cuentas
  • aceptan tarjetas
  • distintos ambientes
  • buena música

Ya enrumbados (más de rumba que de rumbo) bajamos una cuadra y llegamos a La Bohemia. Cantina de tradición hasta para algunxs de lxs miembrxs del grupo mapeante.

  • fácil sentirse bienvenidx en el lugar
  • fuerte conexión entre los clientes habituales
  • hasta nos regalaron del queque de un cumpleaños que estaban celebrando
  • pocas bocas
  • separan cuentas
  • aceptan tarjetas

Ya para esas horas de la noche, el trayecto se hacía inmapeable pero la intención sacrificada no se acababa. Fuimos a chequear otro par de lugares que podríamos incluir en futuras misiones. Wongs es un restaurante más que una cantina y en lugar de bocas fueron dumplings, generando un momento de excepción en la ruta. Terminamos en la madrugada del día siguiente en Area City celebrando ya un cumpleaños de alguien de nuestro grupo mapeante. Gran salida que, de seguir con ese espíritu festivo, puede generar muchxs sacrificadxs voluntarixs futurxs que nos permitan conocer más de esas cantinas tradicionales que aún quedan en San José.

La embajada de Mapillary en Costa Rica

Minar ethereum

Requerimientos

  • Minador
  • Ubuntu Server 16.04
  • Opcional(tarjeta de video)
  • Python y python-twisted
  • Ethereum
  • cpp-ethereum

NOTA: Se considera Ubuntu Server, en caso de Ubuntu Desktop algunos requerimientos ya vienen instalados en el sistema.

Instalación

  1. Instalar ubuntu 16.04

  2. Instalar python y python-wisted

sudo apt-get install python
sudo apt-get install python-twisted
  1. Un vez que se tiene instalado el sistema operativo, activar el ppa de ethereum
sudo add-apt-repository ppa:ethereum/ethereum
sudo add-apt-repository ppa:ethereum/ethereum-qt
sudo add-apt-repository ppa:ethereum/ethereum-dev
sudo apt-get update
  1. Instalar ethereum
sudo apt-get install ethereum
  1. Instalar cpp-ethereum
sudo apt-get install cpp-ethereum
  1. Clonar el repositorio de eth-proxy.

  2. Crear un wallet con geth o parity.

  3. Instalar los drivers de vídeo, en el caso de usar una tarjeta de vídeo.

  4. Modificar el archivo de configuración de eth-proxy para usar el wallet.

  5. En el directorio eth-proxy, ejecutar eth-proxy.py

sudo python eth-proxy/eth-proxy.py
  1. Ejecutar ethminer apuntando a localhost
ethminer -F http://127.0.0.1:8080/minador -G

NOTA: La opción -G indica a ethminer que utilice GPU para minar, en caso de no contar con GPU utilice --allow-opencl-cpu.

Referencias

https://github.com/paritytech/parity https://github.com/Atrides/eth-proxy https://launchpad.net/~ethereum/+archive/ubuntu/ethereum http://ethdocs.org/en/latest/ethereum-clients/cpp-ethereum/installing-binaries/linux-ubuntu-ppa.html