iota-ruby 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d338628f110c60d06b1b639d25dd4773839658bf
4
+ data.tar.gz: 7e06e28d669a453fcf59373c099f209519342c17
5
+ SHA512:
6
+ metadata.gz: 95ef1fb4978e645b6b6baed2822764ee0e60fa4cfa9f2439073c3a0a35b7ddf2e4bfb854b640123805246a440ec9ca441ba18a8c2a4804ccdd7c10556105332c
7
+ data.tar.gz: e12509d8fa7ec4b673aaafbccd7a7fee6888fb70d33943e4ca66a73b986653f6ce857a6f3f837cc2181c955d6397040eac62354b8bd644795c91195380e03541
data/.editorconfig ADDED
@@ -0,0 +1,8 @@
1
+ root=true
2
+
3
+ [*]
4
+ end_of_line=lf
5
+ insert_final_newline=true
6
+ indent_style=space
7
+ indent_size=2
8
+ trim_trailing_whitespace=true
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ *.gem
2
+ Gemfile.lock
3
+ /.bundle/
4
+ /.yardoc
5
+ /Gemfile.lock
6
+ /_yardoc/
7
+ /coverage/
8
+ /doc/
9
+ /pkg/
10
+ /spec/reports/
11
+ /tmp/
12
+ .DS_Store
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.15.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Vivek Marakana
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # IOTA Ruby Gem
2
+
3
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/vivekmarakana/iota.lib.rb/master/LICENSE)
4
+
5
+ This is the **unofficial** Ruby gem for the IOTA Core. It implements both the [official API](https://iota.readme.io/), as well as newly proposed functionality (such as signing, bundles, utilities and conversion).
6
+
7
+ This gem is a **beta release**. If you find any bug or face issue, please [post it here](https://github.com/vivekmarakana/iota.lib.rb/issues).
8
+
9
+ **Note: This release does not have multi signature support. It will be added after successful testing of this version**
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'iota-ruby', '~> 1.0.1', require: 'iota'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+
24
+ # Documentation
25
+
26
+ You can find basic documentation at: [https://vivekmarakana.gitbooks.io/iota-ruby](https://vivekmarakana.gitbooks.io/iota-ruby/)
27
+
28
+
29
+ ## Getting Started
30
+
31
+ After you've successfully installed the library, it is fairly easy to get started by simply launching a new instance of the IOTA object with an optional settings object. When instantiating the object you have the option to decide the API provider that is used to send the requests to and you can also connect directly to the Sandbox environment.
32
+
33
+ The optional settings object can have the following values:
34
+
35
+ 1. **`host`**: `String` Host you want to connect to. Can be DNS, IPv4 or IPv6. Defaults to `localhost `
36
+ 2. **`port`**: `Int` port of the host you want to connect to. Defaults to 14265.
37
+ 3. **`provider`**: `String` If you don't provide host and port, you can supply the full provider value to connect to
38
+ 4. **`sandbox`**: `Bool` Optional value to determine if your provider is the IOTA Sandbox or not.
39
+ 5. **`token`**: `String` Token string in case you are using sandbox.
40
+
41
+ You can either supply the remote node directly via the `provider` option, or individually with `host` and `port`, as can be seen in the example below:
42
+
43
+ ```ruby
44
+ # Create client with host and port as provider
45
+ client = IOTA::Client.new(host: 'http://localhost', port: 14265)
46
+
47
+ # Create client directly with provider
48
+ client = IOTA::Client.new(provider: 'http://localhost:14265')
49
+
50
+ # now you can start using all of the functions
51
+ status, data = client.api.getNodeInfo
52
+ ```
53
+
54
+ Overall, there are currently four subclasses that are accessible from the IOTA object:
55
+ - **`api`**: Core API functionality for interacting with the IOTA core.
56
+ - **`utils`**: Utility related functions for conversions, validation and so on
57
+ - **`validator`**: Validator functions that can help with determining whether the inputs or results that you get are valid.
58
+ - **`multisig`**: *In progress*
59
+
60
+
61
+ ## How to use the Library
62
+
63
+ All API calls are executed **synchronously** and returns array with 2 entries. First entry is `status` and second is `data`. However, you can use it by passing block to it as well.
64
+
65
+ Here is a simple example of how to access the `getNodeInfo` function:
66
+
67
+ ```ruby
68
+ # Method 1
69
+ client.api.getNodeInfo do |status, data|
70
+ if !status
71
+ # If status is `false`, `data` contains error message...
72
+ puts data
73
+ else
74
+ # If status is `true`, `data` contains response...
75
+ puts data.inspect
76
+ end
77
+ end
78
+
79
+ # Method 2
80
+ status, data = client.api.getNodeInfo
81
+ if !status
82
+ puts data
83
+ else
84
+ puts data.inspect
85
+ end
86
+ ```
87
+
88
+ If you'd like to support development, please consider donating to my IOTA address: **LOBYBAFIKJFEGTLCCQSHOPX9ROIRAI99QSTD9BRALHMHUOISF9DIZJYICXAIQMJCPZYTXMRMPOWG9WOICGMMNHQIH9**
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/iota-console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "iota"
5
+
6
+ require "irb"
7
+ require "irb/completion"
8
+
9
+ # Config IRB to enable --simple-prompt and auto indent
10
+ IRB.conf[:PROMPT_MODE] = :SIMPLE
11
+ IRB.conf[:AUTO_INDENT] = true
12
+
13
+ puts "Gem loaded..."
14
+ IRB.start
15
+
@@ -0,0 +1,57 @@
1
+ require "bundler/setup"
2
+ require "iota"
3
+
4
+ iota = IOTA::Client.new(provider: 'http://node.lukaseder.de:14265')
5
+
6
+ # First co-signer uses index 0 and security level 3
7
+ digestOne = iota.multisig.getDigest('ABCDFG', 0, 3)
8
+
9
+ # Second cosigner also uses index 0 and security level 3 for the private key
10
+ digestTwo = iota.multisig.getDigest('FDSAG', 0, 3)
11
+
12
+ # Initiate the multisig address generation
13
+ address = IOTA::Multisig::Address.new
14
+
15
+ # Absorb the first cosigners key digest
16
+ address.absorb(digestOne)
17
+
18
+ # Absorb the second cosigners key digest
19
+ address.absorb(digestTwo)
20
+
21
+ # and finally we finalize the address itself
22
+ finalAddress = address.finalize()
23
+
24
+ puts "MULTISIG ADDRESS: #{finalAddress}"
25
+
26
+ # Simple validation if the multisig was created correctly
27
+ # Can be called by each cosigner independently
28
+ isValid = iota.multisig.validateAddress(finalAddress, [digestOne, digestTwo])
29
+
30
+ puts "IS VALID MULTISIG ADDRESS: #{isValid}"
31
+
32
+
33
+ # Transfers object
34
+ multisigTransfer = [
35
+ {
36
+ address: 'ZGHXPZYDKXPEOSQTAQOIXEEI9K9YKFKCWKYYTYAUWXK9QZAVMJXWAIZABOXHHNNBJIEBEUQRTBWGLYMTX',
37
+ value: 999,
38
+ message: '',
39
+ tag: '999999999999999999999999999'
40
+ }
41
+ ]
42
+
43
+ # Multisig address object, used as input
44
+ input = {
45
+ address: finalAddress,
46
+ security: 6,
47
+ balance: 1000
48
+ }
49
+
50
+ # Define remainder address
51
+ remainderAddress = 'NZRALDYNVGJWUVLKDWFKJVNYLWQGCWYCURJIIZRLJIKSAIVZSGEYKTZRDBGJLOA9AWYJQB9IPWRAKUC9FBDRZJZXZG'
52
+
53
+ initiatedBundle = iota.multisig.initiateTransfer(input, remainderAddress, multisigTransfer)
54
+ firstSignedBundle = iota.multisig.addSignature(initiatedBundle, finalAddress, iota.multisig.getKey('ABCDFG', 0, 3))
55
+ finalBundle = iota.multisig.addSignature(firstSignedBundle, finalAddress, iota.multisig.getKey('FDSAG', 0, 3))
56
+
57
+ puts "IS VALID SIGNATURE: #{iota.utils.validateSignatures(finalBundle, finalAddress)}"
data/iota-ruby.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "iota/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "iota-ruby"
8
+ spec.version = IOTA::VERSION
9
+ spec.authors = ["Vivek Marakana"]
10
+ spec.email = ["vivek.marakana@gmail.com"]
11
+
12
+ spec.summary = "IOTA API wrapper for Ruby"
13
+ spec.description = "Ruby gem for the IOTA core"
14
+ spec.homepage = "https://github.com/vivekmarakana/iota.lib.rb"
15
+
16
+ spec.files = `git ls-files`.split("\n")
17
+ spec.test_files = `git ls-files -- test/*`.split("\n")
18
+ spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.15"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "minitest", "~> 5.0"
24
+ spec.add_dependency "digest-sha3", "~> 1.1"
25
+ end
@@ -0,0 +1,194 @@
1
+ module IOTA
2
+ module API
3
+ class Api
4
+ include Wrappers
5
+
6
+ def initialize(broker, sandbox)
7
+ @broker = broker
8
+ @sandbox = sandbox
9
+ @commands = Commands.new
10
+ @utils = IOTA::Utils::Utils.new
11
+ @validator = @utils.validator
12
+ end
13
+
14
+ def sendCommand(command, &callback)
15
+ @broker.send(command, &callback)
16
+ end
17
+
18
+ def findTransactions(searchValues, &callback)
19
+ if !@validator.isObject(searchValues)
20
+ return sendData(false, "You have provided an invalid key value", &callback)
21
+ end
22
+
23
+ searchKeys = searchValues.keys
24
+ validKeys = ['bundles', 'addresses', 'tags', 'approvees']
25
+
26
+ error = false;
27
+
28
+ searchKeys.each do |key|
29
+ if !validKeys.include?(key.to_s)
30
+ error = "You have provided an invalid key value"
31
+ break
32
+ end
33
+
34
+ hashes = searchValues[key]
35
+
36
+ if key.to_s == 'addresses'
37
+ searchValues[key] = hashes.map do |address|
38
+ @utils.noChecksum(address)
39
+ end
40
+ end
41
+
42
+ # If tags, append to 27 trytes
43
+ if key.to_s == 'tags'
44
+ searchValues[key] = hashes.map do |hash|
45
+ # Simple padding to 27 trytes
46
+ while hash.length < 27
47
+ hash += '9'
48
+ end
49
+ # validate hash
50
+ if !@validator.isTrytes(hash, 27)
51
+ error = "Invalid Trytes provided"
52
+ break
53
+ end
54
+
55
+ hash
56
+ end
57
+ else
58
+ # Check if correct array of hashes
59
+ if !@validator.isArrayOfHashes(hashes)
60
+ error = "Invalid Trytes provided"
61
+ break
62
+ end
63
+ end
64
+ end
65
+
66
+ if error
67
+ return sendData(false, error, &callback)
68
+ else
69
+ sendCommand(@commands.findTransactions(searchValues), &callback)
70
+ end
71
+ end
72
+
73
+ def getBalances(addresses, threshold, &callback)
74
+ # Check if correct transaction hashes
75
+ if !@validator.isArrayOfHashes(addresses)
76
+ return sendData(false, "Invalid Trytes provided", &callback)
77
+ end
78
+
79
+ command = @commands.getBalances(addresses.map{|address| @utils.noChecksum(address)}, threshold)
80
+ sendCommand(command, &callback)
81
+ end
82
+
83
+ def getTrytes(hashes, &callback)
84
+ if !@validator.isArrayOfHashes(hashes)
85
+ return sendData(false, "Invalid Trytes provided", &callback)
86
+ end
87
+
88
+ sendCommand(@commands.getTrytes(hashes), &callback)
89
+ end
90
+
91
+ def getInclusionStates(transactions, tips, &callback)
92
+ # Check if correct transaction hashes
93
+ if !@validator.isArrayOfHashes(transactions)
94
+ return sendData(false, "Invalid Trytes provided", &callback)
95
+ end
96
+
97
+ # Check if correct tips
98
+ if !@validator.isArrayOfHashes(tips)
99
+ return sendData(false, "Invalid Trytes provided", &callback)
100
+ end
101
+
102
+ sendCommand(@commands.getInclusionStates(transactions, tips), &callback)
103
+ end
104
+
105
+ def getNodeInfo(&callback)
106
+ sendCommand(@commands.getNodeInfo, &callback)
107
+ end
108
+
109
+ def getNeighbors(&callback)
110
+ sendCommand(@commands.getNeighbors, &callback)
111
+ end
112
+
113
+ def addNeighbors(uris, &callback)
114
+ (0...uris.length).step(1) do |i|
115
+ return sendData(false, "You have provided an invalid URI for your Neighbor: " + uris[i], &callback) if !@validator.isUri(uris[i])
116
+ end
117
+
118
+ sendCommand(@commands.addNeighbors(uris), &callback)
119
+ end
120
+
121
+ def removeNeighbors(uris, &callback)
122
+ (0...uris.length).step(1) do |i|
123
+ return sendData(false, "You have provided an invalid URI for your Neighbor: " + uris[i], &callback) if !@validator.isUri(uris[i])
124
+ end
125
+
126
+ sendCommand(@commands.removeNeighbors(uris), &callback)
127
+ end
128
+
129
+ def getTips(&callback)
130
+ sendCommand(@commands.getTips, &callback)
131
+ end
132
+
133
+ def getTransactionsToApprove(depth, &callback)
134
+ # Check if correct depth
135
+ if !@validator.isValue(depth)
136
+ return sendData(false, "Invalid inputs provided", &callback)
137
+ end
138
+
139
+ sendCommand(@commands.getTransactionsToApprove(depth), &callback)
140
+ end
141
+
142
+ def attachToTangle(trunkTransaction, branchTransaction, minWeightMagnitude, trytes, &callback)
143
+ # Check if correct trunk
144
+ if !@validator.isHash(trunkTransaction)
145
+ return sendData(false, "You have provided an invalid hash as a trunk/branch: " + trunkTransaction, &callback)
146
+ end
147
+
148
+ # Check if correct branch
149
+ if !@validator.isHash(branchTransaction)
150
+ return sendData(false, "You have provided an invalid hash as a trunk/branch: " + branchTransaction, &callback)
151
+ end
152
+
153
+ # Check if minweight is integer
154
+ if !@validator.isValue(minWeightMagnitude)
155
+ return sendData(false, "Invalid inputs provided", &callback)
156
+ end
157
+
158
+ # Check if array of trytes
159
+ if !@validator.isArrayOfTrytes(trytes)
160
+ return sendData(false, "Invalid Trytes provided", &callback)
161
+ end
162
+
163
+ command = @commands.attachToTangle(trunkTransaction, branchTransaction, minWeightMagnitude, trytes)
164
+
165
+ sendCommand(command, &callback)
166
+ end
167
+
168
+ def interruptAttachingToTangle(&callback)
169
+ this.sendCommand(@commands.interruptAttachingToTangle, &callback)
170
+ end
171
+
172
+ def broadcastTransactions(trytes, &callback)
173
+ if !@validator.isArrayOfAttachedTrytes(trytes)
174
+ return sendData(false, "Invalid attached Trytes provided", &callback)
175
+ end
176
+
177
+ sendCommand(@commands.broadcastTransactions(trytes), &callback)
178
+ end
179
+
180
+ def storeTransactions(trytes, &callback)
181
+ if !@validator.isArrayOfAttachedTrytes(trytes)
182
+ return sendData(false, "Invalid attached Trytes provided", &callback)
183
+ end
184
+
185
+ sendCommand(@commands.storeTransactions(trytes), &callback)
186
+ end
187
+
188
+ private
189
+ def sendData(status, data, &callback)
190
+ callback ? callback.call(status, data) : [status, data]
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,99 @@
1
+ module IOTA
2
+ module API
3
+ class Commands
4
+ def findTransactions(searchValues)
5
+ command = {
6
+ command: 'findTransactions'
7
+ }
8
+
9
+ searchValues.each { |k, v| command[k] = v }
10
+
11
+ command
12
+ end
13
+
14
+ def getBalances(addresses, threshold)
15
+ {
16
+ command: 'getBalances',
17
+ addresses: addresses,
18
+ threshold: threshold
19
+ }
20
+ end
21
+
22
+ def getTrytes(hashes)
23
+ {
24
+ command: 'getTrytes',
25
+ hashes: hashes
26
+ }
27
+ end
28
+
29
+ def getInclusionStates(transactions, tips)
30
+ {
31
+ command: 'getInclusionStates',
32
+ transactions: transactions,
33
+ tips: tips
34
+ }
35
+ end
36
+
37
+ def getNodeInfo
38
+ { command: 'getNodeInfo'}
39
+ end
40
+
41
+ def getNeighbors
42
+ { command: 'getNeighbors' }
43
+ end
44
+
45
+ def addNeighbors(uris)
46
+ {
47
+ command: 'addNeighbors',
48
+ uris: uris
49
+ }
50
+ end
51
+
52
+ def removeNeighbors(uris)
53
+ {
54
+ command: 'removeNeighbors',
55
+ uris: uris
56
+ }
57
+ end
58
+
59
+ def getTips
60
+ { command: 'getTips' }
61
+ end
62
+
63
+ def getTransactionsToApprove(depth)
64
+ {
65
+ command: 'getTransactionsToApprove',
66
+ depth: depth
67
+ }
68
+ end
69
+
70
+ def attachToTangle(trunkTransaction, branchTransaction, minWeightMagnitude, trytes)
71
+ {
72
+ command: 'attachToTangle',
73
+ trunkTransaction: trunkTransaction,
74
+ branchTransaction: branchTransaction,
75
+ minWeightMagnitude: minWeightMagnitude,
76
+ trytes: trytes
77
+ }
78
+ end
79
+
80
+ def interruptAttachingToTangle
81
+ { command: 'interruptAttachingToTangle' }
82
+ end
83
+
84
+ def broadcastTransactions(trytes)
85
+ {
86
+ command: 'broadcastTransactions',
87
+ trytes: trytes
88
+ }
89
+ end
90
+
91
+ def storeTransactions(trytes)
92
+ {
93
+ command: 'storeTransactions',
94
+ trytes: trytes
95
+ }
96
+ end
97
+ end
98
+ end
99
+ end