From c0accf31e0409e729ae8560f6a0b14dd1ff92a73 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 11 Oct 2017 23:19:44 +0800 Subject: [PATCH 01/24] cli: show timestamp of the block --- cli_printchain.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli_printchain.go b/cli_printchain.go index 81b7edf7..6c344dec 100644 --- a/cli_printchain.go +++ b/cli_printchain.go @@ -3,6 +3,7 @@ package main import ( "fmt" "strconv" + "time" ) func (cli *CLI) printChain(nodeID string) { @@ -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 { From 5e43c770eefec45373382b76e939788a0c493ba5 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Mon, 16 Oct 2017 12:41:40 +0800 Subject: [PATCH 02/24] merkle tree: fix the implementation and add a test case Merkle tree requires a full binary tree, which means the number of node is power of 2 and the level of the tree is log(N) instead of N/2. This patch duplicate the node to a power of 2 number and correct the level calculation. Besides it adds a test case with 6 nodes. Signed-off-by: Wei Yang --- merkle_tree.go | 6 ++++-- merkle_tree_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/merkle_tree.go b/merkle_tree.go index 7a4156bd..0b7deb6c 100644 --- a/merkle_tree.go +++ b/merkle_tree.go @@ -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..d104ceee 100644 --- a/merkle_tree_test.go +++ b/merkle_tree_test.go @@ -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") +} From cee6700c219ef6a39d7e6ce4ee153badfa41830d Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Mon, 16 Oct 2017 21:46:01 +0800 Subject: [PATCH 03/24] base58: fix Base58Decode on calculating the zeroBytes The leading "zeroBytes" in address is b58Alphabet[0] instead of 0x00. Signed-off-by: Wei Yang --- base58.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/base58.go b/base58.go index 5db078e4..60b01e8e 100644 --- a/base58.go +++ b/base58.go @@ -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 } } From f9935daa8376572e8fbc686f5c37a40317f479e2 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Mon, 16 Oct 2017 21:50:56 +0800 Subject: [PATCH 04/24] base58_test: add test case for Base58 --- base58_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 base58_test.go diff --git a/base58_test.go b/base58_test.go new file mode 100644 index 00000000..5221340e --- /dev/null +++ b/base58_test.go @@ -0,0 +1,27 @@ +package main + +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, + ) + } +} From 782db48492cbf8ed147ca80f5d7c8822d3568fca Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Mon, 16 Oct 2017 17:07:22 +0800 Subject: [PATCH 05/24] cli: add three cli command for exploring --- cli.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ cli_explore.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 cli_explore.go diff --git a/cli.go b/cli.go index b597b4c7..2e798770 100644 --- a/cli.go +++ b/cli.go @@ -21,6 +21,11 @@ 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(" generateKey - generate KeyPair for exploring") + fmt.Println(" getAddress -pubKey PUBKEY - convert pubKey to address") + fmt.Println(" validateAddress -addr Address - validate an address") } func (cli *CLI) validateArgs() { @@ -48,6 +53,9 @@ func (cli *CLI) Run() { reindexUTXOCmd := flag.NewFlagSet("reindexutxo", flag.ExitOnError) sendCmd := flag.NewFlagSet("send", flag.ExitOnError) startNodeCmd := flag.NewFlagSet("startnode", flag.ExitOnError) + generateKeyCmd := flag.NewFlagSet("generateKey", flag.ExitOnError) + getAddressCmd := flag.NewFlagSet("getAddress", flag.ExitOnError) + validateAddrCmd := flag.NewFlagSet("validateAddress", 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 +64,8 @@ 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") + pubKey := getAddressCmd.String("pubKey", "", "the key where address generated") + address := validateAddrCmd.String("addr", "", "the public address") switch os.Args[1] { case "getbalance": @@ -98,6 +108,21 @@ 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 "generateKey": + err := generateKeyCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } + case "getAddress": + err := getAddressCmd.Parse(os.Args[2:]) + if err != nil { + log.Panic(err) + } default: cli.printUsage() os.Exit(1) @@ -152,4 +177,27 @@ func (cli *CLI) Run() { } cli.startNode(nodeID, *startNodeMiner) } + + if generateKeyCmd.Parsed() { + cli.generateKey() + } + + if getAddressCmd.Parsed() { + if *pubKey == "" { + getAddressCmd.Usage() + os.Exit(1) + } + + cli.getAddress(*pubKey) + } + + if validateAddrCmd.Parsed() { + if *address == "" { + validateAddrCmd.Usage() + os.Exit(1) + } + + cli.validateAddr(*address) + } + } diff --git a/cli_explore.go b/cli_explore.go new file mode 100644 index 00000000..b82e607d --- /dev/null +++ b/cli_explore.go @@ -0,0 +1,35 @@ +package main + +import ( + "encoding/hex" + "fmt" +) + +func (cli *CLI) generateKey() { + _, public := newKeyPair() + fmt.Println("Public Key:") + fmt.Println(hex.EncodeToString(public)) +} + +func (cli *CLI) getAddress(pubKey string) { + public, _ := hex.DecodeString(pubKey) + + pubKeyHash := HashPubKey(public) + + versionedPayload := append([]byte{version}, pubKeyHash...) + checksum := checksum(versionedPayload) + + fullPayload := append(versionedPayload, checksum...) + address := Base58Encode(fullPayload) + fmt.Println() + fmt.Printf("PubKey : %s\nAddress: %s\n", pubKey, address) +} + +func (cli *CLI) validateAddr(address string) { + fmt.Printf("Address: %s\n", address) + if !ValidateAddress(address) { + fmt.Println("Not valid!") + } else { + fmt.Println("Valid!") + } +} From 42b9dbb26e4008945752472e6a339b9a18a74e9b Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 18 Oct 2017 11:47:23 +0800 Subject: [PATCH 06/24] version: display the addree when a new node is connected --- server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server.go b/server.go index b13f520b..6e6789d7 100644 --- a/server.go +++ b/server.go @@ -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) } } From 8ea4bc859d45ecc07d7d40fbce6cad28fc721a29 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 18 Oct 2017 11:51:41 +0800 Subject: [PATCH 07/24] Use View instead of Update in NewBlockchain() --- blockchain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockchain.go b/blockchain.go index 8436035b..535a1309 100644 --- a/blockchain.go +++ b/blockchain.go @@ -82,7 +82,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")) From 29099f0c02077c530b62356a2494c05d3a79e3ca Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 18 Oct 2017 11:54:35 +0800 Subject: [PATCH 08/24] check wallet before sending We could only send money from our own wallet. If the parameter is not an address in our wallet, exit the program gracefully and show an error message. --- cli_send.go | 6 +++++- wallets.go | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cli_send.go b/cli_send.go index 75a59301..67b0091e 100644 --- a/cli_send.go +++ b/cli_send.go @@ -22,8 +22,12 @@ func (cli *CLI) send(from, to string, amount int, nodeID string, mineNow bool) { 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/wallets.go b/wallets.go index 9f376f53..a7d354f4 100644 --- a/wallets.go +++ b/wallets.go @@ -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 From 009fda2b7c8ccb0254a51a9c6c7cbd19de362539 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 18 Oct 2017 11:56:30 +0800 Subject: [PATCH 09/24] display the number of tx mined --- server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.go b/server.go index 6e6789d7..0058cd0c 100644 --- a/server.go +++ b/server.go @@ -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) From 2974ee8571b21ffa63e795685cb6249ffec678c5 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 18 Oct 2017 15:28:36 +0800 Subject: [PATCH 10/24] cli: print PubKeyHash in getAddress --- cli_explore.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cli_explore.go b/cli_explore.go index b82e607d..8d07cfa9 100644 --- a/cli_explore.go +++ b/cli_explore.go @@ -22,7 +22,9 @@ func (cli *CLI) getAddress(pubKey string) { fullPayload := append(versionedPayload, checksum...) address := Base58Encode(fullPayload) fmt.Println() - fmt.Printf("PubKey : %s\nAddress: %s\n", pubKey, address) + fmt.Printf("PubKey : %s\n", pubKey) + fmt.Printf("PubKeyHash : %x\n", pubKeyHash) + fmt.Printf("Address : %s\n", address) } func (cli *CLI) validateAddr(address string) { From 5f4f2f518da2f7d0013d6fc59784206dc00bc4bf Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 18 Oct 2017 15:29:19 +0800 Subject: [PATCH 11/24] cli: add command getPubKeyHash --- cli.go | 17 +++++++++++++++++ cli_explore.go | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/cli.go b/cli.go index 2e798770..c23a9d34 100644 --- a/cli.go +++ b/cli.go @@ -25,6 +25,7 @@ func (cli *CLI) printUsage() { fmt.Println("Exploring cmds:") fmt.Println(" generateKey - generate KeyPair for exploring") 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") } @@ -55,6 +56,7 @@ func (cli *CLI) Run() { startNodeCmd := flag.NewFlagSet("startnode", flag.ExitOnError) generateKeyCmd := flag.NewFlagSet("generateKey", flag.ExitOnError) getAddressCmd := flag.NewFlagSet("getAddress", flag.ExitOnError) + getPubKeyHashCmd := flag.NewFlagSet("getPubKeyHash", flag.ExitOnError) validateAddrCmd := flag.NewFlagSet("validateAddress", flag.ExitOnError) getBalanceAddress := getBalanceCmd.String("address", "", "The address to get balance for") @@ -65,6 +67,7 @@ func (cli *CLI) Run() { sendMine := sendCmd.Bool("mine", false, "Mine immediately on the same node") startNodeMiner := startNodeCmd.String("miner", "", "Enable mining mode and send reward to ADDRESS") pubKey := getAddressCmd.String("pubKey", "", "the key where address generated") + pubKeyAddress := getPubKeyHashCmd.String("address", "", "the pub address") address := validateAddrCmd.String("addr", "", "the public address") switch os.Args[1] { @@ -118,6 +121,11 @@ func (cli *CLI) Run() { 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 { @@ -191,6 +199,15 @@ func (cli *CLI) Run() { cli.getAddress(*pubKey) } + if getPubKeyHashCmd.Parsed() { + if *pubKeyAddress == "" { + getPubKeyHashCmd.Usage() + os.Exit(1) + } + + cli.getPubKeyHash(*pubKeyAddress) + } + if validateAddrCmd.Parsed() { if *address == "" { validateAddrCmd.Usage() diff --git a/cli_explore.go b/cli_explore.go index 8d07cfa9..b5ddb4ca 100644 --- a/cli_explore.go +++ b/cli_explore.go @@ -27,6 +27,11 @@ func (cli *CLI) getAddress(pubKey string) { fmt.Printf("Address : %s\n", address) } +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) { From 36cfffcb64a3b4acf37e1aef02c5c98baca9130e Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 19 Oct 2017 15:42:05 +0800 Subject: [PATCH 12/24] cli: add getBlock command --- cli.go | 17 +++++++++++++++++ cli_printchain.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/cli.go b/cli.go index c23a9d34..e01794da 100644 --- a/cli.go +++ b/cli.go @@ -27,6 +27,7 @@ func (cli *CLI) printUsage() { 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() { @@ -58,6 +59,7 @@ func (cli *CLI) Run() { 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") @@ -69,6 +71,7 @@ func (cli *CLI) Run() { 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": @@ -131,6 +134,11 @@ func (cli *CLI) Run() { 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) @@ -217,4 +225,13 @@ func (cli *CLI) Run() { cli.validateAddr(*address) } + if getBlockCmd.Parsed() { + if *blockHash == "" { + getBlockCmd.Usage() + os.Exit(1) + } + + cli.printBlock(*blockHash, nodeID) + } + } diff --git a/cli_printchain.go b/cli_printchain.go index 6c344dec..49c4deb1 100644 --- a/cli_printchain.go +++ b/cli_printchain.go @@ -31,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 + } + } +} From 01d18ce4d5de331b1c51c3a545f5b1c0173428d4 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 19 Oct 2017 21:48:55 +0800 Subject: [PATCH 13/24] add Address in TXOut --- transaction.go | 3 ++- transaction_output.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/transaction.go b/transaction.go index f2031d3d..c06c23e7 100644 --- a/transaction.go +++ b/transaction.go @@ -105,6 +105,7 @@ func (tx Transaction) String() string { 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 +121,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} diff --git a/transaction_output.go b/transaction_output.go index 2ae68dec..deb49cdb 100644 --- a/transaction_output.go +++ b/transaction_output.go @@ -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 From 5c88b24c2c3fb6021abf366295e2372540ba4e07 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 19 Oct 2017 22:28:49 +0800 Subject: [PATCH 14/24] display the addresse in TXIn and optmize getAddress command --- cli_explore.go | 6 ++---- transaction.go | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cli_explore.go b/cli_explore.go index b5ddb4ca..d2213212 100644 --- a/cli_explore.go +++ b/cli_explore.go @@ -17,14 +17,12 @@ func (cli *CLI) getAddress(pubKey string) { pubKeyHash := HashPubKey(public) versionedPayload := append([]byte{version}, pubKeyHash...) - checksum := checksum(versionedPayload) + fullPayload := append(versionedPayload, checksum(versionedPayload)...) - fullPayload := append(versionedPayload, checksum...) - address := Base58Encode(fullPayload) fmt.Println() fmt.Printf("PubKey : %s\n", pubKey) fmt.Printf("PubKeyHash : %x\n", pubKeyHash) - fmt.Printf("Address : %s\n", address) + fmt.Printf("Address : %s\n", Base58Encode(fullPayload)) } func (cli *CLI) getPubKeyHash(address string) { diff --git a/transaction.go b/transaction.go index c06c23e7..f3fc0973 100644 --- a/transaction.go +++ b/transaction.go @@ -94,11 +94,16 @@ 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 { From c410eb2f1dc27425cc24d9aac242c11e7f934a79 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Fri, 20 Oct 2017 08:44:58 +0800 Subject: [PATCH 15/24] display the private key --- cli_explore.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cli_explore.go b/cli_explore.go index d2213212..9b6c4dda 100644 --- a/cli_explore.go +++ b/cli_explore.go @@ -6,9 +6,13 @@ import ( ) func (cli *CLI) generateKey() { - _, public := newKeyPair() + private, public := newKeyPair() fmt.Println("Public Key:") fmt.Println(hex.EncodeToString(public)) + priKey := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...) + priKey = append(priKey, private.D.Bytes()...) + fmt.Println("Private Key:") + fmt.Printf("%d%s\n", private.Curve, hex.EncodeToString(priKey)) } func (cli *CLI) getAddress(pubKey string) { From 9f6ac35835687eaac135d41e1d3cc5abac467668 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Sat, 21 Oct 2017 22:01:19 +0800 Subject: [PATCH 16/24] update gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 1b07dece..e2c8a2c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *.db *.dat +cscope.* +.*.swp +blockchain_go From 64cf474f277774474fc70ff00d117ebcba59ee24 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Sat, 21 Oct 2017 22:32:49 +0800 Subject: [PATCH 17/24] fix private key display --- cli_explore.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cli_explore.go b/cli_explore.go index 9b6c4dda..c6e39a51 100644 --- a/cli_explore.go +++ b/cli_explore.go @@ -7,12 +7,10 @@ import ( func (cli *CLI) generateKey() { private, public := newKeyPair() + fmt.Println("Private Key:") + fmt.Println(hex.EncodeToString(private.D.Bytes())) fmt.Println("Public Key:") fmt.Println(hex.EncodeToString(public)) - priKey := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...) - priKey = append(priKey, private.D.Bytes()...) - fmt.Println("Private Key:") - fmt.Printf("%d%s\n", private.Curve, hex.EncodeToString(priKey)) } func (cli *CLI) getAddress(pubKey string) { From 04318d7ca48de29f539e34da517a7d43dacd8bc7 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Sun, 22 Oct 2017 00:47:37 +0800 Subject: [PATCH 18/24] cli: generatePrivKey --- cli.go | 28 ++++++++++++++++++++++------ cli_explore.go | 16 +++++++++++----- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/cli.go b/cli.go index e01794da..e0f17a70 100644 --- a/cli.go +++ b/cli.go @@ -23,7 +23,8 @@ func (cli *CLI) printUsage() { 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(" generateKey - generate KeyPair for exploring") + 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") @@ -55,7 +56,8 @@ func (cli *CLI) Run() { reindexUTXOCmd := flag.NewFlagSet("reindexutxo", flag.ExitOnError) sendCmd := flag.NewFlagSet("send", flag.ExitOnError) startNodeCmd := flag.NewFlagSet("startnode", flag.ExitOnError) - generateKeyCmd := flag.NewFlagSet("generateKey", 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) @@ -68,6 +70,7 @@ 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") @@ -119,8 +122,13 @@ func (cli *CLI) Run() { if err != nil { log.Panic(err) } - case "generateKey": - err := generateKeyCmd.Parse(os.Args[2:]) + 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) } @@ -194,8 +202,16 @@ func (cli *CLI) Run() { cli.startNode(nodeID, *startNodeMiner) } - if generateKeyCmd.Parsed() { - cli.generateKey() + if generatePrivKeyCmd.Parsed() { + cli.generatePrivKey() + } + + if getPubKeyCmd.Parsed() { + if *privateKey == "" { + getPubKeyCmd.Usage() + os.Exit(1) + } + cli.getPubKey(*privateKey) } if getAddressCmd.Parsed() { diff --git a/cli_explore.go b/cli_explore.go index c6e39a51..28494af5 100644 --- a/cli_explore.go +++ b/cli_explore.go @@ -1,16 +1,22 @@ package main import ( + "crypto/elliptic" "encoding/hex" "fmt" ) -func (cli *CLI) generateKey() { - private, public := newKeyPair() - fmt.Println("Private Key:") +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())) - fmt.Println("Public Key:") - fmt.Println(hex.EncodeToString(public)) } func (cli *CLI) getAddress(pubKey string) { From b415f08c4659ddc126b9a764b5687c92e400d992 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Sun, 22 Oct 2017 10:13:25 +0800 Subject: [PATCH 19/24] make it a package bc --- base58.go | 2 +- base58_test.go | 2 +- block.go | 2 +- blockchain.go | 2 +- blockchain_iterator.go | 2 +- cli.go | 2 +- cli_createblockchain.go | 2 +- cli_createwallet.go | 2 +- cli_explore.go | 2 +- cli_getbalance.go | 2 +- cli_listaddress.go | 2 +- cli_printchain.go | 2 +- cli_reindexutxo.go | 2 +- cli_send.go | 2 +- cli_startnode.go | 2 +- main.go | 6 ------ merkle_tree.go | 2 +- merkle_tree_test.go | 2 +- proofofwork.go | 2 +- server.go | 2 +- transaction.go | 2 +- transaction_input.go | 2 +- transaction_output.go | 2 +- utils.go | 2 +- utxo_set.go | 2 +- wallet.go | 2 +- wallets.go | 2 +- 27 files changed, 26 insertions(+), 32 deletions(-) delete mode 100644 main.go diff --git a/base58.go b/base58.go index 60b01e8e..6b7089f3 100644 --- a/base58.go +++ b/base58.go @@ -1,4 +1,4 @@ -package main +package bc import ( "bytes" diff --git a/base58_test.go b/base58_test.go index 5221340e..8d80cd33 100644 --- a/base58_test.go +++ b/base58_test.go @@ -1,4 +1,4 @@ -package main +package bc import ( "testing" diff --git a/block.go b/block.go index e403e549..2c70cb58 100644 --- a/block.go +++ b/block.go @@ -1,4 +1,4 @@ -package main +package bc import ( "bytes" diff --git a/blockchain.go b/blockchain.go index 535a1309..28b01c02 100644 --- a/blockchain.go +++ b/blockchain.go @@ -1,4 +1,4 @@ -package main +package bc import ( "bytes" 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 e0f17a70..90d7165d 100644 --- a/cli.go +++ b/cli.go @@ -1,4 +1,4 @@ -package main +package bc import ( "flag" diff --git a/cli_createblockchain.go b/cli_createblockchain.go index 7e69405c..5b86845d 100644 --- a/cli_createblockchain.go +++ b/cli_createblockchain.go @@ -1,4 +1,4 @@ -package main +package bc import ( "fmt" 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 index 28494af5..d68a0e91 100644 --- a/cli_explore.go +++ b/cli_explore.go @@ -1,4 +1,4 @@ -package main +package bc import ( "crypto/elliptic" diff --git a/cli_getbalance.go b/cli_getbalance.go index a86e3dc1..6c8b7fa9 100644 --- a/cli_getbalance.go +++ b/cli_getbalance.go @@ -1,4 +1,4 @@ -package main +package bc import ( "fmt" 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 49c4deb1..bcb66ba2 100644 --- a/cli_printchain.go +++ b/cli_printchain.go @@ -1,4 +1,4 @@ -package main +package bc import ( "fmt" 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 67b0091e..1f22cbb9 100644 --- a/cli_send.go +++ b/cli_send.go @@ -1,4 +1,4 @@ -package main +package bc import ( "fmt" 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 0b7deb6c..985dde45 100644 --- a/merkle_tree.go +++ b/merkle_tree.go @@ -1,4 +1,4 @@ -package main +package bc import ( "crypto/sha256" diff --git a/merkle_tree_test.go b/merkle_tree_test.go index d104ceee..5275a5e1 100644 --- a/merkle_tree_test.go +++ b/merkle_tree_test.go @@ -1,4 +1,4 @@ -package main +package bc import ( "encoding/hex" 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 0058cd0c..0895ad98 100644 --- a/server.go +++ b/server.go @@ -1,4 +1,4 @@ -package main +package bc import ( "bytes" diff --git a/transaction.go b/transaction.go index f3fc0973..84a31a1b 100644 --- a/transaction.go +++ b/transaction.go @@ -1,4 +1,4 @@ -package main +package bc import ( "bytes" 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 deb49cdb..d3163e81 100644 --- a/transaction_output.go +++ b/transaction_output.go @@ -1,4 +1,4 @@ -package main +package bc import ( "bytes" 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..902b530c 100644 --- a/utxo_set.go +++ b/utxo_set.go @@ -1,4 +1,4 @@ -package main +package bc import ( "encoding/hex" 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 a7d354f4..763d3547 100644 --- a/wallets.go +++ b/wallets.go @@ -1,4 +1,4 @@ -package main +package bc import ( "bytes" From c72ff7fcee039df8c17fad2853c7826721b8baec Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Sun, 22 Oct 2017 10:23:40 +0800 Subject: [PATCH 20/24] Change README for bc package --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 76ae180b..3bec1dbf 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,26 @@ 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` +```go +package main + +import "github.com/richardweiyang/blockchain_go" + +func main() { + cli := bc.CLI{} + cli.Run() +} +``` +#### Build and run + + go build main.go + ./main + From cb4ae18f437ccc3cf53284313404f08c9bee28ae Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Sun, 22 Oct 2017 10:39:31 +0800 Subject: [PATCH 21/24] export DB in blockchain --- blockchain.go | 14 +++++++------- cli_createblockchain.go | 2 +- cli_getbalance.go | 2 +- cli_printchain.go | 4 ++-- cli_send.go | 2 +- utxo_set.go | 10 +++++----- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/blockchain.go b/blockchain.go index 28b01c02..71357327 100644 --- a/blockchain.go +++ b/blockchain.go @@ -19,7 +19,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 @@ -99,7 +99,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 +199,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 +208,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 +227,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 +277,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 +294,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 { diff --git a/cli_createblockchain.go b/cli_createblockchain.go index 5b86845d..bc77fe96 100644 --- a/cli_createblockchain.go +++ b/cli_createblockchain.go @@ -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_getbalance.go b/cli_getbalance.go index 6c8b7fa9..9aa7c0f1 100644 --- a/cli_getbalance.go +++ b/cli_getbalance.go @@ -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_printchain.go b/cli_printchain.go index bcb66ba2..7aaa63fb 100644 --- a/cli_printchain.go +++ b/cli_printchain.go @@ -8,7 +8,7 @@ import ( func (cli *CLI) printChain(nodeID string) { bc := NewBlockchain(nodeID) - defer bc.db.Close() + defer bc.DB.Close() bci := bc.Iterator() @@ -34,7 +34,7 @@ func (cli *CLI) printChain(nodeID string) { func (cli *CLI) printBlock(blockHash, nodeID string) { bc := NewBlockchain(nodeID) - defer bc.db.Close() + defer bc.DB.Close() bci := bc.Iterator() diff --git a/cli_send.go b/cli_send.go index 1f22cbb9..f2f58767 100644 --- a/cli_send.go +++ b/cli_send.go @@ -15,7 +15,7 @@ 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 { diff --git a/utxo_set.go b/utxo_set.go index 902b530c..7adbad9f 100644 --- a/utxo_set.go +++ b/utxo_set.go @@ -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)) From 9b2f73b11bad63234a304e7269f2822eb532833a Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Sun, 22 Oct 2017 10:49:02 +0800 Subject: [PATCH 22/24] add PrintHTML method to chain --- block.go | 20 ++++++++++++++++++++ blockchain.go | 18 ++++++++++++++++++ transaction.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/block.go b/block.go index 2c70cb58..3e90caec 100644 --- a/block.go +++ b/block.go @@ -3,7 +3,10 @@ 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 71357327..60b1235f 100644 --- a/blockchain.go +++ b/blockchain.go @@ -8,6 +8,7 @@ import ( "fmt" "log" "os" + "strings" "github.com/boltdb/bolt" ) @@ -358,3 +359,20 @@ 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") + +} diff --git a/transaction.go b/transaction.go index 84a31a1b..bf1b1bc4 100644 --- a/transaction.go +++ b/transaction.go @@ -249,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, "") +} From 56c9e3faa65b442a6207a836e4e7ab9fc44d59ec Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Sun, 22 Oct 2017 10:53:22 +0800 Subject: [PATCH 23/24] add GetBalance to chain --- blockchain.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/blockchain.go b/blockchain.go index 60b1235f..da5e78a2 100644 --- a/blockchain.go +++ b/blockchain.go @@ -376,3 +376,16 @@ func (bc *Blockchain) PrintHTML() string { 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 +} From db3fe41ad844a2e7f41f637806930355bc18e4d5 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Tue, 31 Oct 2017 12:04:10 +0800 Subject: [PATCH 24/24] more detail explanation --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3bec1dbf..d79518c0 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ A blockchain implementation in Go, as described in these articles: go get github.com/richardweiyang/blockchain_go ## Create file `main.go` + +source code + ```go package main