Skip to content

Instantly share code, notes, and snippets.

@KireinaHoro
Created October 23, 2019 16:05
Show Gist options
  • Select an option

  • Save KireinaHoro/7f88944bdb4e70d96b1e9b71c934ee02 to your computer and use it in GitHub Desktop.

Select an option

Save KireinaHoro/7f88944bdb4e70d96b1e9b71c934ee02 to your computer and use it in GitHub Desktop.

Revisions

  1. KireinaHoro created this gist Oct 23, 2019.
    117 changes: 117 additions & 0 deletions hashcash.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,117 @@
    package main

    import (
    "crypto/sha256"
    "encoding/binary"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "math/big"
    "os"
    "strconv"
    )

    const Id = "1700012980"

    type Input struct {
    Block int
    Version string
    HashPrevBlock string `json:"hashPrevBlock"`
    HashMerkleRoot string `json:"hashMerkleRoot"`
    Time string
    Bits string
    }

    type Output struct {
    Block int
    Nonce string
    Id string
    }

    func reverse(a []byte) {
    for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
    a[i], a[j] = a[j], a[i]
    }
    }

    func reverseString(a string) string {
    runes := []rune(a)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
    runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
    }

    func decodeToLittleEndian(str string) []byte {
    ret, err := hex.DecodeString(str[2:])
    if err != nil {
    panic(err)
    }
    reverse(ret)
    return ret
    }

    func constructHeader(input Input) []byte {
    var ret []byte
    ret = append(ret, decodeToLittleEndian(input.Version)...)
    ret = append(ret, decodeToLittleEndian(input.HashPrevBlock)...)
    ret = append(ret, decodeToLittleEndian(input.HashMerkleRoot)...)
    ret = append(ret, decodeToLittleEndian(input.Time)...)
    ret = append(ret, decodeToLittleEndian(input.Bits)...)
    return ret
    }

    func hashOnce(headerWithoutNonce []byte, nonce uint32) []byte {
    bs := make([]byte, 4)
    binary.LittleEndian.PutUint32(bs, nonce)
    fullBlock := append(headerWithoutNonce, bs...)
    firstPass := sha256.Sum256(fullBlock)
    secondPass := sha256.Sum256(firstPass[:])
    return secondPass[:]
    }

    func findNonceWithHint(headerWithoutNonce []byte, hint uint32, input Input) *Output {
    bits, _ := strconv.ParseUint(input.Bits[2:], 16, 32)
    mantissa := bits & 0x00ffffff
    exponent := (bits & 0xff000000) >> 24
    target := big.NewInt(int64(mantissa))
    target.Mul(target, new(big.Int).Exp(big.NewInt(256), big.NewInt(int64(exponent)-3), nil))
    targetStr := fmt.Sprintf("%064x", target)
    fmt.Printf("Target: 0x%s\n", targetStr)

    for nonce, i := hint, 0; nonce > 0; nonce, i = nonce+1, i+1 {
    if i%1000 == 0 {
    fmt.Printf("%d...", i)
    if i%10000 == 0 {
    fmt.Print("\n")
    }
    }
    hashStr := reverseString(hex.EncodeToString(hashOnce(headerWithoutNonce, nonce)))
    if hashStr < targetStr {
    fmt.Println("===== We have a block now =====")
    return &Output{
    Block: input.Block,
    Nonce: fmt.Sprintf("0x%08x", nonce),
    Id: Id,
    }
    }
    }
    return nil
    }

    func runRequest() string {
    var input Input
    err := json.NewDecoder(os.Stdin).Decode(&input)
    if err != nil {
    panic(err)
    }
    ret, err := json.Marshal(findNonceWithHint(constructHeader(input), 0, input))
    if err != nil {
    panic(err)
    }
    return string(ret)
    }

    func main() {
    fmt.Println(runRequest())
    }