From b5b005ad6363e110acc130765cc22ec47a74df60 Mon Sep 17 00:00:00 2001 From: Kirk Haines Date: Tue, 14 Mar 2023 17:55:40 -0600 Subject: [PATCH] Commit the changes that let SS58 work with any arbitrary length payload. Substrates SS58 address format doc prescribes checksum lengths only for payloads of 1, 2, 4, 8, or 32 bytes. For any other payload, the behaviour is undefined. For lack of any other clear guidance beyond the abundance of 2 byte checksums in the actual live use of SS58, this library is implementing a default checksum of 2 bytes for otherwise undefined payload lengths. This change adds a `Base58::SS58.encode` and a `Base58::SS58.decode` method for dealing with arbitrary data, of arbitrary sizes. --- VERSION | 2 +- shard.yml | 2 +- src/base58/ss58.cr | 43 ++++++++++++++++++++++++++++++------------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/VERSION b/VERSION index 0ea3a94..0c62199 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0 +0.2.1 diff --git a/shard.yml b/shard.yml index 32f70c3..274b0e9 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,5 @@ name: base58 -version: 0.2.0 +version: 0.2.1 authors: - Kirk Haines diff --git a/src/base58/ss58.cr b/src/base58/ss58.cr index f7b7e52..9a70b62 100644 --- a/src/base58/ss58.cr +++ b/src/base58/ss58.cr @@ -62,10 +62,6 @@ module Base58 # :nodoc: def self.check_args(address, format) - if format < 0 || format > 16383 || format == 46 || format == 47 - raise ArgumentError.new("Invalid address format: #{format}") - end - address_size = address.size if TwoByteChecksumAddresses.includes?(address_size) checksum_length = 2 @@ -75,20 +71,36 @@ module Base58 raise ArgumentError.new("Invalid address size: #{address_size}; Substrate addresses can only be 1, 2, 4, 8, 32, or 33 bytes long") end + { + type: Base58::Checksum::SS58, + prefix: String.new(derive_format(format)), + checksum_length: checksum_length, + checksum_prefix: ChecksumPrefix, + } + end + + # :nodoc: + def self.derive_format(format) + if format < 0 || format > 16383 || format == 46 || format == 47 + raise ArgumentError.new("Invalid address format: #{format}") + end + if format < 64 - format_bytes = Slice[format.to_u8] + Slice[format.to_u8] else - format_bytes = Slice[ + Slice[ (((format & 0b0000_000011111100) >> 2) | 0b01000000).to_u8, ((format >> 8) | ((format & 0b0000000000000011) << 6)).to_u8, ] end - { - type: Base58::Checksum::SS58, - prefix: String.new(format_bytes), - checksum_length: checksum_length, - checksum_prefix: ChecksumPrefix, - } + end + + # To encode content with an arbitrary length payload, use the `encode` method instead + # of `encode_address`. The Substrate SS58 spec only prescribes checksum lengths for + # payloads which are 1, 2, 4, 8, or 32 bytes long. So, for other payloads, this library + # is defaulting to a 2 byte checksum. + def self.encode(payload, into, format : Int = 42) + Base58.encode(payload, into: into, check: Check.new(type: Base58::Checksum::SS58, prefix: String.new(derive_format(format)), checksum_length: checksum_length(payload), checksum_prefix: ChecksumPrefix)) end def self.encode_address(address, into : String.class = String, format : Int = 42) @@ -147,6 +159,11 @@ module Base58 Base58.encode(address, into: into, check: Check.new(**check_args(address, format))) end + # To decode content with an arbitrary length payload, use `decode` instead of `decode_address`. + def self.decode(payload, into, format = 42) + decode_address(payload, into: into, format: format) + end + def self.decode_address(address, into : String.class = String, format : Int? = nil) String.new(decode_address(address, Slice(UInt8), format)) end @@ -278,7 +295,7 @@ module Base58 when 17 8 else - 0 + 2 end end