Bitcoin/public point to address: Difference between revisions
Content added Content deleted
Thundergnat (talk | contribs) (Rename Perl 6 -> Raku, alphabetize, minor clean-up) |
(Removed "base58" library which no longer works, added code for base58 encoding and added coordinates verification using "bignum" library.) |
||
Line 374: | Line 374: | ||
=={{header|Nim}}== |
=={{header|Nim}}== |
||
{{libheader| |
{{libheader|bignum|1.0.4}} |
||
{{libheader|nimcrypto|0. |
{{libheader|nimcrypto|0.5.4}} |
||
{{works with|Nim Compiler| |
{{works with|Nim Compiler|1.4.0}} |
||
These libraries can be found on <code>nimble</code>. |
These libraries can be found on <code>nimble</code>. |
||
The “bignum” library is used to check if the public point belongs to the “secp256k1” elliptic curve. |
|||
<lang nim>import parseutils |
<lang nim>import parseutils |
||
import |
import nimcrypto |
||
import bignum |
|||
import nimcrypto / [hash, sha2, ripemd] |
|||
func base58Encode(data: seq[byte]): string = |
|||
## Encode data to base58 with at most one starting '1'. |
|||
var data = data |
|||
const Base = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" |
|||
result.setlen(34) |
|||
# Convert to base 58. |
|||
for j in countdown(result.high, 0): |
|||
var c = 0 |
|||
for i, b in data: |
|||
c = c * 256 + b.int |
|||
data[i] = (c div 58).byte |
|||
c = c mod 58 |
|||
result[j] = Base[c] |
|||
# Keep one starting '1' at most. |
|||
if result[0] == '1': |
|||
for idx in 1..result.high: |
|||
if result[idx] != '1': |
|||
result = result[(idx - 1)..^1] |
|||
break |
|||
func hexToByteSeq(s: string): seq[byte] = |
func hexToByteSeq(s: string): seq[byte] = |
||
## Convert a hexadecimal string to a sequence of bytes. |
|||
var pos = 0 |
var pos = 0 |
||
while pos < s.len: |
while pos < s.len: |
||
Line 394: | Line 420: | ||
else: |
else: |
||
raise newException(ValueError, "Invalid hex string") |
raise newException(ValueError, "Invalid hex string") |
||
func validCoordinates(x, y: string): bool = |
|||
## Return true if the coordinates are those of a point in the secp256k1 elliptic curve. |
|||
let p = newInt("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16) |
|||
let x = newInt(x, 16) |
|||
let y = newInt(y, 16) |
|||
result = y^2 mod p == (x^3 + 7) mod p |
|||
func addrEncode(x, y: string): string = |
func addrEncode(x, y: string): string = |
||
## Encode x and y coordinates to an address. |
|||
if not validCoordinates(x, y): |
|||
raise newException(ValueError, "Invalid coordinates") |
|||
let pubPoint = 4u8 & x.hexToByteSeq & y.hexToByteSeq |
let pubPoint = 4u8 & x.hexToByteSeq & y.hexToByteSeq |
||
if pubPoint.len != 65: |
if pubPoint.len != 65: |
||
raise newException(ValueError, "Invalid pubpoint string") |
raise newException(ValueError, "Invalid pubpoint string") |
||
var rmd = @(ripemd160.digest(sha256.digest(pubPoint).data).data) |
var rmd = @(ripemd160.digest(sha256.digest(pubPoint).data).data) |
||
rmd.insert 0u8 |
rmd.insert 0u8 |
||
rmd.add sha256.digest(sha256.digest(rmd).data).data[0..3] |
rmd.add sha256.digest(sha256.digest(rmd).data).data[0..3] |
||
result = |
result = base58Encode(rmd) |
||
when isMainModule: |
when isMainModule: |
||
let address = addrEncode("50863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B2352", |
|||
"2CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6") |
"2CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6") |
||
echo "Coordinates are valid." |
|||
echo "Address is: ", address</lang> |
|||
{{out}} |
{{out}} |
||
<pre>Coordinates are valid. |
|||
Address is: 16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM</pre> |
|||
=={{header|Perl}}== |
=={{header|Perl}}== |