Skip to content

Commit

Permalink
added implementations in more languages
Browse files Browse the repository at this point in the history
  • Loading branch information
lbaird committed May 9, 2021
1 parent 3cf6612 commit ee0d570
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 44 deletions.
23 changes: 14 additions & 9 deletions HIP/hip-15.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ d = int array for the digits of a (using 10 to represent "."), so 0.0.123 is [0,
h = unsigned byte array containing the ledger ID followed by 6 zero bytes
p3 = 26 * 26 * 26
p5 = 26 * 26 * 26 * 26 * 26
s0 = (d[0] + d[2] + d[4] + d[6] + ...) mod 11
s1 = (d[1] + d[3] + d[5] + d[7] + ...) mod 11
s = (...((((d[0] * 31) + d[1]) * 31) + d[2]) * 31 + ... ) * 31 + d[d.length-1]) mod p3
sd0 = (d[0] + d[2] + d[4] + d[6] + ...) mod 11
sd1 = (d[1] + d[3] + d[5] + d[7] + ...) mod 11
sd = (...((((d[0] * 31) + d[1]) * 31) + d[2]) * 31 + ... ) * 31 + d[d.length-1]) mod p3
sh = (...(((h[0] * 31) + h[1]) * 31) + h[2]) * 31 + ... ) * 31 + h[h.length-1]) mod p5
c = (((d.length mod 5) * 11 + s0) * 11 + s1) * p3 + s + sh ) mod p5
c = (c * 1000003) % p5
checksum = c, written as 5 digits in base 26, using a-z
c = (((d.length mod 5) * 11 + sd0) * 11 + sd1) * p3 + sd + sh ) mod p5
cp = (c * 1000003) % p5
checksum = cp, written as 5 digits in base 26, using a-z
```

The checksum is a function of the ledger ID, so that the same address will have different checksums if it is on different ledgers. Cryptographically secure ledger IDs will be implemented as part of state proofs. But for now, the following three ledgers will each have a ledger ID consisting of a single byte:
Expand Down Expand Up @@ -151,10 +151,15 @@ When calculating checksums for all accounts of the form `0.0.x` as `x` counts up

## Reference Implementation

Example code can be downloaded for these languages:
Example code can be downloaded for these languages (the reference implementation is the Java version):
- Pseudocode: [HIP-15-pseudocode.md](https://github.com/hashgraph/hedera-improvement-proposal/assets/hip-15/HIP-15-pseudocode.md)
- Java: [AddressChecksums.java](https://github.com/hashgraph/hedera-improvement-proposal/assets/hip-15/AddressChecksums.java)
- Javascript: [HIP-15-javascript.html](https://github.com/hashgraph/hedera-improvement-proposal/assets/hip-15/HIP-15-javascript.html)
- Spreadsheet: [HIP-15-spreadsheet.xlsx](https://github.com/hashgraph/hedera-improvement-proposal/assets/hip-15/HIP-15-spreadsheet.xlsx)
- Mathematica: [HIP-15-mathematica.nb.txt](https://github.com/hashgraph/hedera-improvement-proposal/assets/hip-15/HIP-15-mathematica.nb.txt)
- All of the above: [HIP-15-all.zip](https://github.com/hashgraph/hedera-improvement-proposal/assets/hip-15/HIP-15-all.zip)


- [AddressChecksums.java.zip](https://github.com/hashgraph/hedera-improvement-proposal/assets/hip-15/AddressChecksums.java.zip)
- [HIP-15-javascript.html.zip](https://github.com/hashgraph/hedera-improvement-proposal/assets/hip-15/HIP-15-javascript.html.zip)

## Rejected Ideas

Expand Down
35 changes: 18 additions & 17 deletions assets/hip-15/AddressChecksums.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ public static ParsedAddress parseAddress(byte[] ledgerId, String addr) {
* h = unsigned byte array containing the ledger ID followed by 6 zero bytes
* p3 = 26 * 26 * 26
* p5 = 26 * 26 * 26 * 26 * 26
* s0 = (d[0] + d[2] + d[4] + d[6] + ...) mod 11
* s1 = (d[1] + d[3] + d[5] + d[7] + ...) mod 11
* s = (...((((d[0] * 31) + d[1]) * 31) + d[2]) * 31 + ... ) * 31 + d[d.length-1]) mod p3
* sd0 = (d[0] + d[2] + d[4] + d[6] + ...) mod 11
* sd1 = (d[1] + d[3] + d[5] + d[7] + ...) mod 11
* sd = (...((((d[0] * 31) + d[1]) * 31) + d[2]) * 31 + ... ) * 31 + d[d.length-1]) mod p3
* sh = (...(((h[0] * 31) + h[1]) * 31) + h[2]) * 31 + ... ) * 31 + h[h.length-1]) mod p5
* c = (((d.length mod 5) * 11 + s0) * 11 + s1) * p3 + s + sh ) mod p5
* c = (c * 1000003) mod p5
* checksum = c, written as 5 digits in base 26, using a-z
* c = (((d.length mod 5) * 11 + sd0) * 11 + sd1) * p3 + sd + sh ) mod p5
* cp = (c * 1000003) mod p5
* checksum = cp, written as 5 digits in base 26, using a-z
* }</pre>
*
* @param ledgerId
Expand All @@ -121,11 +121,12 @@ public static String checksum(byte[] ledgerId, String addr) {
String a = addr; //address, such as "0.0.123"
int[] d = new int[addr.length()]; //digits of address, with 10 for '.', such as [0,10,0,10,1,2,3]
byte[] h = ledgerId; //ledger ID as an array of unsigned bytes
int s0 = 0; //sum of even positions (mod 11)
int s1 = 0; //sum of odd positions (mod 11)
int s = 0; //weighted sum of all positions (mod p3)
int sd0 = 0; //sum of even positions (mod 11)
int sd1 = 0; //sum of odd positions (mod 11)
int sd = 0; //weighted sum of all positions (mod p3)
int sh = 0; //hash of the ledger ID
long c = 0; //the checksum, as a single number (it's a long, to prevent overflow in c * m)
long c = 0; //the checksum, before the final permutation
long cp = 0; //the checksum, as a single number (it's a long, to prevent overflow)
String checksum = ""; //the answer to return
final int p3 = 26 * 26 * 26; //3 digits base 26
final int p5 = 26 * 26 * 26 * 26 * 26; //5 digits base 26
Expand All @@ -138,11 +139,11 @@ public static String checksum(byte[] ledgerId, String addr) {
d[i] = (a.charAt(i) == '.' ? 10 : (a.charAt(i) - ascii_0));
}
for (int i = 0; i < d.length; i++) {
s = (w * s + d[i]) % p3;
sd = (w * sd + d[i]) % p3;
if (i % 2 == 0) {
s0 = (s0 + d[i]) % 11;
sd0 = (sd0 + d[i]) % 11;
} else {
s1 = (s1 + d[i]) % 11;
sd1 = (sd1 + d[i]) % 11;
}
}
for (byte sb : h) {
Expand All @@ -151,11 +152,11 @@ public static String checksum(byte[] ledgerId, String addr) {
for (int i = 0; i < 6; i++) { //process 6 zeros as if they were appended to the ledger ID
sh = (w * sh + 0) % p5;
}
c = ((((a.length() % 5) * 11 + s0) * 11 + s1) * p3 + s + sh) % p5;
c = (c * m) % p5;
c = ((((a.length() % 5) * 11 + sd0) * 11 + sd1) * p3 + sd + sh) % p5;
cp = (c * m) % p5;
for (int i = 0; i < 5; i++) {
checksum = Character.toString(ascii_a + (int)(c % 26)) + checksum;
c /= 26;
checksum = Character.toString(ascii_a + (int)(cp % 26)) + checksum;
cp /= 26;
}

return checksum;
Expand Down
Binary file removed assets/hip-15/AddressChecksums.java.zip
Binary file not shown.
Binary file added assets/hip-15/HIP-15-all.zip
Binary file not shown.
37 changes: 19 additions & 18 deletions assets/hip-15/HIP-15-javascript.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ <h1>HIP-15 Address Checksum Format</h1>
</pre>
<br>
<br>
Last updated May 5, 2021.
Last updated May 9, 2021.
<br>
<br>
(c) 2020-2021 Hedera Hashgraph, released under Apache 2.0 license.
Expand Down Expand Up @@ -139,23 +139,24 @@ <h1>HIP-15 Address Checksum Format</h1>
// h = unsigned byte array containing the ledger ID followed by 6 zero bytes
// p3 = 26 * 26 * 26
// p5 = 26 * 26 * 26 * 26 * 26
// s0 = (d[0] + d[2] + d[4] + d[6] + ...) mod 11
// s1 = (d[1] + d[3] + d[5] + d[7] + ...) mod 11
// s = (...((((d[0] * 31) + d[1]) * 31) + d[2]) * 31 + ... ) * 31 + d[d.length-1]) mod p3
// sd0 = (d[0] + d[2] + d[4] + d[6] + ...) mod 11
// sd1 = (d[1] + d[3] + d[5] + d[7] + ...) mod 11
// sd = (...((((d[0] * 31) + d[1]) * 31) + d[2]) * 31 + ... ) * 31 + d[d.length-1]) mod p3
// sh = (...(((h[0] * 31) + h[1]) * 31) + h[2]) * 31 + ... ) * 31 + h[h.length-1]) mod p5
// c = (((d.length mod 5) * 11 + s0) * 11 + s1) * p3 + s + sh ) mod p5
// c = (c * 1000003) mod p5
// checksum = c, written as 5 digits in base 26, using a-z
// c = (((d.length mod 5) * 11 + sd0) * 11 + sd1) * p3 + sd + sh ) mod p5
// cp = (c * 1000003) mod p5
// checksum = cp, written as 5 digits in base 26, using a-z
//
//in ports to other languages, answer can be a string, digits an int32[] and the rest int32 (or uint32[] and uint32)
function checksum(ledgerId, addr) {
let answer = "";
let d = []; //digits with 10 for ".", so if addr == "0.0.123" then d == [0, 10, 0, 10, 1, 2, 3]
let s0 = 0; //sum of even positions (mod 11)
let s1 = 0; //sum of odd positions (mod 11)
let s = 0; //weighted sum of all positions (mod p3)
let sd0 = 0; //sum of even positions (mod 11)
let sd1 = 0; //sum of odd positions (mod 11)
let sd = 0; //weighted sum of all positions (mod p3)
let sh = 0; //hash of the ledger ID
let c = 0; //the checksum, as a single number
let c = 0; //the checksum, before the final permutation
let cp = 0; //the checksum, as a single number
const p3 = 26 * 26 * 26; //3 digits in base 26
const p5 = 26 * 26 * 26 * 26 * 26; //5 digits in base 26
const ascii_a = "a".charCodeAt(); //97
Expand All @@ -171,22 +172,22 @@ <h1>HIP-15 Address Checksum Format</h1>
d.push(addr[i]=="." ? 10 : parseInt(addr[i],10));
}
for (let i=0; i<d.length; i++) {
s = (w * s + d[i]) % p3;
sd = (w * sd + d[i]) % p3;
if (i % 2 == 0) {
s0 = (s0 + d[i]) % 11;
sd0 = (sd0 + d[i]) % 11;
} else {
s1 = (s1 + d[i]) % 11;
sd1 = (sd1 + d[i]) % 11;
}
}
for (let i=0; i<h.length; i++) {
sh = (w * sh + h[i]) % p5;
}
c = ((((addr.length % 5) * 11 + s0) * 11 + s1) * p3 + s + sh) % p5;
c = (c * m) % p5;
c = ((((addr.length % 5) * 11 + sd0) * 11 + sd1) * p3 + sd + sh) % p5;
cp = (c * m) % p5;

for (let i=0; i<5; i++) {
answer = String.fromCharCode(ascii_a + (c % 26)) + answer;
c /= 26;
answer = String.fromCharCode(ascii_a + (cp % 26)) + answer;
cp /= 26;
}

return answer;
Expand Down
Binary file removed assets/hip-15/HIP-15-javascript.html.zip
Binary file not shown.
55 changes: 55 additions & 0 deletions assets/hip-15/HIP-15-mathematica.nb.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
(* (c) 2020-2021 Hedera Hashgraph,released under Apache 2.0 license.*)

(* find the HIP-15 checksum for a given Hedera address and ledger ID *)

checksum[ledgerId_, address_] :=
Module[{h, d, p3, p5, s0, s1, s, sh, c, cp},
h = Join[ledgerId, {0, 0, 0, 0, 0, 0}];
d = (ToCharacterCode[address] //. {46 -> 10 + 48}) - 48; (* "." is 46, "0" is 48 *)
p3 = 26^3;
p5 = 26^5;
sd0 = Mod[Total[d[[1 ;; Length[d] ;; 2]]], 11];
sd1 = Mod[Total[d[[2 ;; Length[d] ;; 2]]], 11];
sd = Fold[Mod[#1*31 + #2, p3] &, d];
sh = Fold[Mod[#1*31 + #2, p5] &, h];
c = Mod[((Mod[Length[d], 5]*11 + sd0)*11 + sd1)*p3 + sd + sh, p5];
cp = Mod[c*1000003, p5];
StringJoin @@
FromCharacterCode[(IntegerDigits[cp, 26, 5] +
ToCharacterCode["a"][[1]])]];

(* Output the given ledger ID and address along with the calculated \
checksum *)

output[ledgerId_, address_] :=
Print["ledger: ", ledgerId, " address: ", address, "-",
checksum[ledgerId, address]];

(* Output checksums for all the examples given in HIP-15 *)

addresses = {"0.0.1", "0.0.4", "0.0.5", "0.0.6", "0.0.12", "0.0.123",
"0.0.1234567890", "12.345.6789", "1.23.456"};
output[{0}, #] & /@ addresses;
output[{161, 255, 1}, #] & /@ addresses;

(*
OUTPUT:
ledger:{0} address:0.0.1-dfkxr
ledger:{0} address:0.0.4-cjcuq
ledger:{0} address:0.0.5-ktach
ledger:{0} address:0.0.6-tcxjy
ledger:{0} address:0.0.12-uuuup
ledger:{0} address:0.0.123-vfmkw
ledger:{0} address:0.0.1234567890-zbhlt
ledger:{0} address:12.345.6789-aoyyt
ledger:{0} address:1.23.456-adpbr
ledger:{161,255,1} address:0.0.1-xzlgq
ledger:{161,255,1} address:0.0.4-xdddp
ledger:{161,255,1} address:0.0.5-fnalg
ledger:{161,255,1} address:0.0.6-nwxsx
ledger:{161,255,1} address:0.0.12-povdo
ledger:{161,255,1} address:0.0.123-pzmtv
ledger:{161,255,1} address:0.0.1234567890-tvhus
ledger:{161,255,1} address:12.345.6789-vizhs
ledger:{161,255,1} address:1.23.456-uxpkq
*)
54 changes: 54 additions & 0 deletions assets/hip-15/HIP-15-pseudocode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# HIP-15 Checksum calculation Pseudocode

The checksum (such as `vfmkw`) is calculated from the no-checksum address (such as `0.0.123` ) by this algorithm:

```
a = a valid no-checksum address string, such as 0.0.123
d = int array for the digits of a (using 10 to represent "."), so 0.0.123 is [0,10,0,10,1,2,3]
h = unsigned byte array containing the ledger ID followed by 6 zero bytes
p3 = 26 * 26 * 26
p5 = 26 * 26 * 26 * 26 * 26
sd0 = (d[0] + d[2] + d[4] + d[6] + ...) mod 11
sd1 = (d[1] + d[3] + d[5] + d[7] + ...) mod 11
sd = (...((((d[0] * 31) + d[1]) * 31) + d[2]) * 31 + ... ) * 31 + d[d.length-1]) mod p3
sh = (...(((h[0] * 31) + h[1]) * 31) + h[2]) * 31 + ... ) * 31 + h[h.length-1]) mod p5
c = (((d.length mod 5) * 11 + sd0) * 11 + sd1) * p3 + sd + sh ) mod p5
cp = (c * 1000003) % p5
checksum = cp, written as 5 digits in base 26, using a-z
```

Cryptographically secure ledger IDs will be implemented as part of state proofs. But for now, the following three ledgers will each have a ledger ID consisting of a single byte:

```
0 = Hedera mainnet
1 = stable testnet
2 = preview net
```

Test vectors:

```
For ledger ID 0x00:
0.0.1-dfkxr
0.0.4-cjcuq
0.0.5-ktach
0.0.6-tcxjy
0.0.12-uuuup
0.0.123-vfmkw
0.0.1234567890-zbhlt
12.345.6789-aoyyt
1.23.456-adpbr
For ledger ID 0xa1ff01:
0.0.1-xzlgq
0.0.4-xdddp
0.0.5-fnalg
0.0.6-nwxsx
0.0.12-povdo
0.0.123-pzmtv
0.0.1234567890-tvhus
12.345.6789-vizhs
1.23.456-uxpkq
```

(c) 2020-2021 Hedera Hashgraph,released under Apache 2.0 license.
Binary file added assets/hip-15/HIP-15-spreadsheet.xlsx
Binary file not shown.

0 comments on commit ee0d570

Please sign in to comment.