September 2017 Update
This commit is contained in:
parent
bba7bfd280
commit
ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions
|
|
@ -0,0 +1,16 @@
|
|||
USING: byte-arrays checksums checksums.sha io.binary kernel math
|
||||
math.parser sequences ;
|
||||
IN: rosetta-code.bitcoin.validation
|
||||
|
||||
CONSTANT: ALPHABET "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
|
||||
: base58>bigint ( str -- n )
|
||||
[ ALPHABET index ]
|
||||
[ [ 58 * ] [ + ] bi* ] map-reduce ;
|
||||
|
||||
: base58> ( str -- bytes ) base58>bigint 25 >be ;
|
||||
|
||||
: btc-checksum ( bytes -- checksum-bytes )
|
||||
21 head 2 [ sha-256 checksum-bytes ] times 4 head ;
|
||||
|
||||
: btc-valid? ( str -- ? ) base58> [ btc-checksum ] [ 4 tail* ] bi = ;
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
' version 05-04-2017
|
||||
' compile with: fbc -s console
|
||||
|
||||
' function adapted from the SHA-256 task
|
||||
Function SHA_256(test_str As String, bitcoin As ULong = 0) As String
|
||||
|
||||
#Macro Ch (x, y, z)
|
||||
(((x) And (y)) Xor ((Not (x)) And z))
|
||||
#EndMacro
|
||||
|
||||
#Macro Maj (x, y, z)
|
||||
(((x) And (y)) Xor ((x) And (z)) Xor ((y) And (z)))
|
||||
#EndMacro
|
||||
|
||||
#Macro sigma0 (x)
|
||||
(((x) Shr 2 Or (x) Shl 30) Xor ((x) Shr 13 Or (x) Shl 19) Xor ((x) Shr 22 Or (x) Shl 10))
|
||||
#EndMacro
|
||||
|
||||
#Macro sigma1 (x)
|
||||
(((x) Shr 6 Or (x) Shl 26) Xor ((x) Shr 11 Or (x) Shl 21) Xor ((x) Shr 25 Or (x) Shl 7))
|
||||
#EndMacro
|
||||
|
||||
#Macro sigma2 (x)
|
||||
(((x) Shr 7 Or (x) Shl 25) Xor ((x) Shr 18 Or (x) Shl 14) Xor ((x) Shr 3))
|
||||
#EndMacro
|
||||
|
||||
#Macro sigma3 (x)
|
||||
(((x) Shr 17 Or (x) Shl 15) Xor ((x) Shr 19 Or (x) Shl 13) Xor ((x) Shr 10))
|
||||
#EndMacro
|
||||
|
||||
Dim As String message = test_str ' strings are passed as ByRef's
|
||||
|
||||
Dim As Long i, j
|
||||
Dim As UByte Ptr ww1
|
||||
Dim As UInteger<32> Ptr ww4
|
||||
|
||||
Do
|
||||
Dim As ULongInt l = Len(message)
|
||||
' set the first bit after the message to 1
|
||||
message = message + Chr(1 Shl 7)
|
||||
' add one char to the length
|
||||
Dim As ULong padding = 64 - ((l +1) Mod (512 \ 8)) ' 512 \ 8 = 64 char.
|
||||
|
||||
' check if we have enough room for inserting the length
|
||||
If padding < 8 Then padding = padding + 64
|
||||
|
||||
message = message + String(padding, Chr(0)) ' adjust length
|
||||
Dim As ULong l1 = Len(message) ' new length
|
||||
|
||||
l = l * 8 ' orignal length in bits
|
||||
' create ubyte ptr to point to l ( = length in bits)
|
||||
Dim As UByte Ptr ub_ptr = Cast(UByte Ptr, @l)
|
||||
|
||||
For i = 0 To 7 'copy length of message to the last 8 bytes
|
||||
message[l1 -1 - i] = ub_ptr[i]
|
||||
Next
|
||||
|
||||
'table of constants
|
||||
Dim As UInteger<32> K(0 To ...) = _
|
||||
{ &H428a2f98, &H71374491, &Hb5c0fbcf, &He9b5dba5, &H3956c25b, &H59f111f1, _
|
||||
&H923f82a4, &Hab1c5ed5, &Hd807aa98, &H12835b01, &H243185be, &H550c7dc3, _
|
||||
&H72be5d74, &H80deb1fe, &H9bdc06a7, &Hc19bf174, &He49b69c1, &Hefbe4786, _
|
||||
&H0fc19dc6, &H240ca1cc, &H2de92c6f, &H4a7484aa, &H5cb0a9dc, &H76f988da, _
|
||||
&H983e5152, &Ha831c66d, &Hb00327c8, &Hbf597fc7, &Hc6e00bf3, &Hd5a79147, _
|
||||
&H06ca6351, &H14292967, &H27b70a85, &H2e1b2138, &H4d2c6dfc, &H53380d13, _
|
||||
&H650a7354, &H766a0abb, &H81c2c92e, &H92722c85, &Ha2bfe8a1, &Ha81a664b, _
|
||||
&Hc24b8b70, &Hc76c51a3, &Hd192e819, &Hd6990624, &Hf40e3585, &H106aa070, _
|
||||
&H19a4c116, &H1e376c08, &H2748774c, &H34b0bcb5, &H391c0cb3, &H4ed8aa4a, _
|
||||
&H5b9cca4f, &H682e6ff3, &H748f82ee, &H78a5636f, &H84c87814, &H8cc70208, _
|
||||
&H90befffa, &Ha4506ceb, &Hbef9a3f7, &Hc67178f2 }
|
||||
|
||||
Dim As UInteger<32> h0 = &H6a09e667
|
||||
Dim As UInteger<32> h1 = &Hbb67ae85
|
||||
Dim As UInteger<32> h2 = &H3c6ef372
|
||||
Dim As UInteger<32> h3 = &Ha54ff53a
|
||||
Dim As UInteger<32> h4 = &H510e527f
|
||||
Dim As UInteger<32> h5 = &H9b05688c
|
||||
Dim As UInteger<32> h6 = &H1f83d9ab
|
||||
Dim As UInteger<32> h7 = &H5be0cd19
|
||||
Dim As UInteger<32> a, b, c, d, e, f, g, h
|
||||
Dim As UInteger<32> t1, t2, w(0 To 63)
|
||||
|
||||
For j = 0 To (l1 -1) \ 64 ' split into block of 64 bytes
|
||||
ww1 = Cast(UByte Ptr, @message[j * 64])
|
||||
ww4 = Cast(UInteger<32> Ptr, @message[j * 64])
|
||||
|
||||
For i = 0 To 60 Step 4 'little endian -> big endian
|
||||
Swap ww1[i ], ww1[i +3]
|
||||
Swap ww1[i +1], ww1[i +2]
|
||||
Next
|
||||
|
||||
For i = 0 To 15 ' copy the 16 32bit block into the array
|
||||
W(i) = ww4[i]
|
||||
Next
|
||||
|
||||
For i = 16 To 63 ' fill the rest of the array
|
||||
w(i) = sigma3(W(i -2)) + W(i -7) + sigma2(W(i -15)) + W(i -16)
|
||||
Next
|
||||
|
||||
a = h0 : b = h1 : c = h2 : d = h3 : e = h4 : f = h5 : g = h6 : h = h7
|
||||
|
||||
For i = 0 To 63
|
||||
t1 = h + sigma1(e) + Ch(e, f, g) + K(i) + W(i)
|
||||
t2 = sigma0(a) + Maj(a, b, c)
|
||||
h = g : g = f : f = e
|
||||
e = d + t1
|
||||
d = c : c = b : b = a
|
||||
a = t1 + t2
|
||||
Next
|
||||
|
||||
h0 += a : h1 += b : h2 += c : h3 += d
|
||||
h4 += e : h5 += f : h6 += g : h7 += h
|
||||
|
||||
Next j
|
||||
|
||||
Dim As String answer = Hex(h0, 8) + Hex(h1, 8) + Hex(h2, 8) + Hex(h3, 8) _
|
||||
+ Hex(h4, 8) + Hex(h5, 8) + Hex(h6, 8) + Hex(h7, 8)
|
||||
|
||||
If bitcoin = 0 Then
|
||||
Return LCase(answer)
|
||||
Else 'conver hex value's to integer value's
|
||||
message = String(32,0)
|
||||
For i = 0 To 31
|
||||
message[i] = Val("&h" + Mid(answer, i * 2 + 1, 2))
|
||||
Next
|
||||
bitcoin = 0
|
||||
End If
|
||||
|
||||
Loop
|
||||
|
||||
End Function
|
||||
|
||||
Function conv_base58(bitcoin_address As String) As String
|
||||
|
||||
Dim As String base58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
Dim As String tmp = String(24, 0)
|
||||
Dim As Long x, y, z
|
||||
|
||||
For x = 1 To Len(bitcoin_address) -1
|
||||
z = InStr(base58, Chr(bitcoin_address[x])) -1
|
||||
If z = -1 Then
|
||||
Print " bitcoin address contains illegal character"
|
||||
Return ""
|
||||
End If
|
||||
For y = 23 To 0 Step -1
|
||||
z = z + tmp[y] * 58
|
||||
tmp[y] = z And 255 ' test_str[y] = z Mod 256
|
||||
z Shr= 8 ' z \= 256
|
||||
Next
|
||||
If z <> 0 Then
|
||||
Print " bitcoin address is to long"
|
||||
Return ""
|
||||
End If
|
||||
Next
|
||||
|
||||
z = InStr(base58, Chr(bitcoin_address[0])) -1
|
||||
Return Chr(z) + tmp
|
||||
|
||||
End Function
|
||||
|
||||
' ------=< MAIN >=------
|
||||
|
||||
Data "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" ' original
|
||||
Data "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j" ' checksum changed
|
||||
Data "1NAGa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" ' address changed
|
||||
Data "0AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" ' only 1 or 3 as first char. allowed
|
||||
Data "1AGNa15ZQXAZUgFlqJ2i7Z2DPU2J6hW62i" ' illegal character in address
|
||||
|
||||
Dim As String tmp, result, checksum, bitcoin_address
|
||||
Dim As Long i, j
|
||||
|
||||
For i = 1 To 5
|
||||
|
||||
Read bitcoin_address
|
||||
Print "Bitcoin address: "; bitcoin_address;
|
||||
tmp = Left(bitcoin_address,1)
|
||||
|
||||
If tmp <> "1" And tmp <> "3" Then
|
||||
Print " first character is not 1 or 3"
|
||||
Continue For
|
||||
End If
|
||||
|
||||
' convert bitcoinaddress
|
||||
tmp = conv_base58(bitcoin_address)
|
||||
If tmp = "" Then Continue For
|
||||
' get the checksum, last 4 digits
|
||||
For j As Long = 21 To 24
|
||||
checksum = checksum + LCase(Hex(tmp[j], 2))
|
||||
Next
|
||||
|
||||
' send the first 21 characters to the SHA 256 routine
|
||||
result = SHA_256(Left(tmp, 21), 2)
|
||||
result = Left(result, 8) ' get the checksum (the first 8 digits (hex))
|
||||
If checksum = result Then ' test the found checksum against
|
||||
Print " is valid" ' the one from the address
|
||||
Else
|
||||
Print " is not valid, checksum fails"
|
||||
End If
|
||||
|
||||
Next
|
||||
|
||||
' empty keyboard buffer
|
||||
While InKey <> "" : Wend
|
||||
Print : Print "hit any key to end program"
|
||||
Sleep
|
||||
End
|
||||
|
|
@ -22,7 +22,7 @@ validityCheck encodedAddress =
|
|||
Just ev ->
|
||||
let address = toBytes ev
|
||||
addressLength = length address
|
||||
in if addressLength > 24
|
||||
in if addressLength > 25
|
||||
then Left "Address length exceeds 25 bytes"
|
||||
else
|
||||
if addressLength < 4
|
||||
|
|
|
|||
|
|
@ -1,63 +1,71 @@
|
|||
private final static String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public static void main(String[] args) {
|
||||
Assert("Test 01", ValidateBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"), true);
|
||||
Assert("Test 02", ValidateBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j"), false);
|
||||
Assert("Test 03", ValidateBitcoinAddress("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"), true);
|
||||
Assert("Test 04", ValidateBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X"), false);
|
||||
Assert("Test 05", ValidateBitcoinAddress("1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"), false);
|
||||
Assert("Test 06", ValidateBitcoinAddress("1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"), false);
|
||||
Assert("Test 07", ValidateBitcoinAddress("BZbvjr"), false);
|
||||
Assert("Test 08", ValidateBitcoinAddress("i55j"), false);
|
||||
Assert("Test 09", ValidateBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!"), false);
|
||||
Assert("Test 10", ValidateBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz"), false);
|
||||
Assert("Test 11", ValidateBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz"), false);
|
||||
Assert("Test 12", ValidateBitcoinAddress("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9"), false);
|
||||
Assert("Test 13", ValidateBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"), false);
|
||||
}
|
||||
public class BitcoinAddressValidator {
|
||||
|
||||
public static boolean ValidateBitcoinAddress(String addr) {
|
||||
if (addr.length() < 26 || addr.length() > 35) return false;
|
||||
byte[] decoded = DecodeBase58(addr, 58, 25);
|
||||
if (decoded == null) return false;
|
||||
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
byte[] hash = Sha256(decoded, 0, 21, 2);
|
||||
public static boolean validateBitcoinAddress(String addr) {
|
||||
if (addr.length() < 26 || addr.length() > 35)
|
||||
return false;
|
||||
byte[] decoded = decodeBase58(addr, 25);
|
||||
if (decoded == null)
|
||||
return false;
|
||||
|
||||
return Arrays.equals(Arrays.copyOfRange(hash, 0, 4), Arrays.copyOfRange(decoded, 21, 25));
|
||||
}
|
||||
byte[] hash1 = sha256(Arrays.copyOfRange(decoded, 0, 21));
|
||||
byte[] hash2 = sha256(hash1);
|
||||
|
||||
private static byte[] DecodeBase58(String input, int base, int len) {
|
||||
byte[] output = new byte[len];
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
char t = input.charAt(i);
|
||||
return Arrays.equals(Arrays.copyOfRange(hash2, 0, 4), Arrays.copyOfRange(decoded, 21, 25));
|
||||
}
|
||||
|
||||
int p = ALPHABET.indexOf(t);
|
||||
if (p == -1) return null;
|
||||
for (int j = len - 1; j > 0; j--, p /= 256) {
|
||||
p += base * (output[j] & 0xFF);
|
||||
output[j] = (byte) (p % 256);
|
||||
private static byte[] decodeBase58(String input, int len) {
|
||||
byte[] output = new byte[len];
|
||||
for (char t : input.toCharArray()) {
|
||||
int p = ALPHABET.indexOf(t);
|
||||
if (p == -1)
|
||||
return null;
|
||||
for (int j = len - 1; j >= 0; j--) {
|
||||
p += 58 * (output[j] & 0xFF);
|
||||
output[j] = (byte) (p % 256);
|
||||
p /= 256;
|
||||
}
|
||||
if (p != 0)
|
||||
return null;
|
||||
}
|
||||
if (p != 0) return null;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
private static byte[] sha256(byte[] data) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(data);
|
||||
return md.digest();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] Sha256(byte[] data, int start, int len, int recursion) {
|
||||
if (recursion == 0) return data;
|
||||
public static void main(String[] args) {
|
||||
assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", true);
|
||||
assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j", false);
|
||||
assertBitcoin("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", true);
|
||||
assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X", false);
|
||||
assertBitcoin("1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", false);
|
||||
assertBitcoin("1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", false);
|
||||
assertBitcoin("BZbvjr", false);
|
||||
assertBitcoin("i55j", false);
|
||||
assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!", false);
|
||||
assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz", false);
|
||||
assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz", false);
|
||||
assertBitcoin("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9", false);
|
||||
assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I", false);
|
||||
}
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(Arrays.copyOfRange(data, start, start + len));
|
||||
return Sha256(md.digest(), 0, 32, recursion - 1);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
private static void assertBitcoin(String address, boolean expected) {
|
||||
boolean actual = validateBitcoinAddress(address);
|
||||
if (actual != expected)
|
||||
throw new AssertionError(String.format("Expected %s for %s, but got %s.", expected, address, actual));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Assert(String name, boolean value, boolean expected) {
|
||||
if (value ^ expected)
|
||||
throw new Error("Test " + name + " failed");
|
||||
else
|
||||
System.out.println(name + " passed");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
import java.security.MessageDigest
|
||||
|
||||
object Bitcoin {
|
||||
private const val ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
|
||||
private fun ByteArray.contentEquals(other: ByteArray): Boolean {
|
||||
if (this.size != other.size) return false
|
||||
return (0 until this.size).none { this[it] != other[it] }
|
||||
}
|
||||
|
||||
private fun decodeBase58(input: String): ByteArray? {
|
||||
val output = ByteArray(25)
|
||||
for (c in input) {
|
||||
var p = ALPHABET.indexOf(c)
|
||||
if (p == -1) return null
|
||||
for (j in 24 downTo 1) {
|
||||
p += 58 * (output[j].toInt() and 0xff)
|
||||
output[j] = (p % 256).toByte()
|
||||
p = p shr 8
|
||||
}
|
||||
if (p != 0) return null
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
private fun sha256(data: ByteArray, start: Int, len: Int, recursion: Int): ByteArray {
|
||||
if (recursion == 0) return data
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
md.update(data.sliceArray(start until start + len))
|
||||
return sha256(md.digest(), 0, 32, recursion - 1)
|
||||
}
|
||||
|
||||
fun validateAddress(address: String): Boolean {
|
||||
if (address.length !in 26..35) return false
|
||||
val decoded = decodeBase58(address)
|
||||
if (decoded == null) return false
|
||||
val hash = sha256(decoded, 0, 21, 2)
|
||||
return hash.sliceArray(0..3).contentEquals(decoded.sliceArray(21..24))
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val addresses = arrayOf(
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",
|
||||
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"BZbvjr",
|
||||
"i55j",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz",
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"
|
||||
)
|
||||
for (address in addresses)
|
||||
println("${address.padEnd(36)} -> ${if (Bitcoin.validateAddress(address)) "valid" else "invalid"}")
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
extern crate crypto;
|
||||
|
||||
use crypto::digest::Digest;
|
||||
use crypto::sha2::Sha256;
|
||||
|
||||
const DIGITS58: [char; 58] = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
|
||||
|
||||
fn main() {
|
||||
println!("{}", validate_address("1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i"));
|
||||
println!("{}", validate_address("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"));
|
||||
println!("{}", validate_address("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j"));
|
||||
println!("{}", validate_address("17NdbrSGoUotzeGCcMMC?nFkEvLymoou9j"));
|
||||
}
|
||||
|
||||
fn validate_address(address: &str) -> bool {
|
||||
let decoded = match from_base58(address, 25) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return false
|
||||
};
|
||||
if decoded[0] != 0 {
|
||||
return false;
|
||||
}
|
||||
let mut sha = Sha256::new();
|
||||
sha.input(&decoded[0..21]);
|
||||
let mut first_round = vec![0u8; sha.output_bytes()];
|
||||
sha.result(&mut first_round);
|
||||
sha.reset();
|
||||
|
||||
sha.input(&first_round);
|
||||
let mut second_round = vec![0u8; sha.output_bytes()];
|
||||
sha.result(&mut second_round);
|
||||
if second_round[0..4] != decoded[21..25] {
|
||||
return false
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn from_base58(encoded: &str, size: usize) -> Result<Vec<u8>, String> {
|
||||
let mut res: Vec<u8> = vec![0; size];
|
||||
for base58_value in encoded.chars() {
|
||||
let mut value: u32 = match DIGITS58
|
||||
.iter()
|
||||
.position(|x| *x == base58_value){
|
||||
Some(x) => x as u32,
|
||||
None => return Err(String::from("Invalid character found in encoded string."))
|
||||
};
|
||||
for result_index in (0..size).rev() {
|
||||
value += 58 * res[result_index] as u32;
|
||||
res[result_index] = (value % 256) as u8;
|
||||
value /= 256;
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
var [const] MsgHash=Import("zklMsgHash"); // SHA-256, etc
|
||||
const symbols="123456789" // 58 characters: no cap i,o; ell, zero
|
||||
"ABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||
"abcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
fcn unbase58(str){ // --> Data (byte bucket)
|
||||
out:=Data().fill(0,25);
|
||||
str.pump(Void,symbols.index,'wrap(n){ // throws on out of range
|
||||
[24..0,-1].reduce('wrap(c,idx){
|
||||
c+=58*out[idx]; // throws if not enough data
|
||||
out[idx]=c;
|
||||
c/256; // should be zero when done
|
||||
},n) : if(_) throw(Exception.ValueError("address too long"));
|
||||
});
|
||||
out;
|
||||
}
|
||||
|
||||
fcn coinValide(addr){
|
||||
reg dec,chkSum;
|
||||
try{ dec=unbase58(addr) }catch{ return(False) }
|
||||
chkSum=dec[-4,*]; dec.del(21,*);
|
||||
// hash then hash the hash --> binary hash (instead of hex string)
|
||||
(2).reduce(MsgHash.SHA256.fp1(1,dec),dec); // dec is i/o buffer
|
||||
dec[0,4]==chkSum;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
T("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X", // checksum changed, original data.
|
||||
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", // data changed, original checksum.
|
||||
"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", // invalid chars
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz", // too long
|
||||
).apply(coinValide).println();
|
||||
Loading…
Add table
Add a link
Reference in a new issue