Address Encoding¶
Unlike a (hex-encoded) binary representation of a script, an Ergo address use a Base58-encoding
and therefore has some advantageous characteristics which the binary representation does not offer:
- We can quickly check the integrity of an address via an integrated checksum (a "small-sized datum derived from a block of digital data to detect errors that may have been introduced during its transmission or storage", according to Wikipedia).
- A prefix of the Address shows you the network and address type. In particular, the network prefix prevents you from mistakenly sending mainnet tokens to the testnet Address.
- The Address uses an encoding (namely, Base58, as mentioned) that avoids similarly-looking characters and is friendly to double-clicking and line-breaking in emails.
- An address encodes network type, address type, checksum, and enough information to correspond with particular scripts.
Below is an academic exercise to understand what's going on, you would use AppKit in practice.
A Jupyter notebook was created to demonstrate the process of generating a P2S (Pay-to-Script) address using the provided ErgoTree. The example used in the notebook was the GuapSwap proxy address.
The exploration of the box in the Ergo explorer provided the P2S address in base58 format, along with the serialized ErgoTree encoded in base16. The goal was to replicate the P2S address using the ErgoTree.
The replication process was quite successful, with only a small discrepancy of 4 bytes due to the checksum. The mismatch occurred because not all the parameters used by Ergo in the blake2b256() hash function were known. If the missing parameters can be obtained or located, it should be possible to reproduce the same result accurately.
# Credit: lgd#5847
# Necessary imports
from hashlib import blake2b
from baseconv import base16, base58
# ErgoTree encoded in base16
content_ergotree_base16 = '100f0500053205a09c0108cd0279aed8dea2b2a25316d5d49d13bf51c0b2c1dc696974bb4b0c07b5894e998e5605aa9105040004020580ade20404000ef301100a04000500058092f40105060402040408cd03c6543ac8e8059748b1c6209ee419dd49a19ffaf5712a2f34a9412016a3a1d96708cd035b736bebf0c5393f78329f6894af84d1864c7496cc65ddc250ef60cdd75df52008cd021b63e19ab452c84cdc6687242e8494957b1f11e3750c8c184a8425f8a8171d9b0580a8d6b907d805d601b2a5730000d602b0a47301d9010241639a8c720201c18c720202d6039d99720273027303d604b2a5730400d605b2a5730500d1968304019683020193c17201720393c27201d073069683020193c17204720393c27204d073079683020193c17205720393c27205d073089272027309040605b40a04000580ade2040100d804d601e4e30005d602b0a47300d9010241639a8c720201c18c720202d6039d9c730172027302d6047303959372017304d803d605e4e30105d606b2a5730500d607b2a5730600d196830301968303019272029a9a72037307720592c17206997202720593c2720674e4e3020e830104730883010872049683020192c17207720393c27207730993b1a5730a95937201730bd801d605b2a5730c00d19683020192c17205997202730d93c27205d07204d1730e'.upper()
content_ergotree_base16
'100F0500053205A09C0108CD0279AED8DEA2B2A25316D5D49D13BF51C0B2C1DC696974BB4B0C07B5894E998E5605AA9105040004020580ADE20404000EF301100A04000500058092F40105060402040408CD03C6543AC8E8059748B1C6209EE419DD49A19FFAF5712A2F34A9412016A3A1D96708CD035B736BEBF0C5393F78329F6894AF84D1864C7496CC65DDC250EF60CDD75DF52008CD021B63E19AB452C84CDC6687242E8494957B1F11E3750C8C184A8425F8A8171D9B0580A8D6B907D805D601B2A5730000D602B0A47301D9010241639A8C720201C18C720202D6039D99720273027303D604B2A5730400D605B2A5730500D1968304019683020193C17201720393C27201D073069683020193C17204720393C27204D073079683020193C17205720393C27205D073089272027309040605B40A04000580ADE2040100D804D601E4E30005D602B0A47300D9010241639A8C720201C18C720202D6039D9C730172027302D6047303959372017304D803D605E4E30105D606B2A5730500D607B2A5730600D196830301968303019272029A9A72037307720592C17206997202720593C2720674E4E3020E830104730883010872049683020192C17207720393C27207730993B1A5730A95937201730BD801D605B2A5730C00D19683020192C17205997202730D93C27205D07204D1730E'
# P2S address of the above ErgoTree, in base58
p2s_address_base58 = '3Gax9E764DUmwJKhMrG69fuUHqaBp59WqnVfnU8sLx8kFEVg59sVvxY5LWyZ4rkBYS9MWSZxqRVC55YwEzvYycCCroq9jUYKew6LpQxJUQyu8TbXJUjCLPBCpji3E66vv7PBwb53eWFgf2NrtA98CR5ucZASJEQMfbm4nvUuBEipZVDA3r9HHnh1WnnQiuL5qEzQNHGUothszfq17JRX8Dby511iZD9VKpWSNu5fMcfFCtuEzSKZtUb1a4ndaF8TLyG87ZxMzvniBPFFtYLjVdGytbZteA95vZVMeRAy8wB2J9deLf1frADaNBeeKkbN7gZYoZeq57QGd5jzLY7Nsw4vRLTwASbeoPLTkkFHtRgZmWv2UsF5QgHGuy8osHPpUHcJEUkVSNMSSGbw3YbRkzA9SDLJXdmdZwGeFgRMWSAb5Pp3Xb6UtmxUKGowgHprcmNpct9UTwCSHhNP1jmE8sVSCAnjzLJpUkrrh2VFmcCM4jV3Zmx92H68NjVFrAmt7g71rnsXsNn9RmuN6iQYAGKDSZ8KGBPhJ2uY5e65MZiGV5HHDv6xHHrh3fK9JXCju3fRLynGuNZoBiKCCRDi4iSLcHDPgpc11SY39fv5R4j38KxeJxLDCyyPKRn9dA9wzJz6FL25Xd4xYEKCTVJCfDGn2a5XU4iCV8DojcACG9BBS5m4'
p2s_address_base58
'3Gax9E764DUmwJKhMrG69fuUHqaBp59WqnVfnU8sLx8kFEVg59sVvxY5LWyZ4rkBYS9MWSZxqRVC55YwEzvYycCCroq9jUYKew6LpQxJUQyu8TbXJUjCLPBCpji3E66vv7PBwb53eWFgf2NrtA98CR5ucZASJEQMfbm4nvUuBEipZVDA3r9HHnh1WnnQiuL5qEzQNHGUothszfq17JRX8Dby511iZD9VKpWSNu5fMcfFCtuEzSKZtUb1a4ndaF8TLyG87ZxMzvniBPFFtYLjVdGytbZteA95vZVMeRAy8wB2J9deLf1frADaNBeeKkbN7gZYoZeq57QGd5jzLY7Nsw4vRLTwASbeoPLTkkFHtRgZmWv2UsF5QgHGuy8osHPpUHcJEUkVSNMSSGbw3YbRkzA9SDLJXdmdZwGeFgRMWSAb5Pp3Xb6UtmxUKGowgHprcmNpct9UTwCSHhNP1jmE8sVSCAnjzLJpUkrrh2VFmcCM4jV3Zmx92H68NjVFrAmt7g71rnsXsNn9RmuN6iQYAGKDSZ8KGBPhJ2uY5e65MZiGV5HHDv6xHHrh3fK9JXCju3fRLynGuNZoBiKCCRDi4iSLcHDPgpc11SY39fv5R4j38KxeJxLDCyyPKRn9dA9wzJz6FL25Xd4xYEKCTVJCfDGn2a5XU4iCV8DojcACG9BBS5m4'
# network type byte for mainnet in base16
networktype_mainnet_base16 = '00'
# address type byte for P2S in base16
addresstype_p2s_base16 = '03'
# prefix byte in base16, 0x00 + 0x03
prefix_base16 = networktype_mainnet_base16 + addresstype_p2s_base16
prefix_base16
'03'
# prefix prepended to content bytes
prefix_with_content_base16 = prefix_base16 + content_ergotree_base16
prefix_with_content_base16
'03100F0500053205A09C0108CD0279AED8DEA2B2A25316D5D49D13BF51C0B2C1DC696974BB4B0C07B5894E998E5605AA9105040004020580ADE20404000EF301100A04000500058092F40105060402040408CD03C6543AC8E8059748B1C6209EE419DD49A19FFAF5712A2F34A9412016A3A1D96708CD035B736BEBF0C5393F78329F6894AF84D1864C7496CC65DDC250EF60CDD75DF52008CD021B63E19AB452C84CDC6687242E8494957B1F11E3750C8C184A8425F8A8171D9B0580A8D6B907D805D601B2A5730000D602B0A47301D9010241639A8C720201C18C720202D6039D99720273027303D604B2A5730400D605B2A5730500D1968304019683020193C17201720393C27201D073069683020193C17204720393C27204D073079683020193C17205720393C27205D073089272027309040605B40A04000580ADE2040100D804D601E4E30005D602B0A47300D9010241639A8C720201C18C720202D6039D9C730172027302D6047303959372017304D803D605E4E30105D606B2A5730500D607B2A5730600D196830301968303019272029A9A72037307720592C17206997202720593C2720674E4E3020E830104730883010872049683020192C17207720393C27207730993B1A5730A95937201730BD801D605B2A5730C00D19683020192C17205997202730D93C27205D07204D1730E'
# blake2b256 hash => digest size is 32 bytes == 256 bits
blake2b256_base16 = blake2b(b'03100F0500053205A09C0108CD0279AED8DEA2B2A25316D5D49D13BF51C0B2C1DC696974BB4B0C07B5894E998E5605AA9105040004020580ADE20404000EF301100A04000500058092F40105060402040408CD03C6543AC8E8059748B1C6209EE419DD49A19FFAF5712A2F34A9412016A3A1D96708CD035B736BEBF0C5393F78329F6894AF84D1864C7496CC65DDC250EF60CDD75DF52008CD021B63E19AB452C84CDC6687242E8494957B1F11E3750C8C184A8425F8A8171D9B0580A8D6B907D805D601B2A5730000D602B0A47301D9010241639A8C720201C18C720202D6039D99720273027303D604B2A5730400D605B2A5730500D1968304019683020193C17201720393C27201D073069683020193C17204720393C27204D073079683020193C17205720393C27205D073089272027309040605B40A04000580ADE2040100D804D601E4E30005D602B0A47300D9010241639A8C720201C18C720202D6039D9C730172027302D6047303959372017304D803D605E4E30105D606B2A5730500D607B2A5730600D196830301968303019272029A9A72037307720592C17206997202720593C2720674E4E3020E830104730883010872049683020192C17207720393C27207730993B1A5730A95937201730BD801D605B2A5730C00D19683020192C17205997202730D93C27205D07204D1730E',
digest_size=32).hexdigest().upper()
blake2b256_base16
'CC9663DF4AA48873E5EDA6E7BB68BF0531DAF2C4828D1E71F63F7F8322783853'
# checksum is the first 4 most-significant bytes => the first 8 chars
checksum_base16 = blake2b256_base16[:8]
checksum_base16
'CC9663DF'
# address in base 16: prefix byte || content bytes || checksum bytes
address_base16 = prefix_base16 + content_ergotree_base16 + checksum_base16
address_base16
'03100F0500053205A09C0108CD0279AED8DEA2B2A25316D5D49D13BF51C0B2C1DC696974BB4B0C07B5894E998E5605AA9105040004020580ADE20404000EF301100A04000500058092F40105060402040408CD03C6543AC8E8059748B1C6209EE419DD49A19FFAF5712A2F34A9412016A3A1D96708CD035B736BEBF0C5393F78329F6894AF84D1864C7496CC65DDC250EF60CDD75DF52008CD021B63E19AB452C84CDC6687242E8494957B1F11E3750C8C184A8425F8A8171D9B0580A8D6B907D805D601B2A5730000D602B0A47301D9010241639A8C720201C18C720202D6039D99720273027303D604B2A5730400D605B2A5730500D1968304019683020193C17201720393C27201D073069683020193C17204720393C27204D073079683020193C17205720393C27205D073089272027309040605B40A04000580ADE2040100D804D601E4E30005D602B0A47300D9010241639A8C720201C18C720202D6039D9C730172027302D6047303959372017304D803D605E4E30105D606B2A5730500D607B2A5730600D196830301968303019272029A9A72037307720592C17206997202720593C2720674E4E3020E830104730883010872049683020192C17207720393C27207730993B1A5730A95937201730BD801D605B2A5730C00D19683020192C17205997202730D93C27205D07204D1730ECC9663DF'
# address in base 58
address_base58 = base58.encode(base16.decode(address_base16))
address_base58, p2s_address_base58
('3Gax9E764DUmwJKhMrG69fuUHqaBp59WqnVfnU8sLx8kFEVg59sVvxY5LWyZ4rkBYS9MWSZxqRVC55YwEzvYycCCroq9jUYKew6LpQxJUQyu8TbXJUjCLPBCpji3E66vv7PBwb53eWFgf2NrtA98CR5ucZASJEQMfbm4nvUuBEipZVDA3r9HHnh1WnnQiuL5qEzQNHGUothszfq17JRX8Dby511iZD9VKpWSNu5fMcfFCtuEzSKZtUb1a4ndaF8TLyG87ZxMzvniBPFFtYLjVdGytbZteA95vZVMeRAy8wB2J9deLf1frADaNBeeKkbN7gZYoZeq57QGd5jzLY7Nsw4vRLTwASbeoPLTkkFHtRgZmWv2UsF5QgHGuy8osHPpUHcJEUkVSNMSSGbw3YbRkzA9SDLJXdmdZwGeFgRMWSAb5Pp3Xb6UtmxUKGowgHprcmNpct9UTwCSHhNP1jmE8sVSCAnjzLJpUkrrh2VFmcCM4jV3Zmx92H68NjVFrAmt7g71rnsXsNn9RmuN6iQYAGKDSZ8KGBPhJ2uY5e65MZiGV5HHDv6xHHrh3fK9JXCju3fRLynGuNZoBiKCCRDi4iSLcHDPgpc11SY39fv5R4j38KxeJxLDCyyPKRn9dA9wzJz6FL25Xd4xYEKCTVJCfDGn2a5XU4iCV8DojcACG9Cfr1GJ', '3Gax9E764DUmwJKhMrG69fuUHqaBp59WqnVfnU8sLx8kFEVg59sVvxY5LWyZ4rkBYS9MWSZxqRVC55YwEzvYycCCroq9jUYKew6LpQxJUQyu8TbXJUjCLPBCpji3E66vv7PBwb53eWFgf2NrtA98CR5ucZASJEQMfbm4nvUuBEipZVDA3r9HHnh1WnnQiuL5qEzQNHGUothszfq17JRX8Dby511iZD9VKpWSNu5fMcfFCtuEzSKZtUb1a4ndaF8TLyG87ZxMzvniBPFFtYLjVdGytbZteA95vZVMeRAy8wB2J9deLf1frADaNBeeKkbN7gZYoZeq57QGd5jzLY7Nsw4vRLTwASbeoPLTkkFHtRgZmWv2UsF5QgHGuy8osHPpUHcJEUkVSNMSSGbw3YbRkzA9SDLJXdmdZwGeFgRMWSAb5Pp3Xb6UtmxUKGowgHprcmNpct9UTwCSHhNP1jmE8sVSCAnjzLJpUkrrh2VFmcCM4jV3Zmx92H68NjVFrAmt7g71rnsXsNn9RmuN6iQYAGKDSZ8KGBPhJ2uY5e65MZiGV5HHDv6xHHrh3fK9JXCju3fRLynGuNZoBiKCCRDi4iSLcHDPgpc11SY39fv5R4j38KxeJxLDCyyPKRn9dA9wzJz6FL25Xd4xYEKCTVJCfDGn2a5XU4iCV8DojcACG9BBS5m4')
# decode p2s address into hex
p2s_address_base16 = base16.encode(base58.decode(p2s_address_base58))
p2s_address_base16
'3100F0500053205A09C0108CD0279AED8DEA2B2A25316D5D49D13BF51C0B2C1DC696974BB4B0C07B5894E998E5605AA9105040004020580ADE20404000EF301100A04000500058092F40105060402040408CD03C6543AC8E8059748B1C6209EE419DD49A19FFAF5712A2F34A9412016A3A1D96708CD035B736BEBF0C5393F78329F6894AF84D1864C7496CC65DDC250EF60CDD75DF52008CD021B63E19AB452C84CDC6687242E8494957B1F11E3750C8C184A8425F8A8171D9B0580A8D6B907D805D601B2A5730000D602B0A47301D9010241639A8C720201C18C720202D6039D99720273027303D604B2A5730400D605B2A5730500D1968304019683020193C17201720393C27201D073069683020193C17204720393C27204D073079683020193C17205720393C27205D073089272027309040605B40A04000580ADE2040100D804D601E4E30005D602B0A47300D9010241639A8C720201C18C720202D6039D9C730172027302D6047303959372017304D803D605E4E30105D606B2A5730500D607B2A5730600D196830301968303019272029A9A72037307720592C17206997202720593C2720674E4E3020E830104730883010872049683020192C17207720393C27207730993B1A5730A95937201730BD801D605B2A5730C00D19683020192C17205997202730D93C27205D07204D1730E924D07D3'
"""
Comments:
As we can see, the base16 P2S address is off by the last 4 bytes
when compared to the actual decoded proxy address. This is the checksum,
unfortunately some of the parameters of the hashing function are
not know to us, so we cannot compute it correctly. However,
the rest of the address is exactly the same.
"""