xrbp 0.2.2 → 0.2.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/examples/nodestore1.rb +3 -1
  3. data/lib/xrbp/common.rb +6 -0
  4. data/lib/xrbp/core_ext.rb +21 -0
  5. data/lib/xrbp/nodestore/format.rb +76 -26
  6. data/lib/xrbp/nodestore/ledger.rb +46 -14
  7. data/lib/xrbp/nodestore/parser.rb +47 -14
  8. data/lib/xrbp/nodestore/protocol/indexes.rb +11 -8
  9. data/lib/xrbp/nodestore/protocol/issue.rb +15 -0
  10. data/lib/xrbp/nodestore/protocol/rate.rb +13 -1
  11. data/lib/xrbp/nodestore/shamap/node_factory.rb +6 -1
  12. data/lib/xrbp/nodestore/shamap/node_id.rb +2 -2
  13. data/lib/xrbp/nodestore/sle/st_amount.rb +46 -181
  14. data/lib/xrbp/nodestore/sle/st_amount_arithmatic.rb +126 -0
  15. data/lib/xrbp/nodestore/sle/st_amount_comparison.rb +49 -0
  16. data/lib/xrbp/nodestore/sle/st_amount_conversion.rb +203 -0
  17. data/lib/xrbp/nodestore/sqldb.rb +69 -4
  18. data/lib/xrbp/overlay/handshake.rb +1 -1
  19. data/lib/xrbp/version.rb +1 -1
  20. data/spec/xrbp/crypto/account_spec.rb +7 -2
  21. data/spec/xrbp/nodestore/amendments_spec.rb +11 -0
  22. data/spec/xrbp/nodestore/db_parser.rb +64 -1
  23. data/spec/xrbp/nodestore/fees_spec.rb +3 -0
  24. data/spec/xrbp/nodestore/ledger_access.rb +87 -2
  25. data/spec/xrbp/nodestore/protocol/indexes_spec.rb +43 -0
  26. data/spec/xrbp/nodestore/protocol/rate_spec.rb +12 -0
  27. data/spec/xrbp/nodestore/shamap/inner_node_spec.rb +44 -0
  28. data/spec/xrbp/nodestore/shamap/node_factory_spec.rb +9 -0
  29. data/spec/xrbp/nodestore/shamap/node_id_spec.rb +6 -0
  30. data/spec/xrbp/nodestore/shamap/node_spec.rb +25 -0
  31. data/spec/xrbp/nodestore/shamap_spec.rb +144 -0
  32. data/spec/xrbp/nodestore/sle/st_amount_arithmatic_spec.rb +7 -0
  33. data/spec/xrbp/nodestore/sle/st_amount_comparison_spec.rb +11 -0
  34. data/spec/xrbp/nodestore/sle/st_amount_conversion_spec.rb +64 -0
  35. data/spec/xrbp/nodestore/sle/st_amount_spec.rb +47 -0
  36. data/spec/xrbp/nodestore/sle/st_ledger_entry_spec.rb +5 -0
  37. data/spec/xrbp/nodestore/sle/st_object_spec.rb +29 -0
  38. metadata +20 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdd7b83c144366a77c0a68287e2ae511fa2878ef5b3be0e0f24b0b4d9e6a1be5
4
- data.tar.gz: 1db244c92cefaca23244dd7a20cbb89c0d9e2fc25ab10659483aaa8187f0f117
3
+ metadata.gz: 90ae30e5619f27c7a1f40b891a0e84b12f2de86bec369391d6e6f39f9a02bc89
4
+ data.tar.gz: 68365c498a34d0e4c7c3c805b59718f867fa46a6ab2784eab012b22726361212
5
5
  SHA512:
6
- metadata.gz: 42cdd0d6b998b3550a89233b90c86676fe9e56f7a4981cfad96f209fb3272ae2f669f5b16de399318596d13fb9c0fc2b087fa968155a4f30f211fed46cce74c4
7
- data.tar.gz: ee8e543e9e04557f8e8355dbd4f56a19f9176ba8e3f24c87db128298f38caa0386bf3763180bee734c94f7e91aaba3341c4689b4f7d4a53b3d4d0fa49d22b6d9
6
+ metadata.gz: e0660fab2c5f8248577df45b795d5d6794b03dbb33e88e566ec2e08f477147e74dc92d1703155dc152e103956ac586c38d6e1ad4b02fcd52594f9307f4d97c13
7
+ data.tar.gz: 3d49ec2789f104a194d6f5b6471cf40c3d0fc51695694157f2c79d189e21fe38ec3a94b2d8cbda9594a87f611f7b3440c5c3f0f9987d12a67782cf22f82c7d0e
@@ -21,4 +21,6 @@ puts nledger.order_book iou1, iou2
21
21
  puts nledger.txs
22
22
 
23
23
  require 'xrbp/nodestore/sqldb'
24
- puts XRBP::NodeStore::SQLDB.new("/var/lib/rippled/").ledger_hash_for_seq(49340234)
24
+ sql = XRBP::NodeStore::SQLDB.new("/var/lib/rippled/nudb")
25
+ puts sql.ledgers.hash_for_seq(49340234)
26
+ puts sql.ledgers.count
@@ -13,4 +13,10 @@ module XRBP
13
13
  return nil if xrp_time.nil?
14
14
  Time.at(xrp_time + 946684800)
15
15
  end
16
+
17
+ # Convert local time to XRP Time
18
+ def self.to_xrp_time(local_time)
19
+ return nil if local_time.nil?
20
+ local_time.to_i - 946684800
21
+ end
16
22
  end # module XRBP
@@ -59,6 +59,20 @@ class Integer
59
59
  end
60
60
  b
61
61
  end
62
+
63
+ def byte_string
64
+ bytes.reverse.pack("C*")
65
+ end
66
+
67
+ def to_int32
68
+ self & (2**32-1)
69
+ end
70
+
71
+ alias :to_int :to_int32
72
+
73
+ def from_xrp_time
74
+ XRBP.from_xrp_time(self)
75
+ end
62
76
  end
63
77
 
64
78
  # @private
@@ -71,3 +85,10 @@ class Array
71
85
  fill(x, length...n)
72
86
  end
73
87
  end
88
+
89
+ # @private
90
+ class Time
91
+ def to_xrp_time
92
+ XRBP.to_xrp_time(self)
93
+ end
94
+ end
@@ -383,32 +383,82 @@ module XRBP
383
383
  ###
384
384
 
385
385
  TX_TYPES = {
386
- -1 => :invalid,
387
- 0 => :payment,
388
- 1 => :escrow_create,
389
- 2 => :escrow_finish,
390
- 3 => :account_set,
391
- 4 => :escrow_cancel,
392
- 5 => :regular_key_set,
393
- 6 => :nickname_set, #open
394
- 7 => :offer_create,
395
- 8 => :offer_cancel,
396
-
397
- 9 => :no_longer_used,
398
-
399
- 11 => :ticket_create,
400
- 12 => :ticket_cancel,
401
- 13 => :signer_list_set,
402
- 14 => :paychan_create,
403
- 15 => :paychan_fund,
404
- 16 => :paychan_claim,
405
- 17 => :check_create,
406
- 18 => :check_cash,
407
- 19 => :check_cancel,
408
- 20 => :deposit_preauth,
409
-
410
- 100 => :amendment,
411
- 101 => :fee
386
+ -1 => :Invalid,
387
+ 0 => :Payment,
388
+ 1 => :EscrowCreate,
389
+ 2 => :EscrowFinish,
390
+ 3 => :AccountSet,
391
+ 4 => :EscrowCancel,
392
+ 5 => :SetRegularKey,
393
+ 6 => :NickNameSet, #open
394
+ 7 => :OfferCreate,
395
+ 8 => :OfferCancel,
396
+
397
+ 9 => :unused,
398
+
399
+ 11 => :TicketCreate,
400
+ 12 => :TicketCancel,
401
+ 13 => :SignerListSet,
402
+ 14 => :PaymentChannelCreate,
403
+ 15 => :PaymentChannelFund,
404
+ 16 => :PaymentChannelClaim,
405
+ 17 => :CheckCreate,
406
+ 18 => :CheckCash,
407
+ 19 => :CheckCancel,
408
+ 20 => :DepositPreauth,
409
+
410
+ 100 => :EnableAmendment,
411
+ 101 => :SetFee
412
+ }
413
+
414
+ # https://xrpl.org/transaction-results.html
415
+ TX_RESULTS = {
416
+ # tec
417
+ # https://xrpl.org/tec-codes.html
418
+ # "Transaction failed, but it was applied to a ledger to apply the transaction cost.
419
+ # They have numerical values in the range 100 to 199"
420
+ 100 => :tecCLAIM,
421
+ 146 => :tecCRYPTOCONDITION_ERROR,
422
+ 121 => :tecDIR_FULL,
423
+ 149 => :tecDUPLICATE,
424
+ 143 => :tecDST_TAG_NEEDED,
425
+ 148 => :tecEXPIRED,
426
+ 105 => :tecFAILED_PROCESSING,
427
+ 137 => :tecFROZEN,
428
+ 122 => :tecINSUF_RESERVE_LINE,
429
+ 123 => :tecINSUF_RESERVE_OFFER,
430
+ 141 => :tecINSUFFICIENT_RESERVE,
431
+ 144 => :tecINTERNAL,
432
+ 147 => :tecINVARIANT_FAILED,
433
+ 142 => :tecNEED_MASTER_KEY,
434
+ 130 => :tecNO_ALTERNATIVE_KEY,
435
+ 134 => :tecNO_AUTH,
436
+ 124 => :tecNO_DST,
437
+ 125 => :tecNO_DST_INSUF_XRP,
438
+ 140 => :tecNO_ENTRY,
439
+ 133 => :tecNO_ISSUER,
440
+ 150 => :tecKILLED,
441
+ 135 => :tecNO_LINE,
442
+ 126 => :tecNO_LINE_INSUF_RESERVE,
443
+ 127 => :tecNO_LINE_REDUNDANT,
444
+ 139 => :tecNO_PERMISSION,
445
+ 131 => :tecNO_REGULAR_KEY,
446
+ 138 => :tecNO_TARGET,
447
+ 145 => :tecOVERSIZE,
448
+ 132 => :tecOWNERS,
449
+ 128 => :tecPATH_DRY,
450
+ 101 => :tecPATH_PARTIAL,
451
+ 129 => :tecUNFUNDED,
452
+ 102 => :tecUNFUNDED_ADD,
453
+ 104 => :tecUNFUNDED_PAYMENT,
454
+ 103 => :tecUNFUNDED_OFFER,
455
+
456
+ # tef, tel, tem, ter transactions _not_
457
+ # applied to ledger
458
+
459
+ # tes
460
+ # https://xrpl.org/tes-success.html
461
+ 0 => :tesSUCCESS
412
462
  }
413
463
 
414
464
  ###
@@ -43,12 +43,16 @@ module XRBP
43
43
  @fees ||= Fees.new
44
44
  end
45
45
 
46
+ # Returns boolean indicating if specified account
47
+ # is flagged as globally frozen
46
48
  def global_frozen?(account)
47
49
  return false if account == Crypto.xrp_account
48
50
  sle = state_map.read(Indexes::account(account))
49
51
  return sle && sle.flag?(:global_freeze)
50
52
  end
51
53
 
54
+ # Returns boolean indicating if specific account
55
+ # has frozen trust-line for specified IOU
52
56
  def frozen?(account, iou)
53
57
  return false if iou[:currency] == 'XRP'
54
58
 
@@ -62,16 +66,19 @@ module XRBP
62
66
  :low_freeze)
63
67
  end
64
68
 
69
+ # Return IOU balance which owner account holds
65
70
  def account_holds(owner_id, iou)
66
71
  return xrp_liquid(owner_id, 0) if iou[:currency] == 'XRP'
67
72
  sle = state_map.read(Indexes::line(owner_id, iou))
68
73
  return STAmount.zero if !sle || frozen?(owner_id, iou)
69
74
 
70
75
  amount = sle.amount(:balance)
71
- amount.negate! if owner_id > iou[:account]
76
+ amount.negate! if Crypto.account_id(owner_id).to_bn >
77
+ Crypto.account_id(iou[:account]).to_bn
72
78
  balance_hook(amount)
73
79
  end
74
80
 
81
+ # Returns available (liquid) XRP account holds
75
82
  def xrp_liquid(account, owner_count_adj)
76
83
  sle = state_map.read(Indexes::account(account))
77
84
  return STAmount.zero unless sle
@@ -122,8 +129,12 @@ module XRBP
122
129
  count
123
130
  end
124
131
 
132
+ # Return TransferRate configured for IOU
133
+ #
134
+ # @see {Rate}
125
135
  def transfer_rate(issuer)
126
136
  sle = state_map.read(Indexes::account(issuer))
137
+
127
138
  return Rate.new sle.field(:uint32,
128
139
  :transfer_rate) if sle &&
129
140
  sle.field?(:transfer_rate)
@@ -134,6 +145,10 @@ module XRBP
134
145
 
135
146
  public
136
147
 
148
+ # TODO: helper method to get first funded high quailty
149
+ # offer from order book. Also market depth helper
150
+
151
+ # Return all offers for the given input/output currency pair
137
152
  def order_book(input, output)
138
153
  offers = []
139
154
 
@@ -145,6 +160,7 @@ module XRBP
145
160
  global_freeze = global_frozen?(output[:account]) ||
146
161
  global_frozen?(input[:account])
147
162
 
163
+ # transfer rate multipled to offer output to pay issuer
148
164
  rate = transfer_rate(output[:account])
149
165
 
150
166
  balances = {}
@@ -180,28 +196,32 @@ module XRBP
180
196
  # Read offer from db and process
181
197
  sle_offer = state_map.read(offer_index)
182
198
  if sle_offer
199
+ # Direct info from nodestore offer
183
200
  owner_id = sle_offer.account_id(:account)
184
201
  taker_gets = sle_offer.amount(:taker_gets)
185
202
  taker_pays = sle_offer.amount(:taker_pays)
186
203
 
187
- owner_funds = nil
188
- first_owner_offer = true
204
+ # Owner / Output Calculation
205
+ owner_funds = nil # how much of offer output the owner has
206
+ first_owner_offer = true # owner_funds returned w/ first owner offer
189
207
 
208
+ # issuer is offering it's own IOU, fully funded
190
209
  if output[:account] == owner_id
191
- # issuer is offering it's own IOU, fully funded
192
210
  owner_funds = taker_gets
193
211
 
212
+ # all offers not ours are unfunded
194
213
  elsif global_freeze
195
- # all offers not ours are unfunded
196
214
  owner_funds.clear(output)
197
215
 
198
216
  else
217
+ # if we have owner funds cached
199
218
  if balances[owner_id]
200
219
  owner_funds = balances[owner_id]
201
220
  first_owner_offer = false
202
221
 
222
+ # did not find balance in cache
203
223
  else
204
- # did not find balance in table
224
+ # lookup from nodestore
205
225
  owner_funds = account_holds(owner_id, output)
206
226
 
207
227
  # treat negative funds as zero
@@ -209,29 +229,33 @@ module XRBP
209
229
  end
210
230
  end
211
231
 
212
- offer = Hash[sle_offer.fields]
213
- taker_gets_funded = nil
214
- owner_funds_limit = owner_funds
215
- offer_rate = Rate.parity
232
+ offer = Hash[sle_offer.fields] # copy the offer fields to return
233
+ taker_gets_funded = nil # how much offer owner will actually be able to fund
234
+ owner_funds_limit = owner_funds # how much the offer owner has limited by the output transfer fee
235
+ offer_rate = Rate.parity # offer base output transfer rate
216
236
 
237
+ # Check if transfer fee applies,
217
238
  if rate != Rate.parity && # transfer fee
218
239
  # TODO: provide support for 'taker_id' rpc param:
219
240
  #taker_id != output[:account] && # not taking offers of own IOUs
220
241
  output[:account] != owner_id # offer owner not issuing own funds
221
242
  # Need to charge a transfer fee to offer owner.
222
243
  offer_rate = rate
223
- owner_funds_limit = owner_funds / offer_rate.rate
244
+ owner_funds_limit = owner_funds / offer_rate.to_amount
224
245
  end
225
246
 
226
-
247
+ # Check if owner has enough funds to pay it all
227
248
  if owner_funds_limit >= taker_gets
228
249
  # Sufficient funds no shenanigans.
229
250
  taker_gets_funded = taker_gets
230
251
 
231
252
  else
232
- # Only provide, if not fully funded.
253
+ # Only set these fields, if not fully funded.
233
254
  taker_gets_funded = owner_funds_limit
234
255
  offer[:taker_gets_funded] = taker_gets_funded
256
+
257
+ # the account that takes the offer will need to
258
+ # pay the 'gets' amount actually funded times the dir_rate (quality)
235
259
  offer[:taker_pays_funded] = [taker_pays,
236
260
  taker_gets_funded *
237
261
  dir_rate].min
@@ -240,13 +264,21 @@ module XRBP
240
264
  offer[:taker_pays_funded].issue = taker_pays.issue
241
265
  end
242
266
 
267
+ # Calculate how much owner will pay after this offer,
268
+ # if no transfer fee, then the amount funded,
269
+ # else the minimum of what the owner has or the
270
+ # amount funded w/ transfer fee
243
271
  owner_pays = (Rate.parity == offer_rate) ?
244
272
  taker_gets_funded :
245
273
  [owner_funds,
246
- taker_gets_funded * offer_rate].min
274
+ taker_gets_funded *
275
+ offer_rate.to_amount].min
247
276
 
277
+ # Update balance cache w/ new owner balance
248
278
  balances[owner_id] = owner_funds - owner_pays
249
279
 
280
+ # Set additional params and store the offer
281
+
250
282
  # include all offers funded and unfunded
251
283
  offer[:quality] = dir_rate
252
284
  offer[:owner_funds] = owner_funds if first_owner_offer
@@ -157,6 +157,9 @@ module XRBP
157
157
  elsif e == :transaction_type
158
158
  return Format::TX_TYPES[value]
159
159
 
160
+ elsif e == :transaction_result
161
+ return Format::TX_RESULTS[value]
162
+
160
163
  elsif e == :ledger_entry_type
161
164
  return Format::LEDGER_ENTRY_TYPE_CODES[value.chr]
162
165
  end
@@ -189,22 +192,39 @@ module XRBP
189
192
 
190
193
  # Parse 'Amount' data type from binary data.
191
194
  #
195
+ # Stored internally in 64 bits:
196
+ # - Bit 1 = 0 for XRP, 1 for IOU
197
+ # - Bit 2 = sign, 0 for negative, 1 for positive
198
+ #
199
+ # For XRP:
200
+ # - Remaining bits = value in drops
201
+ #
202
+ # For IOU:
203
+ # - Next 8 bits = Exponent
204
+ # - Next 54 bits = Mantissa
205
+ # Where value = mantissa * (10 ^ exponent)
206
+ # Note: 97 is added to exponent when serializing and subtracted when parsing so
207
+ # that effective nominal exponent is always in range of -96 to 80
208
+ #
192
209
  # @see https://developers.ripple.com/currency-formats.html
210
+ # @see STAmount(SerialIter& sit, SField const& name);
211
+ # @see {STAmount#from_wire}
193
212
  #
194
213
  # @protected
195
214
  def parse_amount(data)
196
215
  amount = data[0..7].unpack("Q>").first
197
- xrp = amount < 0x8000000000000000
198
-
199
- # FIXME : is sign/neg right (?)
200
216
 
201
- return STAmount.new(:issue => NodeStore.xrp_issue,
202
- :mantissa => amount & 0x3FFFFFFFFFFFFFFF), data[8..-1] if xrp
217
+ # native eg, xrp
218
+ is_xrp = (amount & STAmount::NOT_NATIVE) == 0
219
+ if is_xrp
220
+ is_pos = (amount & STAmount::POS_NATIVE) != 0
221
+ return STAmount.new(:issue => NodeStore.xrp_issue,
222
+ :mantissa => amount & ~STAmount::POS_NATIVE), data[8..-1] if is_pos
203
223
 
204
- sign = (amount & 0x4000000000000000) >> 62 # 0 = neg / 1 = pos
205
- neg = (sign == 0)
206
- exp = (amount & 0x3FC0000000000000) >> 54
207
- mant = (amount & 0x003FFFFFFFFFFFFF)
224
+ return STAmount.new(:issue => NodeStore.xrp_issue,
225
+ :mantissa => amount,
226
+ :neg => true), data[8..-1]
227
+ end
208
228
 
209
229
  data = data[8..-1]
210
230
  currency = Format::CURRENCY_CODE.decode(data)
@@ -213,12 +233,25 @@ module XRBP
213
233
  data = data[Format::CURRENCY_CODE.size..-1]
214
234
  issuer, data = parse_account(data, 20)
215
235
 
216
- sle = STAmount.new(:issue => Issue.new(currency, issuer),
217
- :neg => neg,
218
- :mantissa => mant,
219
- :exponent => exp)
236
+ issue = Issue.new(currency, issuer)
237
+
238
+ exp = (amount >> (64 - 10)).to_int
239
+ mant = (amount & ~(1023 << (64 - 10)))
240
+
241
+ if mant
242
+ neg = (exp & 256) == 0
243
+ exp = (exp & 255) - 97
244
+
245
+ sle = STAmount.new(:issue => issue,
246
+ :neg => neg,
247
+ :mantissa => mant,
248
+ :exponent => exp)
249
+
250
+ return sle, data
251
+ end
220
252
 
221
- return sle, data
253
+ raise unless exp == 512
254
+ return STAmount.new(:issue => issue)
222
255
  end
223
256
 
224
257
  # Parse 'Account' data type from binary data.
@@ -4,15 +4,14 @@ module XRBP
4
4
  module Indexes
5
5
 
6
6
  def self.get_quality(base)
7
- # FIXME: reverse to convert big to little endian,
7
+ # FIXME: assuming native platform is big endian,
8
8
  # need to account for all platforms
9
- base[-8..-1].reverse.to_bn
9
+ base[-8..-1].to_bn
10
10
  end
11
11
 
12
12
  def self.get_quality_next(base)
13
13
  nxt = "10000000000000000".to_i(16)
14
- (base.to_bn + nxt).bytes
15
- .reverse.pack("C*")
14
+ (base.to_bn + nxt).byte_string
16
15
  end
17
16
 
18
17
  ###
@@ -45,6 +44,14 @@ module XRBP
45
44
  sha512.digest[0..31]
46
45
  end
47
46
 
47
+ # TODO: Account Owner Dir from id
48
+ def self.owner_dir(id)
49
+ end
50
+
51
+ # TODO: Offer Index for account id and seq
52
+ def self.offer_index(id, seq)
53
+ end
54
+
48
55
  # Trust line for account/iou
49
56
  def self.line(account, iou)
50
57
  account = Crypto.account_id(account)
@@ -68,10 +75,6 @@ module XRBP
68
75
  sha512.digest[0..31]
69
76
  end
70
77
 
71
- # TODO: order book dir hash for ledger
72
- def self.order_book_dir()
73
- end
74
-
75
78
  # Order book index for given input/output
76
79
  def self.order_book(input, output)
77
80
  input = Hash[input]