xrpl-ruby 0.0.3 → 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 +22 -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 +62 -0
- data/lib/binary-codec/enums/constants.rb +8 -0
- data/lib/binary-codec/enums/definitions.json +3774 -0
- data/lib/binary-codec/enums/definitions.rb +90 -0
- data/lib/binary-codec/enums/fields.rb +104 -0
- data/lib/binary-codec/serdes/binary_parser.rb +183 -0
- data/lib/binary-codec/serdes/binary_serializer.rb +93 -0
- data/lib/binary-codec/serdes/bytes_list.rb +47 -0
- data/lib/binary-codec/types/account_id.rb +60 -0
- data/lib/binary-codec/types/amount.rb +304 -0
- data/lib/binary-codec/types/blob.rb +41 -0
- data/lib/binary-codec/types/currency.rb +116 -0
- data/lib/binary-codec/types/hash.rb +106 -0
- 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 +157 -0
- data/lib/binary-codec/types/st_array.rb +71 -0
- data/lib/binary-codec/types/st_object.rb +157 -0
- data/lib/binary-codec/types/uint.rb +166 -0
- 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 +98 -0
- data/lib/core/base_58_xrp.rb +2 -0
- data/lib/core/base_x.rb +10 -0
- data/lib/core/core.rb +79 -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 +32 -1
- metadata +44 -3
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BinaryCodec
|
|
4
|
+
class SerializedType
|
|
5
|
+
|
|
6
|
+
attr_reader :bytes
|
|
7
|
+
|
|
8
|
+
# Initializes a new SerializedType instance.
|
|
9
|
+
# @param bytes [Array<Integer>, nil] The byte array representing the serialized data.
|
|
10
|
+
def initialize(bytes = nil)
|
|
11
|
+
@bytes = bytes
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Creates a new instance of the type from a value.
|
|
15
|
+
# @param value [Object] The value to convert.
|
|
16
|
+
# @return [SerializedType] The created instance.
|
|
17
|
+
def self.from(value)
|
|
18
|
+
raise NotImplementedError, 'from not implemented'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Creates an instance of the type from a parser.
|
|
22
|
+
# @param parser [BinaryParser] The parser to read from.
|
|
23
|
+
# @param size_hint [Integer, nil] Optional size hint.
|
|
24
|
+
# @return [SerializedType] The created instance.
|
|
25
|
+
def self.from_parser(parser, size_hint = nil)
|
|
26
|
+
raise NotImplementedError, 'from_parser not implemented'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Check if this is needed
|
|
30
|
+
def self.from_json(json)
|
|
31
|
+
raise NotImplementedError, 'from_parser not implemented'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Check if this is needed
|
|
35
|
+
def self.from_hex(hex)
|
|
36
|
+
self.from_bytes(hex_to_bytes(hex))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Check if this is needed
|
|
40
|
+
def self.from_bytes(bytes)
|
|
41
|
+
new(bytes)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Puts the serialized data into a byte sink.
|
|
45
|
+
# @param sink [Object] The sink to put bytes into.
|
|
46
|
+
def to_byte_sink(sink)
|
|
47
|
+
sink.put(to_bytes)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Serialize the given value into bytes
|
|
51
|
+
# This method must be implemented in the subclasses
|
|
52
|
+
# @return [Array<Integer>] - Byte array representing the serialized data
|
|
53
|
+
def to_bytes
|
|
54
|
+
@bytes
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Convert to a hex string
|
|
58
|
+
# @return [String] - Hexadecimal representation of the serialized data
|
|
59
|
+
def to_hex
|
|
60
|
+
bytes_to_hex(to_bytes)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Deserialize instance data and convert it to JSON string
|
|
64
|
+
#
|
|
65
|
+
# @param _definitions [Hash] - Definitions for serialization
|
|
66
|
+
# @param _field_name [String] - Field name for serialization
|
|
67
|
+
# @return [String] - JSON representation of the serialized data
|
|
68
|
+
def to_json(_definitions = nil, _field_name = nil)
|
|
69
|
+
to_hex
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Returns the value of the serialized type
|
|
73
|
+
# @return [Object] - The value of the serialized type
|
|
74
|
+
def value_of
|
|
75
|
+
@bytes
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Returns the class for a given type name.
|
|
79
|
+
# @param name [String] The name of the type.
|
|
80
|
+
# @return [Class] The class associated with the type name.
|
|
81
|
+
def self.get_type_by_name(name)
|
|
82
|
+
case name
|
|
83
|
+
when "AccountID" then AccountId
|
|
84
|
+
when "Amount" then Amount
|
|
85
|
+
when "Blob" then Blob
|
|
86
|
+
when "Currency" then Currency
|
|
87
|
+
when "Hash128" then Hash128
|
|
88
|
+
when "Hash160" then Hash160
|
|
89
|
+
when "Hash192" then Hash192
|
|
90
|
+
when "Hash256" then Hash256
|
|
91
|
+
when "STArray" then STArray
|
|
92
|
+
when "STObject" then STObject
|
|
93
|
+
when "UInt8" then Uint8
|
|
94
|
+
when "UInt16" then Uint16
|
|
95
|
+
when "UInt32" then Uint32
|
|
96
|
+
when "UInt64" then Uint64
|
|
97
|
+
when "UInt96" then Uint96
|
|
98
|
+
when "UInt128" then Uint128
|
|
99
|
+
when "UInt160" then Uint160
|
|
100
|
+
when "UInt192" then Uint192
|
|
101
|
+
when "UInt256" then Uint256
|
|
102
|
+
when "UInt384" then Uint384
|
|
103
|
+
when "UInt512" then Uint512
|
|
104
|
+
when "Int32" then Int32
|
|
105
|
+
when "Int64" then Int64
|
|
106
|
+
when "PathSet" then PathSet
|
|
107
|
+
when "Vector256" then Vector256
|
|
108
|
+
when "XChainBridge" then XChainBridge
|
|
109
|
+
when "Issue" then Issue
|
|
110
|
+
when "Transaction" then Blob
|
|
111
|
+
when "LedgerEntry" then Blob
|
|
112
|
+
when "Validation" then Blob
|
|
113
|
+
when "Metadata" then Blob
|
|
114
|
+
else
|
|
115
|
+
raise "unsupported type #{name}"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class ComparableSerializedType < SerializedType
|
|
122
|
+
|
|
123
|
+
# Compare if `self` is less than `other`
|
|
124
|
+
def lt(other)
|
|
125
|
+
compare_to(other) < 0
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Compare if `self` is equal to `other`
|
|
129
|
+
def eq(other)
|
|
130
|
+
compare_to(other) == 0
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Compare if `self` is greater than `other`
|
|
134
|
+
def gt(other)
|
|
135
|
+
compare_to(other) > 0
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Compare if `self` is greater than or equal to `other`
|
|
139
|
+
def gte(other)
|
|
140
|
+
compare_to(other) >= 0
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Compare if `self` is less than or equal to `other`
|
|
144
|
+
def lte(other)
|
|
145
|
+
compare_to(other) <= 0
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Overload this method in subclasses to define comparison logic
|
|
149
|
+
#
|
|
150
|
+
# @param other [Object] - The object to compare `self` to
|
|
151
|
+
# @return [Integer] - Returns -1, 0, or 1 depending on the comparison
|
|
152
|
+
def compare_to(other)
|
|
153
|
+
raise NotImplementedError, "Cannot compare #{self} and #{other}"
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BinaryCodec
|
|
4
|
+
class STArray < SerializedType
|
|
5
|
+
def initialize(byte_buf = nil)
|
|
6
|
+
super(byte_buf || [])
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Creates a new STArray instance from a value.
|
|
10
|
+
# @param value [STArray, String, Array<Hash>] The value to convert.
|
|
11
|
+
# @param definitions [Definitions, nil] Optional definitions.
|
|
12
|
+
# @return [STArray] The created instance.
|
|
13
|
+
def self.from(value, definitions = nil)
|
|
14
|
+
return value if value.is_a?(STArray)
|
|
15
|
+
definitions ||= Definitions.instance
|
|
16
|
+
|
|
17
|
+
if value.is_a?(String)
|
|
18
|
+
return STArray.new(hex_to_bytes(value))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if value.is_a?(Array)
|
|
22
|
+
bytes = []
|
|
23
|
+
value.each do |item|
|
|
24
|
+
obj = STObject.from(item, nil, definitions)
|
|
25
|
+
bytes.concat(obj.to_bytes)
|
|
26
|
+
bytes.concat([0xF1]) # ArrayItemEndMarker
|
|
27
|
+
end
|
|
28
|
+
bytes.concat([0xF1]) # ArrayEndMarker
|
|
29
|
+
return STArray.new(bytes)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
raise StandardError, "Cannot construct STArray from #{value.class}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Creates an STArray instance from a parser.
|
|
36
|
+
# @param parser [BinaryParser] The parser to read from.
|
|
37
|
+
# @param _hint [Integer, nil] Unused hint.
|
|
38
|
+
# @return [STArray] The created instance.
|
|
39
|
+
def self.from_parser(parser, _hint = nil)
|
|
40
|
+
bytes = []
|
|
41
|
+
until parser.end?(1) # Look ahead for end marker
|
|
42
|
+
obj = STObject.from_parser(parser)
|
|
43
|
+
bytes.concat(obj.to_bytes)
|
|
44
|
+
bytes.concat(parser.read(1)) # Should be 0xF1 (ArrayItemEndMarker)
|
|
45
|
+
end
|
|
46
|
+
parser.read(1) # Consume 0xF1 (ArrayEndMarker)
|
|
47
|
+
STArray.new(bytes)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Returns the JSON representation of the STArray.
|
|
51
|
+
# @param _definitions [Definitions, nil] Unused.
|
|
52
|
+
# @param _field_name [String, nil] Unused.
|
|
53
|
+
# @return [Array<Hash>] The JSON representation.
|
|
54
|
+
def to_json(_definitions = nil, _field_name = nil)
|
|
55
|
+
parser = BinaryParser.new(to_hex)
|
|
56
|
+
result = []
|
|
57
|
+
until parser.end?
|
|
58
|
+
obj = STObject.from_parser(parser)
|
|
59
|
+
result << JSON.parse(obj.to_json)
|
|
60
|
+
# In xrpl.js, array items are STObjects.
|
|
61
|
+
# After each STObject, there might be an ArrayItemEndMarker if we're not at the end.
|
|
62
|
+
# But wait, STObject.from_parser already reads until ObjectEndMarker.
|
|
63
|
+
# STArray in XRPL is a list of objects, each ending with ObjectEndMarker.
|
|
64
|
+
# The whole array ends with ArrayEndMarker (0xF1).
|
|
65
|
+
# Actually, standard XRPL STArray: [FieldHeader][STObject][FieldHeader][STObject]...[0xF1]
|
|
66
|
+
# Wait, I need to check how xrpl.js handles STArray.
|
|
67
|
+
end
|
|
68
|
+
result
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BinaryCodec
|
|
4
|
+
class STObject < SerializedType
|
|
5
|
+
|
|
6
|
+
OBJECT_END_MARKER_BYTE = [225]
|
|
7
|
+
OBJECT_END_MARKER = 'ObjectEndMarker'.freeze
|
|
8
|
+
ST_OBJECT = 'STObject'.freeze
|
|
9
|
+
DESTINATION = 'Destination'.freeze
|
|
10
|
+
ACCOUNT = 'Account'.freeze
|
|
11
|
+
SOURCE_TAG = 'SourceTag'.freeze
|
|
12
|
+
DEST_TAG = 'DestinationTag'.freeze
|
|
13
|
+
|
|
14
|
+
# attr_reader :type, :bytes
|
|
15
|
+
#
|
|
16
|
+
def initialize(byte_buf = nil)
|
|
17
|
+
@bytes = byte_buf || Array.new(0)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Creates a new STObject instance from a value.
|
|
21
|
+
# @param value [STObject, String, Array<Integer>, Hash] The value to convert.
|
|
22
|
+
# @param filter [Proc, nil] Optional filter for fields.
|
|
23
|
+
# @param definitions [Definitions, nil] Optional definitions.
|
|
24
|
+
# @return [STObject] The created instance.
|
|
25
|
+
def self.from(value, filter = nil, definitions = nil)
|
|
26
|
+
return value if value.is_a?(STObject)
|
|
27
|
+
definitions ||= Definitions.instance
|
|
28
|
+
|
|
29
|
+
if value.is_a?(String)
|
|
30
|
+
return STObject.new(hex_to_bytes(value))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if value.is_a?(Array)
|
|
34
|
+
return STObject.new(value)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
list = BytesList.new
|
|
38
|
+
serializer = BinarySerializer.new(list)
|
|
39
|
+
is_unl_modify = false
|
|
40
|
+
|
|
41
|
+
# Handle X-Addresses and check for duplicate tags
|
|
42
|
+
processed_value = value.each_with_object({}) do |(key, val), acc|
|
|
43
|
+
if val && val.is_a?(String) && AddressCodec::AddressCodec.new.valid_x_address?(val)
|
|
44
|
+
handled = handle_x_address(key.to_s, val)
|
|
45
|
+
check_for_duplicate_tags(handled, value)
|
|
46
|
+
acc.merge!(handled)
|
|
47
|
+
else
|
|
48
|
+
acc[key.to_s] = val
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
sorted_fields = processed_value.keys.map do |field_name|
|
|
53
|
+
field = definitions.get_field_instance(field_name)
|
|
54
|
+
raise "Field #{field_name} is not defined" if field.nil?
|
|
55
|
+
field
|
|
56
|
+
end.select(&:is_serialized).sort_by(&:ordinal)
|
|
57
|
+
|
|
58
|
+
if filter
|
|
59
|
+
sorted_fields = sorted_fields.select { |f| filter.call(f.name) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
sorted_fields.each do |field|
|
|
63
|
+
associated_value = processed_value[field.name]
|
|
64
|
+
next if associated_value.nil?
|
|
65
|
+
|
|
66
|
+
# Special handling for UNLModify
|
|
67
|
+
if field.name == 'UNLModify' # This might need more specific check depending on value
|
|
68
|
+
is_unl_modify = true
|
|
69
|
+
end
|
|
70
|
+
is_unl_modify_workaround = (field.name == 'Account' && is_unl_modify)
|
|
71
|
+
|
|
72
|
+
serializer.write_field_and_value(field, associated_value, is_unl_modify_workaround)
|
|
73
|
+
|
|
74
|
+
if field.type == 'STObject'
|
|
75
|
+
serializer.put(OBJECT_END_MARKER_BYTE)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
STObject.new(list.to_bytes)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Construct a STObject from a BinaryParser
|
|
83
|
+
#
|
|
84
|
+
# @param parser [BinaryParser] BinaryParser to read STObject from
|
|
85
|
+
# @param size_hint [Integer] Optional size hint for the object
|
|
86
|
+
# @return [STObject] A STObject object
|
|
87
|
+
def self.from_parser(parser, size_hint = nil)
|
|
88
|
+
list = BytesList.new
|
|
89
|
+
bytes = BinarySerializer.new(list)
|
|
90
|
+
|
|
91
|
+
until parser.end?
|
|
92
|
+
field = parser.read_field
|
|
93
|
+
|
|
94
|
+
break if field.name == OBJECT_END_MARKER
|
|
95
|
+
|
|
96
|
+
associated_value = parser.read_field_value(field)
|
|
97
|
+
|
|
98
|
+
bytes.write_field_and_value(field, associated_value)
|
|
99
|
+
bytes.put(OBJECT_END_MARKER_BYTE) if field.type == ST_OBJECT
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
STObject.new(list.to_bytes)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Method to get the JSON interpretation of self.bytes
|
|
106
|
+
#
|
|
107
|
+
# @return [String] A stringified JSON object
|
|
108
|
+
def to_json(_definitions = nil, _field_name = nil)
|
|
109
|
+
parser = BinaryParser.new(to_hex)
|
|
110
|
+
accumulator = {}
|
|
111
|
+
|
|
112
|
+
until parser.end?
|
|
113
|
+
field = parser.read_field
|
|
114
|
+
break if field.name == OBJECT_END_MARKER # Break if the object end marker is reached
|
|
115
|
+
value = parser.read_field_value(field).to_json
|
|
116
|
+
value = JSON.parse(value) if field.type == ST_OBJECT || field.type == Amount
|
|
117
|
+
accumulator[field.name] = value
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
JSON.generate(accumulator)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private
|
|
124
|
+
|
|
125
|
+
# Break down an X-Address into an account and a tag
|
|
126
|
+
#
|
|
127
|
+
# @param field [String] Name of the field
|
|
128
|
+
# @param x_address [String] X-Address corresponding to the field
|
|
129
|
+
# @return [Hash] A hash with the classic address and tag (if present)
|
|
130
|
+
def handle_x_address(field, x_address)
|
|
131
|
+
address_codec = AddressCodec::AddressCodec.new
|
|
132
|
+
decoded = address_codec.x_address_to_classic_address(x_address)
|
|
133
|
+
|
|
134
|
+
tag_name = if field == 'Destination'
|
|
135
|
+
'DestinationTag'
|
|
136
|
+
elsif field == 'Account'
|
|
137
|
+
'SourceTag'
|
|
138
|
+
elsif decoded[:tag]
|
|
139
|
+
raise "#{field} cannot have an associated tag"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
decoded[:tag] ? { field => decoded[:classic_address], tag_name => decoded[:tag] } : { field => decoded[:classic_address] }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def self.check_for_duplicate_tags(obj1, obj2)
|
|
146
|
+
if obj1['SourceTag'] && obj2['SourceTag']
|
|
147
|
+
raise 'Cannot have Account X-Address and SourceTag'
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
if obj1['DestinationTag'] && obj2['DestinationTag']
|
|
151
|
+
raise 'Cannot have Destination X-Address and DestinationTag'
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BinaryCodec
|
|
4
|
+
|
|
5
|
+
class Uint < ComparableSerializedType
|
|
6
|
+
# Returns the width of the Uint type in bytes.
|
|
7
|
+
# @return [Integer] The width.
|
|
8
|
+
def self.width
|
|
9
|
+
@width
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(byte_buf = nil)
|
|
13
|
+
super(byte_buf || Array.new(self.class.width, 0))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Creates a new Uint instance from a value.
|
|
17
|
+
# @param value [Uint, String, Integer] The value to convert.
|
|
18
|
+
# @return [Uint] The created instance.
|
|
19
|
+
def self.from(value)
|
|
20
|
+
return value if value.is_a?(self)
|
|
21
|
+
|
|
22
|
+
if value.is_a?(String)
|
|
23
|
+
# Handle hex strings or numeric strings
|
|
24
|
+
if valid_hex?(value) && value.length == self.width * 2
|
|
25
|
+
return new(hex_to_bytes(value))
|
|
26
|
+
end
|
|
27
|
+
return new(int_to_bytes(value.to_i, width))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if value.is_a?(Integer)
|
|
31
|
+
return new(int_to_bytes(value, width))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
raise StandardError, "Cannot construct #{self} from the value given"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Creates a Uint instance from a parser.
|
|
38
|
+
# @param parser [BinaryParser] The parser to read from.
|
|
39
|
+
# @param _hint [Integer, nil] Unused hint.
|
|
40
|
+
# @return [Uint] The created instance.
|
|
41
|
+
def self.from_parser(parser, _hint = nil)
|
|
42
|
+
new(parser.read(width))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns the numeric value of the Uint.
|
|
46
|
+
# @return [Integer] The numeric value.
|
|
47
|
+
def value_of
|
|
48
|
+
@bytes.reduce(0) { |acc, byte| (acc << 8) + byte }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Compares this Uint to another Uint.
|
|
52
|
+
# @param other [Uint] The other Uint to compare to.
|
|
53
|
+
# @return [Integer] Comparison result (-1, 0, or 1).
|
|
54
|
+
def compare_to(other)
|
|
55
|
+
value_of <=> other.value_of
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class Uint8 < Uint
|
|
60
|
+
# Uint8 is a 1-byte unsigned integer
|
|
61
|
+
@width = 1
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class Uint16 < Uint
|
|
65
|
+
# Uint16 is a 2-byte unsigned integer
|
|
66
|
+
@width = 2
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class Uint32 < Uint
|
|
70
|
+
# Uint32 is a 4-byte unsigned integer
|
|
71
|
+
@width = 4
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class Uint64 < Uint
|
|
75
|
+
# Uint64 is an 8-byte unsigned integer
|
|
76
|
+
@width = 8
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class Uint96 < Uint
|
|
80
|
+
# Uint96 is a 12-byte unsigned integer
|
|
81
|
+
@width = 12
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class Uint128 < Uint
|
|
85
|
+
# Uint128 is a 16-byte unsigned integer
|
|
86
|
+
@width = 16
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class Uint160 < Uint
|
|
90
|
+
# Uint160 is a 20-byte unsigned integer
|
|
91
|
+
@width = 20
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class Uint192 < Uint
|
|
95
|
+
# Uint192 is a 24-byte unsigned integer
|
|
96
|
+
@width = 24
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class Uint256 < Uint
|
|
100
|
+
# Uint256 is a 32-byte unsigned integer
|
|
101
|
+
@width = 32
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class Uint384 < Uint
|
|
105
|
+
# Uint384 is a 48-byte unsigned integer
|
|
106
|
+
@width = 48
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class Uint512 < Uint
|
|
110
|
+
# Uint512 is a 64-byte unsigned integer
|
|
111
|
+
@width = 64
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class Int32 < Uint
|
|
115
|
+
@width = 4
|
|
116
|
+
# Returns the numeric value of the Int32.
|
|
117
|
+
# @return [Integer] The signed 32-bit value.
|
|
118
|
+
def value_of
|
|
119
|
+
val = super
|
|
120
|
+
val > 0x7FFFFFFF ? val - 0x100000000 : val
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Creates a new Int32 instance from a value.
|
|
124
|
+
# @param value [Int32, Integer] The value to convert.
|
|
125
|
+
# @return [Int32] The created instance.
|
|
126
|
+
def self.from(value)
|
|
127
|
+
return value if value.is_a?(self)
|
|
128
|
+
if value.is_a?(Integer)
|
|
129
|
+
# Ensure it fits in 32-bit signed
|
|
130
|
+
if value < -2147483648 || value > 2147483647
|
|
131
|
+
raise StandardError, "Value #{value} out of range for Int32"
|
|
132
|
+
end
|
|
133
|
+
# Convert to unsigned 32-bit for storage
|
|
134
|
+
u_val = value < 0 ? value + 0x100000000 : value
|
|
135
|
+
return new(int_to_bytes(u_val, 4))
|
|
136
|
+
end
|
|
137
|
+
super(value)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class Int64 < Uint
|
|
142
|
+
@width = 8
|
|
143
|
+
# Returns the numeric value of the Int64.
|
|
144
|
+
# @return [Integer] The signed 64-bit value.
|
|
145
|
+
def value_of
|
|
146
|
+
val = super
|
|
147
|
+
val > 0x7FFFFFFFFFFFFFFF ? val - 0x10000000000000000 : val
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Creates a new Int64 instance from a value.
|
|
151
|
+
# @param value [Int64, Integer] The value to convert.
|
|
152
|
+
# @return [Int64] The created instance.
|
|
153
|
+
def self.from(value)
|
|
154
|
+
return value if value.is_a?(self)
|
|
155
|
+
if value.is_a?(Integer)
|
|
156
|
+
if value < -9223372036854775808 || value > 9223372036854775807
|
|
157
|
+
raise StandardError, "Value #{value} out of range for Int64"
|
|
158
|
+
end
|
|
159
|
+
u_val = value < 0 ? value + 0x10000000000000000 : value
|
|
160
|
+
return new(int_to_bytes(u_val, 8))
|
|
161
|
+
end
|
|
162
|
+
super(value)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BinaryCodec
|
|
4
|
+
class Vector256 < SerializedType
|
|
5
|
+
def initialize(bytes = nil)
|
|
6
|
+
super(bytes || [])
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Creates a new Vector256 instance from a value.
|
|
10
|
+
# @param value [Vector256, String, Array<String>] The value to convert.
|
|
11
|
+
# @return [Vector256] The created instance.
|
|
12
|
+
def self.from(value)
|
|
13
|
+
return value if value.is_a?(Vector256)
|
|
14
|
+
|
|
15
|
+
if value.is_a?(String)
|
|
16
|
+
return Vector256.new(hex_to_bytes(value))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if value.is_a?(Array)
|
|
20
|
+
bytes = []
|
|
21
|
+
value.each do |item|
|
|
22
|
+
hash = Hash256.from(item)
|
|
23
|
+
bytes.concat(hash.to_bytes)
|
|
24
|
+
end
|
|
25
|
+
return Vector256.new(bytes)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
raise StandardError, "Cannot construct Vector256 from #{value.class}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Creates a Vector256 instance from a parser.
|
|
32
|
+
# @param parser [BinaryParser] The parser to read from.
|
|
33
|
+
# @param size_hint [Integer] The expected total size in bytes.
|
|
34
|
+
# @return [Vector256] The created instance.
|
|
35
|
+
def self.from_parser(parser, size_hint = nil)
|
|
36
|
+
bytes = []
|
|
37
|
+
num_hashes = size_hint / 32
|
|
38
|
+
num_hashes.times do
|
|
39
|
+
bytes.concat(parser.read(32))
|
|
40
|
+
end
|
|
41
|
+
Vector256.new(bytes)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def to_json(_definitions = nil, _field_name = nil)
|
|
45
|
+
parser = BinaryParser.new(to_hex)
|
|
46
|
+
result = []
|
|
47
|
+
until parser.end?
|
|
48
|
+
result << bytes_to_hex(parser.read(32))
|
|
49
|
+
end
|
|
50
|
+
result
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BinaryCodec
|
|
4
|
+
class XChainBridge < SerializedType
|
|
5
|
+
def initialize(bytes = nil)
|
|
6
|
+
super(bytes || [])
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.from(value)
|
|
10
|
+
return value if value.is_a?(XChainBridge)
|
|
11
|
+
|
|
12
|
+
if value.is_a?(String)
|
|
13
|
+
return XChainBridge.new(hex_to_bytes(value))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
if value.is_a?(Hash)
|
|
17
|
+
bytes = []
|
|
18
|
+
bytes.concat(AccountId.from(value['LockingChainDoor']).to_bytes)
|
|
19
|
+
bytes.concat(Issue.from(value['LockingChainIssue']).to_bytes)
|
|
20
|
+
bytes.concat(AccountId.from(value['IssuingChainDoor']).to_bytes)
|
|
21
|
+
bytes.concat(Issue.from(value['IssuingChainIssue']).to_bytes)
|
|
22
|
+
return XChainBridge.new(bytes)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
raise StandardError, "Cannot construct XChainBridge from #{value.class}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.from_parser(parser, _hint = nil)
|
|
29
|
+
bytes = []
|
|
30
|
+
bytes.concat(parser.read(20)) # LockingChainDoor
|
|
31
|
+
bytes.concat(Issue.from_parser(parser).to_bytes) # LockingChainIssue
|
|
32
|
+
bytes.concat(parser.read(20)) # IssuingChainDoor
|
|
33
|
+
bytes.concat(Issue.from_parser(parser).to_bytes) # IssuingChainIssue
|
|
34
|
+
XChainBridge.new(bytes)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_json(_definitions = nil, _field_name = nil)
|
|
38
|
+
parser = BinaryParser.new(to_hex)
|
|
39
|
+
result = {}
|
|
40
|
+
result['LockingChainDoor'] = AccountId.from_parser(parser).to_json
|
|
41
|
+
result['LockingChainIssue'] = Issue.from_parser(parser).to_json
|
|
42
|
+
result['IssuingChainDoor'] = AccountId.from_parser(parser).to_json
|
|
43
|
+
result['IssuingChainIssue'] = Issue.from_parser(parser).to_json
|
|
44
|
+
result
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|