icostan/cryptos-ruby

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# Cryptos-ruby

[![Build Status](https://travis-ci.org/icostan/cryptos-ruby.svg?branch=master)](https://travis-ci.org/icostan/cryptos-ruby)
[![Maintainability](https://api.codeclimate.com/v1/badges/3e4566b45ebc3f887cef/maintainability)](https://codeclimate.com/github/icostan/cryptos-ruby/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/3e4566b45ebc3f887cef/test_coverage)](https://codeclimate.com/github/icostan/cryptos-ruby/test_coverage)
[![Gem Version](https://badge.fury.io/rb/cryptos.svg)](https://badge.fury.io/rb/cryptos)

### The Why - the vision and goals

  * I believe there are none of very few Ruby implementations and support for different crypto technologies
  * I like to craft my own wallets, transactions, block explorers in all shapes and forms
  * I dream to execute atomic swaps in 3 lines of Ruby code

### The How - the actions

  * Implementing basic cryptography from scratch - elliptic curves math, digital signature schemes, etc
  * Building a simple and easy to use Ruby API
  * Lean and continuous improvement along the way while I understand more advanced cryptography: pairing, lattices

### The What - the features

  * Generate private and public keys
  * Generate addresses for Bitcoin, Litecoin, Ethereum and much more
  * Create transaction to spend standard inputs or more complex multisig, hashed timelock contracts, swaps

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'cryptos'
```

And then execute:

    $ bundle

Or install it yourself as:

    $ gem install cryptos

## Usage

### Bitcoin and friends

#### Generate keys and address

Alright, let's begin, first thing first, lets generate private and public keys:

```ruby
2.5.3 > private_key = Cryptos::PrivateKey.generate
 => #<Cryptos::PrivateKey:0x00007f8cc10c0ad0 @value=1991485315816438798044329630916774278846523543844864946402119577704095054145, @order=115792089237316195423570985008687907852837564279074904382605163141518161494337>
2.5.3 > public_key = Cryptos::PublicKey.new private_key
 => #<Cryptos::PublicKey:0x00007f8cc105ed58 @private_key=#<Cryptos::PrivateKey:0x00007f8cc10c0ad0 @value=1991485315816438798044329630916774278846523543844864946402119577704095054145, @order=115792089237316195423570985008687907852837564279074904382605163141518161494337>, @x=107779388491921327681974754398507503201871466663959093103394577491037829153768, @y=78060352001932916201234328232450653863791592111885208305671830584742527863131>
```

Based on public key above lets create a Bitcoin address:

```ruby
2.5.3 > from_address = Cryptos::Bitcoin::Address.new public_key
 => #<Cryptos::Bitcoin::Address:0x00007f8cc12fc560 @public_key=#<Cryptos::PublicKey:0x00007f8cc105ed58 @private_key=#<Cryptos::PrivateKey:0x00007f8cc10c0ad0 @value=1991485315816438798044329630916774278846523543844864946402119577704095054145, @order=115792089237316195423570985008687907852837564279074904382605163141518161494337>, @x=107779388491921327681974754398507503201871466663959093103394577491037829153768, @y=78060352001932916201234328232450653863791592111885208305671830584742527863131>, @testnet=true>
```

#### Scenario 1: Spend coinbase transaction

Before going any further we need to install bitcoin-core daemon and start node in regtest mode:

```shell
# in MacOS
brew install bitcoin

# in Linux (Debian based)
apt-get install bitcoin

# start Bitcoin daemon in regtest mode
bitcoind -regtest -printtoconsole
```

Now we create a simple Cli connector that will communicate to underlying bitcoin daemon.

```ruby
2.5.3 :004 > cli = Cryptos::Connectors::Cli.new
 => #<Cryptos::Connectors::Cli:0x00007f8cc12ece30 @program="bitcoin-cli", @network="regtest", @verbose=false>
```

Import address into node and generate 101 blocks. If you ask why 101 then it is because coinbase transactions are spendable after 100 confirmatinos.

```ruby
2.5.3 :005 > from_address.import cli
 => true
2.5.3 :006 > cli.generate_to_address from_address, blocks: 101
 => true
```

Generate and import destination address to send BTC to then check that it has no money in it.

```ruby
2.5.3 :007 > to_address = Cryptos::Bitcoin::Address.new Cryptos::PublicKey.new Cryptos::PrivateKey.generate
 => #<Cryptos::Bitcoin::Address:0x00007f8cc134f2b0 @public_key=#<Cryptos::PublicKey:0x00007f8cc128fa78 @private_key=#<Cryptos::PrivateKey:0x00007f8cc128faa0 @value=104555233989943463494354097619221894829574308702717051161491781222000198727347, @order=115792089237316195423570985008687907852837564279074904382605163141518161494337>, @x=1402024405898287938501468401055931693243587868828983898835308320263377717122, @y=89146164815925753866667564550747587615674131412309491381641677989226156891240>, @testnet=true>
2.5.3 :008 > to_address.import cli
 => true
2.5.3 :009 > to_address.get_balance cli
 => "0.00000000"
```

Alright, now we get to real stuff, transactions: create input from our ```from_address```, send 123_456_789 Satoshis (1.23456789 BTC) to our ```to_address``` and change amount back to ```from_address```.

```ruby
2.5.3 :010 > input = Cryptos::Input.from_utxo cli, from_address
 => #<struct Cryptos::Input value=2500000000.0, tx_hash="33fc8506d7a5880cfddca3c950f95fa461398fb764da4527169d5574a7c00c7b", index=0, script_sig=nil, sequence=68719476735>
2.5.3 :011 > output = Cryptos::Output.p2pkh to_address, 123_456_789
 => #<struct Cryptos::Output value=123456789, script_pubkey=#<Cryptos::Script:0x00007f8cc12ed8a8 @script="OP_DUP OP_HASH160 9aae79929e4364ab3aabe1f83a875304d1b67a3a OP_EQUALVERIFY OP_CHECKSIG">>
2.5.3 :012 > change = Cryptos::Output.p2pkh_change from_address, input, output
 => #<struct Cryptos::Output value=2376533211.0, script_pubkey=#<Cryptos::Script:0x00007f8cc12cfdd0 @script="OP_DUP OP_HASH160 57a58e05aedfbb6bd97b373baf65ce7cc318351b OP_EQUALVERIFY OP_CHECKSIG">>
2.5.3 :013 > transaction = Cryptos::Transaction.from_ioc input, output, change
 => #<struct Cryptos::Transaction version=1, inputs=[#<struct Cryptos::Input value=2500000000.0, tx_hash="33fc8506d7a5880cfddca3c950f95fa461398fb764da4527169d5574a7c00c7b", index=0, script_sig=nil, sequence=68719476735>], outputs=[#<struct Cryptos::Output value=123456789, script_pubkey=#<Cryptos::Script:0x00007f8cc12ed8a8 @script="OP_DUP OP_HASH160 9aae79929e4364ab3aabe1f83a875304d1b67a3a OP_EQUALVERIFY OP_CHECKSIG">>, #<struct Cryptos::Output value=2376533211.0, script_pubkey=#<Cryptos::Script:0x00007f8cc12cfdd0 @script="OP_DUP OP_HASH160 57a58e05aedfbb6bd97b373baf65ce7cc318351b OP_EQUALVERIFY OP_CHECKSIG">>], locktime=0>
```

Sign and broadcast the transaction:

```ruby
2.5.3 :014 > transaction.sign_single_input from_address
 => "01000000017b0cc0a774559d162745da64b78f3961a45ff950c9a3dcfd0c88a5d70685fc33000000006a473044022020b53986c2ef08d54137e57f1c231a0c2fe1b6dc88c7208ecef6f7474bae985002203027db653202da53ce081da46431ef1f88f3e1bf47254940a58740a86506cbc3012103ee48f8db1d9a5dfc1b620dbe9566b77d995e0325b91d3b661a697272920f43e8ffffffff0215cd5b07000000001976a9149aae79929e4364ab3aabe1f83a875304d1b67a3a88acdb04a78d000000001976a91457a58e05aedfbb6bd97b373baf65ce7cc318351b88ac00000000"
2.5.3 :015 > transaction.broadcast cli
 => true
```

Mine new block that will contain our hand crafted transaction and VOILA! output amount was transafered to new address.

```ruby
2.5.3 :016 > cli.generate blocks: 1
 => true
2.5.3 :017 > cli.get_received_by_address to_address
 => "1.23456789"
```

#### Scenario 2: Spend multisig transaction

[multisig transaction](spec/cryptos/litecoin_spec.rb)

#### Scenario 3: Atomic swaps between BTC and LTC

[atomic swap](spec/cryptos/swaps_spec.rb)

### Monero

#### Generate keys and address:

```ruby
2.5.3 :015 > seed = "vinegar talent sorry hybrid ultimate template nimbly jukebox axes inactive veered toenail pride plotting chrome victim agnostic science bailed paddles wounded peaches king laptop king"
 => "vinegar talent sorry hybrid ultimate template nimbly jukebox axes inactive veered toenail pride plotting chrome victim agnostic science bailed paddles wounded peaches king laptop king"
2.5.3 :016 > wallet = Cryptos::Monero::Wallet.from_mnemonic seed
 => #<Cryptos::Monero::Wallet:0x00007faa600bfad8 @seed="6ee02ef8647856f4080882a1ec4fabee19ec047ca24d3abb13c0ce589a46f702">
2.5.3 :017 > private_spend_key = wallet.private_spend_key
 => #<Cryptos::Monero::PrivateSpendKey:0x00007faa600aac50 @value=1341524205595389594312687854960107116555340486708078533906470082005332582510, @order=7237005577332262213973186563042994240857116359379907606001950938285454250989, @little=true>
```

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/icostan/cryptos. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.

## Code of Conduct

Everyone interacting in the CryptoCrafts project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/icostan/cryptos/blob/master/CODE_OF_CONDUCT.md).