stellar-base 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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.*"