Merge db3fe41ad8
into c5c21fd069
This commit is contained in:
commit
a104a54db5
|
@ -1,2 +1,5 @@
|
|||
*.db
|
||||
*.dat
|
||||
cscope.*
|
||||
.*.swp
|
||||
blockchain_go
|
||||
|
|
26
README.md
26
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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
22
block.go
22
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("<h2>Block <a href=\"/block/%x\">%x</a> </h2>", b.Hash, b.Hash))
|
||||
lines = append(lines, fmt.Sprintf("Height: %d</br>", b.Height))
|
||||
lines = append(lines, fmt.Sprintf("Prev. block: <a href=\"/block/%x\">%x</a></br>", b.PrevBlockHash, b.PrevBlockHash))
|
||||
lines = append(lines, fmt.Sprintf("Created at : %s</br>", time.Unix(b.Timestamp, 0)))
|
||||
pow := NewProofOfWork(b)
|
||||
lines = append(lines, fmt.Sprintf("PoW: %s</br></br>", strconv.FormatBool(pow.Validate())))
|
||||
if detail {
|
||||
for _, tx := range b.Transactions {
|
||||
lines = append(lines, tx.PrintHTML())
|
||||
}
|
||||
}
|
||||
lines = append(lines, fmt.Sprintf("</br></br>"))
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package bc
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
|
100
cli.go
100
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package bc
|
||||
|
||||
import "fmt"
|
||||
|
||||
|
|
|
@ -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!")
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package bc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package bc
|
||||
|
||||
import "fmt"
|
||||
|
||||
|
|
10
cli_send.go
10
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, "")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package bc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package bc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("<h4> Transaction %x:</h4>", 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("<div>Input %d:</div>", i))
|
||||
lines = append(lines, fmt.Sprintf("<div style=\"text-indent:2em;\">TXID: %x</div>", input.Txid))
|
||||
lines = append(lines, fmt.Sprintf("<div style=\"text-indent:2em;\">Out: %d</div>", input.Vout))
|
||||
lines = append(lines, fmt.Sprintf("<div style=\"text-indent:2em;\">Signature: %x</div>", input.Signature))
|
||||
lines = append(lines, fmt.Sprintf("<div style=\"text-indent:2em;\">PubKey: %x</div>", input.PubKey))
|
||||
lines = append(lines, fmt.Sprintf("<div style=\"text-indent:2em;\">Addr : %s</div>", Base58Encode(fullPayload)))
|
||||
}
|
||||
|
||||
for i, output := range tx.Vout {
|
||||
lines = append(lines, fmt.Sprintf("<div>Output %d:</div>", i))
|
||||
lines = append(lines, fmt.Sprintf("<div style=\"text-indent:2em;\">Value: %d</div>", output.Value))
|
||||
lines = append(lines, fmt.Sprintf("<div style=\"text-indent:2em;\">Script: %x</div>", output.PubKeyHash))
|
||||
lines = append(lines, fmt.Sprintf("<div style=\"text-indent:2em;\">Addr : %s</div>", output.Address))
|
||||
}
|
||||
|
||||
return strings.Join(lines, "")
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package bc
|
||||
|
||||
import "bytes"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
12
utxo_set.go
12
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))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue