parent
37ac634c0a
commit
73b873b90b
@ -0,0 +1,8 @@ |
||||
{ |
||||
"workbench.colorTheme": "Solarized Light", |
||||
"npm.enableScriptExplorer": true, |
||||
"window.zoomLevel": 3, |
||||
"editor.tabSize": 4, |
||||
"editor.insertSpaces": true, |
||||
"editor.detectIndentation": true |
||||
} |
@ -1,43 +1,43 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"crypto/sha256" |
||||
"time" |
||||
"bytes" |
||||
"crypto/sha256" |
||||
"time" |
||||
) |
||||
|
||||
// Block keeps block headers.
|
||||
type Block struct { |
||||
Timestamp int64 |
||||
Transactions []*Transaction |
||||
PrevBlockHash []byte |
||||
Hash []byte |
||||
Timestamp int64 |
||||
Transactions []*Transaction |
||||
PrevBlockHash []byte |
||||
Hash []byte |
||||
} |
||||
|
||||
// HashTransactions returns a hash of the transactions in the block
|
||||
func (b *Block) HashTransactions() []byte { |
||||
var txHashes [][]byte |
||||
var txHash [32]byte |
||||
|
||||
for _, tx := range b.Transactions { |
||||
txHashes = append(txHashes, tx.ID) |
||||
} |
||||
txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) |
||||
return txHash[:] |
||||
var txHashes [][]byte |
||||
var txHash [32]byte |
||||
|
||||
for _, tx := range b.Transactions { |
||||
txHashes = append(txHashes, tx.ID) |
||||
} |
||||
txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) |
||||
return txHash[:] |
||||
} |
||||
|
||||
// NewBlock creates and returns Block.
|
||||
func NewBlock(utxoPool []UTXOPool, prevBlockHash []byte) *Block { |
||||
|
||||
block := &Block{time.Now().Unix(), utxoPool, prevBlockHash, []byte{}} |
||||
block.SetHash() |
||||
return block |
||||
block := &Block{time.Now().Unix(), utxoPool, prevBlockHash, []byte{}} |
||||
block.SetHash() |
||||
return block |
||||
} |
||||
|
||||
// NewGenesisBlock creates and returns genesis Block.
|
||||
func NewGenesisBlock() *Block { |
||||
genesisUTXOPool := UTXOPool{} |
||||
genesisUTXOPool.utxos["genesis"] = TOTAL_COINS |
||||
genesisUTXOPool := UTXOPool{} |
||||
genesisUTXOPool.utxos["genesis"] = TOTAL_COINS |
||||
|
||||
return NewBlock(genesisUTXOPool, []byte{}) |
||||
return NewBlock(genesisUTXOPool, []byte{}) |
||||
} |
||||
|
@ -1,97 +1,98 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"encoding/hex" |
||||
"encoding/hex" |
||||
) |
||||
|
||||
// Blockchain keeps a sequence of Blocks
|
||||
type Blockchain struct { |
||||
blocks []*Block |
||||
blocks []*Block |
||||
} |
||||
|
||||
// AddBlock saves provided data as a block in the blockchain
|
||||
func (bc *Blockchain) AddBlock(data string) { |
||||
prevBlock := bc.blocks[len(bc.blocks)-1] |
||||
prevBlock := bc.blocks[len(bc.blocks)-1] |
||||
|
||||
// TODO(minhdoan): Parse data.
|
||||
newBlock := NewBlock({}, prevBlock.Hash) |
||||
bc.blocks = append(bc.blocks, newBlock) |
||||
// TODO(minhdoan): Parse data.
|
||||
newBlock := NewBlock({}, prevBlock.Hash) |
||||
bc.blocks = append(bc.blocks, newBlock) |
||||
} |
||||
|
||||
// NewBlockchain creates a new Blockchain with genesis Block
|
||||
func NewBlockchain() *Blockchain { |
||||
return &Blockchain{[]*Block{NewGenesisBlock()}} |
||||
return &Blockchain{[]*Block{NewGenesisBlock()}} |
||||
} |
||||
|
||||
// FindUnspentTransactions returns a list of transactions containing unspent outputs
|
||||
func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction { |
||||
var unspentTXs []Transaction |
||||
spentTXOs := make(map[string][]int) |
||||
|
||||
for index := len(bc.blocks); index >= 0; index-- { |
||||
block := bc.blocks[index]; |
||||
|
||||
BreakTransaction: |
||||
for _, tx := range block.Transactions { |
||||
txId := hex.EncodeToString(tx.Id) |
||||
|
||||
idx := -1 |
||||
if spentTXOs[txId] != nil { |
||||
idx = 0 |
||||
} |
||||
for outIdx, txOutput := range tx.txOutput { |
||||
if idx >= 0 spentTXOs[txId][idx] == outIdx { |
||||
continue |
||||
} |
||||
|
||||
if txOutput.address == address { |
||||
unspentTXs = append(unspentTXs, *tx) |
||||
continue BreakTransaction |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return unspentTXs |
||||
var unspentTXs []Transaction |
||||
spentTXOs := make(map[string][]int) |
||||
|
||||
for index := len(bc.blocks) - 1; index >= 0; index-- { |
||||
block := bc.blocks[index]; |
||||
|
||||
BreakTransaction: |
||||
for _, tx := range block.Transactions { |
||||
txId := hex.EncodeToString(tx.Id) |
||||
|
||||
idx := -1 |
||||
if spentTXOs[txId] != nil { |
||||
idx = 0 |
||||
} |
||||
for outIdx, txOutput := range tx.txOutput { |
||||
if idx >= 0 && spentTXOs[txId][idx] == outIdx { |
||||
idx++ |
||||
continue |
||||
} |
||||
|
||||
if txOutput.address == address { |
||||
unspentTXs = append(unspentTXs, *tx) |
||||
continue BreakTransaction |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return unspentTXs |
||||
} |
||||
|
||||
// FindUTXO finds and returns all unspent transaction outputs
|
||||
func (bc *Blockchain) FindUTXO(address string) []TXOutput { |
||||
var UTXOs []TXOutput |
||||
unspentTXs := bc.FindUnspentTransactions(address) |
||||
|
||||
for _, tx := range unspentTXs { |
||||
for _, txOutput := range tx.txOutput { |
||||
if txOutput.address == address { |
||||
UTXOs = append(UTXOs, txOutput) |
||||
break |
||||
} |
||||
} |
||||
} |
||||
|
||||
return UTXOs |
||||
var UTXOs []TXOutput |
||||
unspentTXs := bc.FindUnspentTransactions(address) |
||||
|
||||
for _, tx := range unspentTXs { |
||||
for _, txOutput := range tx.txOutput { |
||||
if txOutput.address == address { |
||||
UTXOs = append(UTXOs, txOutput) |
||||
break |
||||
} |
||||
} |
||||
} |
||||
|
||||
return UTXOs |
||||
} |
||||
|
||||
// FindSpendableOutputs finds and returns unspent outputs to reference in inputs
|
||||
func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) { |
||||
unspentOutputs := make(map[string][]int) |
||||
unspentTXs := bc.FindUnspentTransactions(address) |
||||
accumulated := 0 |
||||
unspentOutputs := make(map[string][]int) |
||||
unspentTXs := bc.FindUnspentTransactions(address) |
||||
accumulated := 0 |
||||
|
||||
Work: |
||||
for _, tx := range unspentTXs { |
||||
txID := hex.EncodeToString(tx.ID) |
||||
|
||||
for outIdx, txOutput := range tx.txOutput { |
||||
if txOutput.address == address && accumulated < amount { |
||||
accumulated += txOutput.value |
||||
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx) |
||||
|
||||
if accumulated >= amount { |
||||
break Work |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return accumulated, unspentOutputs |
||||
for _, tx := range unspentTXs { |
||||
txID := hex.EncodeToString(tx.ID) |
||||
|
||||
for outIdx, txOutput := range tx.txOutput { |
||||
if txOutput.address == address && accumulated < amount { |
||||
accumulated += txOutput.value |
||||
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx) |
||||
|
||||
if accumulated >= amount { |
||||
break Work |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return accumulated, unspentOutputs |
||||
} |
||||
|
@ -1,40 +1,40 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"crypto/sha256" |
||||
"encoding/gob" |
||||
"log" |
||||
"bytes" |
||||
"crypto/sha256" |
||||
"encoding/gob" |
||||
"log" |
||||
) |
||||
|
||||
// Transaction represents a Bitcoin transaction
|
||||
type Transaction struct { |
||||
id []byte |
||||
txInput []TXInput |
||||
txOutput []TXOutput |
||||
id []byte |
||||
txInput []TXInput |
||||
txOutput []TXOutput |
||||
} |
||||
|
||||
type TXOutput struct { |
||||
address string |
||||
value int |
||||
address string |
||||
value int |
||||
} |
||||
|
||||
type TXInput struct { |
||||
txId []byte |
||||
txOutputIndex int |
||||
address string |
||||
txId []byte |
||||
txOutputIndex int |
||||
address string |
||||
} |
||||
|
||||
// SetID sets ID of a transaction
|
||||
func (tx *Transaction) SetId() { |
||||
var encoded bytes.Buffer |
||||
var hash [32]byte |
||||
var encoded bytes.Buffer |
||||
var hash [32]byte |
||||
|
||||
enc := gob.NewEncoder(&encoded) |
||||
err := enc.Encode(tx) |
||||
if err != nil { |
||||
log.Panic(err) |
||||
} |
||||
hash = sha256.Sum256(encoded.Bytes()) |
||||
tx.ID = hash[:] |
||||
enc := gob.NewEncoder(&encoded) |
||||
err := enc.Encode(tx) |
||||
if err != nil { |
||||
log.Panic(err) |
||||
} |
||||
hash = sha256.Sum256(encoded.Bytes()) |
||||
tx.ID = hash[:] |
||||
} |
||||
|
@ -1,70 +1,70 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/binary" |
||||
"encoding/gob" |
||||
"log" |
||||
"strconv" |
||||
"strings" |
||||
"bytes" |
||||
"encoding/binary" |
||||
"encoding/gob" |
||||
"log" |
||||
"strconv" |
||||
"strings" |
||||
) |
||||
|
||||
// IntToHex converts an int64 to a byte array
|
||||
func IntToHex(num int64) []byte { |
||||
buff := new(bytes.Buffer) |
||||
err := binary.Write(buff, binary.BigEndian, num) |
||||
if err != nil { |
||||
log.Panic(err) |
||||
} |
||||
buff := new(bytes.Buffer) |
||||
err := binary.Write(buff, binary.BigEndian, num) |
||||
if err != nil { |
||||
log.Panic(err) |
||||
} |
||||
|
||||
return buff.Bytes() |
||||
return buff.Bytes() |
||||
} |
||||
|
||||
// Serialize is to serialize a block into []byte.
|
||||
func (b *Block) Serialize() []byte { |
||||
var result bytes.Buffer |
||||
encoder := gob.NewEncoder(&result) |
||||
var result bytes.Buffer |
||||
encoder := gob.NewEncoder(&result) |
||||
|
||||
err := encoder.Encode(b) |
||||
err := encoder.Encode(b) |
||||
|
||||
return result.Bytes() |
||||
return result.Bytes() |
||||
} |
||||
|
||||
// DeserializeBlock is to deserialize []byte into a Block.
|
||||
func DeserializeBlock(d []byte) *Block { |
||||
var block Block |
||||
var block Block |
||||
|
||||
decoder := gob.NewDecoder(bytes.NewReader(d)) |
||||
err := decoder.Decode(&block) |
||||
decoder := gob.NewDecoder(bytes.NewReader(d)) |
||||
err := decoder.Decode(&block) |
||||
|
||||
return &block |
||||
return &block |
||||
} |
||||
|
||||
// Helper library to convert '1,2,3,4' into []int{1,2,3,4}.
|
||||
func ConvertIntoInts(data string) []int { |
||||
var res = []int{} |
||||
items := strings.Split(data, " ") |
||||
for _, value := range items { |
||||
intValue, err := strconv.Atoi(value) |
||||
checkError(err) |
||||
res = append(res, intValue) |
||||
} |
||||
return res |
||||
var res = []int{} |
||||
items := strings.Split(data, " ") |
||||
for _, value := range items { |
||||
intValue, err := strconv.Atoi(value) |
||||
checkError(err) |
||||
res = append(res, intValue) |
||||
} |
||||
return res |
||||
} |
||||
|
||||
// Helper library to convert '1,2,3,4' into []int{1,2,3,4}.
|
||||
func ConvertIntoMap(data string) map[string]int { |
||||
var res = map[string]int |
||||
items := strings.Split(data, ",") |
||||
for _, value := range items { |
||||
pair := strings.Split(value, " ") |
||||
if len(pair) == 3 { |
||||
intValue, err := strconv.Atoi(pair[2]) |
||||
if err != nil { |
||||
pair[0] = strings.Trim(pair[0]) |
||||
res[pair[0]] = intValue |
||||
} |
||||
} |
||||
} |
||||
return res |
||||
var res = map[string]int |
||||
items := strings.Split(data, ",") |
||||
for _, value := range items { |
||||
pair := strings.Split(value, " ") |
||||
if len(pair) == 3 { |
||||
intValue, err := strconv.Atoi(pair[2]) |
||||
if err != nil { |
||||
pair[0] = strings.Trim(pair[0]) |
||||
res[pair[0]] = intValue |
||||
} |
||||
} |
||||
} |
||||
return res |
||||
} |
||||
|
Loading…
Reference in new issue