Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Basic implementation of the Bitcoin hashcash algorithm,
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())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.