diff --git a/block.go b/block.go index eb45918..ec81560 100644 --- a/block.go +++ b/block.go @@ -1,31 +1,35 @@ package main import ( - "bytes" - "crypto/sha256" - "strconv" + "math" "time" ) +var ( + maxNonce = math.MaxInt64 +) + type Block struct { Timestamp int64 Data []byte PrevBlockHash []byte Hash []byte + Nonce int } // NewBlock returns a new block, ready to be added to the chain func NewBlock(data string, prevBlockHash []byte) *Block { - block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}} - block.SetHash() + block := &Block{ + Timestamp: time.Now().Unix(), + Data: []byte(data), + PrevBlockHash: prevBlockHash, + Hash: []byte{}, + } + pow := NewProofOfWork(block) + nonce, hash := pow.Run() + + block.Hash = hash[:] + block.Nonce = nonce + return block } - -// SetHash creates the block hash -func (b *Block) SetHash() { - timestamp := []byte(strconv.FormatInt(b.Timestamp, 10)) - headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{}) - hash := sha256.Sum256(headers) - - b.Hash = hash[:] -} diff --git a/proof.go b/proof.go new file mode 100644 index 0000000..6bb9d88 --- /dev/null +++ b/proof.go @@ -0,0 +1,60 @@ +package main + +import ( + "bytes" + "crypto/sha256" + "fmt" + "math/big" +) + +const proofTargetBits = 16 + +type ProofOfWork struct { + block *Block + target *big.Int +} + +func NewProofOfWork(b *Block) *ProofOfWork { + target := big.NewInt(1) + target.Lsh(target, uint(256-proofTargetBits)) + + pow := &ProofOfWork{b, target} + + return pow +} + +func (pow *ProofOfWork) prepareData(nonce int) []byte { + data := bytes.Join( + [][]byte{ + pow.block.PrevBlockHash, + pow.block.Data, + IntToHex(pow.block.Timestamp), + IntToHex(int64(proofTargetBits)), + IntToHex(int64(nonce)), + }, + []byte{}, + ) + return data +} + +func (pow *ProofOfWork) Run() (int, []byte) { + var hashInt big.Int + var hash [32]byte + nonce := 0 + + fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Data) + for nonce < maxNonce { + data := pow.prepareData(nonce) + hash = sha256.Sum256(data) + fmt.Printf("\r%x", hash) + hashInt.SetBytes(hash[:]) + + if hashInt.Cmp(pow.target) == -1 { + break + } else { + nonce++ + } + } + fmt.Print("\n\n") + return nonce, hash[:] +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..20690bc --- /dev/null +++ b/utils.go @@ -0,0 +1,17 @@ +package main + +import ( + "bytes" + "encoding/binary" + "log" +) + +// IntToHex converts an int64 to a byte array +func IntToHex(num int64) []byte { + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.BigEndian, num) + if err != nil { + log.Panic(err) + } + return buf.Bytes() +}