cryptos 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7eab144e238bbc87fd1a9d71dab7c57d590a294b30a489b1cd85e87f9a2e60db
4
+ data.tar.gz: 19ecae744e1f98ebc17ebfcde4ec4673183085455b62641f0d684d569aa2c50b
5
+ SHA512:
6
+ metadata.gz: 373c5b5c1775ce8c485aa7f93706c7619ce78a09b42e499b3ff5d22256385c33767cfa681fc3c76d2ce55016fe6456861da5bfe7ca4c5866db7dfecd70af750f
7
+ data.tar.gz: 42851acdf1e22ee3c6537be65c3537c6dc9c7dea69e556bc676f1cd322bdca0782467f2092850adbe5026a32c8b378803fc95eb0d3696df098e66451e60a7f12
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ /TAGS
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1 @@
1
+ ruby-2.5.3
@@ -0,0 +1,25 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.3
7
+
8
+ before_install:
9
+ - gem install bundler -v 1.16.6
10
+ - sudo apt-add-repository ppa:bitcoin/bitcoin -y
11
+ - sudo apt-get update -qq
12
+ - sudo apt-get install bitcoind -y
13
+ - wget https://download.litecoin.org/litecoin-0.16.3/linux/litecoin-0.16.3-x86_64-linux-gnu.tar.gz
14
+ - tar -xvf litecoin-0.16.3-x86_64-linux-gnu.tar.gz
15
+ - export PATH=./litecoin-0.16.3/bin:$PATH
16
+ - echo $PATH
17
+
18
+ apt_packages:
19
+ - bitcoind
20
+
21
+ before_script:
22
+ # - mkdir -p /home/travis/.bitcoin && cp bitcoin.conf /home/travis/.bitcoin/bitcoin.conf
23
+ - bitcoind -regtest -daemon
24
+ - litecoind -regtest -daemon
25
+ - sleep 15
@@ -0,0 +1 @@
1
+ # CryptoCrafts changelog
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at iulian.costan@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in cryptos.gemspec
6
+ gemspec
@@ -0,0 +1,53 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cryptos (0.0.1)
5
+ hashie
6
+ httparty
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ coderay (1.1.2)
12
+ diff-lcs (1.3)
13
+ ecdsa (1.2.0)
14
+ hashie (3.6.0)
15
+ httparty (0.16.3)
16
+ mime-types (~> 3.0)
17
+ multi_xml (>= 0.5.2)
18
+ method_source (0.9.2)
19
+ mime-types (3.2.2)
20
+ mime-types-data (~> 3.2015)
21
+ mime-types-data (3.2018.0812)
22
+ multi_xml (0.6.0)
23
+ pry (0.12.2)
24
+ coderay (~> 1.1.0)
25
+ method_source (~> 0.9.0)
26
+ rake (12.3.1)
27
+ rspec (3.8.0)
28
+ rspec-core (~> 3.8.0)
29
+ rspec-expectations (~> 3.8.0)
30
+ rspec-mocks (~> 3.8.0)
31
+ rspec-core (3.8.0)
32
+ rspec-support (~> 3.8.0)
33
+ rspec-expectations (3.8.2)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.8.0)
36
+ rspec-mocks (3.8.0)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.8.0)
39
+ rspec-support (3.8.0)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ bundler
46
+ cryptos!
47
+ ecdsa
48
+ pry
49
+ rake
50
+ rspec
51
+
52
+ BUNDLED WITH
53
+ 1.17.1
@@ -0,0 +1,41 @@
1
+ # Cryptos
2
+ [![Build Status](https://travis-ci.org/icostan/cryptos.svg?branch=master)](https://travis-ci.org/icostan/cryptos)
3
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/50a2b33cfb10cd5293dc/test_coverage)](https://codeclimate.com/github/icostan/cryptos/test_coverage)
4
+
5
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/cryptos`. To experiment with that code, run `bin/console` for an interactive prompt.
6
+
7
+ TODO: Delete this and the text above, and describe your gem
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'cryptos'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install cryptos
24
+
25
+ ## Usage
26
+
27
+ TODO: Write usage instructions here
28
+
29
+ ## Development
30
+
31
+ 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.
32
+
33
+ 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).
34
+
35
+ ## Contributing
36
+
37
+ 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.
38
+
39
+ ## Code of Conduct
40
+
41
+ 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).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ * TODOs
2
+ * Integration tests with -regtest mode
3
+ * Support for LTC
4
+ * Atomic swap BTCLTC
5
+ * brew decred
6
+ * brew litecoin, move cask
7
+ * autoload modules
8
+ * deterministic signature (https://tools.ietf.org/html/rfc6979)
9
+ * refactor elliptic curve module, extract to classes
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cryptos"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,45 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'cryptos/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'cryptos'
7
+ spec.version = Cryptos::VERSION
8
+ spec.authors = ['Iulian Costan']
9
+ spec.email = ['iulian.costan@gmail.com']
10
+
11
+ spec.summary = %q{Crypto craft your own transactions}
12
+ spec.description = %q{The easiest way to craft your own transactions for multiple crypto currencies}
13
+ spec.homepage = 'https://github.com/icostan/cryptos'
14
+
15
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
16
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
17
+ if spec.respond_to?(:metadata)
18
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
19
+
20
+ spec.metadata['homepage_uri'] = spec.homepage
21
+ spec.metadata['source_code_uri'] = 'https://github.com/icostan/cryptos.git'
22
+ spec.metadata['changelog_uri'] = 'https://github.com/icostan/cryptos/blob/master/CHANGELOG'
23
+ else
24
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
25
+ 'public gem pushes.'
26
+ end
27
+
28
+ # Specify which files should be added to the gem when it is released.
29
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
30
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
31
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
32
+ end
33
+ spec.bindir = 'exe'
34
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ['lib']
36
+
37
+ spec.add_dependency 'httparty'
38
+ spec.add_dependency 'hashie'
39
+
40
+ spec.add_development_dependency 'bundler'
41
+ spec.add_development_dependency 'ecdsa'
42
+ spec.add_development_dependency 'rake'
43
+ spec.add_development_dependency 'rspec'
44
+ spec.add_development_dependency 'pry'
45
+ end
@@ -0,0 +1,29 @@
1
+ require 'httparty'
2
+ require 'hashie'
3
+
4
+ require 'cryptos/version'
5
+
6
+ require 'cryptos/base'
7
+ require 'cryptos/base58'
8
+ require 'cryptos/hashing'
9
+ require 'cryptos/elliptic_curve'
10
+ require 'cryptos/connectors/cli'
11
+
12
+ require 'cryptos/private_key'
13
+ require 'cryptos/public_key'
14
+ require 'cryptos/address'
15
+ require 'cryptos/script'
16
+ require 'cryptos/input'
17
+ require 'cryptos/output'
18
+ require 'cryptos/transaction'
19
+ require 'cryptos/der'
20
+
21
+ module Cryptos
22
+ # currencies
23
+ autoload :Bitcoin, 'cryptos/bitcoin'
24
+ autoload :Litecoin, 'cryptos/litecoin'
25
+
26
+ # exchanges
27
+ autoload :Bitmex, 'cryptos/bitmex/client'
28
+ autoload :Deribit, 'cryptos/deribit/client'
29
+ end
@@ -0,0 +1,46 @@
1
+ module Cryptos
2
+ class Address
3
+ include Base58, Hashing
4
+ extend Base58, Hashing
5
+
6
+ attr_reader :public_key
7
+ attr_reader :testnet
8
+
9
+ def initialize(public_key, testnet: true)
10
+ @public_key = public_key
11
+ @testnet = testnet
12
+ end
13
+
14
+ #
15
+ # https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
16
+ #
17
+ def generate(network)
18
+ prefix = network.to_s(16).rjust 2, '0'
19
+ ripemd160 = hash160 public_key.compressed
20
+ with_version = "#{prefix}#{ripemd160}"
21
+ checksum = hash256(with_version)[0, 8]
22
+ wrap_encode = "#{with_version}#{checksum}"
23
+ base58_encode(wrap_encode)
24
+ end
25
+
26
+ def self.to_hash160(value)
27
+ base58_decode(value)[2, 40]
28
+ end
29
+
30
+ def to_hash160
31
+ hash160(public_key.compressed)
32
+ end
33
+
34
+ def p2pkh
35
+ generate p2pkh_prefix
36
+ end
37
+
38
+ def p2sh
39
+ generate p2sh_prefix
40
+ end
41
+
42
+ def to_s
43
+ p2pkh
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,74 @@
1
+ require 'json'
2
+ require 'digest'
3
+
4
+ class Struct
5
+ OPCODES = {
6
+ 'OP_DUP' => 0x76,
7
+ 'OP_HASH160' => 0xA9,
8
+ 'OP_EQUAL' => 0x87,
9
+ 'OP_EQUALVERIFY' => 0x88,
10
+ 'OP_CHECKSIG' => 0xAC
11
+ }.freeze
12
+
13
+ def opcode(token)
14
+ raise "opcode #{token} not found" unless OPCODES.include?(token)
15
+ OPCODES[token].to_s 16
16
+ end
17
+
18
+ def data(token)
19
+ bin_size = hex_size token
20
+ # TODO: data size is defined as 1-9 bytes
21
+ byte_to_hex(bin_size) + token
22
+ end
23
+
24
+ def hex_size(hex)
25
+ [hex].pack('H*').size
26
+ end
27
+
28
+ def to_hex(binary_bytes)
29
+ binary_bytes.unpack('H*').first
30
+ end
31
+
32
+ def hash_to_hex(value)
33
+ to_hex [value].pack('H*').reverse
34
+ end
35
+
36
+ def int_to_hex(value)
37
+ to_hex [value].pack('V')
38
+ end
39
+
40
+ def byte_to_hex(value)
41
+ to_hex [value].pack('C')
42
+ end
43
+
44
+ def bytes_to_hex(bytes)
45
+ to_hex bytes.pack('C*')
46
+ end
47
+
48
+ def long_to_hex(value)
49
+ to_hex [value].pack('Q<')
50
+ end
51
+
52
+ def script_to_hex(script_string)
53
+ script_string.split.map { |token| token.start_with?('OP') ? opcode(token) : data(token) }.join
54
+ end
55
+
56
+ def sha256(hex)
57
+ Digest::SHA256.hexdigest([hex].pack('H*'))
58
+ end
59
+ end
60
+
61
+ def bytes_to_bignum(bytes_string)
62
+ bytes_string.bytes.reduce { |n, b| (n << 8) + b }
63
+ end
64
+
65
+ def bignum_to_bytes(n, length=nil, stringify=true)
66
+ a = []
67
+ while n > 0
68
+ a << (n & 0xFF)
69
+ n >>= 8
70
+ end
71
+ a.fill 0x00, a.length, length - a.length if length
72
+ bytes = a.reverse
73
+ stringify ? bytes.pack('C*') : bytes
74
+ end
@@ -0,0 +1,24 @@
1
+ module Base58
2
+ def base58_encode(ripe160_hash)
3
+ alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
4
+ value = ripe160_hash.to_i 16
5
+ output = ''
6
+ while value > 0
7
+ remainder = value % 58
8
+ value /= 58
9
+ output += alphabet[remainder]
10
+ end
11
+ output += alphabet[0] * [ripe160_hash].pack('H*').bytes.find_index{|b| b != 0}
12
+ output.reverse
13
+ end
14
+ def base58_decode(address)
15
+ alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
16
+ int_val = 0
17
+ address.reverse.chars.each_with_index do |char, index|
18
+ char_index = alphabet.index(char)
19
+ int_val += char_index * 58**index
20
+ end
21
+ # TODO: hard coded 25 bytes?
22
+ bignum_to_bytes(int_val, 25).unpack('H*').first
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ puts 'loading bitcoin...'
2
+
3
+ require_relative 'bitcoin/address'
4
+ require_relative 'bitcoin/script'
5
+
6
+ module Cryptos
7
+ module Bitcoin
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Cryptos
2
+ module Bitcoin
3
+ class Address < Cryptos::Address
4
+ def p2pkh_prefix
5
+ testnet ? 0x6f : 0x00
6
+ end
7
+
8
+ def p2sh_prefix
9
+ testnet ? 0xc4 : 0x05
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ module Bitcoin
2
+ class Script
3
+ end
4
+ end
@@ -0,0 +1,38 @@
1
+ puts 'loading bitmex...'
2
+
3
+ module Cryptos
4
+ module Bitmex
5
+ class Client
6
+ include HTTParty
7
+ base_uri 'https://testnet.bitmex.com/api/v1'
8
+
9
+ def initialize
10
+ @options = {}
11
+ end
12
+
13
+ def get_instruments
14
+ execute '/instrument/active' do |response|
15
+ response.to_a.map do |i|
16
+ Hashie::Mash.new i
17
+ end
18
+ end
19
+ end
20
+
21
+ def stats
22
+ execute '/stats' do |response|
23
+ response.to_a.map do |s|
24
+ Hashie::Mash.new s
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def execute(uri, &ablock)
32
+ response = self.class.get uri, query: { count: 10 }
33
+ fail response.message unless response.success?
34
+ yield response
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ module Connectors
2
+ class Cli
3
+ attr_reader :program, :network
4
+
5
+ def initialize(program = 'bitcoin-cli', network = 'regtest')
6
+ @program = program
7
+ @network = network
8
+ end
9
+
10
+ def run(args, run_mode: :inline, v: false)
11
+ cmd = "#{program} -#{network} #{args}"
12
+ puts "==> #{cmd}"
13
+ case run_mode
14
+ when :inline
15
+ output = `#{cmd}`
16
+ puts output if v
17
+ output
18
+ when :system
19
+ success = system cmd
20
+ fail "failed command: #{args}" unless success
21
+ success
22
+ when :daemon
23
+ pid = spawn cmd
24
+ sleep (ENV['BOOTSTRAP'] || 10).to_i
25
+ pid
26
+ else
27
+ raise "dont know how to run #{run_mode}"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ Der = Struct.new :der, :length, :ri, :rl, :r, :si, :sl, :s, :sighash_type do
2
+ def initialize(der: 0x30, length: 0x44, ri: 0x02, rl: 0x20, r: nil, si: 0x02, sl: 0x20, s: nil, sighash_type: 0x01)
3
+ super der, length, ri, rl, r, si, sl, s, sighash_type
4
+ end
5
+
6
+ def serialize
7
+ r_bytes = bignum_to_bytes(r, 32, false)
8
+ if r_bytes.first & 0x80 == 128
9
+ r_bytes = [0x00] + r_bytes
10
+ self.length += 1
11
+ self.rl += 1
12
+ end
13
+ byte_to_hex(der) + byte_to_hex(length) +
14
+ byte_to_hex(ri) + byte_to_hex(rl) + bytes_to_hex(r_bytes) +
15
+ byte_to_hex(si) + byte_to_hex(sl) + to_hex(bignum_to_bytes(s, 32)) +
16
+ byte_to_hex(sighash_type)
17
+ end
18
+
19
+ def self.parse(signature)
20
+ fields = *[signature].pack('H*').unpack('CCCCH66CCH64C')
21
+ Der.new r: fields[4], s: fields[7], sighash_type: fields[8]
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ puts 'loading deribit...'
2
+
3
+ module Cryptos
4
+ module Deribit
5
+ class Client
6
+ include HTTParty
7
+ base_uri 'https://test.deribit.com/api/v1/public'
8
+
9
+ def initialize
10
+ @options = {}
11
+ end
12
+
13
+ def get_instruments
14
+ execute '/getinstruments'
15
+ end
16
+
17
+ def get_trades(type = :all)
18
+ execute '/getlasttrades', query: { instrument: type }
19
+ end
20
+
21
+ def get_summary(type = :all)
22
+ execute '/getsummary', query: { instrument: type }
23
+ end
24
+
25
+ def stats
26
+ execute '/stats'
27
+ end
28
+
29
+ private
30
+
31
+ def execute(uri, options = {})
32
+ response = self.class.get uri, options
33
+ fail response.message unless response.success?
34
+ fail response.body unless response['success']
35
+
36
+ Hashie::Mash.new(response.to_hash).result
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,97 @@
1
+ module EllipticCurve
2
+ Group = Struct.new :gx, :gy, :prime, :order, :cofactor
3
+ Secp256k1 = Group.new 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
4
+ 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8,
5
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
6
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
7
+ 1
8
+ end
9
+
10
+ EC_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
11
+ EC_Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
12
+ EC_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
13
+ EC_n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
14
+
15
+ LOW_S = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
16
+
17
+ #
18
+ # Elliptic Curve
19
+ #
20
+ def extended_euclidean_algorithm(a, b)
21
+ s, old_s = 0, 1
22
+ t, old_t = 1, 0
23
+ r, old_r = b, a
24
+ while r != 0
25
+ quotient = old_r / r
26
+ old_r, r = r, old_r - quotient * r
27
+ old_s, s = s, old_s - quotient * s
28
+ old_t, t = t, old_t - quotient * t
29
+ end
30
+ [old_r, old_s, old_t]
31
+ end
32
+ def inverse(n, p)
33
+ gcd, x, y = extended_euclidean_algorithm(n, p)
34
+ (n * x + p * y) % p == gcd || raise('invalid gcd')
35
+ gcd == 1 || raise('no multiplicative inverse')
36
+ x % p
37
+ end
38
+ def ec_double(px, py, pn)
39
+ i_2y = inverse(2 * py, pn)
40
+ slope = (3 * px**2 * i_2y) % pn
41
+ x = (slope**2 - 2 * px) % pn
42
+ y = (slope*(px - x) - py) % pn
43
+ [x, y]
44
+ end
45
+ def ec_add(ax, ay, bx, by, pn)
46
+ return [ax, ay] if bx == 0 && by == 0
47
+ return [bx, by] if ax == 0 && ay == 0
48
+ return ec_double(ax, ay, pn) if ax == bx && ay == by
49
+
50
+ i_bax = inverse(ax - bx, pn)
51
+ slope = ((ay - by) * i_bax) % pn
52
+ x = (slope**2 - ax - bx) % pn
53
+ y = (slope*(ax - x) - ay) % pn
54
+ [x, y]
55
+ end
56
+ def ec_multiply(m, px, py, pn)
57
+ nx, ny = px, py
58
+ qx, qy = 0, 0
59
+ while m > 0
60
+ qx, qy = ec_add qx, qy, nx, ny, pn if m&1 == 1
61
+ nx, ny = ec_double nx, ny, pn
62
+ m >>= 1
63
+ end
64
+ [qx, qy]
65
+ end
66
+
67
+ #
68
+ # ECDSA
69
+ #
70
+ def ecdsa_sign(private_key, digest, temp_key = nil)
71
+ temp_key ||= Cryptos::PrivateKey.generate.value
72
+ rx, _ry = ec_multiply(temp_key, EC_Gx, EC_Gy, EC_p)
73
+ r = rx % EC_n
74
+ r > 0 || raise('r is zero, try again new temp key')
75
+ i_tk = inverse temp_key, EC_n
76
+ m = bytes_to_bignum digest
77
+ s = (i_tk * (m + r * private_key)) % EC_n
78
+ s > 0 || raise('s is zero, try again new temp key')
79
+ if s > LOW_S
80
+ # https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#low-s-values-in-signatures
81
+ # puts 'WARN: s is too big, use low s values'
82
+ s = (EC_n - s) % EC_n
83
+ end
84
+ [r, s]
85
+ end
86
+
87
+ def ecdsa_verify?(px, py, digest, signature)
88
+ r, s = signature
89
+ i_s = inverse s, EC_n
90
+ m = bytes_to_bignum digest
91
+ u1 = i_s * m % EC_n
92
+ u2 = i_s * r % EC_n
93
+ u1Gx, u1Gy = ec_multiply u1, EC_Gx, EC_Gy, EC_p
94
+ u2Px, u2Py = ec_multiply u2, px, py, EC_p
95
+ rx, _ry = ec_add u1Gx, u1Gy, u2Px, u2Py, EC_p
96
+ r == rx
97
+ end
@@ -0,0 +1,13 @@
1
+ require 'digest'
2
+
3
+ module Hashing
4
+ def hash160(data)
5
+ sha256 = Digest::SHA256.digest([data].pack('H*'))
6
+ Digest::RMD160.hexdigest sha256
7
+ end
8
+
9
+ def hash256(data)
10
+ sha256 = Digest::SHA256.digest([data].pack('H*'))
11
+ Digest::SHA256.hexdigest sha256
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ module Cryptos
2
+ Input = Struct.new :value, :tx_hash, :index, :unlock_script, :sequence do
3
+ def self.from_utxo(data, index = 0, options = {debug: false})
4
+ utxo = JSON.parse(data)[index]
5
+ puts utxo if options[:debug]
6
+ txid = utxo['txid']
7
+ vout = utxo['vout']
8
+ amount = utxo['amount']
9
+ sequence = options[:sequence] || 0xfffffffff
10
+ Input.new amount * 10**8, txid, vout, sequence: sequence
11
+ end
12
+ def initialize(value, tx_hash, index, unlock_script: '', sequence: 0xfffffffff)
13
+ super value, tx_hash, index, unlock_script, sequence
14
+ end
15
+ def serialize
16
+ script_hex = script_to_hex(unlock_script)
17
+ hash_to_hex(tx_hash) + int_to_hex(index) +
18
+ byte_to_hex(hex_size(script_hex)) + script_hex + int_to_hex(sequence)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ puts 'loading litecoin...'
2
+
3
+ require_relative 'litecoin/address'
4
+
5
+ module Cryptos
6
+ module Litecoin
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ module Cryptos
2
+ module Litecoin
3
+ #
4
+ # https://bitcoin.stackexchange.com/questions/65282/how-is-a-litecoin-address-generated
5
+ # https://bitcoin.stackexchange.com/questions/62781/litecoin-constants-and-prefixes
6
+ #
7
+ class Address < Cryptos::Address
8
+ def p2pkh_prefix
9
+ testnet ? 0x6f : 0x30
10
+ end
11
+
12
+ def p2sh_prefix
13
+ testnet ? 0x3a : 0x32
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Cryptos
2
+ Output = Struct.new :value, :lock_script do
3
+ def self.p2pkh(address, amount)
4
+ output_script = Cryptos::Script.for_address address
5
+ Output.new amount, output_script
6
+ end
7
+ def self.p2pkh_change(address, input, output, fee = 10_000)
8
+ change_value = input.value - output.value - fee
9
+ Output.p2pkh address, change_value
10
+ end
11
+ def serialize
12
+ script_hex = script_to_hex(lock_script)
13
+ long_to_hex(value) + byte_to_hex(hex_size(script_hex)) + script_hex
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require 'securerandom'
2
+
3
+ module Cryptos
4
+ class PrivateKey
5
+ attr_reader :value, :order
6
+
7
+ def self.generate(group = EllipticCurve::Secp256k1)
8
+ value = 1 + SecureRandom.random_number(group.order - 1)
9
+ new value, group.order
10
+ end
11
+
12
+ def initialize(value, order = nil)
13
+ @value = value
14
+ @order = order
15
+ end
16
+
17
+ def to_s
18
+ value.to_s
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ module Cryptos
2
+ class PublicKey
3
+ attr_reader :x, :y, :private_key
4
+
5
+ def self.from_pk(private_key)
6
+ new *ec_multiply(private_key.value, EC_Gx, EC_Gy, EC_p), private_key
7
+ end
8
+
9
+ def initialize(x, y, private_key)
10
+ @x = x
11
+ @y = y
12
+ @private_key = private_key
13
+ end
14
+
15
+ def check!
16
+ (x**3 + 7 - y**2) % EC_p == 0 || raise('public key point is not on the curve')
17
+ end
18
+
19
+ def compressed
20
+ "#{y > 0 ? '02' : '03'}#{x.to_s(16)}"
21
+ end
22
+
23
+ def uncompressed
24
+ "04#{x.to_s(16)}#{y.to_s(16)}"
25
+ end
26
+
27
+ def coordinates
28
+ [x, y]
29
+ end
30
+
31
+ def to_s
32
+ coordinates.to_s
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ module Cryptos
2
+ class Script
3
+ def self.for_address(address)
4
+ hash160 = address.to_hash160
5
+ if address.to_s.start_with? '2'
6
+ "OP_HASH160 #{hash160} OP_EQUAL"
7
+ else
8
+ p2pkh hash160
9
+ end
10
+ end
11
+
12
+ def self.for_public(pa)
13
+ p2pkh Address.to_hash160 pa
14
+ end
15
+
16
+ def self.p2pkh(hash160)
17
+ "OP_DUP OP_HASH160 #{hash160} OP_EQUALVERIFY OP_CHECKSIG"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ module Cryptos
2
+ Transaction = Struct.new :version, :inputs, :outputs, :locktime do
3
+ def self.from_ioc(input, output, change, version: 1, locktime: 0)
4
+ new version, [input], [output, change], locktime
5
+ end
6
+ def serialize
7
+ inputs_hex = inputs.map(&:serialize).join
8
+ outputs_hex = outputs.map(&:serialize).join
9
+ int_to_hex(version) + byte_to_hex(inputs.size) + inputs_hex +
10
+ byte_to_hex(outputs.size) + outputs_hex + int_to_hex(locktime)
11
+ end
12
+
13
+ def hash
14
+ hash_to_hex sha256(sha256(serialize))
15
+ end
16
+
17
+ def signature_hash(lock_script = nil, sighash_type = 0x01)
18
+ inputs.first.unlock_script = lock_script if lock_script
19
+ hash = sha256(sha256(serialize + int_to_hex(sighash_type)))
20
+ [hash].pack('H*')
21
+ end
22
+
23
+ def sign(private_key, public_key, lock_script, sighash_type = 0x01)
24
+ bytes_string = signature_hash lock_script, sighash_type
25
+ r, s = ecdsa_sign private_key.value, bytes_string
26
+ der = Der.new r: r, s: s
27
+ inputs.first.unlock_script = "#{der.serialize} #{public_key.compressed}"
28
+ serialize
29
+ end
30
+ def sign_input(index, address, sighash_type = 0x01)
31
+ # TODO: get lock_script from input?
32
+ lock_script = Cryptos::Script.for_address address
33
+ bytes_string = signature_hash lock_script, sighash_type
34
+ r, s = ecdsa_sign address.public_key.private_key.value, bytes_string
35
+ der = Der.new r: r, s: s
36
+ inputs[index].unlock_script = "#{der.serialize} #{address.public_key.compressed}"
37
+ serialize
38
+ end
39
+ def to_s
40
+ "Transaction[inputs:#{inputs.map &:value}, outputs:#{outputs.map &:value}"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module Cryptos
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,181 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cryptos
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Iulian Costan
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-11-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hashie
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ecdsa
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: The easiest way to craft your own transactions for multiple crypto currencies
112
+ email:
113
+ - iulian.costan@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rspec"
120
+ - ".ruby-version"
121
+ - ".travis.yml"
122
+ - CHANGELOG.md
123
+ - CODE_OF_CONDUCT.md
124
+ - Gemfile
125
+ - Gemfile.lock
126
+ - README.md
127
+ - Rakefile
128
+ - TODOs.org
129
+ - bin/console
130
+ - bin/setup
131
+ - cryptos.gemspec
132
+ - lib/cryptos.rb
133
+ - lib/cryptos/address.rb
134
+ - lib/cryptos/base.rb
135
+ - lib/cryptos/base58.rb
136
+ - lib/cryptos/bitcoin.rb
137
+ - lib/cryptos/bitcoin/address.rb
138
+ - lib/cryptos/bitcoin/script.rb
139
+ - lib/cryptos/bitmex/client.rb
140
+ - lib/cryptos/connectors/cli.rb
141
+ - lib/cryptos/der.rb
142
+ - lib/cryptos/deribit/client.rb
143
+ - lib/cryptos/elliptic_curve.rb
144
+ - lib/cryptos/hashing.rb
145
+ - lib/cryptos/input.rb
146
+ - lib/cryptos/litecoin.rb
147
+ - lib/cryptos/litecoin/address.rb
148
+ - lib/cryptos/output.rb
149
+ - lib/cryptos/private_key.rb
150
+ - lib/cryptos/public_key.rb
151
+ - lib/cryptos/script.rb
152
+ - lib/cryptos/transaction.rb
153
+ - lib/cryptos/version.rb
154
+ homepage: https://github.com/icostan/cryptos
155
+ licenses: []
156
+ metadata:
157
+ allowed_push_host: https://rubygems.org
158
+ homepage_uri: https://github.com/icostan/cryptos
159
+ source_code_uri: https://github.com/icostan/cryptos.git
160
+ changelog_uri: https://github.com/icostan/cryptos/blob/master/CHANGELOG
161
+ post_install_message:
162
+ rdoc_options: []
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ requirements: []
176
+ rubyforge_project:
177
+ rubygems_version: 2.7.6
178
+ signing_key:
179
+ specification_version: 4
180
+ summary: Crypto craft your own transactions
181
+ test_files: []