xrbp 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/nodestore1.rb +3 -1
- data/lib/xrbp/common.rb +6 -0
- data/lib/xrbp/core_ext.rb +21 -0
- data/lib/xrbp/nodestore/format.rb +76 -26
- data/lib/xrbp/nodestore/ledger.rb +46 -14
- data/lib/xrbp/nodestore/parser.rb +47 -14
- data/lib/xrbp/nodestore/protocol/indexes.rb +11 -8
- data/lib/xrbp/nodestore/protocol/issue.rb +15 -0
- data/lib/xrbp/nodestore/protocol/rate.rb +13 -1
- data/lib/xrbp/nodestore/shamap/node_factory.rb +6 -1
- data/lib/xrbp/nodestore/shamap/node_id.rb +2 -2
- data/lib/xrbp/nodestore/sle/st_amount.rb +46 -181
- data/lib/xrbp/nodestore/sle/st_amount_arithmatic.rb +126 -0
- data/lib/xrbp/nodestore/sle/st_amount_comparison.rb +49 -0
- data/lib/xrbp/nodestore/sle/st_amount_conversion.rb +203 -0
- data/lib/xrbp/nodestore/sqldb.rb +69 -4
- data/lib/xrbp/overlay/handshake.rb +1 -1
- data/lib/xrbp/version.rb +1 -1
- data/spec/xrbp/crypto/account_spec.rb +7 -2
- data/spec/xrbp/nodestore/amendments_spec.rb +11 -0
- data/spec/xrbp/nodestore/db_parser.rb +64 -1
- data/spec/xrbp/nodestore/fees_spec.rb +3 -0
- data/spec/xrbp/nodestore/ledger_access.rb +87 -2
- data/spec/xrbp/nodestore/protocol/indexes_spec.rb +43 -0
- data/spec/xrbp/nodestore/protocol/rate_spec.rb +12 -0
- data/spec/xrbp/nodestore/shamap/inner_node_spec.rb +44 -0
- data/spec/xrbp/nodestore/shamap/node_factory_spec.rb +9 -0
- data/spec/xrbp/nodestore/shamap/node_id_spec.rb +6 -0
- data/spec/xrbp/nodestore/shamap/node_spec.rb +25 -0
- data/spec/xrbp/nodestore/shamap_spec.rb +144 -0
- data/spec/xrbp/nodestore/sle/st_amount_arithmatic_spec.rb +7 -0
- data/spec/xrbp/nodestore/sle/st_amount_comparison_spec.rb +11 -0
- data/spec/xrbp/nodestore/sle/st_amount_conversion_spec.rb +64 -0
- data/spec/xrbp/nodestore/sle/st_amount_spec.rb +47 -0
- data/spec/xrbp/nodestore/sle/st_ledger_entry_spec.rb +5 -0
- data/spec/xrbp/nodestore/sle/st_object_spec.rb +29 -0
- metadata +20 -2
@@ -0,0 +1,49 @@
|
|
1
|
+
module XRBP
|
2
|
+
module NodeStore
|
3
|
+
class STAmount
|
4
|
+
module Comparison
|
5
|
+
def <(o)
|
6
|
+
return self < STAmount.new(:mantissa => o) if o.kind_of?(Numeric)
|
7
|
+
|
8
|
+
return neg if neg && !o.neg
|
9
|
+
if mantissa == 0
|
10
|
+
return false if o.neg
|
11
|
+
return o.mantissa != 0
|
12
|
+
end
|
13
|
+
|
14
|
+
return false if o.mantissa == 0
|
15
|
+
return neg if exponent > o.exponent
|
16
|
+
return !neg if exponent < o.exponent
|
17
|
+
return neg if mantissa > o.mantissa
|
18
|
+
return !neg if mantissa < o.mantissa
|
19
|
+
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
|
23
|
+
def >=(o)
|
24
|
+
!(self < o)
|
25
|
+
end
|
26
|
+
|
27
|
+
def >(o)
|
28
|
+
self >= o && self != o
|
29
|
+
end
|
30
|
+
|
31
|
+
def ==(o)
|
32
|
+
return self == STAmount.new(:mantissa => o) if o.kind_of?(Numeric)
|
33
|
+
|
34
|
+
neg == o.neg &&
|
35
|
+
mantissa == o.mantissa &&
|
36
|
+
exponent == o.exponent
|
37
|
+
end
|
38
|
+
|
39
|
+
def <=>(o)
|
40
|
+
return self <=> STAmount.new(:mantissa => o) if o.kind_of?(Numeric)
|
41
|
+
|
42
|
+
return 0 if self == o
|
43
|
+
return -1 if self < o
|
44
|
+
return 1 if self > o
|
45
|
+
end
|
46
|
+
end # module Comparison
|
47
|
+
end # class STAmount
|
48
|
+
end # module NodeStore
|
49
|
+
end # module XRBP
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module XRBP
|
2
|
+
module NodeStore
|
3
|
+
class STAmount
|
4
|
+
module Conversion
|
5
|
+
module ClassMethods
|
6
|
+
# @see {NodeStore::Parser::parse_amount}
|
7
|
+
def from_wire(data)
|
8
|
+
native = (data & STAmount::NOT_NATIVE) == 0
|
9
|
+
neg = (data & ~STAmount::NOT_NATIVE & STAmount::POS_NATIVE) == 0
|
10
|
+
value = (data & ~STAmount::NOT_NATIVE & ~STAmount::POS_NATIVE)
|
11
|
+
|
12
|
+
if native
|
13
|
+
STAmount.new :issue => NodeStore.xrp_issue,
|
14
|
+
:neg => neg,
|
15
|
+
:mantissa => value
|
16
|
+
else
|
17
|
+
exp = (value >> 54) - 97
|
18
|
+
mant = value & 0x3fffffffffffff
|
19
|
+
STAmount.new :neg => neg,
|
20
|
+
:exponent => exp,
|
21
|
+
:mantissa => mant
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convert string to STAmount
|
26
|
+
#
|
27
|
+
# @see STAmount#amountFromString (in rippled)
|
28
|
+
def parse(str, issue=nil)
|
29
|
+
match = "^"+ # the beginning of the string
|
30
|
+
"([-+]?)"+ # (optional) + or - character
|
31
|
+
"(0|[1-9][0-9]*)"+ # a number (no leading zeroes, unless 0)
|
32
|
+
"(\\.([0-9]+))?"+ # (optional) period followed by any number
|
33
|
+
"([eE]([+-]?)([0-9]+))?"+ # (optional) E, optional + or -, any number
|
34
|
+
"$"
|
35
|
+
match = Regexp.new(match)
|
36
|
+
match = str.match(match)
|
37
|
+
raise "Number '#{str}' is not valid" unless match
|
38
|
+
|
39
|
+
# Match fields:
|
40
|
+
#
|
41
|
+
# 0 = whole input
|
42
|
+
# 1 = sign
|
43
|
+
# 2 = integer portion
|
44
|
+
# 3 = whole fraction (with '.')
|
45
|
+
# 4 = fraction (without '.')
|
46
|
+
# 5 = whole exponent (with 'e')
|
47
|
+
# 6 = exponent sign
|
48
|
+
# 7 = exponent number
|
49
|
+
|
50
|
+
raise "Number '#{str}' is overlong" if ((match[2] || "").length +
|
51
|
+
(match[4] || "").length) > 32
|
52
|
+
|
53
|
+
neg = !!match[1] && match[1] == '-'
|
54
|
+
|
55
|
+
raise "XRP must be specified in integral drops" if issue && issue.xrp? && !!match[3]
|
56
|
+
|
57
|
+
mantissa = 0
|
58
|
+
exponent = 0
|
59
|
+
|
60
|
+
if !match[4]
|
61
|
+
# integral only
|
62
|
+
mantissa = match[2].to_i
|
63
|
+
|
64
|
+
else
|
65
|
+
# integer and fraction
|
66
|
+
mantissa = (match[2] + match[4]).to_i
|
67
|
+
exponent = -(match[4].length)
|
68
|
+
end
|
69
|
+
|
70
|
+
if !!match[5]
|
71
|
+
# exponent
|
72
|
+
if match[6] && match[6] == '-'
|
73
|
+
exponent -= match[7].to_i
|
74
|
+
else
|
75
|
+
exponent += match[7].to_i
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
return STAmount.new :issue => issue,
|
80
|
+
:mantissa => mantissa,
|
81
|
+
:exponent => exponent,
|
82
|
+
:neg => neg
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.included(base)
|
87
|
+
base.extend(ClassMethods)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Encode STAmount into binary format
|
91
|
+
def to_wire
|
92
|
+
xrp_bit = ((native? ? 0 : 1) << 63)
|
93
|
+
neg_bit = (( neg ? 0 : 1) << 62)
|
94
|
+
value_bits = native? ? mantissa :
|
95
|
+
(((exponent+97) << 54) + mantissa)
|
96
|
+
|
97
|
+
xrp_bit + neg_bit + value_bits
|
98
|
+
end
|
99
|
+
|
100
|
+
###
|
101
|
+
|
102
|
+
def to_h
|
103
|
+
{:mantissa => mantissa,
|
104
|
+
:exponent => exponent,
|
105
|
+
:neg => neg,
|
106
|
+
:issue => issue.to_h}
|
107
|
+
end
|
108
|
+
|
109
|
+
def negate!
|
110
|
+
return if zero?
|
111
|
+
@neg = !@neg
|
112
|
+
end
|
113
|
+
|
114
|
+
###
|
115
|
+
|
116
|
+
protected
|
117
|
+
|
118
|
+
def canonicalize
|
119
|
+
if native?
|
120
|
+
if @mantissa == 0
|
121
|
+
@exponent = 0
|
122
|
+
@neg = false
|
123
|
+
return
|
124
|
+
end
|
125
|
+
|
126
|
+
while @exponent < 0
|
127
|
+
@mantissa /= 10
|
128
|
+
@exponent += 1
|
129
|
+
end
|
130
|
+
|
131
|
+
while @exponent > 0
|
132
|
+
@mantissa *= 10
|
133
|
+
@exponent -= 1
|
134
|
+
end
|
135
|
+
|
136
|
+
raise if @mantissa > MAX_NATIVE
|
137
|
+
return
|
138
|
+
end
|
139
|
+
|
140
|
+
if @mantissa == 0
|
141
|
+
@exponent = -100
|
142
|
+
@negative = false
|
143
|
+
return
|
144
|
+
end
|
145
|
+
|
146
|
+
while ((@mantissa < MIN_VAL) && (@exponent > MIN_OFFSET))
|
147
|
+
@mantissa *= 10;
|
148
|
+
@exponent -= 1
|
149
|
+
end
|
150
|
+
|
151
|
+
while (@mantissa > MAX_VAL)
|
152
|
+
raise "value overflow" if (@exponent >= MAX_OFFSET)
|
153
|
+
|
154
|
+
@mantissa /= 10
|
155
|
+
@exponent += 1
|
156
|
+
end
|
157
|
+
|
158
|
+
if @exponent < MIN_OFFSET || @mantissa < MIN_VAL
|
159
|
+
@mantissa = 0;
|
160
|
+
@neg = false;
|
161
|
+
@exponent = -100;
|
162
|
+
return
|
163
|
+
end
|
164
|
+
|
165
|
+
raise "value overflow" if (@exponent > MAX_OFFSET)
|
166
|
+
|
167
|
+
raise unless @mantissa == 0 || (@mantissa >= MIN_VAL && @mantissa <= MAX_VAL)
|
168
|
+
raise unless @mantissa == 0 || (@exponent >= MIN_OFFSET && @exponent <= MAX_OFFSET)
|
169
|
+
raise unless @mantissa != 0 || @exponent != -100
|
170
|
+
end
|
171
|
+
|
172
|
+
public
|
173
|
+
|
174
|
+
def clear
|
175
|
+
# From rippled docs:
|
176
|
+
# The -100 is used to allow 0 to sort less than a small positive values
|
177
|
+
# which have a negative exponent.
|
178
|
+
@exponent = native? ? 0 : -100
|
179
|
+
|
180
|
+
@neg = false
|
181
|
+
@mantissa = 0
|
182
|
+
end
|
183
|
+
|
184
|
+
def sn_value
|
185
|
+
neg ? (-mantissa) : mantissa
|
186
|
+
end
|
187
|
+
|
188
|
+
###
|
189
|
+
|
190
|
+
# In drops!
|
191
|
+
def xrp_amount
|
192
|
+
neg ? (-value) : value
|
193
|
+
end
|
194
|
+
|
195
|
+
alias :drops :xrp_amount
|
196
|
+
|
197
|
+
def iou_amount
|
198
|
+
(neg ? -1 : 1) * mantissa * 10 ** exponent
|
199
|
+
end
|
200
|
+
end # module Conversion
|
201
|
+
end # class STAmount
|
202
|
+
end # module NodeStore
|
203
|
+
end # module XRBP
|
data/lib/xrbp/nodestore/sqldb.rb
CHANGED
@@ -2,22 +2,87 @@ require 'sqlite3'
|
|
2
2
|
|
3
3
|
module XRBP
|
4
4
|
module NodeStore
|
5
|
+
# Wraps sqlite3 database created/maintianed by rippled. Allows client
|
6
|
+
# to query for data stored in sql database.
|
5
7
|
class SQLDB
|
8
|
+
|
9
|
+
# SQL DB intializer
|
10
|
+
#
|
11
|
+
# @param dir [String] directory containing binary nodestore. For consistency
|
12
|
+
# with other nodestore paths this should be set to the directory containing
|
13
|
+
# the actual 'nudb' or 'rocksdb' datafiles, as the sqlite3 databases will be
|
14
|
+
# inferred from the parent directory.
|
6
15
|
def initialize(dir)
|
7
16
|
@dir = dir
|
8
17
|
end
|
9
18
|
|
10
19
|
def ledger_db
|
11
|
-
@ledger_db ||= SQLite3::Database.new File.join(@dir, "ledger.db")
|
20
|
+
@ledger_db ||= SQLite3::Database.new File.join(@dir, "..", "ledger.db")
|
12
21
|
end
|
13
22
|
|
14
23
|
def tx_db
|
15
|
-
@ledger_db ||= SQLite3::Database.new File.join(@dir, "transaction.db")
|
24
|
+
@ledger_db ||= SQLite3::Database.new File.join(@dir, "..", "transaction.db")
|
16
25
|
end
|
17
26
|
|
18
|
-
def
|
19
|
-
|
27
|
+
def ledgers
|
28
|
+
@ledgers ||= Ledgers.new(self)
|
20
29
|
end
|
30
|
+
|
31
|
+
class Ledgers
|
32
|
+
include Enumerable
|
33
|
+
|
34
|
+
def initialize(sql_db)
|
35
|
+
@sql_db = sql_db
|
36
|
+
end
|
37
|
+
|
38
|
+
def between(before, after)
|
39
|
+
@sql_db.ledger_db.execute("select * from ledgers where ClosingTime >= ? and ClosingTime <= ?",
|
40
|
+
before.to_xrp_time,
|
41
|
+
after.to_xrp_time)
|
42
|
+
.collect { |row| from_db(row) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def hash_for_seq(seq)
|
46
|
+
@sql_db.ledger_db.execute("select LedgerHash from ledgers where LedgerSeq = ?", seq).first.first
|
47
|
+
end
|
48
|
+
|
49
|
+
def size
|
50
|
+
@sql_db.ledger_db.execute("select count(*) from ledgers").first.first
|
51
|
+
end
|
52
|
+
|
53
|
+
alias :count :size
|
54
|
+
|
55
|
+
def each
|
56
|
+
all.each do |row|
|
57
|
+
yield row
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def last
|
62
|
+
all.last
|
63
|
+
end
|
64
|
+
|
65
|
+
# TODO: remove memoization, define first(n), last(n) methods
|
66
|
+
def all
|
67
|
+
@all ||= @sql_db.ledger_db.execute("select * from ledgers order by LedgerSeq asc")
|
68
|
+
.collect { |row| from_db(row) }
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def from_db(row)
|
74
|
+
{:hash => row[0],
|
75
|
+
:seq => row[1],
|
76
|
+
:prev_hash => row[2],
|
77
|
+
:total_coins => row[3],
|
78
|
+
:closing_time => row[4].from_xrp_time,
|
79
|
+
:prev_closing_time => row[5].from_xrp_time,
|
80
|
+
:close_time_res => row[6],
|
81
|
+
:close_flags => row[7],
|
82
|
+
:account_set_hash => row[8],
|
83
|
+
:trans_set_hash => row[9]}
|
84
|
+
end
|
85
|
+
end # class Ledgers
|
21
86
|
end # class SQLDB
|
22
87
|
end # module NodeStore
|
23
88
|
end # module XRBP
|
@@ -44,7 +44,7 @@ module XRBP
|
|
44
44
|
sf = sha512.digest(sf)
|
45
45
|
pf = sha512.digest(pf)
|
46
46
|
shared = sf.to_bn ^ pf.to_bn
|
47
|
-
shared = shared.
|
47
|
+
shared = shared.byte_string
|
48
48
|
shared = sha512.digest(shared)[0..31]
|
49
49
|
|
50
50
|
shared = Crypto::Key.sign_digest(node, shared)
|
data/lib/xrbp/version.rb
CHANGED
@@ -1,16 +1,21 @@
|
|
1
1
|
describe XRBP::Crypto do
|
2
2
|
let(:account) { "rDbWJ9C7uExThZYAwV8m6LsZ5YSX3sa6US" }
|
3
|
-
let(:
|
3
|
+
let(:account_id) { 788735140293854337814932116604999410196843811141 }
|
4
|
+
let(:parsed) { [0x8a, 0x28, 0x1b, 0x5e, 0x46, 0xb0, 0x27, 0xc3, 0x70, 0x26, 0xe3, 0x8d, 0xbc, 0x5f, 0x9a, 0xa1, 0x4c, 0x37, 0x51, 0x45].pack("C*") }
|
4
5
|
|
5
6
|
it "generates valid account" do
|
6
7
|
acct = described_class.account
|
7
8
|
expect(described_class.account?(acct[:account])).to be(true)
|
8
9
|
end
|
9
10
|
|
11
|
+
it "returns account id for account" do
|
12
|
+
expect(described_class.account_id(account).to_bn).to eq(account_id)
|
13
|
+
end
|
14
|
+
|
10
15
|
it "parses account" do
|
11
16
|
a = described_class.parse_account(account)
|
12
17
|
expect(a).to_not be_nil
|
13
|
-
expect(a).to eq(
|
18
|
+
expect(a).to eq(parsed)
|
14
19
|
end
|
15
20
|
|
16
21
|
it "does not parse invalid account" do
|
@@ -3,7 +3,70 @@ shared_examples "a database parser" do |opts={}|
|
|
3
3
|
let(:ledger) { {"nt_ledger"=>1, "hp_ledger_master"=>"4c575200", "index"=>47508432, "total_coins"=>18248035087498961665, "parent_hash"=>"C4FAD6EB19F6A6089E7AEBC73AF596DB471305BC0EBB99D9D1414BD4DB8C9D43", "tx_hash"=>"934A5E57C598F0505664F9349DA87FD32AFBE1C315A2CB6CFB5B690AECC6B1A3", "account_hash"=>"FEA269DAAA66C820978AC95F0399ECACA7A3ABAC91402C9FC7961A53D053332F", "parent_close_time"=>Time.parse("2019-05-25T20:12:20Z").utc, "close_time"=>Time.parse("2019-05-25T20:12:21Z").utc, "close_time_resolution"=>10, "close_flags"=>0} }
|
4
4
|
|
5
5
|
let(:tx_key) { ["003b960d5e30ff91a91f7c900509a99a8a6b89441b76158e5ed295ad1d27b0e7"].pack("H*") }
|
6
|
-
let(:tx) { {
|
6
|
+
let(:tx) { {
|
7
|
+
:node=>{
|
8
|
+
:transaction_type=>:Payment,
|
9
|
+
:flags=>2147942400,
|
10
|
+
:sequence=>3366484,
|
11
|
+
:last_ledger_sequence=>47713577,
|
12
|
+
:amount=>XRBP::NodeStore::STAmount.new(:issue => XRBP::NodeStore::Issue.new("XCN", "rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"), :mantissa => 1000000000000000, :exponent => -11),
|
13
|
+
:fee=>XRBP::NodeStore::STAmount.new(:issue => XRBP::NodeStore.xrp_issue, :mantissa => 11),
|
14
|
+
:send_max=>XRBP::NodeStore::STAmount.new(:issue => XRBP::NodeStore.xrp_issue, :mantissa => 10000000000),
|
15
|
+
:signing_pub_key=>"030ac4f2ba6e1ff86beb234b639918dafdf0675032ae264d2b39641503822373fe",
|
16
|
+
:txn_signature=>"304402207ac9c62a6e8a876e67945edebcd3f6c71c36c95fbbb0ce05a85871d0794b1b8c0220297fe7195beb083a78ff9de4d822b5e4cb42be7e5a8662bcbc2f42c737e264bf",
|
17
|
+
:account=>"rKLpjpCoXgLQQYQyj13zgay73rsgmzNH13",
|
18
|
+
:destination=>"rKLpjpCoXgLQQYQyj13zgay73rsgmzNH13",
|
19
|
+
:paths=>[[
|
20
|
+
{:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"},
|
21
|
+
{:currency=>"USD", :issuer=>"rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq"},
|
22
|
+
{:currency=>"\x00\x00\x00"},
|
23
|
+
{:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}
|
24
|
+
],[
|
25
|
+
{:currency=>"USD", :issuer=>"rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq"},
|
26
|
+
{:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"},
|
27
|
+
{:currency=>"\x00\x00\x00"},
|
28
|
+
{:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}
|
29
|
+
],[
|
30
|
+
{:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"},
|
31
|
+
{:currency=>"CNY", :issuer=>"razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA"},
|
32
|
+
{:currency=>"\x00\x00\x00"},
|
33
|
+
{:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}
|
34
|
+
],[
|
35
|
+
{:currency=>"CNY", :issuer=>"razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA"},
|
36
|
+
{:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"},
|
37
|
+
{:currency=>"\x00\x00\x00"},
|
38
|
+
{:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}
|
39
|
+
],[
|
40
|
+
{:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"},
|
41
|
+
{:currency=>"EUR", :issuer=>"rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq"},
|
42
|
+
{:currency=>"\x00\x00\x00"},
|
43
|
+
{:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}
|
44
|
+
],[
|
45
|
+
{:currency=>"EUR", :issuer=>"rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq"},
|
46
|
+
{:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"},
|
47
|
+
{:currency=>"\x00\x00\x00"},
|
48
|
+
{:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}]]
|
49
|
+
},:meta=>{
|
50
|
+
:transaction_index=>30,
|
51
|
+
:affected_nodes=>[{
|
52
|
+
:ledger_entry_type=>:account_root,
|
53
|
+
:previous_txn_lgr_seq=>47713575,
|
54
|
+
:previous_txn_id=>"2e397e516ccde852e829d36eb7d0d195dd9a974a188e213ad256c3ae3c131e58",
|
55
|
+
:ledger_index=>"792ba4e4659c27cf3b63f96b34f158748b081cf532f6746a1e3ebd07acba1a0e",
|
56
|
+
:previous_fields=>{
|
57
|
+
:sequence=>3366484,
|
58
|
+
:balance=>XRBP::NodeStore::STAmount.new(:issue => XRBP::NodeStore.xrp_issue, :mantissa => 1920887273)
|
59
|
+
},:final_fields=>{
|
60
|
+
:flags=>0,
|
61
|
+
:sequence=>3366485,
|
62
|
+
:owner_count=>5,
|
63
|
+
:balance=>XRBP::NodeStore::STAmount.new(:issue => XRBP::NodeStore.xrp_issue, :mantissa => 1920887262),
|
64
|
+
:account=>"rKLpjpCoXgLQQYQyj13zgay73rsgmzNH13"
|
65
|
+
}
|
66
|
+
}],
|
67
|
+
:transaction_result=>:tecPATH_DRY},
|
68
|
+
:index=>"5C28E293E88CCEEFBBAB3EF58C1F8C1C02A5194F42A42349597DDCBC86BFEB0D"
|
69
|
+
} }
|
7
70
|
|
8
71
|
let(:ledger_entry_key) { ["002989b414eff398027fce4045f643d68aca440aeea11518a950550ca02c18dd"].pack("H*") }
|
9
72
|
let(:ledger_entry) { {:type=>:account_root, :index=>"9A34C6D1C46168ECE90EB867C78AB2BECE80FAF5D86D09082017E2C89BD68E56", :fields=>{:flags=>0, :sequence=>2, :previous_txn_lgr_seq=>45017187, :owner_count=>0, :previous_txn_id=>"3133839f2401cc9a719778f53319486d11e7ad7ec7891ac083e2aa7eb924516a", :balance=>XRBP::NodeStore::STAmount.new(:issue => XRBP::NodeStore.xrp_issue, :mantissa => 20000000), :account=>"r4HW4bomLvvzA22dACJwUq95ZRr8ZAcXXs"}} }
|