Created
October 23, 2019 16:05
-
-
Save KireinaHoro/7f88944bdb4e70d96b1e9b71c934ee02 to your computer and use it in GitHub Desktop.
Basic implementation of the Bitcoin hashcash algorithm,
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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