stellar-base 0.0.2 → 0.0.3

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