stellar-base 0.0.2 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: be220a6f1b2170167a2d882bbe31bc1ea1b663fd
4
- data.tar.gz: 3078d50d5aa5eebfce79687c3ed7dc99f56a2920
3
+ metadata.gz: 52b90527d5fbb20ee588ab94b7c0d86d84f66fce
4
+ data.tar.gz: 1258a60de1ae91d5c3e1cba5ef19c1ae49aa7e6b
5
5
  SHA512:
6
- metadata.gz: 27a122fd0f53a8d0b3d187017f56a6ae712c10b9d51721c65c97fdee55f3fc37c33bc0910fbe62edb8216ec2390237747d2ac473b7488976081918b1163dcdfa
7
- data.tar.gz: d87e8a4bd376b25cb66c8c517403769e1ba3ac95944d473c7725b4fb5ccd3f072fe814ede7ce926fda1291b23ac867e8d2a41b812db1ab81cf1bee6ba27ed69d
6
+ metadata.gz: 26926d0eeaedd8e4bbd453e232c7b72fe507a03f095f43c6afd2c052f32d7b5f5e10ca239294665a32cb8f9c0802fd43932de33398c4e5258bd1f66a80343521
7
+ data.tar.gz: a242c1f440a7ab985e48b9a4482a765c2cfd66dc25d2dc0966aebe7e4492d6822a7e178a50235e6f88d38e5cc4645dd46390ae262d1dffd6a610a61b120d0328
data/.gitignore CHANGED
@@ -12,3 +12,4 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
+ .DS_Store
data/lib/stellar-base.rb CHANGED
@@ -14,12 +14,13 @@ Stellar.load_all!
14
14
 
15
15
  # extensions onto the generated files must be loaded manually, below
16
16
 
17
- require_relative './stellar/change_trust_op'
18
- require_relative './stellar/create_offer_op'
17
+ require_relative './stellar/account_flags'
19
18
  require_relative './stellar/currency'
20
19
  require_relative './stellar/key_pair'
20
+ require_relative './stellar/operation'
21
21
  require_relative './stellar/payment_op'
22
22
  require_relative './stellar/price'
23
23
  require_relative './stellar/transaction'
24
24
  require_relative './stellar/transaction_envelope'
25
- require_relative './stellar/util/base58'
25
+ require_relative './stellar/util/base58'
26
+ require_relative './stellar/util/continued_fraction'
@@ -0,0 +1,19 @@
1
+ module Stellar
2
+ class AccountFlags
3
+
4
+
5
+ #
6
+ # Converts an array of Stellar::AccountFlags members into
7
+ # an Integer suitable for use in a SetOptionsOp.
8
+ #
9
+ # @param flags=nil [Array<Stellar::AccountFlags>] the flags to combine
10
+ #
11
+ # @return [Fixnum] the combined result
12
+ def self.make_mask(flags=nil)
13
+ flags ||= []
14
+
15
+ flags.map(&:value).inject(&:|) || 0
16
+ end
17
+
18
+ end
19
+ end
@@ -1,5 +1,5 @@
1
1
  module Stellar
2
2
  module Base
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  end
@@ -0,0 +1,206 @@
1
+ module Stellar
2
+ class Operation
3
+
4
+
5
+ #
6
+ # Construct a new Stellar::Operation from the provided
7
+ # source account and body
8
+ #
9
+ # @param [Hash] attributes the attributes to create the operation with
10
+ # @option attributes [Stellar::KeyPair] :source_account
11
+ # @option attributes [Stellar::Operation::Body] :body
12
+ #
13
+ # @return [Stellar::Operation] the built operation
14
+ def self.make(attributes={})
15
+ source_account = attributes[:source_account]
16
+ body = Stellar::Operation::Body.new(*attributes[:body])
17
+
18
+ op = Stellar::Operation.new(body:body)
19
+
20
+ if source_account
21
+ raise ArgumentError, "Bad :source_account" unless source_account.is_a?(Stellar::KeyPair)
22
+ op.source_account = source_account.public_key
23
+ end
24
+
25
+ return op
26
+ end
27
+
28
+
29
+ #
30
+ # Helper method to create a valid PaymentOp, wrapped
31
+ # in the necessary XDR structs to be included within a
32
+ # transactions `operations` array.
33
+ #
34
+ # @see Stellar::Currency
35
+ #
36
+ # @param [Hash] attributes the attributes to create the operation with
37
+ # @option attributes [Stellar::KeyPair] :destination the receiver of the payment
38
+ # @option attributes [Array] :amount the amount to pay
39
+ # @option attributes [Array<Stellar::Currency>] :path the payment path to use
40
+ #
41
+ # @return [Stellar::Operation] the built operation, containing a
42
+ # Stellar::PaymentOp body
43
+ def self.payment(attributes={})
44
+ destination = attributes[:destination]
45
+ amount = attributes[:amount]
46
+ path = attributes[:path] || []
47
+ path = path.map{|p| Stellar::Currency.send(*p)}
48
+
49
+ raise ArgumentError unless destination.is_a?(KeyPair)
50
+
51
+ op = PaymentOp.send(*amount)
52
+ op.destination = destination.public_key
53
+ op.path = path
54
+ op.apply_defaults
55
+
56
+ return make(attributes.merge({
57
+ body:[:payment, op]
58
+ }))
59
+ end
60
+
61
+ #
62
+ # Helper method to create a valid ChangeTrustOp, wrapped
63
+ # in the necessary XDR structs to be included within a
64
+ # transactions `operations` array.
65
+ #
66
+ # @param [Hash] attributes the attributes to create the operation with
67
+ # @option attributes [Stellar::Currrency] :line the currency to trust
68
+ # @option attributes [Fixnum] :limit the maximum amount to trust
69
+ #
70
+ # @return [Stellar::Operation] the built operation, containing a
71
+ # Stellar::ChangeTrustOp body
72
+ def self.change_trust(attributes={})
73
+ line = Currency.send(*attributes[:line])
74
+ limit = attributes[:limit]
75
+
76
+ raise ArgumentError, "Bad :limit #{limit}" unless limit.is_a?(Integer)
77
+
78
+ op = ChangeTrustOp.new(line: line, limit: limit)
79
+
80
+ return make(attributes.merge({
81
+ body:[:change_trust, op]
82
+ }))
83
+ end
84
+
85
+ def self.create_offer(attributes={})
86
+ taker_pays = Currency.send(*attributes[:taker_pays])
87
+ taker_gets = Currency.send(*attributes[:taker_gets])
88
+ amount = attributes[:amount]
89
+ offer_id = attributes[:offer_id] || 0
90
+ price = Price.from_f(attributes[:price])
91
+
92
+ op = CreateOfferOp.new({
93
+ taker_pays: taker_pays,
94
+ taker_gets: taker_gets,
95
+ amount: amount,
96
+ price: price,
97
+ offer_id: offer_id
98
+ })
99
+
100
+ return make(attributes.merge({
101
+ body:[:create_offer, op]
102
+ }))
103
+ end
104
+
105
+ #
106
+ # Helper method to create a valid SetOptionsOp, wrapped
107
+ # in the necessary XDR structs to be included within a
108
+ # transactions `operations` array.
109
+ #
110
+ # @param [Hash] attributes the attributes to create the operation with
111
+ # @option attributes [Stellar::KeyPair] :inflation_dest
112
+ # @option attributes [Array<Stellar::AccountFlags>] :set flags to set
113
+ # @option attributes [Array<Stellar::AccountFlags>] :clear flags to clear
114
+ # @option attributes [String] :thresholds
115
+ # @option attributes [Stellar::Signer] :signer
116
+ #
117
+ # @return [Stellar::Operation] the built operation, containing a
118
+ # Stellar::SetOptionsOp body
119
+ def self.set_options(attributes={})
120
+ op = SetOptionsOp.new()
121
+ op.set_flags = Stellar::AccountFlags.make_mask attributes[:set]
122
+ op.clear_flags = Stellar::AccountFlags.make_mask attributes[:clear]
123
+ op.thresholds = attributes[:thresholds]
124
+ op.signer = attributes[:signer]
125
+
126
+
127
+ inflation_dest = attributes[:inflation_dest]
128
+ if inflation_dest
129
+ raise ArgumentError, "Bad :inflation_dest" unless inflation_dest.is_a?(Stellar::KeyPair)
130
+ op.inflation_dest = inflation_dest.public_key
131
+ end
132
+
133
+
134
+ return make(attributes.merge({
135
+ body:[:set_options, op]
136
+ }))
137
+ end
138
+
139
+ #
140
+ # Helper method to create a valid AllowTrustOp, wrapped
141
+ # in the necessary XDR structs to be included within a
142
+ # transactions `operations` array.
143
+ #
144
+ # @param [Hash] attributes the attributes to create the operation with
145
+ # @option attributes [Stellar::KeyPair] :trustor
146
+ # @option attributes [Stellar::Currency] :currency
147
+ #
148
+ # @return [Stellar::Operation] the built operation, containing a
149
+ # Stellar::AllowTrustOp body
150
+ def self.allow_trust(attributes={})
151
+ op = AllowTrustOp.new()
152
+
153
+ trustor = attributes[:trustor]
154
+ authorize = attributes[:authorize]
155
+ currency = Currency.send(*attributes[:currency])
156
+
157
+ raise ArgumentError, "Bad :trustor" unless trustor.is_a?(Stellar::KeyPair)
158
+ raise ArgumentError, "Bad :authorize" unless authorize == !!authorize # check boolean
159
+ raise ArgumentError, "Bad :currency" unless currency.type == Stellar::CurrencyType.iso4217
160
+
161
+ op.trustor = trustor.public_key
162
+ op.authorize = authorize
163
+ op.currency = currency
164
+
165
+ return make(attributes.merge({
166
+ body:[:allow_trust, op]
167
+ }))
168
+ end
169
+
170
+ #
171
+ # Helper method to create an account merge operation
172
+ #
173
+ # @param [Hash] attributes the attributes to create the operation with
174
+ # @option attributes [Stellar::KeyPair] :destination
175
+ #
176
+ # @return [Stellar::Operation] the built operation
177
+ def self.account_merge(attributes={})
178
+ destination = attributes[:destination]
179
+
180
+ raise ArgumentError, "Bad :destination" unless destination.is_a?(KeyPair)
181
+
182
+ # TODO: add source_account support
183
+ return make(attributes.merge({
184
+ body:[:account_merge, destination.public_key]
185
+ }))
186
+ end
187
+
188
+ #
189
+ # Helper method to create an inflation operation
190
+ #
191
+ # @param [Hash] attributes the attributes to create the operation with
192
+ # @option attributes [Integer] :sequence
193
+ #
194
+ # @return [Stellar::Operation] the built operation
195
+ def self.inflation(attributes={})
196
+ sequence = attributes[:sequence]
197
+
198
+ raise ArgumentError, "Bad :sequence #{sequence}" unless sequence.is_a?(Integer)
199
+
200
+ # TODO: add source_account support
201
+ return make(attributes.merge({
202
+ body:[:inflation, sequence]
203
+ }))
204
+ end
205
+ end
206
+ end
@@ -1,5 +1,5 @@
1
1
  module Stellar
2
- PaymentOp.class_eval do
2
+ class PaymentOp
3
3
 
4
4
  def self.native(amount)
5
5
  currency = Stellar::Currency.new(:native)
@@ -29,10 +29,5 @@ module Stellar
29
29
  self.memo ||= ""
30
30
  end
31
31
 
32
- def to_operation(source_account=nil)
33
- body = Operation::Body.new(:payment, self)
34
- Operation.new(source_account: source_account, body:body)
35
- end
36
-
37
32
  end
38
33
  end
data/lib/stellar/price.rb CHANGED
@@ -1,22 +1,15 @@
1
1
  module Stellar
2
- Price.class_eval do
3
- def self.from_f(number)
4
- return new(n:0,d:0) if number == 0.0
5
-
6
- inverted = number > 0.0
7
2
 
8
- # normalize
9
- number = 1.0 / number if inverted
3
+ # reopen class
4
+ class Price
10
5
 
11
- # fractionalize
12
- r = number.to_r
13
- n = r.numerator
14
- d = r.denominator
6
+ MAX_PRECISION = (2**31) - 1
15
7
 
16
- # pricify
8
+ def self.from_f(number)
9
+ best_r = Util::ContinuedFraction.best_r(number, MAX_PRECISION)
17
10
  new({
18
- n:inverted ? d : n,
19
- d:inverted ? n : d,
11
+ n: best_r.numerator,
12
+ d: best_r.denominator
20
13
  })
21
14
  end
22
15
 
@@ -1,64 +1,88 @@
1
1
  module Stellar
2
2
  Transaction.class_eval do
3
3
 
4
+ #
5
+ # @see Stellar::Operation.payment
4
6
  def self.payment(attributes={})
5
- destination = attributes[:destination]
6
- amount = attributes[:amount]
7
- path = attributes[:path] || []
8
- path = path.map{|p| Stellar::Currency.send(*p)}
9
-
10
- raise ArgumentError unless destination.is_a?(KeyPair)
11
-
12
- for_account(attributes).tap do |result|
13
- payment = PaymentOp.send(*amount)
14
- payment.destination = destination.public_key
15
- payment.path = path
16
- payment.apply_defaults
17
-
18
- result.operations = [payment.to_operation]
19
- end
7
+ make :payment, attributes
20
8
  end
21
9
 
10
+ #
11
+ # @see Stellar::Operation.change_trust
22
12
  def self.change_trust(attributes={})
23
- line = Currency.send(*attributes[:line])
24
- limit = attributes[:limit]
13
+ make :change_trust, attributes
14
+ end
25
15
 
26
- raise ArgumentError, "Bad :limit #{limit}" unless limit.is_a?(Integer)
16
+ #
17
+ # @see Stellar::Operation.create_offer
18
+ def self.create_offer(attributes={})
19
+ make :create_offer, attributes
20
+ end
27
21
 
28
- for_account(attributes).tap do |result|
29
- details = ChangeTrustOp.new(line: line, limit: limit)
30
- result.operations = [details.to_operation]
31
- end
22
+ #
23
+ # @see Stellar::Operation.set_options
24
+ def self.set_options(attributes={})
25
+ make :set_options, attributes
32
26
  end
33
27
 
34
- def self.create_offer(attributes={})
35
- taker_pays = Currency.send(*attributes[:taker_pays])
36
- taker_gets = Currency.send(*attributes[:taker_gets])
37
- amount = attributes[:amount]
38
- offer_id = attributes[:offer_id] || 0
39
- price = Price.from_f(attributes[:price])
28
+ #
29
+ # @see Stellar::Operation.allow_trust
30
+ def self.allow_trust(attributes={})
31
+ make :allow_trust, attributes
32
+ end
33
+
34
+ #
35
+ # @see Stellar::Operation.account_merge
36
+ def self.account_merge(attributes={})
37
+ make :account_merge, attributes
38
+ end
39
+
40
+ #
41
+ # @see Stellar::Operation.inflation
42
+ def self.inflation(attributes={})
43
+ make :inflation, attributes
44
+ end
40
45
 
46
+ #
47
+ # Helper method to create a transaction with a single
48
+ # operation of the provided type. See class methods
49
+ # on Stellar::Operation for available values for
50
+ # operation_type.
51
+ #
52
+ # @see Stellar::Operation
53
+ #
54
+ # @param operation_type [Symbol] the operation to use
55
+ # @param attributes={} [Hash] attributes to use for both the transaction and the operation
56
+ #
57
+ # @return [Stellar::Transaction] the resulting transaction
58
+ def self.make(operation_type, attributes={})
41
59
  for_account(attributes).tap do |result|
42
- details = CreateOfferOp.new({
43
- taker_pays: taker_pays,
44
- taker_gets: taker_gets,
45
- amount: amount,
46
- price: price,
47
- offer_id: offer_id
48
- })
49
- result.operations = [details.to_operation]
60
+ result.operations = [Operation.send(operation_type, attributes)]
50
61
  end
51
62
  end
52
63
 
64
+
65
+ #
66
+ # Helper method to create the skeleton of a transaction.
67
+ # The resulting transaction will have its source account,
68
+ # sequence, fee, min ledger and max ledger set.
69
+ #
70
+ #
71
+ # @param attributes={} [type] [description]
72
+ #
73
+ # @return [Stellar::Transaction] the resulting skeleton
53
74
  def self.for_account(attributes={})
54
- account = attributes[:account]
55
- sequence = attributes[:sequence]
75
+ account = attributes[:account]
76
+ sequence = attributes[:sequence]
77
+ max_fee = attributes[:max_fee]
56
78
 
57
79
  raise ArgumentError, "Bad :account" unless account.is_a?(KeyPair) && account.sign?
58
80
  raise ArgumentError, "Bad :sequence #{sequence}" unless sequence.is_a?(Integer)
81
+ raise ArgumentError, "Bad :max_fee #{sequence}" if max_fee.present? && !max_fee.is_a?(Integer)
59
82
 
60
83
  new.tap do |result|
61
84
  result.seq_num = sequence
85
+ result.max_fee = max_fee
62
86
  result.source_account = account.public_key
63
87
  result.apply_defaults
64
88
  end
@@ -0,0 +1,96 @@
1
+ module Stellar
2
+ module Util
3
+ class ContinuedFraction
4
+ MAX_PRECISION = (2**32) - 1
5
+ attr_reader :i
6
+ attr_reader :f
7
+
8
+ def self.best_r(number, max_precision=MAX_PRECISION)
9
+ cur_cf = new(number)
10
+
11
+ loop do
12
+ next_cf = cur_cf.extend()
13
+ cur_r = cur_cf.to_r(max_precision)
14
+ next_r = next_cf.to_r(max_precision)
15
+
16
+ break cur_r if cur_cf.done? || cur_r == next_r
17
+
18
+ cur_cf = next_cf
19
+ end
20
+
21
+ cur_cf.to_r(max_precision)
22
+ end
23
+
24
+ def initialize(val, parents=[])
25
+ @i = val.floor
26
+ @f = val - @i
27
+ @parents = parents
28
+ end
29
+
30
+ def to_a
31
+ @parents + [i]
32
+ end
33
+
34
+ def error(actual)
35
+ (actual - to_f).abs
36
+ end
37
+
38
+ def to_f
39
+ convergent = convergents.last
40
+ convergent.n / convergent.d.to_f
41
+ end
42
+
43
+ def convergents
44
+ return @convergents if defined? @convergents
45
+
46
+ c = [Fraction.new(0,1), Fraction.new(1,0)]
47
+ to_a.each_with_index do |a, i|
48
+ i = i + 2
49
+
50
+ h = a * c[i-1].n + c[i-2].n
51
+ k = a * c[i-1].d + c[i-2].d
52
+ c << Fraction.new(h,k)
53
+ end
54
+
55
+ @converegents = c[2..-1]
56
+ end
57
+
58
+ def to_r(max_precision=MAX_PRECISION)
59
+ fraction = convergents.take_while do |c|
60
+ c.n <= max_precision && c.d <= max_precision
61
+ end.last
62
+
63
+ Rational(fraction.n, fraction.d)
64
+ end
65
+
66
+ def done?
67
+ @f == 0
68
+ end
69
+
70
+ def extend(count=1)
71
+ result = self
72
+
73
+ count.times do
74
+ break if result.done?
75
+ result = ContinuedFraction.new(1 / result.f, result.to_a)
76
+ end
77
+
78
+ result
79
+ end
80
+
81
+ class Fraction
82
+ attr_reader :n
83
+ attr_reader :d
84
+
85
+ def initialize(n,d)
86
+ @n = n
87
+ @d = d
88
+ end
89
+
90
+ def to_r
91
+ Rational(@n, @d)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ describe Stellar::Price, "#from_f" do
4
+ subject{ Stellar::Price }
5
+ let(:seed){ 225571644875421139403973254661022579608 } #generated using Random.new
6
+ let(:random){ Random.new(seed) }
7
+ let(:iterations){ ENV["SMOKE_ITERATIONS"].present? ? ENV["SMOKE_ITERATIONS"].to_i : 2000}
8
+
9
+ it "withstands a random smoke test" do
10
+ iterations.times do |i|
11
+ expected = random.rand
12
+ actual_p = subject.from_f(expected)
13
+ actual = actual_p.to_f
14
+
15
+ expect(actual).to be_within(0.000000001).of(actual)
16
+ expect(actual_p.n).to be <= Stellar::Price::MAX_PRECISION
17
+ expect(actual_p.d).to be <= Stellar::Price::MAX_PRECISION
18
+ end
19
+ end
20
+
21
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stellar-base
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Fleckenstein
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-27 00:00:00.000000000 Z
11
+ date: 2015-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: xdr
@@ -279,19 +279,21 @@ files:
279
279
  - generated/stellar/transaction_set.rb
280
280
  - generated/stellar/trust_line_entry.rb
281
281
  - lib/stellar-base.rb
282
+ - lib/stellar/account_flags.rb
282
283
  - lib/stellar/base.rb
283
284
  - lib/stellar/base/version.rb
284
- - lib/stellar/change_trust_op.rb
285
- - lib/stellar/create_offer_op.rb
286
285
  - lib/stellar/currency.rb
287
286
  - lib/stellar/key_pair.rb
287
+ - lib/stellar/operation.rb
288
288
  - lib/stellar/payment_op.rb
289
289
  - lib/stellar/price.rb
290
290
  - lib/stellar/transaction.rb
291
291
  - lib/stellar/transaction_envelope.rb
292
292
  - lib/stellar/util/base58.rb
293
+ - lib/stellar/util/continued_fraction.rb
293
294
  - ruby-stellar-base.gemspec
294
295
  - spec/lib/stellar/key_pair_spec.rb
296
+ - spec/lib/stellar/price_spec.rb
295
297
  - spec/lib/stellar/transaction_envelope_spec.rb
296
298
  - spec/lib/stellar/transaction_spec.rb
297
299
  - spec/lib/stellar/util/base58_spec.rb
@@ -336,6 +338,7 @@ specification_version: 4
336
338
  summary: 'Stellar client library: XDR'
337
339
  test_files:
338
340
  - spec/lib/stellar/key_pair_spec.rb
341
+ - spec/lib/stellar/price_spec.rb
339
342
  - spec/lib/stellar/transaction_envelope_spec.rb
340
343
  - spec/lib/stellar/transaction_spec.rb
341
344
  - spec/lib/stellar/util/base58_spec.rb
@@ -1,10 +0,0 @@
1
- module Stellar
2
- ChangeTrustOp.class_eval do
3
-
4
- def to_operation(source_account=nil)
5
- body = Operation::Body.new(:change_trust, self)
6
- Operation.new(source_account: source_account, body:body)
7
- end
8
-
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- module Stellar
2
- CreateOfferOp.class_eval do
3
-
4
- def to_operation(source_account=nil)
5
- body = Operation::Body.new(:create_offer, self)
6
- Operation.new(source_account: source_account, body:body)
7
- end
8
-
9
- end
10
- end