Skip to content

Commit

Permalink
Commit the changes that let SS58 work with any arbitrary length payload.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wyhaines committed Mar 14, 2023
1 parent 00c7cb6 commit b5b005a
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 15 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.0
0.2.1
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: base58
version: 0.2.0
version: 0.2.1

authors:
- Kirk Haines <[email protected]>
Expand Down
43 changes: 30 additions & 13 deletions src/base58/ss58.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -278,7 +295,7 @@ module Base58
when 17
8
else
0
2
end
end

Expand Down

0 comments on commit b5b005a

Please sign in to comment.