stellar-base 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +15 -0
  4. data/.yardopts +8 -0
  5. data/Gemfile +15 -0
  6. data/Guardfile +5 -0
  7. data/LICENSE.txt +202 -0
  8. data/README.md +79 -0
  9. data/Rakefile +6 -0
  10. data/examples/low_level_transaction_post.rb +53 -0
  11. data/examples/mid_level_transaction_post.rb +34 -0
  12. data/examples/non_native_payment.rb +60 -0
  13. data/generated/stellar/account_entry.rb +37 -0
  14. data/generated/stellar/account_flags.rb +20 -0
  15. data/generated/stellar/account_merge_result.rb +25 -0
  16. data/generated/stellar/account_merge_result_code.rb +30 -0
  17. data/generated/stellar/allow_trust_op/currency.rb +28 -0
  18. data/generated/stellar/allow_trust_op.rb +35 -0
  19. data/generated/stellar/allow_trust_result.rb +25 -0
  20. data/generated/stellar/allow_trust_result_code.rb +28 -0
  21. data/generated/stellar/bucket_entry.rb +28 -0
  22. data/generated/stellar/bucket_entry_type.rb +22 -0
  23. data/generated/stellar/change_trust_op.rb +22 -0
  24. data/generated/stellar/change_trust_result.rb +25 -0
  25. data/generated/stellar/change_trust_result_code.rb +28 -0
  26. data/generated/stellar/claim_offer_atom.rb +29 -0
  27. data/generated/stellar/create_offer_effect.rb +24 -0
  28. data/generated/stellar/create_offer_op.rb +28 -0
  29. data/generated/stellar/create_offer_result.rb +26 -0
  30. data/generated/stellar/create_offer_result_code.rb +45 -0
  31. data/generated/stellar/create_offer_success_result/offer.rb +30 -0
  32. data/generated/stellar/create_offer_success_result.rb +34 -0
  33. data/generated/stellar/currency.rb +29 -0
  34. data/generated/stellar/currency_type.rb +22 -0
  35. data/generated/stellar/decorated_signature.rb +20 -0
  36. data/generated/stellar/dont_have.rb +20 -0
  37. data/generated/stellar/error.rb +20 -0
  38. data/generated/stellar/hello.rb +24 -0
  39. data/generated/stellar/inflation_payout.rb +20 -0
  40. data/generated/stellar/inflation_result.rb +26 -0
  41. data/generated/stellar/inflation_result_code.rb +24 -0
  42. data/generated/stellar/iso_currency_issuer.rb +20 -0
  43. data/generated/stellar/ledger_entry.rb +33 -0
  44. data/generated/stellar/ledger_entry_type.rb +24 -0
  45. data/generated/stellar/ledger_header.rb +45 -0
  46. data/generated/stellar/ledger_header_history_entry.rb +20 -0
  47. data/generated/stellar/ledger_key/account.rb +20 -0
  48. data/generated/stellar/ledger_key/offer.rb +22 -0
  49. data/generated/stellar/ledger_key/trust_line.rb +22 -0
  50. data/generated/stellar/ledger_key.rb +50 -0
  51. data/generated/stellar/message_type.rb +45 -0
  52. data/generated/stellar/offer_entry.rb +34 -0
  53. data/generated/stellar/operation/body.rb +49 -0
  54. data/generated/stellar/operation.rb +45 -0
  55. data/generated/stellar/operation_result/tr.rb +49 -0
  56. data/generated/stellar/operation_result.rb +47 -0
  57. data/generated/stellar/operation_result_code.rb +25 -0
  58. data/generated/stellar/operation_type.rb +32 -0
  59. data/generated/stellar/payment_op.rb +35 -0
  60. data/generated/stellar/payment_result.rb +29 -0
  61. data/generated/stellar/payment_result_code.rb +41 -0
  62. data/generated/stellar/payment_success_multi_result.rb +20 -0
  63. data/generated/stellar/peer_address.rb +22 -0
  64. data/generated/stellar/price.rb +20 -0
  65. data/generated/stellar/scp_ballot.rb +20 -0
  66. data/generated/stellar/scp_envelope.rb +22 -0
  67. data/generated/stellar/scp_quorum_set.rb +20 -0
  68. data/generated/stellar/scp_statement/pledges/prepare.rb +24 -0
  69. data/generated/stellar/scp_statement/pledges.rb +40 -0
  70. data/generated/stellar/scp_statement.rb +42 -0
  71. data/generated/stellar/scp_statement_type.rb +26 -0
  72. data/generated/stellar/set_options_op.rb +31 -0
  73. data/generated/stellar/set_options_result.rb +25 -0
  74. data/generated/stellar/set_options_result_code.rb +28 -0
  75. data/generated/stellar/signer.rb +20 -0
  76. data/generated/stellar/simple_payment_result.rb +22 -0
  77. data/generated/stellar/stellar_ballot.rb +22 -0
  78. data/generated/stellar/stellar_ballot_value.rb +22 -0
  79. data/generated/stellar/stellar_message.rb +66 -0
  80. data/generated/stellar/transaction.rb +37 -0
  81. data/generated/stellar/transaction_envelope.rb +20 -0
  82. data/generated/stellar/transaction_history_entry.rb +20 -0
  83. data/generated/stellar/transaction_history_result_entry.rb +20 -0
  84. data/generated/stellar/transaction_meta.rb +18 -0
  85. data/generated/stellar/transaction_result/result.rb +30 -0
  86. data/generated/stellar/transaction_result.rb +33 -0
  87. data/generated/stellar/transaction_result_code.rb +46 -0
  88. data/generated/stellar/transaction_result_pair.rb +20 -0
  89. data/generated/stellar/transaction_result_set.rb +18 -0
  90. data/generated/stellar/transaction_set.rb +20 -0
  91. data/generated/stellar/trust_line_entry.rb +28 -0
  92. data/generated/stellar-base-generated.rb +160 -0
  93. data/lib/stellar/base/version.rb +5 -0
  94. data/lib/stellar/base.rb +1 -0
  95. data/lib/stellar/change_trust_op.rb +10 -0
  96. data/lib/stellar/currency.rb +28 -0
  97. data/lib/stellar/key_pair.rb +94 -0
  98. data/lib/stellar/payment_op.rb +38 -0
  99. data/lib/stellar/transaction.rb +72 -0
  100. data/lib/stellar/transaction_envelope.rb +32 -0
  101. data/lib/stellar/util/base58.rb +127 -0
  102. data/lib/stellar-base.rb +23 -0
  103. data/ruby-stellar-base.gemspec +34 -0
  104. data/spec/lib/stellar/key_pair_spec.rb +199 -0
  105. data/spec/lib/stellar/transaction_envelope_spec.rb +93 -0
  106. data/spec/lib/stellar/transaction_spec.rb +43 -0
  107. data/spec/lib/stellar/util/base58_spec.rb +74 -0
  108. data/spec/spec_helper.rb +16 -0
  109. data/spec/support/matchers/be_base58_check.rb +9 -0
  110. data/spec/support/matchers/eq_bytes.rb +5 -0
  111. data/spec/support/matchers/have_length.rb +5 -0
  112. data/tasks/rspec.rake +6 -0
  113. data/tasks/travis.rake +9 -0
  114. data/tasks/xdr.rake +48 -0
  115. data/xdr/SCPXDR.x +61 -0
  116. data/xdr/Stellar-ledger-entries.x +105 -0
  117. data/xdr/Stellar-ledger.x +117 -0
  118. data/xdr/Stellar-overlay.x +100 -0
  119. data/xdr/Stellar-transaction.x +497 -0
  120. data/xdr/Stellar-types.x +53 -0
  121. data/xdr/StellarXDR.x +11 -0
  122. metadata +342 -0
@@ -0,0 +1,127 @@
1
+ module Stellar
2
+ module Util
3
+ class Base58
4
+ STELLAR_ALPHABET = "gsphnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCr65jkm8oFqi1tuvAxyz"
5
+ BITCOIN_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
6
+ BASE = 58
7
+
8
+ # TODO: improve the conversion to bitstring, perhaps `Fixnum#to_byte`?
9
+ VERSION_BYTES = {
10
+ none: [1].pack("C").encode("BINARY"),
11
+ account_id: [0].pack("C").encode("BINARY"),
12
+ seed: [33].pack("C").encode("BINARY"),
13
+ }
14
+
15
+ def self.stellar
16
+ Thread.current[:stellar_base58] ||= new(STELLAR_ALPHABET)
17
+ end
18
+
19
+ def self.bitcoin
20
+ Thread.current[:bitcoin_base58] ||= new(BITCOIN_ALPHABET)
21
+ end
22
+
23
+ def initialize(alphabet)
24
+ raise ArgumentError, "Invalid alphabet length" if alphabet.length != BASE
25
+ @alphabet = alphabet
26
+ end
27
+
28
+ def encode(byte_str)
29
+ return "" if byte_str.nil? || byte_str.empty?
30
+
31
+ leading_zeros = byte_str.each_byte.take_while{|b| b == 0}.length
32
+ int = bytes_to_int(byte_str) # step 1
33
+
34
+ encode_int(int, leading_zeros)
35
+ end
36
+
37
+ def check_encode(version, byte_str)
38
+ version_byte = VERSION_BYTES[version]
39
+ raise ArgumentError, "Invalid version: #{version}" if version_byte.blank?
40
+
41
+ payload = version_byte + byte_str.dup.force_encoding("BINARY")
42
+ check = checksum(payload)
43
+ encode(payload + check)
44
+ end
45
+
46
+ def decode(str)
47
+ leading_zeros = str.each_char.take_while{|c| c == @alphabet[0]}.length
48
+
49
+ ("\x00" * leading_zeros) + decode_int(str)
50
+ end
51
+
52
+ def check_decode(expected_version, str)
53
+ decoded = decode(str)
54
+ version_byte = decoded[0]
55
+ payload = decoded[1...-4]
56
+ check = decoded[-4..-1]
57
+ version = VERSION_BYTES.key(version_byte)
58
+
59
+ raise ArgumentError, "Unexpected version: #{version.inspect}" if version != expected_version
60
+ raise ArgumentError, "Invalid checksum" if check != checksum(decoded[0...-4])
61
+ payload
62
+ end
63
+
64
+ def checksum(bytes)
65
+ inner = Digest::SHA256.digest(bytes)
66
+ Digest::SHA256.digest(inner)[0...4]
67
+ end
68
+
69
+ private
70
+ def encode_int(int, leading_zeros=0)
71
+ # algorithm:
72
+ # 1. convert the bytes to a bignum
73
+ # 2. turn the bignum into a array of digits, least significant first
74
+ # 3. add any leading zero bytes as leading 0 digits
75
+ # 4. alphabatize the digits based upon the chosen alphabet
76
+ # 5. reverse the alphabetized digits (to get most significant digit first)
77
+
78
+ digits = []
79
+
80
+ # step 2
81
+ while int > 0
82
+ int, rem = int.divmod(BASE)
83
+ digits.push rem
84
+ end
85
+
86
+ # step 3
87
+ leading_zeros.times{ digits.push 0 }
88
+
89
+ digits
90
+ .map{|d| @alphabet[d]} # step 4
91
+ .reverse # step 5
92
+ .join
93
+ end
94
+
95
+ def decode_int(str)
96
+ int = str.reverse.each_char.with_index.inject(0) do |result, (digit, index)|
97
+ digit_val = @alphabet.index(digit)
98
+ raise ArgumentError, "#{digit} is not a valid base58 digit" if digit_val.nil?
99
+ result + (digit_val * (BASE**index))
100
+ end
101
+
102
+ int_to_bytes(int)
103
+ end
104
+
105
+ def bytes_to_int(bytes)
106
+ bytes.unpack("C*").inject do |result, byte|
107
+ result <<= 8
108
+ result + byte
109
+ end
110
+ end
111
+
112
+ def int_to_bytes(int)
113
+ return "\x00" if int == 0
114
+
115
+ bytes = []
116
+ current = int
117
+
118
+ while current > 0
119
+ bytes.unshift(current & 0xFF)
120
+ current >>= 8
121
+ end
122
+
123
+ bytes.pack("C*")
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,23 @@
1
+ require 'xdr'
2
+ require 'rbnacl'
3
+ require 'digest/sha2'
4
+ require 'active_support/core_ext/object/blank'
5
+ require 'active_support/core_ext/enumerable'
6
+ require 'active_support/core_ext/kernel/reporting'
7
+
8
+ # See ../generated for code-gen'ed files
9
+
10
+ silence_warnings do
11
+ require 'stellar-base-generated'
12
+ end
13
+ Stellar.load_all!
14
+
15
+ # extensions onto the generated files must be loaded manually, below
16
+
17
+ require_relative './stellar/change_trust_op'
18
+ require_relative './stellar/currency'
19
+ require_relative './stellar/key_pair'
20
+ require_relative './stellar/payment_op'
21
+ require_relative './stellar/transaction'
22
+ require_relative './stellar/transaction_envelope'
23
+ require_relative './stellar/util/base58'
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'stellar/base/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "stellar-base"
8
+ spec.version = Stellar::Base::VERSION
9
+ spec.authors = ["Scott Fleckenstein"]
10
+ spec.email = ["scott@stellar.org"]
11
+ spec.summary = %q{Stellar client library: XDR}
12
+ spec.homepage = "https://github.com/stellar/ruby-stellar-vase"
13
+ spec.license = "Apache 2.0"
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 = ["generated", "lib"]
19
+
20
+ spec.add_dependency "xdr"
21
+ spec.add_dependency "rbnacl"
22
+ spec.add_dependency "activesupport", "~> 4"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "xdrgen"
27
+ spec.add_development_dependency "rspec", "~> 3.1"
28
+ spec.add_development_dependency "guard-rspec"
29
+ spec.add_development_dependency "simplecov"
30
+ spec.add_development_dependency "octokit"
31
+ spec.add_development_dependency "netrc"
32
+ spec.add_development_dependency "yard"
33
+
34
+ end
@@ -0,0 +1,199 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe Stellar::KeyPair do
5
+
6
+ describe ".from_seed" do
7
+ subject{ Stellar::KeyPair.from_seed(seed) }
8
+
9
+ context "when provided a base58check encoded seed" do
10
+ let(:seed){ "s9aaUNPaT9t1x7vCeDzQYvLZDm5XxSUKkwnqQowV6D3kMr678uZ" }
11
+ it { should be_a(Stellar::KeyPair) }
12
+ end
13
+
14
+ context "provided value is not base58 encoded" do
15
+ let(:seed){ "allmylifemyhearthasbeensearching" }
16
+ it { expect{ subject }.to raise_error(ArgumentError) }
17
+ end
18
+
19
+ context "provided value is not base58 encoded as a seed" do
20
+ let(:raw_seed){ "allmylifemyhearthasbeensearching" }
21
+ let(:seed){ Stellar::Util::Base58.stellar.check_encode(:account_id, raw_seed) }
22
+ it { expect{ subject }.to raise_error(ArgumentError) }
23
+ end
24
+ end
25
+
26
+ describe ".from_raw_seed" do
27
+ subject{ Stellar::KeyPair.from_raw_seed(raw_seed) }
28
+
29
+ context "when the provided value is a 32-byte string" do
30
+ let(:raw_seed){ "allmylifemyhearthasbeensearching" }
31
+ it { should be_a(Stellar::KeyPair) }
32
+ end
33
+
34
+ context "when the provided value is < 32-byte string" do
35
+ let(:raw_seed){ "\xFF" * 31 }
36
+ it { expect{ subject }.to raise_error(ArgumentError) }
37
+ end
38
+
39
+ context "when the provided value is > 32-byte string" do
40
+ let(:raw_seed){ "\xFF" * 33 }
41
+ it { expect{ subject }.to raise_error(ArgumentError) }
42
+ end
43
+
44
+ context "when the provided value is a 32 character, but > 32 byte string (i.e. multi-byte characters)" do
45
+ let(:raw_seed){ "ü" + ("\x00" * 31) }
46
+ it { expect{ subject }.to raise_error(ArgumentError) }
47
+ end
48
+ end
49
+
50
+ describe ".from_public_key" do
51
+ subject{ Stellar::KeyPair.from_public_key(key) }
52
+
53
+ context "when the provided value is a 32-byte string" do
54
+ let(:key){ "\xFF" * 32 }
55
+ it { should be_a(Stellar::KeyPair) }
56
+ end
57
+
58
+ context "when the provided value is < 32-byte string" do
59
+ let(:key){ "\xFF" * 31 }
60
+ it { expect{ subject }.to raise_error(ArgumentError) }
61
+ end
62
+
63
+ context "when the provided value is > 32-byte string" do
64
+ let(:key){ "\xFF" * 33 }
65
+ it { expect{ subject }.to raise_error(ArgumentError) }
66
+ end
67
+
68
+ context "when the provided value is a 32 character, but > 32 byte string (i.e. multi-byte characters)" do
69
+ let(:key){ "ü" + ("\x00" * 31) }
70
+ it { expect{ subject }.to raise_error(ArgumentError) }
71
+ end
72
+ end
73
+
74
+ describe ".from_address" do
75
+ subject{ Stellar::KeyPair.from_address(address) }
76
+
77
+ context "when provided a base58check encoded account_id" do
78
+ let(:address){ "gsYRSEQhTffqA9opPepAENCr2WG6z5iBHHubxxbRzWaHf8FBWcu" }
79
+ it { should be_a(Stellar::KeyPair) }
80
+ end
81
+
82
+ context "provided value is not base58 encoded" do
83
+ let(:address){ "some address" }
84
+ it { expect{ subject }.to raise_error(ArgumentError) }
85
+ end
86
+
87
+ context "provided value is not base58 encoded as an account_id" do
88
+ let(:public_key){ "\xFF" * 32 }
89
+ let(:address){ Stellar::Util::Base58.stellar.check_encode(:seed, public_key) }
90
+ it { expect{ subject }.to raise_error(ArgumentError) }
91
+ end
92
+
93
+ end
94
+
95
+ describe ".random" do
96
+ subject{ Stellar::KeyPair.random }
97
+
98
+ it "returns a new KeyPair every time" do
99
+ other = Stellar::KeyPair.random
100
+ expect(subject.raw_seed == other.raw_seed).to eq(false)
101
+ end
102
+ end
103
+
104
+ describe "#public_key" do
105
+ let(:key_pair){ Stellar::KeyPair.random }
106
+ subject{ key_pair.public_key }
107
+
108
+ it { should be_a(String) }
109
+ it { should have_length(32) }
110
+ end
111
+
112
+ describe "#raw_seed" do
113
+ let(:key_pair){ Stellar::KeyPair.random }
114
+ subject{ key_pair.raw_seed }
115
+
116
+ it { should be_a(String) }
117
+ it { should have_length(32) }
118
+ end
119
+
120
+ describe "#address" do
121
+ let(:key_pair){ Stellar::KeyPair.random }
122
+ subject{ key_pair.address }
123
+ it{ should be_base58_check(:account_id)}
124
+ end
125
+
126
+ describe "#seed" do
127
+ let(:key_pair){ Stellar::KeyPair.random }
128
+ subject{ key_pair.seed }
129
+ it{ should be_base58_check(:seed)}
130
+ end
131
+
132
+ describe "#sign" do
133
+ let(:message) { "hello" }
134
+ subject{ key_pair.sign(message) }
135
+
136
+ context "when the key_pair has no private key" do
137
+ let(:key_pair){ Stellar::KeyPair.from_public_key("\x00" * 32)}
138
+
139
+ it{ expect{ subject }.to raise_error("no private key") }
140
+ end
141
+
142
+ context "when the key_pair has both public/private keys" do
143
+ let(:key_pair){ Stellar::KeyPair.from_raw_seed("\x00" * 32)}
144
+
145
+ it { should have_length(64) }
146
+
147
+ it "should be a ed25519 signature" do
148
+ verification = key_pair.rbnacl_verify_key.verify(subject, message)
149
+ expect(verification).to be_truthy
150
+ end
151
+
152
+ context "when the message is nil" do
153
+ let(:message){ nil }
154
+ it { expect{subject}.to raise_error(TypeError) }
155
+ end
156
+ end
157
+ end
158
+
159
+ describe "#verify" do
160
+ let(:key_pair) { Stellar::KeyPair.random }
161
+ let(:message) { "hello" }
162
+ subject { key_pair.verify(signature, message) }
163
+
164
+ context "when the signature is correct" do
165
+ let(:signature) { key_pair.sign(message) }
166
+ it{ should be_truthy }
167
+ end
168
+
169
+ context "when the signature is incorrect" do
170
+ let(:signature) { key_pair.sign("some other message") }
171
+ it{ should be_falsey }
172
+ end
173
+
174
+ context "when the signature is invalid" do
175
+ let(:signature) { "food" }
176
+ it{ should be_falsey }
177
+ end
178
+
179
+ context "when the signature is from a different key" do
180
+ let(:signature) { Stellar::KeyPair.random.sign(message) }
181
+ it{ should be_falsey }
182
+ end
183
+
184
+ end
185
+
186
+ describe "#sign?" do
187
+ subject{ key_pair.sign? }
188
+
189
+ context "when the key_pair has no private key" do
190
+ let(:key_pair){ Stellar::KeyPair.from_public_key("\x00" * 32)}
191
+ it{ should eq(false) }
192
+ end
193
+
194
+ context "when the key_pair has both public/private keys" do
195
+ let(:key_pair){ Stellar::KeyPair.from_raw_seed("\x00" * 32)}
196
+ it{ should eq(true) }
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,93 @@
1
+ require "spec_helper"
2
+
3
+ describe Stellar::TransactionEnvelope do
4
+ let(:sender) { Stellar::KeyPair.random }
5
+ let(:receiver){ Stellar::KeyPair.random }
6
+ let(:transaction) do
7
+ Stellar::Transaction.payment({
8
+ account: sender,
9
+ destination: receiver,
10
+ sequence: 1,
11
+ amount: [:native, 20000000]
12
+ })
13
+ end
14
+
15
+ let(:envelope){ transaction.to_envelope(*signers) }
16
+
17
+ describe "#signed_correctly?" do
18
+ subject{ envelope.signed_correctly?(*verifiers) }
19
+
20
+ context "when unsigned" do
21
+ let(:signers) { [] }
22
+ let(:verifiers){ [sender] }
23
+
24
+ it{ should be_falsey }
25
+ end
26
+
27
+ context "when signed by a single account" do
28
+ let(:signers) { [sender] }
29
+ let(:verifiers){ signers }
30
+
31
+ context "and signed correctly" do
32
+ it{ should be_truthy }
33
+ end
34
+
35
+ context "and the signature is corrupted" do
36
+ before(:each){ envelope.signatures.first.signature = "\xFF" * 32}
37
+ it{ should be_falsey }
38
+ end
39
+
40
+ context "and the signature is from a different message" do
41
+ before(:each){ envelope.signatures = [sender.sign_decorated("hello")]}
42
+ it{ should be_falsey }
43
+ end
44
+
45
+ context "and the key for the signing account is not provided" do
46
+ let(:verifiers){ [] }
47
+ it{ should be_falsey }
48
+ end
49
+
50
+ context "and the key for the signing account is wrong" do
51
+ let(:verifiers){ [receiver] }
52
+ it{ should be_falsey }
53
+ end
54
+ end
55
+
56
+ context "when signed by a multiple accounts" do
57
+ let(:alternate_signer){ Stellar::KeyPair.random }
58
+ let(:signers) { [sender, alternate_signer] }
59
+ let(:verifiers){ signers }
60
+
61
+ context "and all public keys are provided" do
62
+ it{ should be_truthy }
63
+ end
64
+
65
+ context "and all public keys are provided, with additional unused keys provided" do
66
+ let(:verifiers){ signers + [Stellar::KeyPair.random] }
67
+ it{ should be_truthy }
68
+ end
69
+
70
+ context "and not all public keys for the signers are provided" do
71
+ let(:verifiers){ [alternate_signer] }
72
+ it{ should be_falsey }
73
+ end
74
+
75
+ context "and one of the signatures is corrupted" do
76
+ before(:each){ envelope.signatures.last.signature = "\xFF" * 32}
77
+ it{ should be_falsey }
78
+ end
79
+
80
+ context "and the signature is from a different message" do
81
+ before(:each){ envelope.signatures = signers.map{|s| s.sign_decorated("hello")}}
82
+ it{ should be_falsey }
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+ describe "#hash" do
89
+ let(:signers) { [sender] }
90
+ subject{ envelope.hash }
91
+ it{ should eq(Digest::SHA256.digest envelope.to_xdr)}
92
+ end
93
+ end
@@ -0,0 +1,43 @@
1
+ require "spec_helper"
2
+
3
+ describe Stellar::Transaction do
4
+ subject do
5
+ Stellar::Transaction.new({
6
+ source_account: "\x00" * 32,
7
+ max_fee: 10,
8
+ seq_num: 1,
9
+ max_ledger: 10,
10
+ min_ledger: 0,
11
+ operations: [
12
+ Stellar::Operation.new(body: Stellar::Operation::Body.new(:inflation, 1))
13
+ ]
14
+ })
15
+ end
16
+ let(:key_pair){ Stellar::KeyPair.random }
17
+
18
+ describe "#sign" do
19
+ let(:result){ subject.sign(key_pair) }
20
+
21
+ it "returns a signature of SHA256(xdr form of the transaction)" do
22
+ xdr = subject.to_xdr
23
+ hash = Digest::SHA256.digest(xdr)
24
+ expected = key_pair.sign(hash)
25
+ expect(result).to eq(expected)
26
+ end
27
+ end
28
+
29
+ describe "#to_envelope" do
30
+ let(:result){ subject.to_envelope(key_pair) }
31
+
32
+ it "return a Stellar::TransactionEnvelope" do
33
+ expect(result).to be_a(Stellar::TransactionEnvelope)
34
+ end
35
+
36
+ it "correctly signs the transaction" do
37
+ expect(result.signatures.length).to eq(1)
38
+ expect(result.signatures.first).to be_a(Stellar::DecoratedSignature)
39
+ expect(result.signatures.first.hint).to eq(key_pair.public_key_hint)
40
+ expect(result.signatures.first.signature).to eq(subject.sign(key_pair))
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stellar::Util::Base58 do
4
+ subject{ Stellar::Util::Base58.new(Stellar::Util::Base58::BITCOIN_ALPHABET) }
5
+
6
+ describe "#decode" do
7
+ it "properly encodes" do
8
+ expect(decode "z").to eq_bytes("\x39")
9
+ expect(decode "111z").to eq_bytes("\x00\x00\x00\x39")
10
+ expect(decode "2UzHL").to eq_bytes("\xFF\xFF\xFF")
11
+ end
12
+
13
+ it "raises an ArgumentError when the string cantains an invalid character" do
14
+ expect{ decode "OOO" }.to raise_error(ArgumentError)
15
+ expect{ decode "\xFF" }.to raise_error(ArgumentError)
16
+ expect{ decode "\x00" }.to raise_error(ArgumentError)
17
+ end
18
+
19
+ def decode(bytes)
20
+ subject.decode(bytes)
21
+ end
22
+ end
23
+
24
+ describe "#check_decode" do
25
+ it "properly decodes" do
26
+ expect(decode :none, "cKyAv51").to eq_bytes("\x39")
27
+ expect(decode :seed, "RN3BaguaZ6Mi").to eq_bytes("\x00\x00\x00\x39")
28
+ expect(decode :account_id, "1Ahg1iQXoss").to eq_bytes("\xFF\xFF\xFF")
29
+ end
30
+
31
+ it "raises an ArgumentError if the decoded version byte does not match the expected value" do
32
+ expect{ decode :none, "1Ahg1iQXoss" }.to raise_error(ArgumentError)
33
+ expect{ decode :seed, "cKyAv51" }.to raise_error(ArgumentError)
34
+ expect{ decode :account_id, "RN3BaguaZ6Mi" }.to raise_error(ArgumentError)
35
+ end
36
+
37
+ it "raises an ArgumentError if the decoded value cannot be validated by the checksum" do
38
+ expect{decode :seed, "RN3BaguaZ6MM"}.to raise_error(ArgumentError)
39
+ end
40
+
41
+ def decode(version, bytes)
42
+ subject.check_decode(version, bytes)
43
+ end
44
+ end
45
+
46
+ describe"#encode" do
47
+ it "properly encodes" do
48
+ expect(encode "\x39").to eq("z")
49
+ expect(encode "\x00\x00\x00\x39").to eq("111z")
50
+ expect(encode "\xFF\xFF\xFF").to eq("2UzHL")
51
+ end
52
+
53
+ def encode(bytes)
54
+ subject.encode(bytes)
55
+ end
56
+ end
57
+
58
+ describe"#check_encode" do
59
+ it "properly encodes" do
60
+ expect(encode :none, "\x39").to eq("cKyAv51")
61
+ expect(encode :seed, "\x00\x00\x00\x39").to eq("RN3BaguaZ6Mi")
62
+ expect(encode :account_id, "\xFF\xFF\xFF").to eq("1Ahg1iQXoss")
63
+ end
64
+
65
+ it "raises an ArgumentError when an invalid version is provided" do
66
+ expect{ encode :floob, "\x39" }.to raise_error(ArgumentError)
67
+ end
68
+
69
+ def encode(version, bytes)
70
+ subject.check_encode(version, bytes)
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,16 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'simplecov'
5
+ SimpleCov.start
6
+
7
+ require 'pry'
8
+ require 'stellar-base'
9
+
10
+ SPEC_ROOT = File.dirname(__FILE__)
11
+
12
+ Dir["#{SPEC_ROOT}/support/**/*.rb"].each { |f| require f }
13
+
14
+ RSpec.configure do |config|
15
+
16
+ end
@@ -0,0 +1,9 @@
1
+ RSpec::Matchers.define :be_base58_check do |version_byte|
2
+ match do |actual|
3
+ begin
4
+ Stellar::Util::Base58.stellar.check_decode(version_byte, actual)
5
+ rescue ArgumentError
6
+ false
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ RSpec::Matchers.define :eq_bytes do |expected|
2
+ match do |actual|
3
+ expected.force_encoding("ASCII-8BIT") == actual.force_encoding("ASCII-8BIT")
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ RSpec::Matchers.define :have_length do |length|
2
+ match do |actual|
3
+ actual.length == length
4
+ end
5
+ end
data/tasks/rspec.rake ADDED
@@ -0,0 +1,6 @@
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+ RSpec::Core::RakeTask.new(:spec)
4
+ task :default => :spec
5
+ rescue LoadError
6
+ end
data/tasks/travis.rake ADDED
@@ -0,0 +1,9 @@
1
+ file "lib/libsodium.so" => :build_libsodium do
2
+ cp $LIBSODIUM_PATH, "lib/libsodium.so"
3
+ end
4
+
5
+ task "ci:sodium" => "lib/libsodium.so"
6
+
7
+ task :travis => %w(ci:sodium spec)
8
+
9
+ CLEAN.add "lib/libsodium.*"