xrpl-ruby 0.2.4 → 0.5.0
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 +4 -4
- data/lib/address-codec/address_codec.rb +21 -4
- data/lib/address-codec/codec.rb +15 -2
- data/lib/address-codec/xrp_codec.rb +29 -2
- data/lib/binary-codec/binary_codec.rb +46 -22
- data/lib/binary-codec/enums/definitions.json +592 -1
- data/lib/binary-codec/enums/definitions.rb +17 -5
- data/lib/binary-codec/enums/fields.rb +2 -0
- data/lib/binary-codec/serdes/binary_parser.rb +38 -0
- data/lib/binary-codec/serdes/binary_serializer.rb +18 -7
- data/lib/binary-codec/serdes/bytes_list.rb +11 -0
- data/lib/binary-codec/types/account_id.rb +18 -37
- data/lib/binary-codec/types/amount.rb +43 -23
- data/lib/binary-codec/types/blob.rb +14 -5
- data/lib/binary-codec/types/currency.rb +15 -4
- data/lib/binary-codec/types/hash.rb +37 -36
- data/lib/binary-codec/types/issue.rb +50 -0
- data/lib/binary-codec/types/path_set.rb +93 -0
- data/lib/binary-codec/types/serialized_type.rb +52 -28
- data/lib/binary-codec/types/st_array.rb +71 -0
- data/lib/binary-codec/types/st_object.rb +100 -3
- data/lib/binary-codec/types/uint.rb +116 -3
- data/lib/binary-codec/types/vector256.rb +53 -0
- data/lib/binary-codec/types/xchain_bridge.rb +47 -0
- data/lib/binary-codec/utilities.rb +18 -0
- data/lib/core/base_58_xrp.rb +2 -0
- data/lib/core/base_x.rb +10 -0
- data/lib/core/core.rb +44 -6
- data/lib/core/utilities.rb +38 -0
- data/lib/key-pairs/ed25519.rb +64 -0
- data/lib/key-pairs/key_pairs.rb +92 -0
- data/lib/key-pairs/secp256k1.rb +116 -0
- data/lib/wallet/wallet.rb +117 -0
- data/lib/xrpl-ruby.rb +25 -1
- metadata +26 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'json'
|
|
3
|
+
require 'digest'
|
|
3
4
|
|
|
4
5
|
module BinaryCodec
|
|
5
6
|
|
|
@@ -44,19 +45,30 @@ module BinaryCodec
|
|
|
44
45
|
|
|
45
46
|
end
|
|
46
47
|
|
|
48
|
+
# Returns the singleton instance of the Definitions class.
|
|
49
|
+
# @return [Definitions] The singleton instance.
|
|
47
50
|
def self.instance
|
|
48
51
|
@@instance ||= new
|
|
49
52
|
end
|
|
50
53
|
|
|
51
|
-
|
|
54
|
+
# Returns the field header for a given field name.
|
|
55
|
+
# @param field_name [String] The name of the field.
|
|
56
|
+
# @return [FieldHeader] The field header.
|
|
57
|
+
def get_field_header_from_name(field_name)
|
|
52
58
|
@field_header_map[field_name]
|
|
53
59
|
end
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
# Returns the field name for a given field header.
|
|
62
|
+
# @param field_header [FieldHeader] The field header.
|
|
63
|
+
# @return [String] The name of the field.
|
|
64
|
+
def get_field_name_from_header(field_header)
|
|
56
65
|
@field_id_name_map[Digest::MD5.hexdigest(Marshal.dump(field_header))]
|
|
57
66
|
end
|
|
58
67
|
|
|
59
|
-
|
|
68
|
+
# Returns a FieldInstance for a given field name.
|
|
69
|
+
# @param field_name [String] The name of the field.
|
|
70
|
+
# @return [FieldInstance] The field instance.
|
|
71
|
+
def get_field_instance(field_name)
|
|
60
72
|
field_info = @field_info_map[field_name]
|
|
61
73
|
field_header = get_field_header_from_name(field_name)
|
|
62
74
|
|
|
@@ -66,10 +78,10 @@ module BinaryCodec
|
|
|
66
78
|
is_serialized: field_info.is_serialized,
|
|
67
79
|
is_signing_field: field_info.is_signing_field,
|
|
68
80
|
type: field_info.type,
|
|
69
|
-
ordinal: (
|
|
81
|
+
ordinal: (@type_ordinals[field_info.type] << 16) | field_info.nth,
|
|
70
82
|
name: field_name,
|
|
71
83
|
header: field_header,
|
|
72
|
-
associated_type: field_info.type
|
|
84
|
+
associated_type: SerializedType.get_type_by_name(field_info.type)
|
|
73
85
|
)
|
|
74
86
|
end
|
|
75
87
|
|
|
@@ -11,6 +11,8 @@ module BinaryCodec
|
|
|
11
11
|
@definitions = Definitions.instance
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
# Returns the first byte in the stream without consuming it.
|
|
15
|
+
# @return [Integer] The first byte.
|
|
14
16
|
def peek
|
|
15
17
|
if @bytes.empty?
|
|
16
18
|
raise StandardError.new
|
|
@@ -18,6 +20,8 @@ module BinaryCodec
|
|
|
18
20
|
@bytes[0]
|
|
19
21
|
end
|
|
20
22
|
|
|
23
|
+
# Consumes n bytes from the stream.
|
|
24
|
+
# @param n [Integer] The number of bytes to skip.
|
|
21
25
|
def skip(n)
|
|
22
26
|
if n > @bytes.length
|
|
23
27
|
raise StandardError.new
|
|
@@ -25,6 +29,9 @@ module BinaryCodec
|
|
|
25
29
|
@bytes = @bytes[n..-1]
|
|
26
30
|
end
|
|
27
31
|
|
|
32
|
+
# Reads n bytes from the stream.
|
|
33
|
+
# @param n [Integer] The number of bytes to read.
|
|
34
|
+
# @return [Array<Integer>] The read bytes.
|
|
28
35
|
def read(n)
|
|
29
36
|
if n > @bytes.length
|
|
30
37
|
raise StandardError.new('End of byte stream reached')
|
|
@@ -35,6 +42,9 @@ module BinaryCodec
|
|
|
35
42
|
slice
|
|
36
43
|
end
|
|
37
44
|
|
|
45
|
+
# Reads n bytes and converts them to an unsigned integer.
|
|
46
|
+
# @param n [Integer] The number of bytes to read (1-4).
|
|
47
|
+
# @return [Integer] The resulting integer.
|
|
38
48
|
def read_uint_n(n)
|
|
39
49
|
if n <= 0 || n > 4
|
|
40
50
|
raise StandardError.new('invalid n')
|
|
@@ -42,31 +52,46 @@ module BinaryCodec
|
|
|
42
52
|
read(n).reduce(0) { |a, b| (a << 8) | b }
|
|
43
53
|
end
|
|
44
54
|
|
|
55
|
+
# Reads a 1-byte unsigned integer.
|
|
56
|
+
# @return [Integer] The 8-bit integer.
|
|
45
57
|
def read_uint8
|
|
46
58
|
read_uint_n(1)
|
|
47
59
|
end
|
|
48
60
|
|
|
61
|
+
# Reads a 2-byte unsigned integer.
|
|
62
|
+
# @return [Integer] The 16-bit integer.
|
|
49
63
|
def read_uint16
|
|
50
64
|
read_uint_n(2)
|
|
51
65
|
end
|
|
52
66
|
|
|
67
|
+
# Reads a 4-byte unsigned integer.
|
|
68
|
+
# @return [Integer] The 32-bit integer.
|
|
53
69
|
def read_uint32
|
|
54
70
|
read_uint_n(4)
|
|
55
71
|
end
|
|
56
72
|
|
|
73
|
+
# Returns the number of bytes remaining in the stream.
|
|
74
|
+
# @return [Integer] The remaining size.
|
|
57
75
|
def size
|
|
58
76
|
@bytes.length
|
|
59
77
|
end
|
|
60
78
|
|
|
79
|
+
# Checks if the end of the stream has been reached.
|
|
80
|
+
# @param custom_end [Integer, nil] Optional offset to check against.
|
|
81
|
+
# @return [Boolean] True if at the end, false otherwise.
|
|
61
82
|
def end?(custom_end = nil)
|
|
62
83
|
length = @bytes.length
|
|
63
84
|
length == 0 || (!custom_end.nil? && length <= custom_end)
|
|
64
85
|
end
|
|
65
86
|
|
|
87
|
+
# Reads variable length data from the stream.
|
|
88
|
+
# @return [Array<Integer>] The read bytes.
|
|
66
89
|
def read_variable_length
|
|
67
90
|
read(read_variable_length_length)
|
|
68
91
|
end
|
|
69
92
|
|
|
93
|
+
# Reads the length of a variable length data segment.
|
|
94
|
+
# @return [Integer] The length.
|
|
70
95
|
def read_variable_length_length
|
|
71
96
|
b1 = read_uint8
|
|
72
97
|
if b1 <= 192
|
|
@@ -83,6 +108,8 @@ module BinaryCodec
|
|
|
83
108
|
end
|
|
84
109
|
end
|
|
85
110
|
|
|
111
|
+
# Reads a field header from the stream.
|
|
112
|
+
# @return [FieldHeader] The field header.
|
|
86
113
|
def read_field_header
|
|
87
114
|
type = read_uint8
|
|
88
115
|
nth = type & 15
|
|
@@ -105,6 +132,8 @@ module BinaryCodec
|
|
|
105
132
|
FieldHeader.new(type: type, nth: nth) # (type << 16) | nth for read_field_ordinal
|
|
106
133
|
end
|
|
107
134
|
|
|
135
|
+
# Reads a field instance from the stream.
|
|
136
|
+
# @return [FieldInstance] The field instance.
|
|
108
137
|
def read_field
|
|
109
138
|
field_header = read_field_header
|
|
110
139
|
field_name = @definitions.get_field_name_from_header(field_header)
|
|
@@ -112,14 +141,23 @@ module BinaryCodec
|
|
|
112
141
|
@definitions.get_field_instance(field_name)
|
|
113
142
|
end
|
|
114
143
|
|
|
144
|
+
# Reads a value of the specified type from the stream.
|
|
145
|
+
# @param type [Class] The class of the type to read (subclass of SerializedType).
|
|
146
|
+
# @return [SerializedType] The read value.
|
|
115
147
|
def read_type(type)
|
|
116
148
|
type.from_parser(self)
|
|
117
149
|
end
|
|
118
150
|
|
|
151
|
+
# Returns the associated type for a given field.
|
|
152
|
+
# @param field [FieldInstance] The field instance.
|
|
153
|
+
# @return [Class] The associated SerializedType subclass.
|
|
119
154
|
def type_for_field(field)
|
|
120
155
|
field.associated_type
|
|
121
156
|
end
|
|
122
157
|
|
|
158
|
+
# Reads the value of a specific field from the stream.
|
|
159
|
+
# @param field [FieldInstance] The field to read.
|
|
160
|
+
# @return [SerializedType] The read value.
|
|
123
161
|
def read_field_value(field)
|
|
124
162
|
type = SerializedType.get_type_by_name(field.type)
|
|
125
163
|
|
|
@@ -7,30 +7,38 @@ module BinaryCodec
|
|
|
7
7
|
@sink = sink || BytesList.new
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
# Serializes a value into the sink.
|
|
11
|
+
# @param value [SerializedType] The value to write.
|
|
10
12
|
def write(value)
|
|
11
13
|
value.to_byte_sink(@sink)
|
|
12
14
|
end
|
|
13
15
|
|
|
16
|
+
# Adds raw bytes to the sink.
|
|
17
|
+
# @param bytes [Array<Integer>] The bytes to add.
|
|
14
18
|
def put(bytes)
|
|
15
19
|
@sink.put(bytes)
|
|
16
20
|
end
|
|
17
21
|
|
|
22
|
+
# Serializes a value of a given type.
|
|
23
|
+
# @param type [Class] The class of the type (subclass of SerializedType).
|
|
24
|
+
# @param value [Object] The value to serialize.
|
|
18
25
|
def write_type(type, value)
|
|
19
26
|
write(type.from(value))
|
|
20
27
|
end
|
|
21
28
|
|
|
29
|
+
# Writes a BytesList into the sink.
|
|
30
|
+
# @param bytes_list [BytesList] The bytes list to write.
|
|
22
31
|
def write_bytes_list(bytes_list)
|
|
23
32
|
bytes_list.to_byte_sink(@sink)
|
|
24
33
|
end
|
|
25
34
|
|
|
35
|
+
# Writes a field and its value into the sink.
|
|
36
|
+
# @param field [FieldInstance] The field to write.
|
|
37
|
+
# @param value [Object] The value of the field.
|
|
38
|
+
# @param is_unl_modify_workaround [Boolean] Whether to apply the UNLModify workaround.
|
|
26
39
|
def write_field_and_value(field, value, is_unl_modify_workaround = false)
|
|
27
|
-
field_header =
|
|
28
|
-
|
|
29
|
-
associated_value = type_class.from(value)
|
|
30
|
-
|
|
31
|
-
if !associated_value.respond_to?(:to_byte_sink) || field.name.nil?
|
|
32
|
-
raise 'Error'
|
|
33
|
-
end
|
|
40
|
+
field_header = field.header
|
|
41
|
+
associated_value = field.associated_type.from(value)
|
|
34
42
|
|
|
35
43
|
@sink.put(field_header.to_bytes)
|
|
36
44
|
|
|
@@ -41,6 +49,9 @@ module BinaryCodec
|
|
|
41
49
|
end
|
|
42
50
|
end
|
|
43
51
|
|
|
52
|
+
# Writes a value with its length encoded prefix.
|
|
53
|
+
# @param value [SerializedType] The value to write.
|
|
54
|
+
# @param is_unl_modify_workaround [Boolean] Whether to apply the UNLModify workaround.
|
|
44
55
|
def write_length_encoded(value, is_unl_modify_workaround = false)
|
|
45
56
|
bytes = BytesList.new
|
|
46
57
|
|
|
@@ -9,24 +9,35 @@ module BinaryCodec
|
|
|
9
9
|
@bytes_array = []
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
# Returns the total length of all bytes in the list.
|
|
13
|
+
# @return [Integer] The total length.
|
|
12
14
|
def get_length
|
|
13
15
|
@bytes_array.inject(0) { |sum, arr| sum + arr.length }
|
|
14
16
|
end
|
|
15
17
|
|
|
18
|
+
# Adds bytes to the list.
|
|
19
|
+
# @param bytes_arg [Array<Integer>] The bytes to add.
|
|
20
|
+
# @return [BytesList] self for chaining.
|
|
16
21
|
def put(bytes_arg)
|
|
17
22
|
bytes = bytes_arg.dup
|
|
18
23
|
@bytes_array << bytes
|
|
19
24
|
self # Allow chaining
|
|
20
25
|
end
|
|
21
26
|
|
|
27
|
+
# Puts the bytes into another byte sink.
|
|
28
|
+
# @param list [Object] The sink to put bytes into.
|
|
22
29
|
def to_byte_sink(list)
|
|
23
30
|
list.put(to_bytes)
|
|
24
31
|
end
|
|
25
32
|
|
|
33
|
+
# Returns all bytes as a single flat array.
|
|
34
|
+
# @return [Array<Integer>] The flattened byte array.
|
|
26
35
|
def to_bytes
|
|
27
36
|
@bytes_array.flatten # TODO: Uses concat in xrpl.js, maybe implement that instead
|
|
28
37
|
end
|
|
29
38
|
|
|
39
|
+
# Returns the hex representation of all bytes in the list.
|
|
40
|
+
# @return [String] The hex string.
|
|
30
41
|
def to_hex
|
|
31
42
|
bytes_to_hex(to_bytes)
|
|
32
43
|
end
|
|
@@ -1,38 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
require 'address-codec/xrp_codec'
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
module BinaryCodec
|
|
5
4
|
class AccountId < Hash160
|
|
6
|
-
|
|
7
|
-
# Create a single instance of AddressCodec for use in static functions
|
|
8
|
-
@address_codec = AddressCodec::AddressCodec.new
|
|
9
|
-
|
|
10
|
-
attr_reader :bytes
|
|
11
|
-
|
|
12
5
|
@width = 20
|
|
13
6
|
|
|
14
7
|
def initialize(bytes = nil)
|
|
15
|
-
super(bytes
|
|
8
|
+
super(bytes)
|
|
16
9
|
end
|
|
17
10
|
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
# Defines how to construct an AccountID
|
|
23
|
-
#
|
|
24
|
-
# @param value [AccountID, String] Either an existing AccountID, a hex string, or a base58 r-Address
|
|
25
|
-
# @return [AccountID] An AccountID object
|
|
11
|
+
# Creates a new AccountId instance from a value.
|
|
12
|
+
# @param value [AccountId, String] The value to convert (hex or base58 address).
|
|
13
|
+
# @return [AccountId] The created instance.
|
|
26
14
|
def self.from(value)
|
|
27
|
-
if value.is_a?(AccountId)
|
|
28
|
-
return value
|
|
29
|
-
end
|
|
15
|
+
return value if value.is_a?(AccountId)
|
|
30
16
|
|
|
31
17
|
if value.is_a?(String)
|
|
32
|
-
return
|
|
18
|
+
return new if value.empty?
|
|
33
19
|
|
|
34
20
|
if valid_hex?(value)
|
|
35
|
-
return
|
|
21
|
+
return new(hex_to_bytes(value))
|
|
36
22
|
else
|
|
37
23
|
return from_base58(value)
|
|
38
24
|
end
|
|
@@ -41,13 +27,13 @@ module BinaryCodec
|
|
|
41
27
|
raise 'Cannot construct AccountID from the value provided'
|
|
42
28
|
end
|
|
43
29
|
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# @
|
|
47
|
-
# @return [AccountID] An AccountID object
|
|
30
|
+
# Creates an AccountId instance from a base58 address.
|
|
31
|
+
# @param value [String] The classic or X-address.
|
|
32
|
+
# @return [AccountId] The created instance.
|
|
48
33
|
def self.from_base58(value)
|
|
49
|
-
|
|
50
|
-
|
|
34
|
+
address_codec = AddressCodec::AddressCodec.new
|
|
35
|
+
if address_codec.valid_x_address?(value)
|
|
36
|
+
classic = address_codec.x_address_to_classic_address(value)
|
|
51
37
|
|
|
52
38
|
if classic[:tag] != false
|
|
53
39
|
raise 'Only allowed to have tag on Account or Destination'
|
|
@@ -56,24 +42,19 @@ module BinaryCodec
|
|
|
56
42
|
value = classic[:classic_address]
|
|
57
43
|
end
|
|
58
44
|
|
|
59
|
-
|
|
45
|
+
new(address_codec.decode_account_id(value))
|
|
60
46
|
end
|
|
61
47
|
|
|
62
|
-
|
|
63
|
-
#
|
|
64
|
-
# @return [String] The base58 string for this AccountID
|
|
65
|
-
def to_json
|
|
48
|
+
def to_json(_definitions = nil, _field_name = nil)
|
|
66
49
|
to_base58
|
|
67
50
|
end
|
|
68
51
|
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
# @return [String] The base58 string defined by this.bytes
|
|
52
|
+
# Returns the base58 representation of the account ID.
|
|
53
|
+
# @return [String] The base58 address.
|
|
72
54
|
def to_base58
|
|
73
55
|
address_codec = AddressCodec::AddressCodec.new
|
|
74
56
|
address_codec.encode_account_id(@bytes)
|
|
75
57
|
end
|
|
76
|
-
|
|
77
58
|
end
|
|
78
59
|
|
|
79
60
|
end
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
require 'bigdecimal'
|
|
4
4
|
require 'bigdecimal/util'
|
|
5
5
|
|
|
6
|
-
require_relative '../utilities'
|
|
7
|
-
|
|
8
6
|
module BinaryCodec
|
|
9
7
|
class Amount < SerializedType
|
|
10
8
|
|
|
@@ -31,6 +29,9 @@ module BinaryCodec
|
|
|
31
29
|
#
|
|
32
30
|
# @param value [Amount, Hash, String] representing the amount
|
|
33
31
|
# @return [Amount] an Amount object
|
|
32
|
+
# Creates a new Amount instance from a value.
|
|
33
|
+
# @param value [Amount, String, Hash, Integer] The value to convert.
|
|
34
|
+
# @return [Amount] The created instance.
|
|
34
35
|
def self.from(value)
|
|
35
36
|
return value if value.is_a?(Amount)
|
|
36
37
|
|
|
@@ -93,8 +94,12 @@ module BinaryCodec
|
|
|
93
94
|
# Read an amount from a BinaryParser
|
|
94
95
|
#
|
|
95
96
|
# @param parser [BinaryParser] The BinaryParser to read the Amount from
|
|
96
|
-
# @return [Amount] An Amount object
|
|
97
|
-
|
|
97
|
+
# @return [Amount] An Amount bundle exec rspec spec/binary-codec/types/st_object_spec.rb object
|
|
98
|
+
# Creates an Amount instance from a parser.
|
|
99
|
+
# @param parser [BinaryParser] The parser to read from.
|
|
100
|
+
# @param _size_hint [Integer, nil] Optional size hint (unused).
|
|
101
|
+
# @return [Amount] The created instance.
|
|
102
|
+
def self.from_parser(parser, _size_hint = nil)
|
|
98
103
|
is_iou = parser.peek & 0x80 != 0
|
|
99
104
|
return Amount.new(parser.read(48)) if is_iou
|
|
100
105
|
|
|
@@ -107,9 +112,13 @@ module BinaryCodec
|
|
|
107
112
|
# The JSON representation of this Amount
|
|
108
113
|
#
|
|
109
114
|
# @return [Hash, String] The JSON interpretation of this.bytes
|
|
110
|
-
|
|
115
|
+
# Returns the JSON representation of the Amount.
|
|
116
|
+
# @param _definitions [Definitions, nil] Unused.
|
|
117
|
+
# @param _field_name [String, nil] Optional field name.
|
|
118
|
+
# @return [String, Hash] The JSON representation.
|
|
119
|
+
def to_json(_definitions = nil, _field_name = nil)
|
|
111
120
|
if is_native?
|
|
112
|
-
bytes = @bytes.dup
|
|
121
|
+
bytes = @bytes.dup
|
|
113
122
|
is_positive = (bytes[0] & 0x40) != 0
|
|
114
123
|
sign = is_positive ? '' : '-'
|
|
115
124
|
bytes[0] &= 0x3f
|
|
@@ -122,47 +131,52 @@ module BinaryCodec
|
|
|
122
131
|
end
|
|
123
132
|
|
|
124
133
|
if is_iou?
|
|
125
|
-
parser = BinaryParser.new(
|
|
126
|
-
|
|
134
|
+
parser = BinaryParser.new(to_hex)
|
|
135
|
+
mantissa_bytes = parser.read(8)
|
|
127
136
|
currency = Currency.from_parser(parser)
|
|
128
137
|
issuer = AccountId.from_parser(parser)
|
|
129
138
|
|
|
130
|
-
b1 =
|
|
131
|
-
b2 =
|
|
139
|
+
b1 = mantissa_bytes[0]
|
|
140
|
+
b2 = mantissa_bytes[1]
|
|
132
141
|
|
|
133
142
|
is_positive = (b1 & 0x40) != 0
|
|
134
143
|
sign = is_positive ? '' : '-'
|
|
135
144
|
exponent = ((b1 & 0x3f) << 2) + ((b2 & 0xff) >> 6) - 97
|
|
136
145
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
146
|
+
mantissa_bytes[0] = 0
|
|
147
|
+
mantissa_bytes[1] &= 0x3f
|
|
148
|
+
|
|
149
|
+
# Convert mantissa bytes to integer
|
|
150
|
+
mantissa_int = mantissa_bytes.reduce(0) { |acc, b| (acc << 8) + b }
|
|
151
|
+
|
|
152
|
+
value = BigDecimal(mantissa_int) * (BigDecimal(10)**exponent)
|
|
153
|
+
value = -value unless is_positive
|
|
140
154
|
self.class.assert_iou_is_valid(value)
|
|
141
155
|
|
|
142
156
|
return {
|
|
143
|
-
"value" => value.to_s('F'),
|
|
157
|
+
"value" => value.to_s('F').sub(/\.0$/, ''),
|
|
144
158
|
"currency" => currency.to_json,
|
|
145
159
|
"issuer" => issuer.to_json
|
|
146
|
-
}
|
|
160
|
+
}
|
|
147
161
|
end
|
|
148
162
|
|
|
149
163
|
if is_mpt?
|
|
150
|
-
parser = BinaryParser.new(
|
|
164
|
+
parser = BinaryParser.new(to_hex)
|
|
151
165
|
leading_byte = parser.read(1)
|
|
152
|
-
|
|
166
|
+
amount_bytes = parser.read(8)
|
|
153
167
|
mpt_id = Hash192.from_parser(parser)
|
|
154
168
|
|
|
155
169
|
is_positive = (leading_byte[0] & 0x40) != 0
|
|
156
170
|
sign = is_positive ? '' : '-'
|
|
157
171
|
|
|
158
|
-
msb = read_uint32be(
|
|
159
|
-
lsb = read_uint32be(
|
|
172
|
+
msb = BinaryCodec.read_uint32be(amount_bytes[0, 4])
|
|
173
|
+
lsb = BinaryCodec.read_uint32be(amount_bytes[4, 4])
|
|
160
174
|
num = (msb << 32) | lsb
|
|
161
175
|
|
|
162
176
|
return {
|
|
163
|
-
value
|
|
164
|
-
mpt_issuance_id
|
|
165
|
-
}
|
|
177
|
+
"value" => "#{sign}#{num}",
|
|
178
|
+
"mpt_issuance_id" => mpt_id.to_hex
|
|
179
|
+
}
|
|
166
180
|
end
|
|
167
181
|
|
|
168
182
|
raise 'Invalid amount to construct JSON'
|
|
@@ -247,7 +261,7 @@ module BinaryCodec
|
|
|
247
261
|
# Ensure that the value, after being multiplied by the exponent, does not
|
|
248
262
|
# contain a decimal. This function is typically used to validate numbers
|
|
249
263
|
# that need to be represented as precise integers after scaling, such as
|
|
250
|
-
# amounts in financial transactions.
|
|
264
|
+
# amounts in financial transactions. Example failure:1.1234567891234567
|
|
251
265
|
#
|
|
252
266
|
# @param decimal [BigDecimal] A BigDecimal object
|
|
253
267
|
# @raise [ArgumentError] if the value contains a decimal
|
|
@@ -262,6 +276,8 @@ module BinaryCodec
|
|
|
262
276
|
# Check if this amount is in units of Native Currency (XRP)
|
|
263
277
|
#
|
|
264
278
|
# @return [Boolean] true if Native (XRP)
|
|
279
|
+
# Checks if the amount is a native XRP amount.
|
|
280
|
+
# @return [Boolean] True if native, false otherwise.
|
|
265
281
|
def is_native?
|
|
266
282
|
(self.bytes[0] & 0x80).zero? && (self.bytes[0] & 0x20).zero?
|
|
267
283
|
end
|
|
@@ -269,6 +285,8 @@ module BinaryCodec
|
|
|
269
285
|
# Check if this amount is in units of MPT
|
|
270
286
|
#
|
|
271
287
|
# @return [Boolean] true if MPT
|
|
288
|
+
# Checks if the amount is a multi-purpose token (MPT).
|
|
289
|
+
# @return [Boolean] True if MPT, false otherwise.
|
|
272
290
|
def is_mpt?
|
|
273
291
|
(self.bytes[0] & 0x80).zero? && (self.bytes[0] & 0x20) != 0
|
|
274
292
|
end
|
|
@@ -276,6 +294,8 @@ module BinaryCodec
|
|
|
276
294
|
# Check if this amount is in units of IOU
|
|
277
295
|
#
|
|
278
296
|
# @return [Boolean] true if IOU
|
|
297
|
+
# Checks if the amount is an IOU amount.
|
|
298
|
+
# @return [Boolean] True if IOU, false otherwise.
|
|
279
299
|
def is_iou?
|
|
280
300
|
(self.bytes[0] & 0x80) != 0
|
|
281
301
|
end
|
|
@@ -1,29 +1,38 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '../../core/core'
|
|
4
|
-
|
|
5
3
|
module BinaryCodec
|
|
6
4
|
class Blob < SerializedType
|
|
7
5
|
|
|
8
6
|
def initialize(byte_buf = nil)
|
|
9
|
-
|
|
7
|
+
super(byte_buf || [])
|
|
10
8
|
end
|
|
11
9
|
|
|
10
|
+
# Creates a new Blob instance from a value.
|
|
11
|
+
# @param value [Blob, String, Array<Integer>] The value to convert.
|
|
12
|
+
# @return [Blob] The created instance.
|
|
12
13
|
def self.from(value)
|
|
13
14
|
return value if value.is_a?(Blob)
|
|
14
15
|
|
|
15
16
|
if value.is_a?(String)
|
|
16
|
-
|
|
17
|
+
unless valid_hex?(value)
|
|
17
18
|
raise StandardError, 'Cannot construct Blob from a non-hex string'
|
|
18
19
|
end
|
|
19
20
|
return Blob.new(hex_to_bytes(value))
|
|
20
21
|
end
|
|
21
22
|
|
|
23
|
+
if value.is_a?(Array)
|
|
24
|
+
return Blob.new(value)
|
|
25
|
+
end
|
|
26
|
+
|
|
22
27
|
raise StandardError, 'Cannot construct Blob from value given'
|
|
23
28
|
end
|
|
24
29
|
|
|
30
|
+
# Creates a Blob instance from a parser.
|
|
31
|
+
# @param parser [BinaryParser] The parser to read from.
|
|
32
|
+
# @param hint [Integer, nil] Optional width hint.
|
|
33
|
+
# @return [Blob] The created instance.
|
|
25
34
|
def self.from_parser(parser, hint = nil)
|
|
26
|
-
|
|
35
|
+
new(parser.read(hint))
|
|
27
36
|
end
|
|
28
37
|
|
|
29
38
|
end
|
|
@@ -15,8 +15,8 @@ module BinaryCodec
|
|
|
15
15
|
@width = 20
|
|
16
16
|
|
|
17
17
|
def initialize(byte_buf = nil)
|
|
18
|
-
super(byte_buf
|
|
19
|
-
hex =
|
|
18
|
+
super(byte_buf)
|
|
19
|
+
hex = to_hex
|
|
20
20
|
|
|
21
21
|
if XRP_HEX_REGEX.match?(hex)
|
|
22
22
|
@_iso = 'XRP'
|
|
@@ -27,20 +27,31 @@ module BinaryCodec
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# Returns the ISO code of the currency, if applicable.
|
|
31
|
+
# @return [String, nil] The ISO code.
|
|
30
32
|
def iso
|
|
31
33
|
@_iso
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
# Creates a new Currency instance from a value.
|
|
37
|
+
# @param value [Currency, String, Array<Integer>] The value to convert.
|
|
38
|
+
# @return [Currency] The created instance.
|
|
34
39
|
def self.from(value)
|
|
35
40
|
return value if value.is_a?(Currency)
|
|
36
41
|
|
|
37
42
|
if value.is_a?(String)
|
|
38
|
-
return
|
|
43
|
+
return new(bytes_from_representation(value))
|
|
39
44
|
end
|
|
40
45
|
|
|
41
|
-
|
|
46
|
+
if value.is_a?(Array)
|
|
47
|
+
return new(value)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
raise StandardError, "Cannot construct Currency from #{value.class}"
|
|
42
51
|
end
|
|
43
52
|
|
|
53
|
+
# Returns the JSON representation of the currency.
|
|
54
|
+
# @return [String] The ISO code or hex string.
|
|
44
55
|
def to_json
|
|
45
56
|
iso = self.iso
|
|
46
57
|
return iso unless iso.nil?
|