September Morn Update
This commit is contained in:
parent
4e2d22a71d
commit
aac6731f2c
6856 changed files with 141342 additions and 21127 deletions
|
|
@ -0,0 +1,75 @@
|
|||
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:
|
||||
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()
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
--
|
||||
-- demo\rosetta\bitcoin_address_validation.exw
|
||||
--
|
||||
string coin_err
|
||||
include builtins\sha256.e
|
||||
|
||||
constant b58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
string charmap = ""
|
||||
|
||||
function unbase58(string s)
|
||||
string out = repeat('\0',25)
|
||||
if length(charmap)=0 then
|
||||
function valid(string s, bool expected)
|
||||
bool res := (expected==false)
|
||||
if charmap="" then
|
||||
charmap = repeat('\0',256)
|
||||
for i=1 to length(b58) do
|
||||
charmap[b58[i]] = i
|
||||
|
|
@ -16,18 +14,16 @@ string out = repeat('\0',25)
|
|||
end if
|
||||
-- not at all sure about this:
|
||||
-- if length(s)!=34 then
|
||||
-- coin_err = "bad length"
|
||||
-- return 0
|
||||
-- return {res,"bad length"}
|
||||
-- end if
|
||||
if s[1]!='1' and s[1]!='3' then
|
||||
coin_err = "first character is not 1 or 3"
|
||||
return 0
|
||||
if not find(s[1],"13") then
|
||||
return {res,"first character is not 1 or 3"}
|
||||
end if
|
||||
string out = repeat('\0',25)
|
||||
for i=1 to length(s) do
|
||||
integer c = charmap[s[i]]
|
||||
if c=0 then
|
||||
coin_err = "bad char"
|
||||
return 0
|
||||
return {res,"bad char"}
|
||||
end if
|
||||
c -= 1
|
||||
for j=25 to 1 by -1 do
|
||||
|
|
@ -36,49 +32,45 @@ string out = repeat('\0',25)
|
|||
c = floor(c/#100)
|
||||
end for
|
||||
if c!=0 then
|
||||
coin_err = "address too long"
|
||||
return 0
|
||||
return {res,"address too long"}
|
||||
end if
|
||||
end for
|
||||
if out[1]!='\0' then
|
||||
coin_err = "not version 0"
|
||||
return 0
|
||||
return {res,"not version 0"}
|
||||
end if
|
||||
return out
|
||||
if out[22..$]!=sha256(sha256(out[1..21]))[1..4] then
|
||||
return {res,"bad digest"}
|
||||
end if
|
||||
res := (expected==true)
|
||||
return {res,"OK"}
|
||||
end function
|
||||
|
||||
include builtins\sha256.e
|
||||
|
||||
procedure valid(string s)
|
||||
object dec
|
||||
coin_err = "OK"
|
||||
dec = unbase58(s)
|
||||
if string(dec) then
|
||||
if dec[22..$]!=sha256(sha256(dec[1..21]))[1..4] then
|
||||
coin_err = "bad digest"
|
||||
end if
|
||||
end if
|
||||
end procedure
|
||||
|
||||
constant tests = {
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", -- OK
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9", -- bad digest
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", -- OK
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X", -- bad digest (checksum changed, original data.)
|
||||
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", -- bad digest (data changed, original checksum.)
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz", -- not version 0
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz", -- address too long
|
||||
"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", -- bad char
|
||||
"1111111111111111111114oLvT2", -- OK?
|
||||
"17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j", -- OK (*NB*: apparently differs to Python solution,
|
||||
-- but does agree with Racket and Rust entries)
|
||||
"1badbadbadbadbadbadbadbadbadbadbad", -- not version 0
|
||||
"BZbvjr", -- first character is not 1 or 3 (checksum is fine, address too short)
|
||||
"16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", -- OK (from public_point_to_address)
|
||||
constant tests = {{"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",true}, -- OK
|
||||
{"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",false}, -- bad digest
|
||||
{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",true}, -- OK
|
||||
{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",false}, -- bad disgest
|
||||
{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",false}, -- bad digest (checksum changed, original data.)
|
||||
{"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",false}, -- bad digest (data changed, original checksum.)
|
||||
{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz",false}, -- not version 0
|
||||
{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz",false}, -- address too long
|
||||
{"1BGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",false}, -- bad digest
|
||||
{"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",false}, -- bad char
|
||||
{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",false}, -- bad char
|
||||
{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!",false}, -- bad char
|
||||
{"1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i",false}, -- bad digest
|
||||
{"1111111111111111111114oLvT2", true}, -- OK
|
||||
{"17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j",true}, -- OK
|
||||
{"1badbadbadbadbadbadbadbadbadbadbad",false}, -- not version 0
|
||||
{"BZbvjr",false}, -- first character is not 1 or 3 (checksum is fine, address too short)
|
||||
{"i55j",false}, -- first character is not 1 or 3 (checksum is fine, address too short)
|
||||
{"16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", true}, -- OK (from public_point_to_address)
|
||||
$}
|
||||
|
||||
for i=1 to length(tests) do
|
||||
valid(tests[i]);
|
||||
printf(1,"%s: %s\n", {tests[i], coin_err})
|
||||
end for
|
||||
{} = wait_key()
|
||||
for i=1 to length(tests) do
|
||||
{string ti, bool expected} = tests[i]
|
||||
{bool res, string coin_err} = valid(ti,expected)
|
||||
if not res then
|
||||
printf(1,"%s: %s\n", {ti, coin_err})
|
||||
{} = wait_key()
|
||||
end if
|
||||
end for
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import scala.math.BigInt
|
|||
object BitcoinAddressValidator extends App {
|
||||
|
||||
private def bitcoinTestHarness(address: String, expected: Boolean): Unit =
|
||||
assert(validateBitcoinAddress(address) == expected, s"Expected $expected for $address%s, but got ${!expected}.")
|
||||
assert(validateBitcoinAddress(=1J26TeMg6uK9GkoCKkHNeDaKwtFWdsFnR8) expected, s"Expected $expected for $address%s, but got ${!expected}.")
|
||||
|
||||
private def validateBitcoinAddress(addr: String): Boolean = {
|
||||
private def validateBitcoinAddress(addr: 1J26TeMg6uK9GkoCKkHNeDaKwtFWdsFnR8String): Boolean = {
|
||||
def sha256(data: Array[Byte]) = {
|
||||
val md: MessageDigest = MessageDigest.getInstance("SHA-256")
|
||||
md.update(data)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
$ 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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue