coloredcoins 0.0.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1b7110f96832703908f365f49cc54d66bc929092
4
+ data.tar.gz: c16656da6604392b8c96fcae5c5de0d49fa419b0
5
+ SHA512:
6
+ metadata.gz: 90c41400aecb0bb3e4f485f902cf3c6c7162b907cf71c377799d7d699df4a0f327f74d83c7ec1374b01de374d5f9ead62e55e150a150acf0265ca1edfbea9a6b
7
+ data.tar.gz: 31f70f9d03eb6b28506071ccea60c0ab4c598798863341bde2e0d56848501c2707b3cfdab6415141d9c03bc0425de2f72c68d6aca0d69927988faa074e72f2fc
data/.editorconfig ADDED
@@ -0,0 +1,20 @@
1
+ # EditorConfig helps developers define and maintain consistent
2
+ # coding styles between different editors and IDEs
3
+ # editorconfig.org
4
+
5
+ root = true
6
+
7
+ [*]
8
+
9
+ # Change these settings to your own preference
10
+ indent_style = space
11
+ indent_size = 2
12
+
13
+ # We recommend you to keep these unchanged
14
+ end_of_line = lf
15
+ charset = utf-8
16
+ trim_trailing_whitespace = true
17
+ insert_final_newline = true
18
+
19
+ [*.md]
20
+ trim_trailing_whitespace = false
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --format documentation
data/.rubocop.yml ADDED
@@ -0,0 +1,11 @@
1
+ Style/AccessModifierIndentation:
2
+ EnforcedStyle: outdent
3
+
4
+ Metrics/LineLength:
5
+ Max: 90
6
+
7
+ Style/Documentation:
8
+ Enabled: false
9
+
10
+ Style/HashSyntax:
11
+ EnforcedStyle: ruby19
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0
6
+ - 2.1
7
+ - 2.2
8
+ - 2.3.0
9
+ notifications:
10
+ email:
11
+ on_success: change
12
+ on_failure: always
13
+ script:
14
+ - bundle exec rubocop
15
+ - bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in coloredcoins.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem 'coveralls', require: false
8
+ gem 'guard'
9
+ gem 'guard-rspec'
10
+ gem 'terminal-notifier-guard'
11
+ end
data/Guardfile ADDED
@@ -0,0 +1,23 @@
1
+ notification :terminal_notifier
2
+
3
+ rspec_options = {
4
+ all_after_pass: true,
5
+ cmd: 'rspec spec',
6
+ failed_mode: :focus
7
+ }
8
+
9
+ clearing :on
10
+
11
+ guard :rspec, rspec_options do
12
+ require 'ostruct'
13
+
14
+ # Generic Ruby apps
15
+ rspec = OpenStruct.new
16
+ rspec.spec = ->(m) { "spec/#{m}_spec.rb" }
17
+ rspec.spec_dir = 'spec'
18
+ rspec.spec_helper = 'spec/spec_helper.rb'
19
+
20
+ watch(%r{^spec/.+_spec\.rb$})
21
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
22
+ watch(rspec.spec_helper) { rspec.spec_dir }
23
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Genaro Madrid
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # Coloredcoins
2
+
3
+ [![Build Status][travis-image]][travis-url]
4
+ [![Coverage Status][coverage-image]][coverage-url]
5
+
6
+ Ruby wrapper for coloredcoins.org
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'coloredcoins'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install coloredcoins
21
+
22
+ ## Usage
23
+
24
+ Initialize the API
25
+
26
+ ```ruby
27
+ network = Coloredcoins::TESTNET
28
+ api_version = 'v3' # defaults to 'v3'
29
+ api = Coloredcoins::API.new(network, api_version)
30
+ ```
31
+
32
+ > You can use any method with the initialized object or simply by calling the method from the `Coloredcoins` module.
33
+
34
+ The default network is *MAINNET*, use the following to change to *TESTNET*.
35
+
36
+ ```ruby
37
+ Coloredcoins.network = Coloredcoins::TESTNET
38
+ ```
39
+
40
+ ### Issue Asset
41
+
42
+ http://coloredcoins.org/documentation/#IssueAsset
43
+
44
+ ```ruby
45
+ asset = { ... }
46
+
47
+ response = Coloredcoins.issue_asset(asset)
48
+ response[:txHex] # => 0100000001ee58da20e...8ac00000000
49
+ response[:assetId] # => LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY
50
+ ```
51
+
52
+ ### Send asset
53
+
54
+ http://coloredcoins.org/documentation/#SendAsset
55
+
56
+ ```ruby
57
+ asset = { ... }
58
+
59
+ response = Coloredcoins.send_asset(asset)
60
+ response[:txHex] # => 0100000001ee58da20e...8ac00000000
61
+ ```
62
+
63
+ ### Broadcast Multiig Transaction
64
+
65
+ http://coloredcoins.org/documentation/#BroadcastTransaction
66
+
67
+ ```ruby
68
+ publ_keys = [
69
+ '03ae3cf1075ab2c7544d973903c089295ab195af63a8f3c168c9b8901b457d9ce2',
70
+ '0352f75a371a1331fa51a20b5e6e1e4ab8f86a1f65dd36fe44a9f7ce5d2a706946',
71
+ '0330959f464f88f7294cc412a81f72f3cb817a2738a16e187d99b8e78c4ccf9e3b'
72
+ ]
73
+ wif = 'Kz6XuRHniKZfWxSLSC7YdN8AmB6oXaDSfHhxa6TPfwmcAC8URE7b'
74
+ transaction = Coloredcoins::MultisigTx.build(tx_hex) do |tx|
75
+ tx.m = 2 # 'm' x n Multisig
76
+ tx.pub_keys = pub_keys
77
+ end
78
+ transaction.sign(wif)
79
+ # Broadcast returns the broadcasted transaction id
80
+ transaction.broadcast # => '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de'
81
+ ```
82
+
83
+ ### Get Address Info
84
+
85
+ http://coloredcoins.org/documentation/#GetAddressInfo
86
+
87
+ ```ruby
88
+ Coloredcoins.address_info('n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj')
89
+ ```
90
+
91
+ ### Get Asset Holders
92
+
93
+ http://coloredcoins.org/documentation/#GetAssetHolders
94
+
95
+ ```ruby
96
+ asset_id = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'
97
+ num_confirmations = 2 # defaults to 1
98
+
99
+ response = Coloredcoins.asset_holders(asset_id, num_confirmations)
100
+ response[:holders] # [{ address: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY', amount: 100 }]
101
+ response[:divisibility] # 0
102
+ response[:lockStatus] # nil
103
+ ```
104
+
105
+ ### Get Asset Metadata
106
+
107
+ http://coloredcoins.org/documentation/#GetAssetMetadata
108
+
109
+ ```ruby
110
+ asset_id = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'
111
+ tx_id = '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de'
112
+ utxo = tx_id+':1'
113
+
114
+ Coloredcoins.asset_metadata(asset_id, utxo)
115
+ ```
116
+
117
+ ## Contributing
118
+
119
+ 1. Fork it ( https://github.com/[my-github-username]/coloredcoins/fork )
120
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
121
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
122
+ 4. Push to the branch (`git push origin my-new-feature`)
123
+ 5. Create a new Pull Request
124
+
125
+ [travis-image]: https://travis-ci.org/genaromadrid/coloredcoins-ruby.svg?branch=master
126
+ [travis-url]: https://travis-ci.org/genaromadrid/coloredcoins-ruby
127
+ [coverage-image]: https://coveralls.io/repos/github/genaromadrid/coloredcoins-ruby/badge.svg?branch=master
128
+ [coverage-url]: https://coveralls.io/github/genaromadrid/coloredcoins-ruby?branch=master
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :console do
4
+ exec 'irb -r coloredcoins -I ./lib'
5
+ end
data/bump ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env python
2
+ import re, glob, sys, os
3
+
4
+ __USAGE__ = \
5
+ """BUMP is a semantic versioning bump script which accepts the following
6
+ mutually exclusive arguments:
7
+ -m - a "major" version bump equal to +1.0.0
8
+ -n - a "minor" version bump equal to +0.1.0
9
+ -p - a "patch" version bump equal to +0.0.1
10
+ -h - a "hot fix" version bump equal to +0.0.1
11
+
12
+ All of these options allow for the -r flag, which indicates that the state
13
+ is a RELEASE not a SNAPSHOT. If -r is not specified, then -SNAPSHOT is
14
+ appended to the updated version string."""
15
+
16
+ __INITIAL__ = ['0', '0', '1']
17
+
18
+
19
+ if __name__ == "__main__":
20
+ v = []
21
+ try:
22
+ version_file = glob.glob("lib/*/version.rb")[0]
23
+ raw_v = re.search(r'VERSION = \'(.*)\'', open(version_file).read(), re.M|re.I|re.S).group(1)
24
+ v = re.split(re.compile("\.|-"), raw_v)
25
+ v = v[0:3]
26
+ map(int, v)
27
+
28
+ except ValueError:
29
+ print("failed to parse the existing VERSION file, assuming v 0.0.1")
30
+ v = ['0', '0', '1']
31
+
32
+ except FileNotFoundError:
33
+ print("failed to find a VERSION file, assuming v 0.0.0")
34
+ v = ['0', '0', '0']
35
+
36
+ op = ''
37
+ try:
38
+ op = sys.argv[1]
39
+ except:
40
+ print(__USAGE__)
41
+ sys.exit(-1)
42
+
43
+ if(op == '-m'):
44
+ v = [str(int(v[0])+1), '0', '0']
45
+
46
+ elif(op == '-n'):
47
+ v = [v[0], str(int(v[1])+1), '0']
48
+
49
+ elif(op == '-p' or op == '-h'):
50
+ v = [v[0], v[1], str(int(v[2])+1)]
51
+
52
+ else:
53
+ print(__USAGE__)
54
+ sys.exit(-1)
55
+
56
+ v = '.'.join(v)
57
+
58
+ if(op == '-h'):
59
+ os.system("git checkout -b hotfix/v%s master" % v)
60
+
61
+ else:
62
+ os.system("git checkout -b release/v%s develop" % v)
63
+
64
+ os.system("bump set %s" % v)
65
+
66
+ v += "\n"
67
+
68
+ print(v)
69
+
70
+ sys.exit(0)
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'coloredcoins/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'coloredcoins'
8
+ spec.version = Coloredcoins::VERSION
9
+ spec.authors = ['Genaro Madrid']
10
+ spec.email = ['genmadrid@gmail.com']
11
+ spec.summary = 'Ruby wrapper for coloredcoins.org'
12
+ spec.homepage = 'https://github.com/genaromadrid/coloredcoins-ruby'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_runtime_dependency 'rest-client', '~> 1.7'
21
+ spec.add_runtime_dependency 'json', '~> 1.8'
22
+ spec.add_runtime_dependency 'bitcoin-ruby', '~> 0.0.8'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.6'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec', '3.1'
27
+ spec.add_development_dependency 'bump', '~> 0.5', '>= 0.5.3'
28
+ spec.add_development_dependency 'rubocop', '~> 0.36'
29
+ spec.add_development_dependency 'simplecov', '~> 0.11'
30
+ spec.add_development_dependency 'webmock'
31
+ end
@@ -0,0 +1,57 @@
1
+ module Coloredcoins
2
+ class API
3
+ attr_reader :network,
4
+ :api_version,
5
+ :connection
6
+
7
+ def initialize(network = Coloredcoins.network, api_version = 'v3')
8
+ @network = network
9
+ @api_version = api_version
10
+ @connection = Connection.new(url)
11
+ end
12
+
13
+ def url
14
+ return "http://testnet.api.coloredcoins.org:80/#{api_version}" if testnet?
15
+ "http://api.coloredcoins.org:80/#{api_version}"
16
+ end
17
+
18
+ def testnet?
19
+ network == Coloredcoins::TESTNET
20
+ end
21
+
22
+ def network=(network)
23
+ @network = network
24
+ @connection = Connection.new(url)
25
+ end
26
+
27
+ # http://coloredcoins.org/documentation/#IssueAsset
28
+ def issue_asset(asset)
29
+ @connection.post('/issue', asset)
30
+ end
31
+
32
+ # http://coloredcoins.org/documentation/#SendAsset
33
+ def send_asset(asset)
34
+ @connection.post('/sendasset', asset)
35
+ end
36
+
37
+ # http://coloredcoins.org/documentation/#BroadcastTransaction
38
+ def broadcast(tx_hex)
39
+ @connection.post('/broadcast', txHex: tx_hex)
40
+ end
41
+
42
+ # http://coloredcoins.org/documentation/#GetAddressInfo
43
+ def address_info(address)
44
+ @connection.get("/addressinfo/#{address}")
45
+ end
46
+
47
+ # http://coloredcoins.org/documentation/#GetAssetHolders
48
+ def asset_holders(asset_id, num_confirmations = 1)
49
+ @connection.get("/stakeholders/#{asset_id}/#{num_confirmations}")
50
+ end
51
+
52
+ # http://coloredcoins.org/documentation/#GetAssetMetadata
53
+ def asset_metadata(asset_id, utxo)
54
+ @connection.get("/assetmetadata/#{asset_id}/#{utxo}")
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,39 @@
1
+ require 'rest-client'
2
+
3
+ module Coloredcoins
4
+ class Connection
5
+ attr_reader :url
6
+
7
+ def initialize(url = nil)
8
+ @url = url
9
+ end
10
+
11
+ def get(path, payload = {})
12
+ query :get, path, payload
13
+ end
14
+
15
+ def post(path, payload = {})
16
+ query :post, path, payload
17
+ end
18
+
19
+ def query(method, path, payload = {})
20
+ uri = endpoint_uri(path)
21
+ response = RestClient::Request.execute(
22
+ method: method,
23
+ url: uri,
24
+ payload: payload,
25
+ ssl_version: 'SSLv23'
26
+ )
27
+ JSON.parse(response, symbolize_names: true)
28
+ rescue RestClient::ExceptionWithResponse => e
29
+ raise Coloredcoins::ConnectionError, e.response
30
+ end
31
+
32
+ private
33
+
34
+ def endpoint_uri(path = '')
35
+ path[0] = '' if path[0] == '/'
36
+ "#{@url}/#{path}"
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,62 @@
1
+ module Coloredcoins
2
+ class MultisigTx < Transaction
3
+ attr_accessor :m, :pub_keys, :redeem_script
4
+
5
+ def self.build(tx_hex)
6
+ transaction = MultisigTx.new(tx_hex)
7
+ yield transaction
8
+ transaction
9
+ end
10
+
11
+ def sign(key)
12
+ check
13
+ key = build_key(key)
14
+ tx.inputs.each_with_index do |input, i|
15
+ sig_hash = tx.signature_hash_for_input(i, redeem_script)
16
+ sigs = build_sigs(key, sig_hash)
17
+ initial = input.script_sig.or(initial_script)
18
+
19
+ input.script_sig = build_script_sig(sigs, sig_hash, initial)
20
+ end
21
+ true
22
+ end
23
+
24
+ def redeem_script
25
+ @redeem_script ||= Bitcoin::Script.to_p2sh_multisig_script(m, *pub_keys).last
26
+ end
27
+
28
+ def address
29
+ @address ||= Bitcoin.hash160_to_p2sh_address(Bitcoin.hash160(redeem_script.hth))
30
+ end
31
+
32
+ private
33
+
34
+ def initial_script
35
+ @initial_script ||= Bitcoin::Script.to_p2sh_multisig_script_sig(redeem_script)
36
+ end
37
+
38
+ def build_script_sig(sigs, sig_hash, initial)
39
+ sigs.each do |sig|
40
+ Bitcoin::Script.add_sig_to_multisig_script_sig(sig, initial)
41
+ end
42
+ Bitcoin::Script.sort_p2sh_multisig_signatures(initial, sig_hash)
43
+ end
44
+
45
+ def valid_sig?(i, script)
46
+ tx.verify_input_signature(i, script)
47
+ end
48
+
49
+ def check
50
+ raise ArgumentError, 'Set "m" before signing' unless m
51
+ if !pub_keys && !redeem_script
52
+ raise ArgumentError, 'Set "pub_keys" or "redeem_script" before signing'
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ class String
59
+ def or(what)
60
+ strip.empty? ? what : self
61
+ end
62
+ end
@@ -0,0 +1,46 @@
1
+ require 'bitcoin'
2
+
3
+ module Coloredcoins
4
+ class Transaction
5
+ attr_reader :tx
6
+
7
+ def initialize(hex)
8
+ @tx = Bitcoin::P::Tx.new([hex].pack('H*'))
9
+ end
10
+
11
+ def to_hex
12
+ raw_tx = Bitcoin::P::Tx.binary_from_json(new_tx.to_json)
13
+ raw_tx.unpack('H*').first
14
+ end
15
+
16
+ def new_tx
17
+ @new_tx = Bitcoin::P::Tx.new
18
+ @new_tx.ver = tx.ver
19
+ @new_tx.lock_time = tx.lock_time
20
+ tx.in.each { |input| @new_tx.add_in(input) }
21
+ tx.out.each { |output| @new_tx.add_out(output) }
22
+ @new_tx
23
+ end
24
+
25
+ def broadcast
26
+ response = Coloredcoins.broadcast(to_hex)
27
+ return response[:txId] if response[:txId]
28
+ response
29
+ end
30
+
31
+ protected
32
+
33
+ def build_key(key)
34
+ return key if key.is_a?(Array)
35
+ key = Bitcoin::Key.from_base58(key) unless key.is_a?(Bitcoin::Key)
36
+ key
37
+ rescue RuntimeError => e
38
+ raise InvalidKeyError, 'Invalid key' if e.message == 'Invalid version'
39
+ end
40
+
41
+ def build_sigs(key, sig_hash)
42
+ key = [key] unless key.is_a?(Array)
43
+ key.map { |k| k.sign(sig_hash) }
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ module Coloredcoins
2
+ VERSION = '0.0.0'.freeze
3
+ end
@@ -0,0 +1,37 @@
1
+ require 'coloredcoins/version'
2
+
3
+ module Coloredcoins
4
+ autoload :API, 'coloredcoins/api'
5
+ autoload :Connection, 'coloredcoins/connection'
6
+ autoload :Transaction, 'coloredcoins/transaction'
7
+ autoload :MultisigTx, 'coloredcoins/multisig_tx'
8
+
9
+ attr_writer :api
10
+
11
+ InvalidSignatureError = Class.new SecurityError
12
+ ConnectionError = Class.new StandardError
13
+ InvalidKeyError = Class.new RuntimeError
14
+
15
+ API_VERSION = 'v3'.freeze
16
+ NETS = [
17
+ MAINNET = 'mainnet'.freeze,
18
+ TESTNET = 'testnet'.freeze
19
+ ].freeze
20
+
21
+ def self.api
22
+ @api ||= API.new(network, API_VERSION)
23
+ end
24
+
25
+ def self.network
26
+ @network || MAINNET
27
+ end
28
+
29
+ def self.network=(network)
30
+ @network = network
31
+ @api = API.new(network, API_VERSION)
32
+ end
33
+
34
+ def self.method_missing(sym, *args, &block)
35
+ api.send(sym, *args, &block)
36
+ end
37
+ end
@@ -0,0 +1,78 @@
1
+ describe Coloredcoins::API do
2
+ describe 'changing network' do
3
+ Coloredcoins::NETS.each do |network|
4
+ describe "in #{network}" do
5
+ context 'when Coloredcoins.network changes' do
6
+ before { Coloredcoins.network = network }
7
+
8
+ it { expect(Coloredcoins.api.network).to eq(network) }
9
+
10
+ context 'when instanciating the API' do
11
+ subject { Coloredcoins::API.new }
12
+
13
+ it { expect(subject.network).to eq(network) }
14
+ end
15
+ end
16
+
17
+ context 'when changing the nexwork on an instance' do
18
+ subject { Coloredcoins::API.new }
19
+ before { subject.network = network }
20
+
21
+ it 'should change the url in its connection' do
22
+ url = network == Coloredcoins::TESTNET ? '//testnet' : '//api'
23
+ expect(subject.connection.url).to include(url)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ describe 'conncetion methods' do
31
+ before do
32
+ allow(subject.connection).to receive(:post)
33
+ allow(subject.connection).to receive(:get)
34
+ end
35
+
36
+ describe '#issue_asset' do
37
+ it 'should call connection' do
38
+ subject.issue_asset({})
39
+ expect(subject.connection).to have_received(:post).with(/issue/, {})
40
+ end
41
+ end
42
+
43
+ describe '#send_asset' do
44
+ it 'should call connection' do
45
+ subject.send_asset({})
46
+ expect(subject.connection).to have_received(:post).with(/sendasset/, {})
47
+ end
48
+ end
49
+
50
+ describe '#broadcast' do
51
+ it 'should call connection' do
52
+ subject.broadcast('hex')
53
+ expect(subject.connection).to have_received(:post).with(/broadcast/, txHex: 'hex')
54
+ end
55
+ end
56
+
57
+ describe '#address_info' do
58
+ it 'should call connection' do
59
+ subject.address_info('2NDm6HFAeyQYUfMXyFnd9yz5JBSkkWsj7Vz')
60
+ expect(subject.connection).to have_received(:get).with(/addressinfo/)
61
+ end
62
+ end
63
+
64
+ describe '#asset_holders' do
65
+ it 'should call connection' do
66
+ subject.asset_holders('asset_id', 1)
67
+ expect(subject.connection).to have_received(:get).with(/stakeholders/)
68
+ end
69
+ end
70
+
71
+ describe '#asset_metadata' do
72
+ it 'should call connection' do
73
+ subject.asset_metadata('asset_id', 'utxo:1')
74
+ expect(subject.connection).to have_received(:get).with(/assetmetadata/)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,39 @@
1
+ describe Coloredcoins::Connection do
2
+ let!(:base_url) { 'http://example.com/api/v1' }
3
+ let!(:path) { '/some/path' }
4
+ let!(:payload) { { a: 'payload' } }
5
+ let!(:resp) { { a: 'response' } }
6
+ let!(:params) do
7
+ {
8
+ method: nil,
9
+ url: "#{base_url}#{path}",
10
+ payload: payload,
11
+ ssl_version: 'SSLv23'
12
+ }
13
+ end
14
+
15
+ subject { Coloredcoins::Connection.new(base_url) }
16
+
17
+ before do
18
+ allow(RestClient::Request).to receive(:execute).and_return(resp.to_json)
19
+ end
20
+
21
+ describe 'get' do
22
+ it 'should execute the call' do
23
+ subject.get(path, payload)
24
+ get_params = params.merge(method: :get)
25
+ expect(RestClient::Request).to have_received(:execute).with(get_params)
26
+ end
27
+ end
28
+
29
+ describe 'post' do
30
+ it 'should execute the call' do
31
+ subject.post(path, payload)
32
+ post_params = params.merge(method: :post)
33
+ expect(RestClient::Request).to have_received(:execute).with(post_params)
34
+ end
35
+ end
36
+
37
+ describe 'query' do
38
+ end
39
+ end
@@ -0,0 +1,172 @@
1
+ require 'bitcoin'
2
+
3
+ describe Coloredcoins::MultisigTx do
4
+ let!(:tx_hex) do
5
+ %w(
6
+ 010000000125b956a45ff3c6927ab85aaed92d88cdbea00a0d81
7
+ 341cfe535b982ab50cf9f70000000000ffffffff03ac02000000
8
+ 00000047512103ffffffffffffffffffffffffffffffffffffff
9
+ ffffffffffffffffffffffffff2103caf376cb1cb7d09e33bf35
10
+ 5c4c7f5b7962502173627efbd1e3d2fc16573a65fb52ae000000
11
+ 00000000001c6a1a4343020287d1e95ca859b73cda02c81c772b
12
+ a186dc1a12a00110e45c01000000000017a914e1088f81edc427
13
+ ec81f128e2eac8e2f0e62465cd8700000000
14
+ ).join
15
+ end
16
+ let!(:prev_tx_hex) do
17
+ %w(
18
+ 0100000001463e1b35f72a4e978f2b7fb82f95823e90e4f50b30
19
+ a1372f92fee0694fc92159020000006a47304402202f5b00e3fc
20
+ 0879488196c394283245f8f6d4b310b61fd14cc4bc38dbb2fb7e
21
+ 6e02202bc37e902b4cafc58defc87bd009ca4d6aa2ef1ab12219
22
+ 2062d87ed43cf5a9860121020b4dfdc746ea23b6815b108227bc
23
+ be5aa86f3611275f56bb06476f7d256c2d6cffffffff03a08601
24
+ 000000000017a914e1088f81edc427ec81f128e2eac8e2f0e624
25
+ 65cd8710270000000000001976a914c854b52234e57ac254ae35
26
+ ef6b3dd7ead98ce14288ac9cc71200000000001976a914eed95b
27
+ 73ccde2019ef1da4c6e9b4062c5cda881988ac00000000
28
+ ).join
29
+ end
30
+ let!(:prev_tx) { Bitcoin::P::Tx.new([prev_tx_hex].pack('H*')) }
31
+ let!(:priv_keys) do
32
+ %w(
33
+ 44159687da678ccc3374cd63a8be34727bb34a9a7ce02e1de0bb2c0a2c0a9ca8
34
+ 7ac707d5cf0198fd10b0aa267367c09feae8cf8cbe008b4b9840c67c3d0af4a1
35
+ eb3ed894a8ea4f897884a65b9ef8d9f4d1704b9fe88f4133bb0c9d66d6420a7a
36
+ )
37
+ end
38
+ # rubocop:disable Metrics/LineLength
39
+ let!(:pub_keys) do
40
+ %w(
41
+ 047a130127056525587eac13ad69f23c5e198d596025f65ac33d769e54fa3e3094b7aa4bad47aec9a0b1d487fd2b4f41eed468079de70f4526c721924ac3bfd121
42
+ 0442dbb77ea49ebe546982d2ad1cb0bea3ff3abfe8fb636858295622f8c5cbfd07b5a66e811e5178d8971a5f352299c163258aa6d81b56c980b679f1644b1dfd80
43
+ 0499df3d55d5741a2222d9a5d175e3bbd9124e61b4c7f5830356c0d8082861a0e3c61ee29ec2eaabd693f5f83568dfea0add98b1c3e971b9da7b01ed97ab3b74fb
44
+ )
45
+ end
46
+ # rubocop:enable Metrics/LineLength
47
+ let!(:m) { 2 }
48
+ let!(:redeem_script) { Bitcoin::Script.to_p2sh_multisig_script(m, *pub_keys).last }
49
+ let!(:wif) { priv_keys[0] }
50
+ let!(:keys) do
51
+ priv_keys.map.with_index do |priv_key, i|
52
+ Bitcoin::Key.new(priv_key, pub_keys[i])
53
+ end
54
+ end
55
+
56
+ before do
57
+ Bitcoin.network = :testnet
58
+ Coloredcoins.network = Coloredcoins::TESTNET
59
+ end
60
+
61
+ subject do
62
+ Coloredcoins::MultisigTx.build(tx_hex) do |tx|
63
+ tx.m = m
64
+ tx.pub_keys = pub_keys
65
+ end
66
+ end
67
+
68
+ describe '#sign' do
69
+ let(:tx) { subject.tx }
70
+
71
+ describe 'before sign' do
72
+ it 'inputs should not be signed' do
73
+ tx.inputs.each do |input|
74
+ expect(input.script_sig).to be_empty
75
+ end
76
+ end
77
+
78
+ it 'can be converted to hex' do
79
+ expect { subject.to_hex }.not_to raise_error
80
+ end
81
+
82
+ it 'to_hex should be the same as the given hex' do
83
+ expect(subject.to_hex).to eq(tx_hex)
84
+ end
85
+ end
86
+
87
+ describe 'after sign' do
88
+ shared_examples 'after_sign_transaction' do
89
+ it 'inputs should be signed' do
90
+ tx.inputs.each do |input|
91
+ expect(input.script_sig).not_to be_empty
92
+ end
93
+ end
94
+
95
+ it 'can be converted to hex' do
96
+ expect { subject.to_hex }.not_to raise_error
97
+ end
98
+ end
99
+
100
+ context 'with one key' do
101
+ let!(:key) { keys[0] }
102
+ before { subject.sign(key) }
103
+ it_behaves_like 'after_sign_transaction'
104
+
105
+ describe 'signatures' do
106
+ it 'should not be valid yet' do
107
+ tx.inputs.each_with_index do |_input, i|
108
+ expect(tx.verify_input_signature(i, prev_tx)).to be false
109
+ end
110
+ end
111
+
112
+ it 'can be converted to hex' do
113
+ expect { subject.to_hex }.not_to raise_error
114
+ end
115
+
116
+ context 'when signing with another key' do
117
+ let!(:partially_signed_tx) do
118
+ Coloredcoins::MultisigTx.build(subject.to_hex) do |tx|
119
+ tx.m = m
120
+ tx.pub_keys = pub_keys
121
+ end
122
+ end
123
+ let(:tx) { partially_signed_tx.tx }
124
+
125
+ it 'should be valid' do
126
+ partially_signed_tx.sign(keys[1])
127
+ tx.inputs.each_with_index do |_input, i|
128
+ expect(tx.verify_input_signature(i, prev_tx)).to be true
129
+ end
130
+ end
131
+
132
+ it 'can be converted to hex' do
133
+ expect { partially_signed_tx.to_hex }.not_to raise_error
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ context 'with several keys' do
140
+ let!(:key) { [keys[0], keys[1]] }
141
+ before { subject.sign(key) }
142
+ it_behaves_like 'after_sign_transaction'
143
+
144
+ describe 'signatures' do
145
+ it 'should be valid' do
146
+ tx.inputs.each_with_index do |_input, i|
147
+ expect(tx.verify_input_signature(i, prev_tx)).to be true
148
+ end
149
+ end
150
+
151
+ it 'can be converted to hex' do
152
+ expect { subject.to_hex }.not_to raise_error
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ describe '#broadcast' do
160
+ let!(:tx_id) { 'some-transaction-hash' }
161
+ let!(:broadcast_response) { { txId: tx_id } }
162
+
163
+ before do
164
+ allow(Coloredcoins).to receive(:broadcast).and_return(broadcast_response)
165
+ end
166
+ before { @response = subject.broadcast }
167
+
168
+ it { expect(Coloredcoins).to have_received(:broadcast).once }
169
+
170
+ it { expect(@response).to eq(tx_id) }
171
+ end
172
+ end
@@ -0,0 +1,14 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+ SimpleCov.start do
4
+ add_filter '/spec/'
5
+ end
6
+ Coveralls.wear!
7
+
8
+ require 'pry'
9
+ require 'rubygems'
10
+ require 'webmock/rspec'
11
+ require 'bundler/setup'
12
+ Bundler.require(:default)
13
+
14
+ WebMock.disable_net_connect!(allow_localhost: true)
metadata ADDED
@@ -0,0 +1,216 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coloredcoins
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Genaro Madrid
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.8'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bitcoin-ruby
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.8
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.8
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.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: '3.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: '3.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: bump
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.5'
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 0.5.3
107
+ type: :development
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '0.5'
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 0.5.3
117
+ - !ruby/object:Gem::Dependency
118
+ name: rubocop
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.36'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '0.36'
131
+ - !ruby/object:Gem::Dependency
132
+ name: simplecov
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '0.11'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.11'
145
+ - !ruby/object:Gem::Dependency
146
+ name: webmock
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ description:
160
+ email:
161
+ - genmadrid@gmail.com
162
+ executables: []
163
+ extensions: []
164
+ extra_rdoc_files: []
165
+ files:
166
+ - ".editorconfig"
167
+ - ".gitignore"
168
+ - ".rspec"
169
+ - ".rubocop.yml"
170
+ - ".travis.yml"
171
+ - Gemfile
172
+ - Guardfile
173
+ - LICENSE.txt
174
+ - README.md
175
+ - Rakefile
176
+ - bump
177
+ - coloredcoins.gemspec
178
+ - lib/coloredcoins.rb
179
+ - lib/coloredcoins/api.rb
180
+ - lib/coloredcoins/connection.rb
181
+ - lib/coloredcoins/multisig_tx.rb
182
+ - lib/coloredcoins/transaction.rb
183
+ - lib/coloredcoins/version.rb
184
+ - spec/coloredcoins/api_spec.rb
185
+ - spec/coloredcoins/connection_spec.rb
186
+ - spec/coloredcoins/multisig_tx_spec.rb
187
+ - spec/spec_helper.rb
188
+ homepage: https://github.com/genaromadrid/coloredcoins-ruby
189
+ licenses:
190
+ - MIT
191
+ metadata: {}
192
+ post_install_message:
193
+ rdoc_options: []
194
+ require_paths:
195
+ - lib
196
+ required_ruby_version: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ required_rubygems_version: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ requirements: []
207
+ rubyforge_project:
208
+ rubygems_version: 2.2.2
209
+ signing_key:
210
+ specification_version: 4
211
+ summary: Ruby wrapper for coloredcoins.org
212
+ test_files:
213
+ - spec/coloredcoins/api_spec.rb
214
+ - spec/coloredcoins/connection_spec.rb
215
+ - spec/coloredcoins/multisig_tx_spec.rb
216
+ - spec/spec_helper.rb