diff --git a/.gitignore b/.gitignore
index 1b07dece..e2c8a2c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
*.db
*.dat
+cscope.*
+.*.swp
+blockchain_go
diff --git a/README.md b/README.md
index 76ae180b..d79518c0 100644
--- a/README.md
+++ b/README.md
@@ -9,3 +9,29 @@ A blockchain implementation in Go, as described in these articles:
5. [Addresses](https://jeiwan.cc/posts/building-blockchain-in-go-part-5/)
6. [Transactions 2](https://jeiwan.cc/posts/building-blockchain-in-go-part-6/)
7. [Network](https://jeiwan.cc/posts/building-blockchain-in-go-part-7/)
+
+# Quick Start
+
+## Download and install
+
+ go get github.com/richardweiyang/blockchain_go
+
+## Create file `main.go`
+
+source code
+
+```go
+package main
+
+import "github.com/richardweiyang/blockchain_go"
+
+func main() {
+ cli := bc.CLI{}
+ cli.Run()
+}
+```
+#### Build and run
+
+ go build main.go
+ ./main
+
diff --git a/base58.go b/base58.go
index 5db078e4..6b7089f3 100644
--- a/base58.go
+++ b/base58.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"bytes"
@@ -40,8 +40,10 @@ func Base58Decode(input []byte) []byte {
zeroBytes := 0
for _, b := range input {
- if b == 0x00 {
+ if b == b58Alphabet[0] {
zeroBytes++
+ } else {
+ break
}
}
diff --git a/base58_test.go b/base58_test.go
new file mode 100644
index 00000000..8d80cd33
--- /dev/null
+++ b/base58_test.go
@@ -0,0 +1,27 @@
+package bc
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBase58(t *testing.T) {
+ for i := 0; i < 100; i++ {
+ _, public := newKeyPair()
+ pubKeyHash := HashPubKey(public)
+
+ versionedPayload := append([]byte{version}, pubKeyHash...)
+ checksum := checksum(versionedPayload)
+
+ fullPayload := append(versionedPayload, checksum...)
+ address := Base58Encode(fullPayload)
+
+ assert.Equal(
+ t,
+ ValidateAddress(string(address[:])),
+ true,
+ "Address: %s is invalid", address,
+ )
+ }
+}
diff --git a/block.go b/block.go
index e403e549..3e90caec 100644
--- a/block.go
+++ b/block.go
@@ -1,9 +1,12 @@
-package main
+package bc
import (
"bytes"
"encoding/gob"
+ "fmt"
"log"
+ "strconv"
+ "strings"
"time"
)
@@ -71,3 +74,20 @@ func DeserializeBlock(d []byte) *Block {
return &block
}
+
+func (b *Block) PrintHTML(detail bool) string {
+ var lines []string
+ lines = append(lines, fmt.Sprintf("
Block %x
", b.Hash, b.Hash))
+ lines = append(lines, fmt.Sprintf("Height: %d", b.Height))
+ lines = append(lines, fmt.Sprintf("Prev. block: %x", b.PrevBlockHash, b.PrevBlockHash))
+ lines = append(lines, fmt.Sprintf("Created at : %s", time.Unix(b.Timestamp, 0)))
+ pow := NewProofOfWork(b)
+ lines = append(lines, fmt.Sprintf("PoW: %s", strconv.FormatBool(pow.Validate())))
+ if detail {
+ for _, tx := range b.Transactions {
+ lines = append(lines, tx.PrintHTML())
+ }
+ }
+ lines = append(lines, fmt.Sprintf(""))
+ return strings.Join(lines, "\n")
+}
diff --git a/blockchain.go b/blockchain.go
index 8436035b..da5e78a2 100644
--- a/blockchain.go
+++ b/blockchain.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"bytes"
@@ -8,6 +8,7 @@ import (
"fmt"
"log"
"os"
+ "strings"
"github.com/boltdb/bolt"
)
@@ -19,7 +20,7 @@ const genesisCoinbaseData = "The Times 03/Jan/2009 Chancellor on brink of second
// Blockchain implements interactions with a DB
type Blockchain struct {
tip []byte
- db *bolt.DB
+ DB *bolt.DB
}
// CreateBlockchain creates a new blockchain DB
@@ -82,7 +83,7 @@ func NewBlockchain(nodeID string) *Blockchain {
log.Panic(err)
}
- err = db.Update(func(tx *bolt.Tx) error {
+ err = db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
tip = b.Get([]byte("l"))
@@ -99,7 +100,7 @@ func NewBlockchain(nodeID string) *Blockchain {
// AddBlock saves the block into the blockchain
func (bc *Blockchain) AddBlock(block *Block) {
- err := bc.db.Update(func(tx *bolt.Tx) error {
+ err := bc.DB.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
blockInDb := b.Get(block.Hash)
@@ -199,7 +200,7 @@ func (bc *Blockchain) FindUTXO() map[string]TXOutputs {
// Iterator returns a BlockchainIterat
func (bc *Blockchain) Iterator() *BlockchainIterator {
- bci := &BlockchainIterator{bc.tip, bc.db}
+ bci := &BlockchainIterator{bc.tip, bc.DB}
return bci
}
@@ -208,7 +209,7 @@ func (bc *Blockchain) Iterator() *BlockchainIterator {
func (bc *Blockchain) GetBestHeight() int {
var lastBlock Block
- err := bc.db.View(func(tx *bolt.Tx) error {
+ err := bc.DB.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
lastHash := b.Get([]byte("l"))
blockData := b.Get(lastHash)
@@ -227,7 +228,7 @@ func (bc *Blockchain) GetBestHeight() int {
func (bc *Blockchain) GetBlock(blockHash []byte) (Block, error) {
var block Block
- err := bc.db.View(func(tx *bolt.Tx) error {
+ err := bc.DB.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
blockData := b.Get(blockHash)
@@ -277,7 +278,7 @@ func (bc *Blockchain) MineBlock(transactions []*Transaction) *Block {
}
}
- err := bc.db.View(func(tx *bolt.Tx) error {
+ err := bc.DB.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
lastHash = b.Get([]byte("l"))
@@ -294,7 +295,7 @@ func (bc *Blockchain) MineBlock(transactions []*Transaction) *Block {
newBlock := NewBlock(transactions, lastHash, lastHeight+1)
- err = bc.db.Update(func(tx *bolt.Tx) error {
+ err = bc.DB.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
err := b.Put(newBlock.Hash, newBlock.Serialize())
if err != nil {
@@ -358,3 +359,33 @@ func dbExists(dbFile string) bool {
return true
}
+
+func (bc *Blockchain) PrintHTML() string {
+ var lines []string
+ bci := bc.Iterator()
+
+ for {
+ block := bci.Next()
+
+ lines = append(lines, block.PrintHTML(false))
+
+ if len(block.PrevBlockHash) == 0 {
+ break
+ }
+ }
+ return strings.Join(lines, "\n")
+
+}
+
+func (bc *Blockchain) GetBalance(address string) int {
+ UTXOSet := UTXOSet{bc}
+ balance := 0
+ pubKeyHash := Base58Decode([]byte(address))
+ pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-4]
+ UTXOs := UTXOSet.FindUTXO(pubKeyHash)
+
+ for _, out := range UTXOs {
+ balance += out.Value
+ }
+ return balance
+}
diff --git a/blockchain_iterator.go b/blockchain_iterator.go
index 305311d9..5b3699e1 100644
--- a/blockchain_iterator.go
+++ b/blockchain_iterator.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"log"
diff --git a/cli.go b/cli.go
index b597b4c7..90d7165d 100644
--- a/cli.go
+++ b/cli.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"flag"
@@ -21,6 +21,14 @@ func (cli *CLI) printUsage() {
fmt.Println(" reindexutxo - Rebuilds the UTXO set")
fmt.Println(" send -from FROM -to TO -amount AMOUNT -mine - Send AMOUNT of coins from FROM address to TO. Mine on the same node, when -mine is set.")
fmt.Println(" startnode -miner ADDRESS - Start a node with ID specified in NODE_ID env. var. -miner enables mining")
+ fmt.Println()
+ fmt.Println("Exploring cmds:")
+ fmt.Println(" generatePrivKey - generate KeyPair for exploring")
+ fmt.Println(" getPubKey -privKey PRIKEY - generate PubKey from privateKey")
+ fmt.Println(" getAddress -pubKey PUBKEY - convert pubKey to address")
+ fmt.Println(" getPubKeyHash -address Address - get pubKeyHash of an address")
+ fmt.Println(" validateAddress -addr Address - validate an address")
+ fmt.Println(" getBlock -hash BlockHash - get a block with BlockHash")
}
func (cli *CLI) validateArgs() {
@@ -48,6 +56,12 @@ func (cli *CLI) Run() {
reindexUTXOCmd := flag.NewFlagSet("reindexutxo", flag.ExitOnError)
sendCmd := flag.NewFlagSet("send", flag.ExitOnError)
startNodeCmd := flag.NewFlagSet("startnode", flag.ExitOnError)
+ generatePrivKeyCmd := flag.NewFlagSet("generatePrivKey", flag.ExitOnError)
+ getPubKeyCmd := flag.NewFlagSet("getPubKey", flag.ExitOnError)
+ getAddressCmd := flag.NewFlagSet("getAddress", flag.ExitOnError)
+ getPubKeyHashCmd := flag.NewFlagSet("getPubKeyHash", flag.ExitOnError)
+ validateAddrCmd := flag.NewFlagSet("validateAddress", flag.ExitOnError)
+ getBlockCmd := flag.NewFlagSet("getBlock", flag.ExitOnError)
getBalanceAddress := getBalanceCmd.String("address", "", "The address to get balance for")
createBlockchainAddress := createBlockchainCmd.String("address", "", "The address to send genesis block reward to")
@@ -56,6 +70,11 @@ func (cli *CLI) Run() {
sendAmount := sendCmd.Int("amount", 0, "Amount to send")
sendMine := sendCmd.Bool("mine", false, "Mine immediately on the same node")
startNodeMiner := startNodeCmd.String("miner", "", "Enable mining mode and send reward to ADDRESS")
+ privateKey := getPubKeyCmd.String("privKey", "", "generate PubKey based on this")
+ pubKey := getAddressCmd.String("pubKey", "", "the key where address generated")
+ pubKeyAddress := getPubKeyHashCmd.String("address", "", "the pub address")
+ address := validateAddrCmd.String("addr", "", "the public address")
+ blockHash := getBlockCmd.String("hash", "", "the block hash")
switch os.Args[1] {
case "getbalance":
@@ -98,6 +117,36 @@ func (cli *CLI) Run() {
if err != nil {
log.Panic(err)
}
+ case "validateAddress":
+ err := validateAddrCmd.Parse(os.Args[2:])
+ if err != nil {
+ log.Panic(err)
+ }
+ case "generatePrivKey":
+ err := generatePrivKeyCmd.Parse(os.Args[2:])
+ if err != nil {
+ log.Panic(err)
+ }
+ case "getPubKey":
+ err := getPubKeyCmd.Parse(os.Args[2:])
+ if err != nil {
+ log.Panic(err)
+ }
+ case "getPubKeyHash":
+ err := getPubKeyHashCmd.Parse(os.Args[2:])
+ if err != nil {
+ log.Panic(err)
+ }
+ case "getAddress":
+ err := getAddressCmd.Parse(os.Args[2:])
+ if err != nil {
+ log.Panic(err)
+ }
+ case "getBlock":
+ err := getBlockCmd.Parse(os.Args[2:])
+ if err != nil {
+ log.Panic(err)
+ }
default:
cli.printUsage()
os.Exit(1)
@@ -152,4 +201,53 @@ func (cli *CLI) Run() {
}
cli.startNode(nodeID, *startNodeMiner)
}
+
+ if generatePrivKeyCmd.Parsed() {
+ cli.generatePrivKey()
+ }
+
+ if getPubKeyCmd.Parsed() {
+ if *privateKey == "" {
+ getPubKeyCmd.Usage()
+ os.Exit(1)
+ }
+ cli.getPubKey(*privateKey)
+ }
+
+ if getAddressCmd.Parsed() {
+ if *pubKey == "" {
+ getAddressCmd.Usage()
+ os.Exit(1)
+ }
+
+ cli.getAddress(*pubKey)
+ }
+
+ if getPubKeyHashCmd.Parsed() {
+ if *pubKeyAddress == "" {
+ getPubKeyHashCmd.Usage()
+ os.Exit(1)
+ }
+
+ cli.getPubKeyHash(*pubKeyAddress)
+ }
+
+ if validateAddrCmd.Parsed() {
+ if *address == "" {
+ validateAddrCmd.Usage()
+ os.Exit(1)
+ }
+
+ cli.validateAddr(*address)
+ }
+
+ if getBlockCmd.Parsed() {
+ if *blockHash == "" {
+ getBlockCmd.Usage()
+ os.Exit(1)
+ }
+
+ cli.printBlock(*blockHash, nodeID)
+ }
+
}
diff --git a/cli_createblockchain.go b/cli_createblockchain.go
index 7e69405c..bc77fe96 100644
--- a/cli_createblockchain.go
+++ b/cli_createblockchain.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"fmt"
@@ -10,7 +10,7 @@ func (cli *CLI) createBlockchain(address, nodeID string) {
log.Panic("ERROR: Address is not valid")
}
bc := CreateBlockchain(address, nodeID)
- defer bc.db.Close()
+ defer bc.DB.Close()
UTXOSet := UTXOSet{bc}
UTXOSet.Reindex()
diff --git a/cli_createwallet.go b/cli_createwallet.go
index 0e05f202..c605d656 100644
--- a/cli_createwallet.go
+++ b/cli_createwallet.go
@@ -1,4 +1,4 @@
-package main
+package bc
import "fmt"
diff --git a/cli_explore.go b/cli_explore.go
new file mode 100644
index 00000000..d68a0e91
--- /dev/null
+++ b/cli_explore.go
@@ -0,0 +1,48 @@
+package bc
+
+import (
+ "crypto/elliptic"
+ "encoding/hex"
+ "fmt"
+)
+
+func (cli *CLI) getPubKey(privateKey string) {
+ curve := elliptic.P256()
+ priv_key, _ := hex.DecodeString(privateKey)
+ x, y := curve.ScalarBaseMult(priv_key)
+ pubKey := append(x.Bytes(), y.Bytes()...)
+ fmt.Println(hex.EncodeToString(pubKey))
+}
+
+func (cli *CLI) generatePrivKey() {
+ private, _ := newKeyPair()
+ fmt.Println(hex.EncodeToString(private.D.Bytes()))
+}
+
+func (cli *CLI) getAddress(pubKey string) {
+ public, _ := hex.DecodeString(pubKey)
+
+ pubKeyHash := HashPubKey(public)
+
+ versionedPayload := append([]byte{version}, pubKeyHash...)
+ fullPayload := append(versionedPayload, checksum(versionedPayload)...)
+
+ fmt.Println()
+ fmt.Printf("PubKey : %s\n", pubKey)
+ fmt.Printf("PubKeyHash : %x\n", pubKeyHash)
+ fmt.Printf("Address : %s\n", Base58Encode(fullPayload))
+}
+
+func (cli *CLI) getPubKeyHash(address string) {
+ pubKeyHash := Base58Decode([]byte(address))
+ fmt.Printf("%x\n", pubKeyHash[1:len(pubKeyHash)-4])
+}
+
+func (cli *CLI) validateAddr(address string) {
+ fmt.Printf("Address: %s\n", address)
+ if !ValidateAddress(address) {
+ fmt.Println("Not valid!")
+ } else {
+ fmt.Println("Valid!")
+ }
+}
diff --git a/cli_getbalance.go b/cli_getbalance.go
index a86e3dc1..9aa7c0f1 100644
--- a/cli_getbalance.go
+++ b/cli_getbalance.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"fmt"
@@ -11,7 +11,7 @@ func (cli *CLI) getBalance(address, nodeID string) {
}
bc := NewBlockchain(nodeID)
UTXOSet := UTXOSet{bc}
- defer bc.db.Close()
+ defer bc.DB.Close()
balance := 0
pubKeyHash := Base58Decode([]byte(address))
diff --git a/cli_listaddress.go b/cli_listaddress.go
index 0d30563d..66bdf1e4 100644
--- a/cli_listaddress.go
+++ b/cli_listaddress.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"fmt"
diff --git a/cli_printchain.go b/cli_printchain.go
index 81b7edf7..7aaa63fb 100644
--- a/cli_printchain.go
+++ b/cli_printchain.go
@@ -1,13 +1,14 @@
-package main
+package bc
import (
"fmt"
"strconv"
+ "time"
)
func (cli *CLI) printChain(nodeID string) {
bc := NewBlockchain(nodeID)
- defer bc.db.Close()
+ defer bc.DB.Close()
bci := bc.Iterator()
@@ -17,6 +18,7 @@ func (cli *CLI) printChain(nodeID string) {
fmt.Printf("============ Block %x ============\n", block.Hash)
fmt.Printf("Height: %d\n", block.Height)
fmt.Printf("Prev. block: %x\n", block.PrevBlockHash)
+ fmt.Printf("Created at : %s\n", time.Unix(block.Timestamp, 0))
pow := NewProofOfWork(block)
fmt.Printf("PoW: %s\n\n", strconv.FormatBool(pow.Validate()))
for _, tx := range block.Transactions {
@@ -29,3 +31,32 @@ func (cli *CLI) printChain(nodeID string) {
}
}
}
+
+func (cli *CLI) printBlock(blockHash, nodeID string) {
+ bc := NewBlockchain(nodeID)
+ defer bc.DB.Close()
+
+ bci := bc.Iterator()
+
+ for {
+ block := bci.Next()
+
+ hash := fmt.Sprintf("%x", block.Hash)
+ if hash == blockHash {
+ fmt.Printf("============ Block %x ============\n", block.Hash)
+ fmt.Printf("Height: %d\n", block.Height)
+ fmt.Printf("Prev. block: %x\n", block.PrevBlockHash)
+ fmt.Printf("Created at : %s\n", time.Unix(block.Timestamp, 0))
+ pow := NewProofOfWork(block)
+ fmt.Printf("PoW: %s\n\n", strconv.FormatBool(pow.Validate()))
+ for _, tx := range block.Transactions {
+ fmt.Println(tx)
+ }
+ fmt.Printf("\n\n")
+ }
+
+ if len(block.PrevBlockHash) == 0 {
+ break
+ }
+ }
+}
diff --git a/cli_reindexutxo.go b/cli_reindexutxo.go
index 87db3245..74b8dc52 100644
--- a/cli_reindexutxo.go
+++ b/cli_reindexutxo.go
@@ -1,4 +1,4 @@
-package main
+package bc
import "fmt"
diff --git a/cli_send.go b/cli_send.go
index 75a59301..f2f58767 100644
--- a/cli_send.go
+++ b/cli_send.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"fmt"
@@ -15,15 +15,19 @@ func (cli *CLI) send(from, to string, amount int, nodeID string, mineNow bool) {
bc := NewBlockchain(nodeID)
UTXOSet := UTXOSet{bc}
- defer bc.db.Close()
+ defer bc.DB.Close()
wallets, err := NewWallets(nodeID)
if err != nil {
log.Panic(err)
}
wallet := wallets.GetWallet(from)
+ if wallet == nil {
+ fmt.Println("The Address doesn't belongs to you!")
+ return
+ }
- tx := NewUTXOTransaction(&wallet, to, amount, &UTXOSet)
+ tx := NewUTXOTransaction(wallet, to, amount, &UTXOSet)
if mineNow {
cbTx := NewCoinbaseTX(from, "")
diff --git a/cli_startnode.go b/cli_startnode.go
index d390512e..9d6b5814 100644
--- a/cli_startnode.go
+++ b/cli_startnode.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"fmt"
diff --git a/main.go b/main.go
deleted file mode 100644
index a48ad8e5..00000000
--- a/main.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package main
-
-func main() {
- cli := CLI{}
- cli.Run()
-}
diff --git a/merkle_tree.go b/merkle_tree.go
index 7a4156bd..985dde45 100644
--- a/merkle_tree.go
+++ b/merkle_tree.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"crypto/sha256"
@@ -20,7 +20,8 @@ type MerkleNode struct {
func NewMerkleTree(data [][]byte) *MerkleTree {
var nodes []MerkleNode
- if len(data)%2 != 0 {
+ // append node until number is power of 2
+ for (len(data) & (len(data) - 1)) != 0 {
data = append(data, data[len(data)-1])
}
@@ -29,7 +30,8 @@ func NewMerkleTree(data [][]byte) *MerkleTree {
nodes = append(nodes, *node)
}
- for i := 0; i < len(data)/2; i++ {
+ // up level until there is only 1 node
+ for len(nodes) > 1 {
var newLevel []MerkleNode
for j := 0; j < len(nodes); j += 2 {
diff --git a/merkle_tree_test.go b/merkle_tree_test.go
index acff5ffa..5275a5e1 100644
--- a/merkle_tree_test.go
+++ b/merkle_tree_test.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"encoding/hex"
@@ -73,3 +73,41 @@ func TestNewMerkleTree(t *testing.T) {
assert.Equal(t, rootHash, fmt.Sprintf("%x", mTree.RootNode.Data), "Merkle tree root hash is correct")
}
+
+func TestNewMerkleTree8(t *testing.T) {
+ data := [][]byte{
+ []byte("node1"),
+ []byte("node2"),
+ []byte("node3"),
+ []byte("node4"),
+ []byte("node5"),
+ []byte("node6"),
+ }
+ // Level 0
+ n01 := NewMerkleNode(nil, nil, data[0])
+ n02 := NewMerkleNode(nil, nil, data[1])
+ n03 := NewMerkleNode(nil, nil, data[2])
+ n04 := NewMerkleNode(nil, nil, data[3])
+ n05 := NewMerkleNode(nil, nil, data[4])
+ n06 := NewMerkleNode(nil, nil, data[5])
+ n07 := NewMerkleNode(nil, nil, data[5])
+ n08 := NewMerkleNode(nil, nil, data[5])
+
+ // Level 1
+ n11 := NewMerkleNode(n01, n02, nil)
+ n12 := NewMerkleNode(n03, n04, nil)
+ n13 := NewMerkleNode(n05, n06, nil)
+ n14 := NewMerkleNode(n07, n08, nil)
+
+ // Level 2
+ n21 := NewMerkleNode(n11, n12, nil)
+ n22 := NewMerkleNode(n13, n14, nil)
+
+ // Level 3
+ n31 := NewMerkleNode(n21, n22, nil)
+
+ rootHash := fmt.Sprintf("%x", n31.Data)
+ mTree := NewMerkleTree(data)
+
+ assert.Equal(t, rootHash, fmt.Sprintf("%x", mTree.RootNode.Data), "Merkle tree root hash is correct")
+}
diff --git a/proofofwork.go b/proofofwork.go
index 967a4bc1..2160cbe2 100644
--- a/proofofwork.go
+++ b/proofofwork.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"bytes"
diff --git a/server.go b/server.go
index b13f520b..0895ad98 100644
--- a/server.go
+++ b/server.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"bytes"
@@ -341,7 +341,7 @@ func handleTx(request []byte, bc *Blockchain) {
UTXOSet := UTXOSet{bc}
UTXOSet.Reindex()
- fmt.Println("New block is mined!")
+ fmt.Printf("New block with %d tx is mined!\n", len(txs))
for _, tx := range txs {
txID := hex.EncodeToString(tx.ID)
@@ -383,6 +383,7 @@ func handleVersion(request []byte, bc *Blockchain) {
// sendAddr(payload.AddrFrom)
if !nodeIsKnown(payload.AddrFrom) {
+ fmt.Printf("A new node %s is connected\n", payload.AddrFrom)
knownNodes = append(knownNodes, payload.AddrFrom)
}
}
diff --git a/transaction.go b/transaction.go
index f2031d3d..bf1b1bc4 100644
--- a/transaction.go
+++ b/transaction.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"bytes"
@@ -94,17 +94,23 @@ func (tx Transaction) String() string {
for i, input := range tx.Vin {
+ pubKeyHash := HashPubKey(input.PubKey)
+ versionedPayload := append([]byte{version}, pubKeyHash...)
+ fullPayload := append(versionedPayload, checksum(versionedPayload)...)
+
lines = append(lines, fmt.Sprintf(" Input %d:", i))
lines = append(lines, fmt.Sprintf(" TXID: %x", input.Txid))
lines = append(lines, fmt.Sprintf(" Out: %d", input.Vout))
lines = append(lines, fmt.Sprintf(" Signature: %x", input.Signature))
lines = append(lines, fmt.Sprintf(" PubKey: %x", input.PubKey))
+ lines = append(lines, fmt.Sprintf(" Addr : %s", Base58Encode(fullPayload)))
}
for i, output := range tx.Vout {
lines = append(lines, fmt.Sprintf(" Output %d:", i))
lines = append(lines, fmt.Sprintf(" Value: %d", output.Value))
lines = append(lines, fmt.Sprintf(" Script: %x", output.PubKeyHash))
+ lines = append(lines, fmt.Sprintf(" Addr : %s", output.Address))
}
return strings.Join(lines, "\n")
@@ -120,7 +126,7 @@ func (tx *Transaction) TrimmedCopy() Transaction {
}
for _, vout := range tx.Vout {
- outputs = append(outputs, TXOutput{vout.Value, vout.PubKeyHash})
+ outputs = append(outputs, TXOutput{vout.Value, vout.PubKeyHash, vout.Address})
}
txCopy := Transaction{tx.ID, inputs, outputs}
@@ -243,3 +249,32 @@ func DeserializeTransaction(data []byte) Transaction {
return transaction
}
+
+func (tx Transaction) PrintHTML() string {
+ var lines []string
+
+ lines = append(lines, fmt.Sprintf(" Transaction %x:
", tx.ID))
+
+ for i, input := range tx.Vin {
+
+ pubKeyHash := HashPubKey(input.PubKey)
+ versionedPayload := append([]byte{version}, pubKeyHash...)
+ fullPayload := append(versionedPayload, checksum(versionedPayload)...)
+
+ lines = append(lines, fmt.Sprintf("Input %d:
", i))
+ lines = append(lines, fmt.Sprintf("TXID: %x
", input.Txid))
+ lines = append(lines, fmt.Sprintf("Out: %d
", input.Vout))
+ lines = append(lines, fmt.Sprintf("Signature: %x
", input.Signature))
+ lines = append(lines, fmt.Sprintf("PubKey: %x
", input.PubKey))
+ lines = append(lines, fmt.Sprintf("Addr : %s
", Base58Encode(fullPayload)))
+ }
+
+ for i, output := range tx.Vout {
+ lines = append(lines, fmt.Sprintf("Output %d:
", i))
+ lines = append(lines, fmt.Sprintf("Value: %d
", output.Value))
+ lines = append(lines, fmt.Sprintf("Script: %x
", output.PubKeyHash))
+ lines = append(lines, fmt.Sprintf("Addr : %s
", output.Address))
+ }
+
+ return strings.Join(lines, "")
+}
diff --git a/transaction_input.go b/transaction_input.go
index 23beeba5..e928ef6c 100644
--- a/transaction_input.go
+++ b/transaction_input.go
@@ -1,4 +1,4 @@
-package main
+package bc
import "bytes"
diff --git a/transaction_output.go b/transaction_output.go
index 2ae68dec..d3163e81 100644
--- a/transaction_output.go
+++ b/transaction_output.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"bytes"
@@ -10,6 +10,7 @@ import (
type TXOutput struct {
Value int
PubKeyHash []byte
+ Address string
}
// Lock signs the output
@@ -26,7 +27,7 @@ func (out *TXOutput) IsLockedWithKey(pubKeyHash []byte) bool {
// NewTXOutput create a new TXOutput
func NewTXOutput(value int, address string) *TXOutput {
- txo := &TXOutput{value, nil}
+ txo := &TXOutput{value, nil, address}
txo.Lock([]byte(address))
return txo
diff --git a/utils.go b/utils.go
index aecf9192..bd1649f1 100644
--- a/utils.go
+++ b/utils.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"bytes"
diff --git a/utxo_set.go b/utxo_set.go
index 180ce04b..7adbad9f 100644
--- a/utxo_set.go
+++ b/utxo_set.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"encoding/hex"
@@ -18,7 +18,7 @@ type UTXOSet struct {
func (u UTXOSet) FindSpendableOutputs(pubkeyHash []byte, amount int) (int, map[string][]int) {
unspentOutputs := make(map[string][]int)
accumulated := 0
- db := u.Blockchain.db
+ db := u.Blockchain.DB
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(utxoBucket))
@@ -48,7 +48,7 @@ func (u UTXOSet) FindSpendableOutputs(pubkeyHash []byte, amount int) (int, map[s
// FindUTXO finds UTXO for a public key hash
func (u UTXOSet) FindUTXO(pubKeyHash []byte) []TXOutput {
var UTXOs []TXOutput
- db := u.Blockchain.db
+ db := u.Blockchain.DB
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(utxoBucket))
@@ -75,7 +75,7 @@ func (u UTXOSet) FindUTXO(pubKeyHash []byte) []TXOutput {
// CountTransactions returns the number of transactions in the UTXO set
func (u UTXOSet) CountTransactions() int {
- db := u.Blockchain.db
+ db := u.Blockchain.DB
counter := 0
err := db.View(func(tx *bolt.Tx) error {
@@ -97,7 +97,7 @@ func (u UTXOSet) CountTransactions() int {
// Reindex rebuilds the UTXO set
func (u UTXOSet) Reindex() {
- db := u.Blockchain.db
+ db := u.Blockchain.DB
bucketName := []byte(utxoBucket)
err := db.Update(func(tx *bolt.Tx) error {
@@ -141,7 +141,7 @@ func (u UTXOSet) Reindex() {
// Update updates the UTXO set with transactions from the Block
// The Block is considered to be the tip of a blockchain
func (u UTXOSet) Update(block *Block) {
- db := u.Blockchain.db
+ db := u.Blockchain.DB
err := db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(utxoBucket))
diff --git a/wallet.go b/wallet.go
index 506b5444..e1bd6a34 100644
--- a/wallet.go
+++ b/wallet.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"bytes"
diff --git a/wallets.go b/wallets.go
index 9f376f53..763d3547 100644
--- a/wallets.go
+++ b/wallets.go
@@ -1,4 +1,4 @@
-package main
+package bc
import (
"bytes"
@@ -49,8 +49,8 @@ func (ws *Wallets) GetAddresses() []string {
}
// GetWallet returns a Wallet by its address
-func (ws Wallets) GetWallet(address string) Wallet {
- return *ws.Wallets[address]
+func (ws Wallets) GetWallet(address string) *Wallet {
+ return ws.Wallets[address]
}
// LoadFromFile loads wallets from the file