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 +7 -0
- data/.editorconfig +20 -0
- data/.gitignore +22 -0
- data/.rspec +3 -0
- data/.rubocop.yml +11 -0
- data/.travis.yml +15 -0
- data/Gemfile +11 -0
- data/Guardfile +23 -0
- data/LICENSE.txt +22 -0
- data/README.md +128 -0
- data/Rakefile +5 -0
- data/bump +70 -0
- data/coloredcoins.gemspec +31 -0
- data/lib/coloredcoins/api.rb +57 -0
- data/lib/coloredcoins/connection.rb +39 -0
- data/lib/coloredcoins/multisig_tx.rb +62 -0
- data/lib/coloredcoins/transaction.rb +46 -0
- data/lib/coloredcoins/version.rb +3 -0
- data/lib/coloredcoins.rb +37 -0
- data/spec/coloredcoins/api_spec.rb +78 -0
- data/spec/coloredcoins/connection_spec.rb +39 -0
- data/spec/coloredcoins/multisig_tx_spec.rb +172 -0
- data/spec/spec_helper.rb +14 -0
- metadata +216 -0
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
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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
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
|
data/lib/coloredcoins.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|