B
This commit is contained in:
parent
e5e8880e41
commit
518da4a923
1019 changed files with 15877 additions and 0 deletions
22
Task/Bitcoin-address-validation/0DESCRIPTION
Normal file
22
Task/Bitcoin-address-validation/0DESCRIPTION
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
Write a program that takes a [[wp:bitcoin|bitcoin address]] as argument, and checks whether or not this address is valid.
|
||||
|
||||
A bitcoin address uses a base58 encoding, which uses an alphabet of the characters 0 .. 9, A ..Z, a .. z, but without the four characters 0, O, I and l.
|
||||
|
||||
With this encoding, a bitcoin address encodes 25 bytes:
|
||||
* the first byte is the version number, which will be zero for this task ;
|
||||
* the next twenty bytes are a [[RIPEMD-160]] digest, but you don't have to know that for this task: you can consider them a pure arbitrary data ;
|
||||
* the last four bytes are a checksum check. They are the first four bytes of a double [[SHA-256]] digest of the previous 21 bytes.
|
||||
|
||||
To check the bitcoin address, you must read the first twenty-one bytes, compute the checksum, and check that it corresponds to the last four bytes.
|
||||
|
||||
The program can either return a boolean value or throw an exception when not valid.
|
||||
|
||||
You can use a digest library for [[SHA-256]].
|
||||
|
||||
Here is an example of a bitcoin address:
|
||||
|
||||
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i
|
||||
|
||||
It does not belong to anyone. It is part of the test suite of the bitcoin software. You can change a few characters in this string and check that it will fail the test.
|
||||
|
||||
''extra credit'': allow your code to deal with [http://bitcoin.stackexchange.com/questions/3059/what-is-a-compressed-bitcoin-key compressed keys]
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
const char *coin_err;
|
||||
#define bail(s) { coin_err = s; return 0; }
|
||||
|
||||
int unbase58(const char *s, unsigned char *out) {
|
||||
static const char *tmpl = "123456789"
|
||||
"ABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||
"abcdefghijkmnopqrstuvwxyz";
|
||||
int i, j, c;
|
||||
const char *p;
|
||||
|
||||
memset(out, 0, 25);
|
||||
for (i = 0; s[i]; i++) {
|
||||
if (!(p = strchr(tmpl, s[i])))
|
||||
bail("bad char");
|
||||
|
||||
c = p - tmpl;
|
||||
for (j = 25; j--; ) {
|
||||
c += 58 * out[j];
|
||||
out[j] = c % 256;
|
||||
c /= 256;
|
||||
}
|
||||
|
||||
if (c) bail("address too long");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int valid(const char *s) {
|
||||
unsigned char dec[32], d1[SHA256_DIGEST_LENGTH], d2[SHA256_DIGEST_LENGTH];
|
||||
|
||||
coin_err = "";
|
||||
if (!unbase58(s, dec)) return 0;
|
||||
|
||||
SHA256(SHA256(dec, 21, d1), SHA256_DIGEST_LENGTH, d2);
|
||||
|
||||
if (memcmp(dec + 21, d2, 4))
|
||||
bail("bad digest");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main (void) {
|
||||
const char *s[] = {
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",
|
||||
0 };
|
||||
int i;
|
||||
for (i = 0; s[i]; i++) {
|
||||
int status = valid(s[i]);
|
||||
printf("%s: %s\n", s[i], status ? "Ok" : coin_err);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
my @b58 = qw{
|
||||
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
|
||||
};
|
||||
my %b58 = map { $b58[$_] => $_ } 0 .. 57;
|
||||
|
||||
sub unbase58 {
|
||||
use integer;
|
||||
my @out;
|
||||
for my $c ( map { $b58{$_} } shift =~ /./g ) {
|
||||
for (my $j = 25; $j--; ) {
|
||||
$c += 58 * ($out[$j] // 0);
|
||||
$out[$j] = $c % 256;
|
||||
$c /= 256;
|
||||
}
|
||||
}
|
||||
return @out;
|
||||
}
|
||||
|
||||
sub check_bitcoin_address {
|
||||
use Digest::SHA qw(sha256);
|
||||
my @byte = unbase58 shift;
|
||||
die unless join('', map { chr } @byte[21..24]) eq
|
||||
substr sha256(sha256 pack 'C*', @byte[0..20]), 0, 4;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from hashlib import sha256
|
||||
|
||||
digits58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
|
||||
def decode_base58(bc, length):
|
||||
n = 0
|
||||
for char in bc:
|
||||
n = n * 58 + digits58.index(char)
|
||||
return n.to_bytes(length, 'big')
|
||||
|
||||
def check_bc(bc):
|
||||
bcbytes = decode_base58(bc, 25)
|
||||
return bcbytes[-4:] == sha256(sha256(bcbytes[:-4]).digest()).digest()[:4]
|
||||
|
||||
if __name__ == '__main__':
|
||||
bc = '1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'
|
||||
assert check_bc(bc)
|
||||
assert not check_bc( bc.replace('N', 'P', 1) )
|
||||
assert check_bc('1111111111111111111114oLvT2')
|
||||
assert check_bc("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j")
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
>>> n = 2491969579123783355964723219455906992268673266682165637887
|
||||
>>> length = 25
|
||||
>>> list( reversed(range(length)) )
|
||||
[24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
||||
>>> assert n.to_bytes(length, 'big') == bytes( (n >> i*8) & 0xff for i in reversed(range(length)))
|
||||
>>>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package require sha256
|
||||
|
||||
# Generate a large and boring piece of code to do the decoding of
|
||||
# base58-encoded data.
|
||||
apply {{} {
|
||||
set chars "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
set i -1
|
||||
foreach c [split $chars ""] {
|
||||
lappend map $c "return -level 0 [incr i]"
|
||||
}
|
||||
lappend map default {return -code error "bad character \"$c\""}
|
||||
proc base58decode str [string map [list @BODY@ [list $map]] {
|
||||
set num 0
|
||||
set count [expr {ceil(log(58**[string length $str])/log(256))}]
|
||||
foreach c [split $str {}] {
|
||||
set num [expr {$num*58+[switch $c @BODY@]}]
|
||||
}
|
||||
for {set i 0} {$i < $count} {incr i} {
|
||||
append result [binary format c [expr {$num & 255}]]
|
||||
set num [expr {$num >> 8}]
|
||||
}
|
||||
return [string reverse $result]
|
||||
}]
|
||||
}}
|
||||
|
||||
# How to check bitcoin address validity
|
||||
proc bitcoin_addressValid {address} {
|
||||
set a [base58decode $address]
|
||||
set ck [sha2::sha256 -bin [sha2::sha256 -bin [string range $a 0 end-4]]]
|
||||
if {[string range $a end-3 end] ne [string range $ck 0 3]} {
|
||||
return -code error "signature does not match"
|
||||
}
|
||||
return "$address is ok"
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
puts [bitcoin_addressValid 1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9]
|
||||
puts [bitcoin_addressValid 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i]
|
||||
Loading…
Add table
Add a link
Reference in a new issue