xrpl-ruby 0.0.1 → 0.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe5e22e400d255c2c834133232433c9c8a513d3c1fba93dc06f46e18d90b1c3d
4
- data.tar.gz: 0512e71a026bef8893fa8855c702ccdb09e18ce219f286c3cea33b2b5d477e38
3
+ metadata.gz: 601db38742606f9188b1f301ca9e6800308d7650ec4d6c7dd655db9eed6bab27
4
+ data.tar.gz: 188e2ff1112b079a7aed430882fe0c0599a839946dd9ed5842d6e55df964c5e8
5
5
  SHA512:
6
- metadata.gz: c7b52d5c38ead6d5d5910cbd5132be7ea783e24b8c21f452d56138c9e806d016d9186d3213913372b3d5cb8d1c0bdca9c6acf8f7c9de1c5416571bf9d0873b2e
7
- data.tar.gz: '00914805ec76c9fe7a2b475881efde3408f6f609f3ffaedc30d21fe845d3dd247051158561c29fe69af688238d4c86fb5a4124747c806bfe6a64f4c31a244020'
6
+ metadata.gz: 4e819838c6ada42db05efebc7fec715ce22dd890911c7a40242a2945bacde3b832f9fbf4139cee2ffbc384a662a0cbda36336cf8e659eebf69820deae25797f0
7
+ data.tar.gz: 46f08dc1d555d518ce8b98760881e94f29871b1a32c49b6d1003a1197a0e7bd354ec09d415e2ccf4f85f343add4ccb742fdd3587dfabd0f764ed4f397cb47a9a
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+ module AddressCodec
3
+
4
+ class AddressCodec < XrpCodec
5
+
6
+ PREFIX_BYTES = {
7
+ main: [0x05, 0x44],
8
+ test: [0x04, 0x93]
9
+ }
10
+
11
+ MAX_32_BIT_UNSIGNED_INT = 4294967295
12
+
13
+ def classic_address_to_x_address(classic_address, tag, test)
14
+ account_id = decode_account_id(classic_address)
15
+ encode_x_address(account_id, tag, test)
16
+ end
17
+
18
+ def encode_x_address(account_id, tag, test)
19
+ if account_id.length != 20
20
+ # RIPEMD160 -> 160 Bits = 20 Bytes
21
+ raise 'Account ID must be 20 bytes'
22
+ end
23
+ if tag != false && tag > MAX_32_BIT_UNSIGNED_INT
24
+ raise 'Invalid tag'
25
+ end
26
+ the_tag = tag || 0
27
+ flag = tag == false || tag.nil? ? 0 : 1
28
+
29
+ bytes = concat_args(
30
+ test ? PREFIX_BYTES[:test] : PREFIX_BYTES[:main],
31
+ account_id,
32
+ [
33
+ flag,
34
+ the_tag & 0xff,
35
+ (the_tag >> 8) & 0xff,
36
+ (the_tag >> 16) & 0xff,
37
+ (the_tag >> 24) & 0xff,
38
+ 0,
39
+ 0,
40
+ 0,
41
+ 0
42
+ ]
43
+ )
44
+
45
+ encode_checked(bytes)
46
+ end
47
+
48
+ def x_address_to_classic_address(x_address)
49
+ decoded = decode_x_address(x_address)
50
+ account_id = decoded[:account_id]
51
+ tag = decoded[:tag]
52
+ test = decoded[:test]
53
+ classic_address = encode_account_id(account_id)
54
+ {
55
+ classic_address: classic_address,
56
+ tag: tag,
57
+ test: test
58
+ }
59
+ end
60
+
61
+ def decode_x_address(x_address)
62
+ decoded = decode_checked(x_address)
63
+ test = is_uint8_array_for_test_address(decoded)
64
+ account_id = decoded[2, 20]
65
+ tag = tag_from_uint8_array(decoded)
66
+ {
67
+ account_id: account_id,
68
+ tag: tag,
69
+ test: test
70
+ }
71
+ end
72
+
73
+ def valid_x_address?(x_address)
74
+ begin
75
+ decode_x_address(x_address)
76
+ rescue
77
+ return false
78
+ end
79
+ true
80
+ end
81
+
82
+ private
83
+
84
+ def is_uint8_array_for_test_address(buf)
85
+ decoded_prefix = buf[0, 2]
86
+ if decoded_prefix == PREFIX_BYTES[:main]
87
+ return false
88
+ end
89
+ if decoded_prefix == PREFIX_BYTES[:test]
90
+ return true
91
+ end
92
+
93
+ raise 'Invalid X-address: bad prefix'
94
+ end
95
+
96
+ def tag_from_uint8_array(bytes)
97
+ flag = bytes[22]
98
+ if flag >= 2
99
+ # Keine Unterstützung für 64-Bit-Tags zu diesem Zeitpunkt
100
+ raise 'Unsupported X-address'
101
+ end
102
+ if flag == 1
103
+ # Little-endian zu Big-endian
104
+ return bytes[23] + bytes[24] * 0x100 + bytes[25] * 0x10000 + bytes[26] * 0x1000000
105
+ end
106
+ if flag != 0
107
+ raise 'flag must be zero to indicate no tag'
108
+ end
109
+ if '0000000000000000' != bytes_to_hex(bytes[23, 8])
110
+ raise 'remaining bytes must be zero'
111
+ end
112
+ false
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+ require 'digest'
3
+
4
+ require_relative "../core/core"
5
+
6
+ module AddressCodec
7
+ class Codec
8
+
9
+ def initialize
10
+ @codec = Core::Base58XRP.new
11
+ end
12
+
13
+ def encode(bytes, opts)
14
+ versions = opts[:versions]
15
+ encode_versioned(bytes, versions, opts[:expected_length])
16
+ end
17
+
18
+ def decode(base58string, opts)
19
+ versions = opts[:versions]
20
+ types = opts[:version_types]
21
+
22
+ without_sum = decode_checked(base58string)
23
+
24
+ if versions.length > 1 && !opts[:expected_length]
25
+ raise 'expected_length is required because there are >= 2 possible versions'
26
+ end
27
+
28
+ version_length_guess = versions[0].is_a?(Numeric) ? 1 : versions[0].length
29
+ payload_length = opts[:expected_length] || without_sum.length - version_length_guess
30
+ version_bytes = without_sum[0...-payload_length]
31
+ payload = without_sum[-payload_length..-1]
32
+
33
+ versions.each_with_index do |version, i|
34
+ version = Array(version)
35
+ if version_bytes == version
36
+ return {
37
+ version: version,
38
+ bytes: payload,
39
+ type: types ? types[i] : nil
40
+ }
41
+ end
42
+ end
43
+
44
+ raise 'version_invalid: version bytes do not match any of the provided version(s)'
45
+ end
46
+
47
+ def encode_checked(bytes)
48
+ check = sha256(sha256(bytes))[0, 4]
49
+ encode_raw(bytes + check)
50
+ end
51
+
52
+ def decode_checked(base58string)
53
+ bytes = decode_raw(base58string)
54
+
55
+ if bytes.length < 5
56
+ raise 'invalid_input_size: decoded data must have length >= 5'
57
+ end
58
+
59
+ unless verify_check_sum(bytes)
60
+ raise 'checksum_invalid'
61
+ end
62
+
63
+ bytes[0...-4]
64
+ end
65
+
66
+ private
67
+
68
+ def encode_versioned(bytes, versions, expected_length)
69
+ unless check_byte_length(bytes, expected_length)
70
+ raise 'unexpected_payload_length: bytes.length does not match expected_length. Ensure that the bytes are a Uint8Array.'
71
+ end
72
+
73
+ encode_checked(concat_args(versions, bytes))
74
+ end
75
+
76
+ def encode_raw(bytes)
77
+ @codec.encode(bytes.pack('C*'))
78
+ end
79
+
80
+ def decode_raw(base58string)
81
+ @codec.decode(base58string).unpack('C*')
82
+ end
83
+
84
+ def sha256(bytes)
85
+ binary_value = bytes.pack('C*')
86
+ binary_hash = Digest::SHA256.digest(binary_value)
87
+ binary_hash.unpack('C*')
88
+ end
89
+
90
+ def verify_check_sum(bytes)
91
+ computed = sha256(sha256(bytes[0...-4]))[0, 4]
92
+ checksum = bytes[-4, 4]
93
+ computed == checksum
94
+ end
95
+
96
+ end
97
+
98
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../core/core"
4
+
5
+ module AddressCodec
6
+
7
+ class XrpCodec < Codec
8
+
9
+ # base58 encodings: https://xrpl.org/base58-encodings.html
10
+ ACCOUNT_ID = 0 # Account address (20 bytes)
11
+ ACCOUNT_PUBLIC_KEY = 0x23 # Account public key (33 bytes)
12
+ FAMILY_SEED = 0x21 # 33; Seed value (for secret keys) (16 bytes)
13
+ NODE_PUBLIC = 0x1c # 28; Validation public key (33 bytes)
14
+ ED25519_SEED = [0x01, 0xe1, 0x4b].freeze # [1, 225, 75]
15
+
16
+ def encode_seed(entropy, type = nil)
17
+ unless check_byte_length(entropy, 16)
18
+ raise 'entropy must have length 16'
19
+ end
20
+
21
+ opts = {
22
+ expected_length: 16,
23
+ versions: type == 'ed25519' ? ED25519_SEED : [FAMILY_SEED]
24
+ }
25
+
26
+ # prefixes entropy with version bytes
27
+ encode(entropy, opts)
28
+ end
29
+
30
+ def decode_seed(seed, opts = {
31
+ version_types: ['ed25519', 'secp256k1'],
32
+ versions: [ED25519_SEED, FAMILY_SEED],
33
+ expected_length: 16
34
+ })
35
+ decode(seed, opts)
36
+ end
37
+
38
+ def encode_account_id(bytes)
39
+ opts = { versions: [ACCOUNT_ID], expected_length: 20 }
40
+ encode(bytes, opts)
41
+ end
42
+
43
+ def decode_account_id(account_id)
44
+ opts = { versions: [ACCOUNT_ID], expected_length: 20 }
45
+ decode(account_id, opts)[:bytes]
46
+ end
47
+
48
+ def decode_node_public(base58string)
49
+ opts = { versions: [NODE_PUBLIC], expected_length: 33 }
50
+ decode(base58string, opts)[:bytes]
51
+ end
52
+
53
+ def encode_node_public(bytes)
54
+ opts = { versions: [NODE_PUBLIC], expected_length: 33 }
55
+ encode(bytes, opts)
56
+ end
57
+
58
+ def encode_account_public(bytes)
59
+ opts = { versions: [ACCOUNT_PUBLIC_KEY], expected_length: 33 }
60
+ encode(bytes, opts)
61
+ end
62
+
63
+ def decode_account_public(base58string)
64
+ opts = { versions: [ACCOUNT_PUBLIC_KEY], expected_length: 33 }
65
+ decode(base58string, opts)[:bytes]
66
+ end
67
+
68
+ def valid_classic_address?(address)
69
+ begin
70
+ decode_account_id(address)
71
+ rescue
72
+ return false
73
+ end
74
+ true
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,12 @@
1
+ module Core
2
+
3
+ class Base58XRP < BaseX
4
+
5
+ XRP_ALPHABET = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"
6
+
7
+ def initialize
8
+ super(XRP_ALPHABET)
9
+ end
10
+ end
11
+
12
+ end
data/lib/core/base_x.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module Core
2
+
2
3
  class BaseX
3
4
  def initialize(alphabet)
4
5
  @alphabet = alphabet
data/lib/core/core.rb CHANGED
@@ -1,2 +1,37 @@
1
1
  # @!attribute
2
- require_relative 'base_x'
2
+ require_relative 'base_x'
3
+ require_relative 'base_58_xrp'
4
+
5
+ def bytes_to_hex(bytes)
6
+ bytes.pack('C*').unpack1('H*').upcase
7
+ end
8
+
9
+ def hex_to_bytes(hex)
10
+ [hex].pack('H*').bytes
11
+ end
12
+
13
+ def hex_to_bin(hex)
14
+ [hex].pack("H*")
15
+ end
16
+
17
+ def bin_to_hex(bin)
18
+ bin.unpack("H*").first.upcase
19
+ end
20
+
21
+ def check_byte_length(bytes, expected_length)
22
+ if bytes.respond_to?(:byte_length)
23
+ bytes.byte_length == expected_length
24
+ else
25
+ bytes.length == expected_length
26
+ end
27
+ end
28
+
29
+ def concat_args(*args)
30
+ args.flat_map do |arg|
31
+ is_scalar?(arg) ? [arg] : arg.to_a
32
+ end
33
+ end
34
+
35
+ def is_scalar?(val)
36
+ val.is_a?(Numeric)
37
+ end
data/lib/xrpl-ruby.rb CHANGED
@@ -1,3 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'core/core'
3
+ require_relative 'core/core'
4
+
5
+ require_relative 'address-codec/codec'
6
+ require_relative 'address-codec/xrp_codec'
7
+ require_relative 'address-codec/address_codec'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xrpl-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Busse
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-05 00:00:00.000000000 Z
11
+ date: 2024-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -61,6 +61,10 @@ executables: []
61
61
  extensions: []
62
62
  extra_rdoc_files: []
63
63
  files:
64
+ - lib/address-codec/address_codec.rb
65
+ - lib/address-codec/codec.rb
66
+ - lib/address-codec/xrp_codec.rb
67
+ - lib/core/base_58_xrp.rb
64
68
  - lib/core/base_x.rb
65
69
  - lib/core/core.rb
66
70
  - lib/xrpl-ruby.rb