Another update from ingydotnet^djgoku

This commit is contained in:
Ingy döt Net 2015-11-18 06:14:39 +00:00
parent 91df62d461
commit 948b86eafa
7604 changed files with 108452 additions and 22726 deletions

View file

@ -24,5 +24,3 @@ Here is an example of a bitcoin address:
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]

View file

@ -0,0 +1,53 @@
import Data.List (unfoldr, elemIndex)
import Data.Binary (Word8)
import Crypto.Hash.SHA256 (hash)
import Data.ByteString (unpack, pack)
-- Convert from base58 encoded value to Integer
decode58 :: String -> Maybe Integer
decode58 = foldl (\v d -> (+) <$> ((58*) <$> v) <*> (fromIntegral <$> elemIndex d c58 )) $ Just 0
where c58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
-- Convert from base58 encoded value to list of bytes
toBytes :: Integer -> [Word8]
toBytes x = reverse $ unfoldr (\b -> if b == 0 then Nothing else Just (fromIntegral $ b `mod` 256, b `div` 256)) x
-- Check validity of base58 encoded bitcoin address.
-- Result is either an error string (Left) or a validity bool (Right).
validityCheck :: String -> Either String Bool
validityCheck encodedAddress =
let d58 = decode58 encodedAddress
in case d58 of
Nothing -> Left "Invalid base 58 encoding"
Just ev ->
let address = toBytes ev
addressLength = length address
in if addressLength > 24
then Left "Address length exceeds 25 bytes"
else
if addressLength < 4
then Left "Address length less than 4 bytes"
else
let (bs,cs) = splitAt 21 $ replicate (25 - addressLength) 0 ++ address
in Right $ all (uncurry (==)) (zip cs $ unpack $ hash $ hash $ pack bs)
-- Run one validity check and display results.
validate :: String -> IO ()
validate encodedAddress =
let vc = validityCheck encodedAddress
in case vc of
Left err ->
putStrLn $ show encodedAddress ++ " -> " ++ show err
Right validity ->
putStrLn $ show encodedAddress ++ " -> " ++ if validity then "Valid" else "Invalid"
-- Run some validity check tests.
main :: IO ()
main = do
validate "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -- VALID
validate "1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9" -- VALID
validate "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X" -- checksum changed, original data.
validate "1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -- data changed, original checksum.
validate "1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -- invalid chars
validate "1ANa55215ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -- too long
validate "i55j" -- too short

View file

@ -0,0 +1,63 @@
private final static String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
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 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;
byte[] hash = Sha256(decoded, 0, 21, 2);
return Arrays.equals(Arrays.copyOfRange(hash, 0, 4), Arrays.copyOfRange(decoded, 21, 25));
}
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);
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);
}
if (p != 0) return null;
}
return output;
}
private static byte[] Sha256(byte[] data, int start, int len, int recursion) {
if (recursion == 0) return data;
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;
}
}
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");
}

View file

@ -0,0 +1,10 @@
chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; data =
IntegerDigits[
FromDigits[
StringPosition[chars, #][[1]] - 1 & /@ Characters[InputString[]],
58], 256, 25];
data[[-4 ;;]] ==
IntegerDigits[
Hash[FromCharacterCode[
IntegerDigits[Hash[FromCharacterCode[data[[;; -5]]], "SHA256"],
256, 32]], "SHA256"], 256, 32][[;; 4]]

View file

@ -0,0 +1,80 @@
MODULE BitcoinAddress;
IMPORT
Object,
NPCT:Tools,
Crypto:SHA256,
S := SYSTEM,
Out;
CONST
BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
TYPE
BC_RAW = ARRAY 25 OF CHAR;
SHA256_HASH = ARRAY 32 OF CHAR;
VAR
b58: Object.CharsLatin1;
PROCEDURE IndexOfB58Char(c: CHAR): INTEGER;
VAR
i: INTEGER;
BEGIN
i := 0;
WHILE (b58[i] # 0X) & (b58[i] # c) DO INC(i) END;
IF b58[i] = 0X THEN RETURN -1 ELSE RETURN i END
END IndexOfB58Char;
PROCEDURE DecodeB58(s [NO_COPY]: ARRAY OF CHAR;VAR out: BC_RAW): BOOLEAN;
VAR
i,j,k: LONGINT;
BEGIN
FOR i := 0 TO LEN(out) - 1 DO; out[i] := CHR(0) END;
i := 0;
WHILE (s[i] # 0X) DO;
k := IndexOfB58Char(s[i]);
IF k < 0 THEN
Out.String("Error: Bad base58 character");Out.Ln;
RETURN FALSE
END;
FOR j := LEN(out) - 1 TO 0 BY -1 DO
k := k + 58 * ORD(out[j]);
out[j] := CHR(k MOD 256);
k := k DIV 256;
END;
IF k # 0 THEN Out.String("Error: Address to long");Out.Ln; RETURN FALSE END;
INC(i)
END;
RETURN TRUE
END DecodeB58;
PROCEDURE Valid(s [NO_COPY]: ARRAY OF CHAR): BOOLEAN;
VAR
dec: BC_RAW;
d1, d2: SHA256.Hash;
d1Str, d2Str: SHA256_HASH;
x,y: LONGINT;
BEGIN
Out.String(s);Out.String(" is valid? ");
IF ~DecodeB58(s,dec) THEN RETURN FALSE END;
d1 := SHA256.NewHash();d1.Initialize();
d2 := SHA256.NewHash();d2.Initialize();
d1.Update(dec,0,21);d1.GetHash(d1Str,0);
d2.Update(d1Str,0,d1.size);d2.GetHash(d2Str,0);
S.MOVE(S.ADR(dec) + 21,S.ADR(x),4);
S.MOVE(S.ADR(d2Str),S.ADR(y),4);
RETURN (x = y)
END Valid;
BEGIN
b58 := Tools.AsString(BASE58);
Out.Bool(Valid("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"));Out.Ln;
Out.Bool(Valid("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"));Out.Ln;
Out.Bool(Valid("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9"));Out.Ln;
Out.Bool(Valid("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"));Out.Ln
END BitcoinAddress.

View file

@ -0,0 +1,56 @@
function validate($address){
$decoded = decodeBase58($address);
$d1 = hash("sha256", substr($decoded,0,21), true);
$d2 = hash("sha256", $d1, true);
if(substr_compare($decoded, $d2, 21, 4)){
throw new \Exception("bad digest");
}
return true;
}
function decodeBase58($input) {
$alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
$out = array_fill(0, 25, 0);
for($i=0;$i<strlen($input);$i++){
if(($p=strpos($alphabet, $input[$i]))===false){
throw new \Exception("invalid character found");
}
$c = $p;
for ($j = 25; $j--; ) {
$c += (int)(58 * $out[$j]);
$out[$j] = (int)($c % 256);
$c /= 256;
$c = (int)$c;
}
if($c != 0){
throw new \Exception("address too long");
}
}
$result = "";
foreach($out as $val){
$result .= chr($val);
}
return $result;
}
function main () {
$s = array(
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",
);
foreach($s as $btc){
$message = "OK";
try{
validate($btc);
}catch(\Exception $e){ $message = $e->getMessage(); }
echo "$btc: $message\n";
}
}
main();

View file

@ -1,25 +1,17 @@
enum B58 <
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
>;
sub unbase58(Str $str) {
my @out = 0 xx 25;
for B58.enums.hash{$str.comb} {
my $c = $_;
for reverse ^25 {
$c += 58 * @out[$_];
@out[$_] = $c % 256;
$c div= 256;
}
}
return @out;
my regex bitcoin-address {
<+alnum-[0IOl]> ** 26..* # an address is at least 26 characters long
<?{
use Digest::SHA;
.subbuf(21, 4) eqv sha256(sha256 .subbuf(0, 21)).subbuf(0, 4) given
Blob.new: <
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
>.pairs.invert.hash{$/.comb}
.reduce(* * 58 + *)
.polymod(256 xx 24)
.reverse;
}>
}
sub check-bitcoin-address($addr) {
use Digest::SHA;
my @byte = unbase58 $addr;
!!! 'wrong checksum' unless @byte[21..24] ~~
sha256(sha256 Buf.new: @byte[0..20]).subbuf(0, 4).list;
}
say "Here is a bitcoin address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" ~~ /<bitcoin-address>/;

View file

@ -7,14 +7,9 @@ def decode_base58(bc, length):
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")
print(check_bc('1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i'))
print(check_bc("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j"))