Bitcoin/address validation: Difference between revisions
m
→{{header|Wren}}: Minor tidy
(→{{header|Factor}}: use map-reduce instead of map and reduce) |
m (→{{header|Wren}}: Minor tidy) |
||
(37 intermediate revisions by 18 users not shown) | |||
Line 1:
[[Category:Checksums]]
{{alertbox|#ffff70|'''<big>Warning:</big>''' Many of these snippets are [[{{TALKPAGENAME}}#C-based_code_.28and_possibly_others.29_improperly_validates|incomplete]]. It is recommended that you use an established [https://en.bitcoin.it/wiki/Software#Libraries library] for any projects that are likely to see external use}}
{{task}}
;Task:
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 zero
:::* O uppercase oh
:::* I uppercase eye
:::* l lowercase ell
With this encoding, a bitcoin address encodes 25 bytes:
Line 14 ⟶ 19:
* 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.
Line 21 ⟶ 27:
You can use a digest library for [[SHA-256]].
;Example of a bitcoin address:
<big>
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i
</big>
It doesn't belong to anyone and is part of the test suite of the bitcoin software.
<br>You can change a few characters in this string and check that it'll fail the test.
<br><br>
=={{header|Ada}}==
<
with Ada.Streams;
use Ada.Exceptions, Interfaces;
Line 159 ⟶ 165:
end;
end Bitcoin_Addr_Validate;
</syntaxhighlight>
{{out}}
Line 168 ⟶ 174:
1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i validity: *** Error: Invalid BT address.
</pre>
=={{header|C}}==
<
#include <string.h>
#include <openssl/sha.h>
Line 230 ⟶ 235:
return 0;
}</
Compile with -lcrypto
{{out}}
Line 239 ⟶ 244:
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I: bad char
</pre>
=={{header|C sharp|C#}}==
This requires [https://www.nuget.org/packages/NUnit/ NUnit package] to compile.
<
using System;
using System.Linq;
Line 312 ⟶ 316:
}
}
</syntaxhighlight>
=={{header|D}}==
This requires the D module from the SHA-256 Task.
{{trans|Go}}
<
struct A25 {
Line 401 ⟶ 404:
writefln(`"%s": %s`, test, err.empty ? "OK." : err);
}
}</
{{out}}
<pre>"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i": OK.
Line 408 ⟶ 411:
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz": not Bitcoin version 0.
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz": too long Bitcoin address.</pre>
=={{header|dart}}==
This requires [https://pub.dev/packages/crypto Crypto package] to compile.
{{trans|Java}}
<syntaxhighlight lang="dart">import 'package:crypto/crypto.dart';
class Bitcoin {
final String ALPHABET =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
List<int> bigIntToByteArray(BigInt data) {
String str;
bool neg = false;
if (data < BigInt.zero) {
str = (~data).toRadixString(16);
neg = true;
} else str = data.toRadixString(16);
int p = 0;
int len = str.length;
int blen = (len + 1) ~/ 2;
int boff = 0;
List bytes;
if (neg) {
if (len & 1 == 1) {
p = -1;
}
int byte0 = ~int.parse(str.substring(0, p + 2), radix: 16);
if (byte0 < -128) byte0 += 256;
if (byte0 >= 0) {
boff = 1;
bytes = new List<int>(blen + 1);
bytes[0] = -1;
bytes[1] = byte0;
} else {
bytes = new List<int>(blen);
bytes[0] = byte0;
}
for (int i = 1; i < blen; ++i) {
int byte = ~int.parse(str.substring(p + (i << 1), p + (i << 1) + 2),
radix: 16);
if (byte < -128) byte += 256;
bytes[i + boff] = byte;
}
} else {
if (len & 1 == 1) {
p = -1;
}
int byte0 = int.parse(str.substring(0, p + 2), radix: 16);
if (byte0 > 127) byte0 -= 256;
if (byte0 < 0) {
boff = 1;
bytes = new List<int>(blen + 1);
bytes[0] = 0;
bytes[1] = byte0;
} else {
bytes = new List<int>(blen);
bytes[0] = byte0;
}
for (int i = 1; i < blen; ++i) {
int byte =
int.parse(str.substring(p + (i << 1), p + (i << 1) + 2), radix: 16);
if (byte > 127) byte -= 256;
bytes[i + boff] = byte;
}
}
return bytes;
}
List<int> arrayCopy(bytes, srcOffset, result, destOffset, bytesLength) {
for (int i = srcOffset; i < bytesLength; i++) {
result[destOffset + i] = bytes[i];
}
return result;
}
List<int> decodeBase58To25Bytes(String input) {
BigInt number = BigInt.zero;
for (String t in input.split('')) {
int p = ALPHABET.indexOf(t);
if (p == (-1))
return null;
number = number * (BigInt.from(58)) + (BigInt.from(p));
}
List<int> result = new List<int>(24);
List<int> numBytes = bigIntToByteArray(number);
return arrayCopy(
numBytes, 0, result, result.length - numBytes.length, numBytes.length);
}
validateAddress(String address) {
List<int> decoded = new List.from(decodeBase58To25Bytes(address));
List<int> temp = new List<int>.from(decoded);
temp.insert(0, 0);
List<int> hash1 = sha256.convert(temp.sublist(0, 21)).bytes;
List<int> hash2 = sha256.convert(hash1).bytes;
if (hash2[0] != decoded[20] ||
hash2[1] != decoded[21] ||
hash2[2] != decoded[22] ||
hash2[3] != decoded[23]) return false;
return true;
}
}</syntaxhighlight>
{{out}}
<pre>"1BNGaR29FmfAqidXmD9HLwsGv9p5WVvvhq" true
"1BNGaR29FmfAqidXmD9HLws" false
</pre>
=={{header|Delphi}}==
This requires [http://www.cityinthesky.co.uk/opensource/DCPcrypt/ DCPcrypt library] to compile.
<syntaxhighlight lang="delphi">
uses
DCPsha256;
type
TByteArray = array of Byte;
function HashSHA256(const Input: TByteArray): TByteArray;
var
Hasher: TDCP_sha256;
begin
Hasher := TDCP_sha256.Create(nil);
try
Hasher.Init;
Hasher.Update(Input[0], Length(Input));
SetLength(Result, Hasher.HashSize div 8);
Hasher.Final(Result[0]);
finally
Hasher.Free;
end;
end;
function DecodeBase58(const Input: string): TByteArray;
const
Size = 25;
Alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
var
C: Char;
I, J: Integer;
begin
SetLength(Result, Size);
for C in Input do
begin
I := Pos(C, Alphabet) - 1;
if I = -1 then
raise Exception.CreateFmt('Invalid character found: %s', [C]);
for J := High(Result) downto 0 do
begin
I := I + (58 * Result[J]);
Result[J] := I mod 256;
I := I div 256;
end;
if I <> 0 then
raise Exception.Create('Address too long');
end;
end;
procedure ValidateBitcoinAddress(const Address: string);
var
Hashed: TByteArray;
Decoded: TByteArray;
begin
if (Length(Address) < 26) or (Length(Address) > 35) then
raise Exception.Create('Wrong length');
Decoded := DecodeBase58(Address);
Hashed := HashSHA256(HashSHA256(Copy(Decoded, 0, 21)));
if not CompareMem(@Decoded[21], @Hashed[0], 4) then
raise Exception.Create('Bad digest');
end;
</syntaxhighlight>
=={{header|Erlang}}==
Using base58 module from http://github.com/titan098/erl-base58.git.
<syntaxhighlight lang="erlang">
-module( bitcoin_address ).
Line 430 ⟶ 606:
{checksum, Checksum} = {checksum, Four_bytes},
ok.
</syntaxhighlight>
{{out}}
<pre>
Line 442 ⟶ 618:
</pre>
=={{header|Factor}}==
<
math.parser sequences ;
IN: rosetta-code.bitcoin.validation
Line 448 ⟶ 624:
CONSTANT: ALPHABET "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
: base58>bigint (
[ ALPHABET index ]
[ [ 58 * ] [ + ] bi* ] map-reduce ;
: base58> ( str --
: btc-checksum ( bytes -- checksum-bytes )
Line 458 ⟶ 634:
: btc-valid? ( str -- ? ) base58> [ btc-checksum ] [ 4 tail* ] bi = ;
</syntaxhighlight>
{{out}}
Line 466 ⟶ 642:
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" btc-valid? . ! f, data changed, original checksum.
</pre>
=={{header|FreeBASIC}}==
<
' compile with: fbc -s console
Line 673 ⟶ 848:
Print : Print "hit any key to end program"
Sleep
End</
{{out}}
<pre>Bitcoin address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i is valid
Line 680 ⟶ 855:
Bitcoin address: 0AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i first character is not 1 or 3
Bitcoin address: 1AGNa15ZQXAZUgFlqJ2i7Z2DPU2J6hW62i bitcoin address contains illegal character</pre>
=={{header|Go}}==
{{trans|C}}
<
import (
Line 791 ⟶ 965:
os.Stderr.WriteString(m + "\n")
os.Exit(1)
}</
{{out}}
Command line usage examples showing program exit status.
Line 817 ⟶ 991:
</pre>
=={{header|Haskell}}==
<
import Data.
import
import qualified Data.ByteString
import Data.ByteString (ByteString)
import Crypto.Hash.SHA256 (hash) -- from package cryptohash
-- Convert from base58 encoded value to Integer
decode58 :: String -> Maybe Integer
decode58 = fmap combine . traverse parseDigit
where
combine = foldl (\acc digit -> 58 * acc + digit) 0 -- should be foldl', but this trips up the highlighting
parseDigit char = toInteger <$> elemIndex char c58
c58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
-- Convert from base58 encoded value to bytes
toBytes :: Integer -> ByteString
toBytes = BS.reverse . BS.pack . map (fromIntegral . (`mod` 256)) . takeWhile (> 0) . iterate (`div` 256)
-- Check if the hash of the first 21 (padded) bytes matches the last 4 bytes
checksumValid :: ByteString -> Bool
checksumValid address =
let (value, checksum) = BS.splitAt 21 $ leftPad address
in and $ BS.zipWith (==) checksum $ hash $ hash $ value
where
leftPad bs = BS.replicate (25 - BS.length bs) 0 <> bs
-- utility
withError :: e -> Maybe a -> Either e a
withError e = maybe (Left e) Right
-- Check validity of base58 encoded bitcoin address.
-- Result is either an error string (Left) or
validityCheck :: String -> Either String
validityCheck
num <- withError "Invalid base 58 encoding" $ decode58 encoded
let address = toBytes num
when (BS.length address > 25)
when (BS.length address < 4) $ Left "Address length less than 4 bytes"
when (not $ checksumValid address) $ Left "Invalid checksum"
-- Run one validity check and display results.
validate :: String -> IO ()
validate encodedAddress = do
let
putStrLn $ show encodedAddress ++ " -> " ++ result
-- Run some validity check tests.
Line 869 ⟶ 1,048:
validate "1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -- invalid chars
validate "1ANa55215ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -- too long
validate "i55j" -- too short
</syntaxhighlight>
{{out}}
<pre style="font-size:80%">"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -> Valid
Line 878 ⟶ 1,058:
"1ANa55215ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -> "Address length exceeds 25 bytes"
"i55j" -> "Address length less than 4 bytes"</pre>
=={{header|Java}}==
<syntaxhighlight lang="java">import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class BitcoinAddressValidator {
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
public static boolean validateBitcoinAddress(String addr) {
if (addr.length() < 26 || addr.length() > 35)
return false;
byte[] decoded = decodeBase58To25Bytes(addr);
if (decoded == null)
return false;
byte[] hash1 = sha256(Arrays.copyOfRange(decoded, 0, 21));
byte[] hash2 = sha256(hash1);
return Arrays.equals(Arrays.copyOfRange(hash2, 0, 4), Arrays.copyOfRange(decoded, 21, 25));
}
private static byte[] decodeBase58To25Bytes(String input) {
BigInteger num = BigInteger.ZERO;
for (char t : input.toCharArray()) {
int p = ALPHABET.indexOf(t);
if (p == -1)
return null;
num = num.multiply(BigInteger.valueOf(58)).add(BigInteger.valueOf(p));
}
byte[] result = new byte[25];
byte[] numBytes = num.toByteArray();
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length);
return result;
}
private static byte[] sha256(byte[] data) {
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
}
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);
}
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));
}
}</syntaxhighlight>
=={{header|Julia}}==
{{works with|Julia|0.6}}
{{trans|Python}}
<syntaxhighlight lang="julia">using SHA
bytes(n::Integer, l::Int) = collect(UInt8, (n >> 8i) & 0xFF for i in l-1:-1:0)
function decodebase58(bc::String, l::Int)
digits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
num = big(0)
for c in bc
num = num * 58 + search(digits, c) - 1
end
return bytes(num, l)
end
function checkbcaddress(addr::String)
if !(25 ≤ length(addr) ≤ 35) return false end
bcbytes = decodebase58(addr, 25)
sha = sha256(sha256(bcbytes[1:end-4]))
return bcbytes[end-3:end] == sha[1:4]
end
const addresses = Dict(
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" => true,
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j" => false,
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9" => true,
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X" => true,
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" => false,
"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" => false,
"BZbvjr" => false,
"i55j" => false,
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!" => false,
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz" => false,
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz" => false,
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9" => false,
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I" => false)
for (addr, corr) in addresses
println("Address: $addr\nExpected: $corr\tChecked: ", checkbcaddress(addr))
end</syntaxhighlight>
{{out}}
<pre>Address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X
Expected: true Checked: false
Address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i
Expected: true Checked: true
Address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j
Expected: false Checked: false
Address: 1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i
Expected: false Checked: false
Address: 1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9
Expected: true Checked: true
Address: BZbvjr
Expected: false Checked: false
Address: 1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i
Expected: false Checked: false
Address: i55j
Expected: false Checked: false
Address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz
Expected: false Checked: false
Address: 1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9
Expected: false Checked: false
Address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!
Expected: false Checked: false
Address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I
Expected: false Checked: false
Address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz
Expected: false Checked: false</pre>
=={{header|Kotlin}}==
{{trans|Java}}
<syntaxhighlight lang
object Bitcoin {
Line 974 ⟶ 1,206:
private fun ByteArray.contentEquals(other: ByteArray): Boolean {
if (this.size != other.size) return false
}
Line 983 ⟶ 1,214:
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()
Line 992 ⟶ 1,223:
return output
}
private fun sha256(data: ByteArray, start: Int, len: Int, recursion: Int): ByteArray {
if (recursion == 0) return data
Line 998 ⟶ 1,229:
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
Line 1,027 ⟶ 1,258:
for (address in addresses)
println("${address.padEnd(36)} -> ${if (Bitcoin.validateAddress(address)) "valid" else "invalid"}")
}</
{{out}}
Line 1,045 ⟶ 1,276:
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I -> invalid
</pre>
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">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]]</syntaxhighlight>
{{in}}
<pre>1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i
2AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i</pre>
{{out}}
<pre>True
False</pre>
=={{header|Nim}}==
Tests on first digit character and on address length have been added to detect wrong addresses such as "BZbvjr".
===Using “libssl”===
<syntaxhighlight lang="nim">import algorithm
const SHA256Len = 32
const AddrLen = 25
const AddrMsgLen = 21
const AddrChecksumOffset = 21
const AddrChecksumLen = 4
proc SHA256(d: pointer, n: culong, md: pointer = nil): cstring {.cdecl, dynlib: "libssl.so", importc.}
proc decodeBase58(inStr: string, outArray: var openarray[uint8]) =
let base = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
outArray.fill(0)
for aChar in inStr:
var accum = base.find(aChar)
if accum < 0:
raise newException(ValueError, "Invalid character: " & $aChar)
for outIndex in countDown((AddrLen - 1), 0):
accum += 58 * outArray[outIndex].int
outArray[outIndex] = (accum mod 256).uint8
accum = accum div 256
if accum != 0:
raise newException(ValueError, "Address string too long")
proc verifyChecksum(addrData: openarray[uint8]) : bool =
let doubleHash = SHA256(SHA256(cast[ptr uint8](addrData), AddrMsgLen), SHA256Len)
for ii in 0 ..< AddrChecksumLen:
if doubleHash[ii].uint8 != addrData[AddrChecksumOffset + ii]:
return false
return true
proc main() =
let
testVectors : seq[string] = @[
"3yQ",
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ix",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ixx",
"17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j",
"1badbadbadbadbadbadbadbadbadbadbad",
"16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM",
"1111111111111111111114oLvT2",
"BZbvjr",
]
var
buf: array[AddrLen, uint8]
astr: string
for vector in testVectors:
stdout.write(vector & " : ")
try:
if vector[0] notin {'1', '3'}:
raise newException(ValueError, "invalid starting character")
if vector.len < 26:
raise newException(ValueError, "address too short")
decodeBase58(vector, buf)
if buf[0] != 0:
stdout.write("NG - invalid version number\n")
elif verifyChecksum(buf):
stdout.write("OK\n")
else:
stdout.write("NG - checksum invalid\n")
except:
stdout.write("NG - " & getCurrentExceptionMsg() & "\n")
main()
</syntaxhighlight>
{{out}}
<pre>3yQ : NG - address too short
1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9 : OK
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i : OK
1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9 : NG - checksum invalid
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I : NG - Invalid character: I
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ix : NG - invalid version number
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ixx : NG - Address string too long
17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j : OK
1badbadbadbadbadbadbadbadbadbadbad : NG - invalid version number
16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM : OK
1111111111111111111114oLvT2 : OK
BZbvjr : NG - invalid starting character</pre>
===Using “nimcrypto”===
{{libheader|nimcrypto}}
<syntaxhighlight lang="nim">import nimcrypto
import strformat
const
DecodedLength = 25 # Decoded address length.
CheckSumLength = 4 # Checksum length in address.
# Representation of a decoded address.
type Bytes25 = array[DecodedLength, byte]
#---------------------------------------------------------------------------------------------------
proc base58Decode(input: string): Bytes25 =
## Decode a base58 encoded bitcoin address.
const Base = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
for ch in input:
var n = Base.find(ch)
if n < 0:
raise newException(ValueError, "invalid character: " & ch)
for i in countdown(result.high, 0):
n += 58 * result[i].int
result[i] = byte(n and 255)
n = n div 256
if n != 0:
raise newException(ValueError, "decoded address is too long")
#---------------------------------------------------------------------------------------------------
proc verifyChecksum(data: Bytes25) =
## Verify that data has the expected checksum.
var digest = sha256.digest(data.toOpenArray(0, data.high - CheckSumLength))
digest = sha256.digest(digest.data)
if digest.data[0..<CheckSumLength] != data[^CheckSumLength..^1]:
raise newException(ValueError, "wrong checksum")
#---------------------------------------------------------------------------------------------------
proc checkValidity(address: string) =
## Check the validity of a bitcoin address.
try:
if address[0] notin {'1', '3'}:
raise newException(ValueError, "starting character is not 1 or 3")
if address.len < 26:
raise newException(ValueError, "address too short")
address.base58Decode().verifyChecksum()
echo fmt"Address “{address}” is valid."
except ValueError:
echo fmt"Address “{address}” is invalid ({getCurrentExceptionMsg()})."
#———————————————————————————————————————————————————————————————————————————————————————————————————
const testVectors : seq[string] = @[
"3yQ", # Invalid.
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", # Valid.
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", # Valid.
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9", # Invalid.
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I", # Invalid.
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ix", # Invalid.
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ixx", # Invalid.
"17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j", # Valid.
"1badbadbadbadbadbadbadbadbadbadbad", # Invalid.
"16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", # Valid.
"1111111111111111111114oLvT2", # Valid.
"BZbvjr"] # Invalid.
for vector in testVectors:
vector.checkValidity()</syntaxhighlight>
{{out}}
<pre>Address “3yQ” is invalid (address too short).
Address “1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9” is valid.
Address “1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i” is valid.
Address “1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9” is invalid (wrong checksum).
Address “1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I” is invalid (invalid character: I).
Address “1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ix” is invalid (wrong checksum).
Address “1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ixx” is invalid (decoded address is too long).
Address “17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j” is valid.
Address “1badbadbadbadbadbadbadbadbadbadbad” is invalid (wrong checksum).
Address “16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM” is valid.
Address “1111111111111111111114oLvT2” is valid.
Address “BZbvjr” is invalid (starting character is not 1 or 3).</pre>
=={{header|Oberon-2}}==
{{works with|oo2c}}{{libheader|Crypto}}
<
MODULE BitcoinAddress;
IMPORT
Line 1,129 ⟶ 1,572:
Out.Bool(Valid("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"));Out.Ln
END BitcoinAddress.
</syntaxhighlight>
{{out}}
<pre>
Line 1,138 ⟶ 1,581:
FALSE
</pre>
=={{header|Perl}}==
<
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
Line 1,171 ⟶ 1,613:
(pack 'C*', @byte[21..24]) eq
substr sha256(sha256 pack 'C*', @byte[0..20]), 0, 4;
}</
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\bitcoin_address_validation.exw
-- ===========================================
--</span>
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">builtins</span><span style="color: #0000FF;">\</span><span style="color: #7060A8;">sha256</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">b58</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">charmap</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">valid</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">bool</span> <span style="color: #000000;">expected</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">bool</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">:=</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">expected</span><span style="color: #0000FF;">==</span><span style="color: #004600;">false</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">charmap</span><span style="color: #0000FF;">=</span><span style="color: #008000;">""</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">charmap</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'\0'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">256</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">b58</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">charmap</span><span style="color: #0000FF;">[</span><span style="color: #000000;">b58</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">i</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000080;font-style:italic;">-- not at all sure about this:
-- if length(s)!=34 then
-- return {expected==false,"bad length"}
-- end if</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span><span style="color: #008000;">"13"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"first character is not 1 or 3"</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">out</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'\0'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">25</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">charmap</span><span style="color: #0000FF;">[</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"bad char"</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">c</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #000000;">25</span> <span style="color: #008080;">to</span> <span style="color: #000000;">1</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">c</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">58</span> <span style="color: #0000FF;">*</span> <span style="color: #000000;">out</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">];</span>
<span style="color: #000000;">out</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">and_bits</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">,</span><span style="color: #000000;">#FF</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">/</span><span style="color: #000000;">#100</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"address too long"</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">out</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]!=</span><span style="color: #008000;">'\0'</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"not version 0"</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">out</span><span style="color: #0000FF;">[</span><span style="color: #000000;">22</span><span style="color: #0000FF;">..$]!=</span><span style="color: #7060A8;">sha256</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sha256</span><span style="color: #0000FF;">(</span><span style="color: #000000;">out</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">21</span><span style="color: #0000FF;">]))[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">4</span><span style="color: #0000FF;">]</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"bad digest"</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">:=</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">expected</span><span style="color: #0000FF;">==</span><span style="color: #004600;">true</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"OK"</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">tests</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #008000;">"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">true</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- OK</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- bad digest</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">true</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- OK</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- bad disgest</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- bad digest (checksum changed, original data.)</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- bad digest (data changed, original checksum.)</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- not version 0</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- address too long</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1BGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- bad digest</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- bad char</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- bad char</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- bad char</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- bad digest</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1111111111111111111114oLvT2"</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">true</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- OK</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">true</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- OK</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"1badbadbadbadbadbadbadbadbadbadbad"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- not version 0</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"BZbvjr"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- first character is not 1 or 3 (checksum is fine, address too short)</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"i55j"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- first character is not 1 or 3 (checksum is fine, address too short)</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM"</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">true</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- OK (from public_point_to_address)</span>
<span style="color: #0000FF;">$}</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #0000FF;">{</span><span style="color: #004080;">string</span> <span style="color: #000000;">ti</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">bool</span> <span style="color: #000000;">expected</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #0000FF;">{</span><span style="color: #004080;">bool</span> <span style="color: #000000;">res</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">string</span> <span style="color: #000000;">coin_err</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">valid</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ti</span><span style="color: #0000FF;">,</span><span style="color: #000000;">expected</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #000000;">res</span> <span style="color: #008080;">then</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s: %s\n"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">ti</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">coin_err</span><span style="color: #0000FF;">})</span>
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">wait_key</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #0000FF;">?</span><span style="color: #008000;">"done"</span>
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">wait_key</span><span style="color: #0000FF;">()</span>
<!--</syntaxhighlight>-->
(No output other than "done" since all tests pass)
=={{header|PHP}}==
<
function validate($address){
$decoded = decodeBase58($address);
Line 1,253 ⟶ 1,760:
main();
</syntaxhighlight>
{{out}}
Line 1,262 ⟶ 1,769:
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I: invalid character found
</pre>
=={{header|PicoLisp}}==
<
(setq *Alphabet
Line 1,293 ⟶ 1,799:
(valid "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!")
(valid "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz")
(valid "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz") ) )</
=={{header|PureBasic}}==
<syntaxhighlight lang="purebasic">
; using PureBasic 5.50 (x64)
EnableExplicit
Line 1,374 ⟶ 1,879:
EndIf
</syntaxhighlight>
{{out}}
Line 1,382 ⟶ 1,887:
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I -> Invalid
</pre>
=={{header|Python}}==
<
digits58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
Line 1,394 ⟶ 1,898:
return n.to_bytes(length, 'big')
def check_bc(bc):
try:
return bcbytes[-4:] == sha256(sha256(bcbytes[:-4]).digest()).digest()[:4]
except Exception:
return False
print(check_bc('1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i'))
print(check_bc("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j"))</
{{out}}
Line 1,408 ⟶ 1,915:
:Yuuki-chan edit: Delete this help if it's not needed anymore
:For those looking at examples here to try and work out what is required, the <code>n.to_bytes()</code> call is equivalent to this code which converts a (long) integer into individual bytes of a byte array in a particular order:
:<
>>> 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)))
>>> </
=={{header|Racket}}==
<
#lang racket/base
Line 1,461 ⟶ 1,967:
(validate-bitcoin-address "1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9") ; => #t
(validate-bitcoin-address "1badbadbadbadbadbadbadbadbadbadbad") ; => #f
</syntaxhighlight>
=={{header|Raku}}==
(formerly Perl 6)
<syntaxhighlight lang="raku" line>sub sha256(blob8 $b) returns blob8 {
given run <openssl dgst -sha256 -binary>, :in, :out, :bin {
.in.write: $b;
.in.close;
return .out.slurp;
}
}
my $bitcoin-address = rx/
<< <+alnum-[0IOl]> ** 26..* >> # an address is at least 26 characters long
<?{
.subbuf(21, 4) eq sha256(sha256 .subbuf(0, 21)).subbuf(0, 4) given
blob8.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;
}>
/;
say "Here is a bitcoin address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" ~~ $bitcoin-address;</syntaxhighlight>
{{out}}
<pre>「1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i」</pre>
=={{header|Ruby}}==
<
# Validate Bitcoin address
#
Line 1,481 ⟶ 2,014:
(n.length...42).each{n.insert(0,'0')}
puts "I think the checksum should be #{g}\nI calculate that it is #{Digest::SHA256.hexdigest(Digest::SHA256.digest(convert(n)))[0,8]}"
</syntaxhighlight>
{{out}}
With A = '1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'
Line 1,493 ⟶ 2,026:
I calculate that it is c046b2ff
</pre>
=={{header|Rust}}==
This requires the [https://crates.io/crates/rust-crypto rust-crypto] crate for sha256.
<syntaxhighlight lang="rust">
extern crate crypto;
Line 1,552 ⟶ 2,084:
Ok(res)
}
</syntaxhighlight>
{{out}}
<pre>
Line 1,560 ⟶ 2,092:
false
</pre>
=={{header|Scala}}==
<syntaxhighlight lang="scala">import java.security.MessageDigest
import java.util.Arrays.copyOfRange
import scala.annotation.tailrec
import scala.math.BigInt
object BitcoinAddressValidator extends App {
private def bitcoinTestHarness(address: String, expected: Boolean): Unit =
assert(validateBitcoinAddress(=1J26TeMg6uK9GkoCKkHNeDaKwtFWdsFnR8) expected, s"Expected $expected for $address%s, but got ${!expected}.")
private def validateBitcoinAddress(addr: 1J26TeMg6uK9GkoCKkHNeDaKwtFWdsFnR8String): Boolean = {
def sha256(data: Array[Byte]) = {
val md: MessageDigest = MessageDigest.getInstance("SHA-256")
md.update(data)
md.digest
}
def decodeBase58To25Bytes(input: String): Option[Array[Byte]] = {
def ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
@tailrec
def loop(s: String, accu: BigInt): BigInt = {
if (s.isEmpty) accu
else {
val p = ALPHABET.indexOf(s.head)
if (p >= 0) loop(s.tail, accu * 58 + p)
else -1
}
}
val num = loop(input, 0)
if (num >= 0) {
val (result, numBytes) = (new Array[Byte](25), num.toByteArray)
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length)
Some(result)
}
else None
}
if (27 to 34 contains addr.length) {
val decoded = decodeBase58To25Bytes(addr)
if (decoded.isEmpty) false
else {
val hash1 = sha256(copyOfRange(decoded.get, 0, 21))
copyOfRange(sha256(hash1), 0, 4)
.sameElements(copyOfRange(decoded.get, 21, 25))
}
} else false
} // validateBitcoinAddress
bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", true)
bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j", false)
bitcoinTestHarness("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", true)
bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X", false)
bitcoinTestHarness("1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", false)
bitcoinTestHarness("1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", false)
bitcoinTestHarness("BZbvjr", false)
bitcoinTestHarness("i55j", false)
bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!", false)
bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz", false)
bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz", false)
bitcoinTestHarness("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9", false)
bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I", false)
println(s"Successfully completed without errors. [total ${scala.compat.Platform.currentTime - executionStart}ms]")
}</syntaxhighlight>
=={{header|Seed7}}==
The Seed7 library [http://seed7.sourceforge.net/libraries/encoding.htm encoding.s7i] defines
the function [http://seed7.sourceforge.net/libraries/encoding.htm#fromBase58(in_string) fromBase58],
which decodes a Base58 encoded string.
The Seed7 library [http://seed7.sourceforge.net/libraries/msgdigest.htm msgdigest.s7i] defines
the function [http://seed7.sourceforge.net/libraries/msgdigest.htm#sha256(in_var_string) sha256],
which computes a SHA-256 message digest.
No external library is needed.
<syntaxhighlight lang="seed7">$ include "seed7_05.s7i";
include "msgdigest.s7i";
include "encoding.s7i";
const func boolean: validBitcoinAddress (in string: address) is func
result
var boolean: isValid is FALSE;
local
var string: decoded is "";
begin
if succeeds(decoded := fromBase58(address)) and
length(decoded) = 25 and decoded[1] = '\0;' and
sha256(sha256(decoded[.. 21]))[.. 4] = decoded[22 ..] then
isValid := TRUE;
end if;
end func;
const proc: checkValidationFunction (in string: address, in boolean: expected) is func
local
var boolean: isValid is FALSE;
begin
isValid := validBitcoinAddress(address);
writeln((address <& ": ") rpad 37 <& isValid);
if isValid <> expected then
writeln(" *** Expected " <& expected <& " for " <& address <& ", but got " <& isValid <& ".");
end if;
end func;
const proc: main is func
begin
checkValidationFunction("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", TRUE); # okay
checkValidationFunction("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9", FALSE); # bad digest
checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", TRUE); # okay
checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j", FALSE); # bad digest
checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X", FALSE); # bad digest
checkValidationFunction("1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", FALSE); # bad digest
checkValidationFunction("oMRDCDfyQhEerkaSmwCfSPqf3MLgBwNvs", FALSE); # not version 0
checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz", FALSE); # wrong length
checkValidationFunction("1BGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", FALSE); # bad digest
checkValidationFunction("1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", FALSE); # bad char
checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I", FALSE); # bad char
checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!", FALSE); # bad char
checkValidationFunction("1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i", FALSE); # bad digest
checkValidationFunction("1111111111111111111114oLvT2", TRUE); # okay
checkValidationFunction("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j", TRUE); # okay
checkValidationFunction("1badbadbadbadbadbadbadbadbadbadbad", FALSE); # wrong length
checkValidationFunction("BZbvjr", FALSE); # wrong length
checkValidationFunction("i55j", FALSE); # wrong length
checkValidationFunction("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", TRUE); # okay
end func;</syntaxhighlight>
{{out}}
<pre>
1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9: TRUE
1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9: FALSE
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i: TRUE
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j: FALSE
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X: FALSE
1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i: FALSE
oMRDCDfyQhEerkaSmwCfSPqf3MLgBwNvs: FALSE
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz: FALSE
1BGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i: FALSE
1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i: FALSE
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I: FALSE
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!: FALSE
1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i: FALSE
1111111111111111111114oLvT2: TRUE
17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j: TRUE
1badbadbadbadbadbadbadbadbadbadbad: FALSE
BZbvjr: FALSE
i55j: FALSE
16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM: TRUE
</pre>
=={{header|Tcl}}==
{{tcllib|sha256}}
<
# Generate a large and boring piece of code to do the decoding of
Line 1,596 ⟶ 2,277:
}
return "$address is ok"
}</
Testing if it works
<
puts [bitcoin_addressValid 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i]</
{{out}}
<pre>
Line 1,605 ⟶ 2,286:
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i is ok
</pre>
=={{header|UNIX Shell}}==
{{works with|bash}}
<
bitcoinregex="^[$(printf "%s" "${base58[@]}")]{34}$"
Line 1,635 ⟶ 2,315:
else return 2
fi
}</
=={{header|Wren}}==
{{trans|Kotlin}}
{{libheader|Wren-crypto}}
{{libheader|wren-str}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./crypto" for Sha256
import "./str" for Str
import "./fmt" for Conv, Fmt
class Bitcoin {
static alphabet_ { "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" }
static contentEquals_(ba1, ba2) {
if (ba1.count != ba2.count) return false
return !(0...ba1.count).any { |i| ba1[i] != ba2[i] }
}
static decodeBase58_(input) {
var output = List.filled(25, 0)
for (c in input) {
var p = alphabet_.indexOf(c)
if (p == -1) return null
for (j in 24..1) {
p = p + 58 * output[j]
output[j] = p % 256
p = p >> 8
}
if (p != 0) return null
}
return output
}
static sha256_(data, start, len, recursion) {
if (recursion == 0) return data
var md = Sha256.digest(data[start...start+len])
md = Str.chunks(md, 2).map { |x| Conv.atoi(x, 16) }.toList
return sha256_(md, 0, 32, recursion-1)
}
static validateAddress(address) {
var len = address.count
if (len < 26 || len > 35) return false
var decoded = decodeBase58_(address)
if (!decoded) return false
var hash = sha256_(decoded, 0, 21, 2)
return contentEquals_(hash[0..3], decoded[21..24])
}
}
var addresses = [
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
"BZbvjr",
"i55j",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz",
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"
]
for (address in addresses) {
Fmt.print("$-36s -> $s", address, Bitcoin.validateAddress(address) ? "valid" : "invalid")
}</syntaxhighlight>
{{out}}
<pre>
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> valid
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j -> invalid
1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9 -> valid
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X -> invalid
1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> invalid
1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> invalid
BZbvjr -> invalid
i55j -> invalid
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62! -> invalid
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz -> invalid
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz -> invalid
1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9 -> invalid
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I -> invalid
</pre>
=={{header|zkl}}==
Uses shared library zklMsgHash.
<
const symbols="123456789" // 58 characters: no cap i,o; ell, zero
"ABCDEFGHJKLMNPQRSTUVWXYZ"
Line 1,663 ⟶ 2,427:
(2).reduce(MsgHash.SHA256.fp1(1,dec),dec); // dec is i/o buffer
dec[0,4]==chkSum;
}</
<
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X", // checksum changed, original data.
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", // data changed, original checksum.
"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", // invalid chars
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz", // too long
).apply(coinValide).println();</
{{out}}
<pre>L(True,True,False,False,False,False)</pre>
{{omit from|Brlcad}}
{{omit from|GUISS}}
|