iota-ruby 1.1.8-java
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 +8 -0
- data/.gitignore +15 -0
- data/.travis.yml +24 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +18 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +121 -0
- data/Rakefile +36 -0
- data/bin/iota-console +15 -0
- data/examples/multisig.rb +69 -0
- data/ext/ccurl/ccurl.c +134 -0
- data/ext/ccurl/extconf.rb +22 -0
- data/ext/jcurl/JCurl.java +126 -0
- data/ext/jcurl/JCurlService.java +36 -0
- data/ext/pow/ccurl-0.3.0.dll +0 -0
- data/ext/pow/libccurl-0.3.0.dylib +0 -0
- data/ext/pow/libccurl-0.3.0.so +0 -0
- data/iota-ruby.gemspec +37 -0
- data/lib/iota.rb +76 -0
- data/lib/iota/api/api.rb +251 -0
- data/lib/iota/api/commands.rb +113 -0
- data/lib/iota/api/transport.rb +43 -0
- data/lib/iota/api/wrappers.rb +429 -0
- data/lib/iota/crypto/bundle.rb +163 -0
- data/lib/iota/crypto/converter.rb +244 -0
- data/lib/iota/crypto/curl.rb +18 -0
- data/lib/iota/crypto/curl_c.rb +17 -0
- data/lib/iota/crypto/curl_java.rb +18 -0
- data/lib/iota/crypto/curl_ruby.rb +70 -0
- data/lib/iota/crypto/hmac.rb +27 -0
- data/lib/iota/crypto/kerl.rb +82 -0
- data/lib/iota/crypto/pow_provider.rb +27 -0
- data/lib/iota/crypto/private_key.rb +80 -0
- data/lib/iota/crypto/sha3_ruby.rb +122 -0
- data/lib/iota/crypto/signing.rb +97 -0
- data/lib/iota/models/account.rb +489 -0
- data/lib/iota/models/base.rb +13 -0
- data/lib/iota/models/bundle.rb +87 -0
- data/lib/iota/models/input.rb +38 -0
- data/lib/iota/models/seed.rb +33 -0
- data/lib/iota/models/transaction.rb +52 -0
- data/lib/iota/models/transfer.rb +44 -0
- data/lib/iota/multisig/address.rb +41 -0
- data/lib/iota/multisig/multisig.rb +244 -0
- data/lib/iota/utils/ascii.rb +50 -0
- data/lib/iota/utils/broker.rb +124 -0
- data/lib/iota/utils/input_validator.rb +149 -0
- data/lib/iota/utils/object_validator.rb +34 -0
- data/lib/iota/utils/utils.rb +324 -0
- data/lib/iota/version.rb +3 -0
- data/lib/jcurl.jar +0 -0
- data/lib/patch.rb +17 -0
- data/test/ascii_test.rb +114 -0
- data/test/curl_c_test.rb +31 -0
- data/test/curl_java_test.rb +31 -0
- data/test/curl_ruby_test.rb +27 -0
- data/test/kerl_test.rb +52 -0
- data/test/pow_provider_test.rb +36 -0
- data/test/sha3_test.rb +71 -0
- data/test/test_helper.rb +4 -0
- data/test/utils_test.rb +179 -0
- metadata +183 -0
@@ -0,0 +1,163 @@
|
|
1
|
+
module IOTA
|
2
|
+
module Crypto
|
3
|
+
class Bundle
|
4
|
+
attr_reader :bundle
|
5
|
+
|
6
|
+
def initialize(bundle = [])
|
7
|
+
bundle = bundle.map do |b|
|
8
|
+
if b.class != IOTA::Models::Transaction
|
9
|
+
IOTA::Models::Transaction.new(b)
|
10
|
+
else
|
11
|
+
b
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
@bundle = bundle
|
16
|
+
end
|
17
|
+
|
18
|
+
def addEntry(signatureMessageLength, address, value, tag, timestamp)
|
19
|
+
(0...signatureMessageLength).step(1) do |i|
|
20
|
+
transactionObject = IOTA::Models::Transaction.new(
|
21
|
+
address: address,
|
22
|
+
value: i==0 ? value : 0,
|
23
|
+
obsoleteTag: tag,
|
24
|
+
tag: tag,
|
25
|
+
timestamp: timestamp
|
26
|
+
)
|
27
|
+
|
28
|
+
@bundle << transactionObject
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def addTrytes(signatureFragments)
|
33
|
+
emptySignatureFragment = ''
|
34
|
+
emptyHash = '9' * 81
|
35
|
+
emptyTag = '9' * 27
|
36
|
+
emptyTimestamp = '9' * 9
|
37
|
+
|
38
|
+
while emptySignatureFragment.length < 2187
|
39
|
+
emptySignatureFragment += '9'
|
40
|
+
end
|
41
|
+
|
42
|
+
(0...@bundle.length).step(1) do |i|
|
43
|
+
# Fill empty signatureMessageFragment
|
44
|
+
@bundle[i].signatureMessageFragment = signatureFragments[i] ? signatureFragments[i] : emptySignatureFragment
|
45
|
+
|
46
|
+
# Fill empty trunkTransaction
|
47
|
+
@bundle[i].trunkTransaction = emptyHash
|
48
|
+
|
49
|
+
# Fill empty branchTransaction
|
50
|
+
@bundle[i].branchTransaction = emptyHash
|
51
|
+
|
52
|
+
@bundle[i].attachmentTimestamp = emptyTimestamp
|
53
|
+
@bundle[i].attachmentTimestampLowerBound = emptyTimestamp
|
54
|
+
@bundle[i].attachmentTimestampUpperBound = emptyTimestamp
|
55
|
+
|
56
|
+
# Fill empty nonce
|
57
|
+
@bundle[i].nonce = emptyTag
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def finalize
|
62
|
+
validBundle = false
|
63
|
+
|
64
|
+
while !validBundle
|
65
|
+
|
66
|
+
kerl = Kerl.new
|
67
|
+
|
68
|
+
(0...@bundle.length).step(1) do |i|
|
69
|
+
valueTrits = Converter.trits(@bundle[i].value)
|
70
|
+
while valueTrits.length < 81
|
71
|
+
valueTrits << 0
|
72
|
+
end
|
73
|
+
|
74
|
+
timestampTrits = Converter.trits(@bundle[i].timestamp)
|
75
|
+
while timestampTrits.length < 27
|
76
|
+
timestampTrits << 0
|
77
|
+
end
|
78
|
+
|
79
|
+
@bundle[i].currentIndex = i
|
80
|
+
currentIndexTrits = Converter.trits(@bundle[i].currentIndex)
|
81
|
+
while currentIndexTrits.length < 27
|
82
|
+
currentIndexTrits << 0
|
83
|
+
end
|
84
|
+
|
85
|
+
@bundle[i].lastIndex = @bundle.length - 1
|
86
|
+
lastIndexTrits = Converter.trits(@bundle[i].lastIndex)
|
87
|
+
while lastIndexTrits.length < 27
|
88
|
+
lastIndexTrits << 0
|
89
|
+
end
|
90
|
+
|
91
|
+
bundleEssence = Converter.trits(@bundle[i].address + Converter.trytes(valueTrits) + @bundle[i].obsoleteTag + Converter.trytes(timestampTrits) + Converter.trytes(currentIndexTrits) + Converter.trytes(lastIndexTrits))
|
92
|
+
kerl.absorb(bundleEssence, 0, bundleEssence.length)
|
93
|
+
end
|
94
|
+
|
95
|
+
hash = []
|
96
|
+
kerl.squeeze(hash, 0, Kerl::HASH_LENGTH)
|
97
|
+
hash = Converter.trytes(hash)
|
98
|
+
|
99
|
+
(0...@bundle.length).step(1) do |i|
|
100
|
+
@bundle[i].bundle = hash
|
101
|
+
end
|
102
|
+
|
103
|
+
normalizedHash = normalizedBundle(hash)
|
104
|
+
if !normalizedHash.index(13).nil?
|
105
|
+
# Insecure bundle. Increment Tag and recompute bundle hash.
|
106
|
+
increasedTagTrits = Converter.trits(@bundle[0].obsoleteTag)
|
107
|
+
|
108
|
+
# Adder implementation with 1 round
|
109
|
+
(0...increasedTagTrits.length).step(1) do |j|
|
110
|
+
increasedTagTrits[j] += 1
|
111
|
+
|
112
|
+
if increasedTagTrits[j] > 1
|
113
|
+
increasedTagTrits[j] = -1
|
114
|
+
else
|
115
|
+
break
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
@bundle[0].obsoleteTag = Converter.trytes(increasedTagTrits)
|
120
|
+
else
|
121
|
+
validBundle = true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def normalizedBundle(bundleHash)
|
127
|
+
normalizedBundleArr = []
|
128
|
+
|
129
|
+
(0...3).step(1) do |i|
|
130
|
+
sum = 0
|
131
|
+
(0...27).step(1) do |j|
|
132
|
+
normalizedBundleArr[i * 27 + j] = Converter.value(Converter.trits(bundleHash[i * 27 + j]))
|
133
|
+
sum += normalizedBundleArr[i * 27 + j]
|
134
|
+
end
|
135
|
+
|
136
|
+
if sum >= 0
|
137
|
+
while sum > 0
|
138
|
+
sum -= 1
|
139
|
+
(0...27).step(1) do |j|
|
140
|
+
if normalizedBundleArr[i * 27 + j] > -13
|
141
|
+
normalizedBundleArr[i * 27 + j] -= 1
|
142
|
+
break
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
else
|
147
|
+
while sum < 0
|
148
|
+
sum += 1
|
149
|
+
(0...27).step(1) do |j|
|
150
|
+
if normalizedBundleArr[i * 27 + j] < 13
|
151
|
+
normalizedBundleArr[i * 27 + j] += 1
|
152
|
+
break
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
normalizedBundleArr
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
module IOTA
|
2
|
+
module Crypto
|
3
|
+
class Converter
|
4
|
+
RADIX = 3
|
5
|
+
RADIX_BYTES = 256
|
6
|
+
MAX_TRIT_VALUE = 1
|
7
|
+
MIN_TRIT_VALUE = -1
|
8
|
+
BYTE_HASH_LENGTH = 48
|
9
|
+
HASH_LENGTH = Curl::HASH_LENGTH
|
10
|
+
|
11
|
+
# All possible tryte values
|
12
|
+
TRYTES_ALPHABET = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
13
|
+
|
14
|
+
TRYTE_TRITS = [
|
15
|
+
[ 0, 0, 0],
|
16
|
+
[ 1, 0, 0],
|
17
|
+
[-1, 1, 0],
|
18
|
+
[ 0, 1, 0],
|
19
|
+
[ 1, 1, 0],
|
20
|
+
[-1, -1, 1],
|
21
|
+
[ 0, -1, 1],
|
22
|
+
[ 1, -1, 1],
|
23
|
+
[-1, 0, 1],
|
24
|
+
[ 0, 0, 1],
|
25
|
+
[ 1, 0, 1],
|
26
|
+
[-1, 1, 1],
|
27
|
+
[ 0, 1, 1],
|
28
|
+
[ 1, 1, 1],
|
29
|
+
[-1, -1, -1],
|
30
|
+
[ 0, -1, -1],
|
31
|
+
[ 1, -1, -1],
|
32
|
+
[-1, 0, -1],
|
33
|
+
[ 0, 0, -1],
|
34
|
+
[ 1, 0, -1],
|
35
|
+
[-1, 1, -1],
|
36
|
+
[ 0, 1, -1],
|
37
|
+
[ 1, 1, -1],
|
38
|
+
[-1, -1, 0],
|
39
|
+
[ 0, -1, 0],
|
40
|
+
[ 1, -1, 0],
|
41
|
+
[-1, 0, 0]
|
42
|
+
]
|
43
|
+
|
44
|
+
class << self
|
45
|
+
def trits(input, state = [])
|
46
|
+
trits = state
|
47
|
+
|
48
|
+
if input.is_a? Integer
|
49
|
+
absoluteValue = input < 0 ? -input : input
|
50
|
+
|
51
|
+
while absoluteValue > 0
|
52
|
+
remainder = absoluteValue % 3
|
53
|
+
absoluteValue = (absoluteValue / 3).floor
|
54
|
+
|
55
|
+
if remainder > 1
|
56
|
+
remainder = -1
|
57
|
+
absoluteValue += 1
|
58
|
+
end
|
59
|
+
|
60
|
+
trits[trits.length] = remainder
|
61
|
+
end
|
62
|
+
|
63
|
+
if input < 0
|
64
|
+
(0... trits.length).step(1) do |i|
|
65
|
+
trits[i] = -trits[i]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
else
|
69
|
+
(0... input.length).step(1) do |i|
|
70
|
+
index = TRYTES_ALPHABET.index(input[i])
|
71
|
+
tmp = i * 3
|
72
|
+
trits[tmp...tmp+3] = TRYTE_TRITS[index]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
trits
|
77
|
+
end
|
78
|
+
|
79
|
+
def trytes(trits)
|
80
|
+
trytes = ""
|
81
|
+
|
82
|
+
(0...trits.length).step(3) do |i|
|
83
|
+
chunk = trits[i...i+3]
|
84
|
+
index = TRYTE_TRITS.index(chunk)
|
85
|
+
if !index.nil?
|
86
|
+
trytes += TRYTES_ALPHABET[index]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
trytes
|
91
|
+
end
|
92
|
+
|
93
|
+
def value(trits)
|
94
|
+
returnValue = 0
|
95
|
+
|
96
|
+
range = (trits.length..0)
|
97
|
+
|
98
|
+
range.first.downto(range.last).each do |i|
|
99
|
+
returnValue = returnValue * 3 + trits[i].to_i
|
100
|
+
end
|
101
|
+
|
102
|
+
returnValue
|
103
|
+
end
|
104
|
+
|
105
|
+
def fromValue(value)
|
106
|
+
destination = []
|
107
|
+
absoluteValue = value < 0 ? -value : value
|
108
|
+
i = 0
|
109
|
+
|
110
|
+
while absoluteValue > 0
|
111
|
+
remainder = absoluteValue % RADIX
|
112
|
+
absoluteValue = (absoluteValue / RADIX).floor
|
113
|
+
|
114
|
+
if remainder > MAX_TRIT_VALUE
|
115
|
+
remainder = MIN_TRIT_VALUE
|
116
|
+
absoluteValue += 1
|
117
|
+
end
|
118
|
+
destination[i] = remainder
|
119
|
+
i += 1
|
120
|
+
end
|
121
|
+
|
122
|
+
if value < 0
|
123
|
+
(0...destination.length).step(1) do |j|
|
124
|
+
# switch values
|
125
|
+
destination[j] = destination[j] == 0 ? 0 : -destination[j]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
destination
|
130
|
+
end
|
131
|
+
|
132
|
+
### ADOPTED FROM PYTHON LIBRARY
|
133
|
+
# Word to tryte & trytes to words conversion
|
134
|
+
def convertToBytes(trits)
|
135
|
+
bigInt = convertBaseToBigInt(trits, 3)
|
136
|
+
bytes_k = convertBigIntToBytes(bigInt)
|
137
|
+
bytes_k
|
138
|
+
end
|
139
|
+
|
140
|
+
def convertToTrits(bytes)
|
141
|
+
bigInt = convertBytesToBigInt(bytes)
|
142
|
+
trits = convertBigIntToBase(bigInt, 3, HASH_LENGTH)
|
143
|
+
trits
|
144
|
+
end
|
145
|
+
|
146
|
+
# Convert between signed and unsigned bytes
|
147
|
+
def convertSign(byte)
|
148
|
+
if byte < 0
|
149
|
+
return 256 + byte
|
150
|
+
elsif byte > 127
|
151
|
+
return -256 + byte
|
152
|
+
end
|
153
|
+
byte
|
154
|
+
end
|
155
|
+
|
156
|
+
def convertBaseToBigInt(array, base)
|
157
|
+
bigint = 0
|
158
|
+
(0...array.length).step(1) do |i|
|
159
|
+
bigint += array[i] * (base ** i)
|
160
|
+
end
|
161
|
+
bigint
|
162
|
+
end
|
163
|
+
|
164
|
+
def convertBigIntToBase(bigInt, base, length)
|
165
|
+
result = []
|
166
|
+
|
167
|
+
isNegative = bigInt < 0
|
168
|
+
quotient = bigInt.abs
|
169
|
+
|
170
|
+
max, _ = (isNegative ? base : base-1).divmod(2)
|
171
|
+
|
172
|
+
length.times do
|
173
|
+
quotient, remainder = quotient.divmod(base)
|
174
|
+
|
175
|
+
if remainder > max
|
176
|
+
# Lend 1 to the next place so we can make this digit negative.
|
177
|
+
quotient += 1
|
178
|
+
remainder -= base
|
179
|
+
end
|
180
|
+
|
181
|
+
remainder = -remainder if isNegative
|
182
|
+
|
183
|
+
result << remainder
|
184
|
+
end
|
185
|
+
|
186
|
+
result
|
187
|
+
end
|
188
|
+
|
189
|
+
def convertBigIntToBytes(big)
|
190
|
+
bytesArrayTemp = []
|
191
|
+
|
192
|
+
(0...48).step(1) do |pos|
|
193
|
+
bytesArrayTemp << (big.abs >> pos * 8) % (1 << 8)
|
194
|
+
end
|
195
|
+
|
196
|
+
# big endian and balanced
|
197
|
+
bytesArray = bytesArrayTemp.reverse.map { |x| x <= 0x7F ? x : x - 0x100 }
|
198
|
+
|
199
|
+
if big < 0
|
200
|
+
# 1-compliment
|
201
|
+
bytesArray = bytesArray.map { |x| ~x }
|
202
|
+
|
203
|
+
# add1
|
204
|
+
(0...bytesArray.length).reverse_each do |pos|
|
205
|
+
add = (bytesArray[pos] & 0xFF) + 1
|
206
|
+
bytesArray[pos] = add <= 0x7F ? add : add - 0x100
|
207
|
+
break if bytesArray[pos] != 0
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
bytesArray
|
212
|
+
end
|
213
|
+
|
214
|
+
def convertBytesToBigInt(array)
|
215
|
+
# copy of array
|
216
|
+
bytesArray = array.map { |x| x }
|
217
|
+
|
218
|
+
# number sign in MSB
|
219
|
+
signum = bytesArray[0] >= 0 ? 1 : -1
|
220
|
+
|
221
|
+
if signum == -1
|
222
|
+
# sub1
|
223
|
+
(0...bytesArray.length).reverse_each do |pos|
|
224
|
+
sub = (bytesArray[pos] & 0xFF) - 1
|
225
|
+
bytesArray[pos] = sub <= 0x7F ? sub : sub - 0x100
|
226
|
+
break if bytesArray[pos] != -1
|
227
|
+
end
|
228
|
+
|
229
|
+
# 1-compliment
|
230
|
+
bytesArray = bytesArray.map { |x| ~x }
|
231
|
+
end
|
232
|
+
|
233
|
+
# sum magnitudes and set sign
|
234
|
+
sum = 0
|
235
|
+
bytesArray.reverse.each_with_index do |v, pos|
|
236
|
+
sum += (v & 0xFF) << pos * 8
|
237
|
+
end
|
238
|
+
|
239
|
+
sum * signum
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
if RUBY_PLATFORM =~ /java/
|
2
|
+
require "iota/crypto/curl_java"
|
3
|
+
BaseCurl = IOTA::Crypto::JCurl
|
4
|
+
else
|
5
|
+
require "iota/crypto/curl_c"
|
6
|
+
BaseCurl = IOTA::Crypto::CCurl
|
7
|
+
end
|
8
|
+
|
9
|
+
module IOTA
|
10
|
+
module Crypto
|
11
|
+
class Curl < BaseCurl
|
12
|
+
def initialize(rounds = nil)
|
13
|
+
rounds ||= NUMBER_OF_ROUNDS
|
14
|
+
super(rounds)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module IOTA
|
2
|
+
module Crypto
|
3
|
+
class JCurl
|
4
|
+
NUMBER_OF_ROUNDS = 81
|
5
|
+
HASH_LENGTH = 243
|
6
|
+
STATE_LENGTH = 3 * HASH_LENGTH
|
7
|
+
|
8
|
+
if RUBY_PLATFORM =~ /java/
|
9
|
+
require 'jcurl'
|
10
|
+
com.vmarakana.JCurlService.new.basicLoad(JRuby.runtime)
|
11
|
+
end
|
12
|
+
|
13
|
+
def version
|
14
|
+
"Java"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|