0
点赞
收藏
分享

微信扫一扫

区块链项目 - 13 简易的网络服务

13. 简易的网络服务

13.1 NODE_ID设置

按照惯例先把之前的文件复制并重新命名

/Users/qinjianquan/publicChain/Part70-Merkle-NODE_ID

获取节点ID

/Users/qinjianquan/publicChain/Part70-Merkle-NODE_ID/src/CLI.go

func (cli CLI) Run() {

	isValidArgs()
	
	//get node id
	nodeID := os.Getenv("NODE_ID")
	if nodeID == "" {
		fmt.Println("NODE_ID env. var is not set")
		os.Exit(1)
	}
	fmt.Printf("NODE_ID:%s\n",nodeID)
	--
}

在终端编译并运行

export NODE_ID=3000
qinjianquan@MacBook-Pro-10 Part70-Merkle-NODE_ID % go build main.go

13.2 NODE_ID项目配置

按照惯例先把之前的文件复制并重新命名

/Users/qinjianquan/publicChain/Part71-Net-NODE_ID-Configuration

然后我们需要重新配置数据库和钱包文件

格式化数据库

/Users/qinjianquan/publicChain/Part71-Net-NODE_ID-Configuration/src/Blockchain_Iterator.go

const dbName = "blockChain_%s.db"

然后更改部分函数,另外也需要为涉及的函数增加一个参数nodeID string

func CreatBlockchainWithGenesisBlock(address string, nodeID string) *Blockchain {

	//format db name
	dbName := fmt.Sprintf(dbName, nodeID)

	if DBExists(dbName) {
		fmt.Println("Genesis block existed")
		os.Exit(1)
	}
	--
}
func BlockChainObject(nodeID string) *Blockchain {

	//format db name
	dbName := fmt.Sprintf(dbName, nodeID)

	if DBExists(dbName) {
		fmt.Println("database didn't exist")
		os.Exit(1)
	}
	--
}

格式化钱包

/Users/qinjianquan/publicChain/Part71-Net-NODE_ID-Configuration/src/Wallets.go

const walletFile = "wallets_%s.dat"
func NewWallets(nodeID string) (*Wallets, error) {

	walletFile := fmt.Sprintf(walletFile, nodeID)

	//to find the file,if there is no file,create a one
	if _, err := os.Stat(walletFile); os.IsNotExist(err) {
		//create a struct
		wallets := &Wallets{}
		//access its property and initialize it
		wallets.WalletMap = make(map[string]*Wallet)
		return wallets, err
	}
	--
}

当然这也需要更新与之相关的其他函数,根据提示补充即可

这样做的目的在于可以将节点的印记和数据库、钱包关联起来,以作区分

编译并设置NODE_ID

go build main.go
export NODE_ID=3000
//创建钱包
./main createWallet  
NODE_ID:3000
Wallet address: 15wtkW1HNytLtdbmm448Le6WaftU8hq5Lv
//创建创世区块
./main createBlockChain -address 15wtkW1HNytLtdbmm448Le6WaftU8hq5Lv 
NODE_ID:3000
is creating genesis block...
Block hash:00006001e75e2905628142518f09a0fd03ef078b1040518056d2e44e03b80ca1

查看数据库和钱包文件,其名称如预期的一样已被更改

ls
blockChain_3000.db	main			main.go			src			wallets_3000.dat

拷贝重命名数据库

cp blockChain_3000.db blockChain_3001.db
qinjianquan@MacBook-Pro-10 Part71-Net-NODE_ID-Configuration % ls
blockChain_3000.db	blockChain_3001.db	main			main.go			src			wallets_3000.dat

用新的节点创建钱包

NODE_ID=3001
qinjianquan@MacBook-Pro-10 Part71-Net-NODE_ID-Configuration % ./bc createWallet
NODE_ID:3001
Wallet address: 1G6xGK8Zn1AZVAW6LcdDr2fwf7RqWXWhEs
1
./bc getAddressList
NODE_ID:3001
Address list:
1G6xGK8Zn1AZVAW6LcdDr2fwf7RqWXWhEs
13iAHiLFxYiqNSJHP2baiFyDumd9chEfAU

注意:此时还是在一条链上

13.3 区块链的验证逻辑

按照惯例先把之前的文件复制并重新命名

/Users/qinjianquan/publicChain/Part72-Net-Mine_Cli

现在我们在转账时增加验证环节

/Users/qinjianquan/publicChain/Part72-Net-Mine_Cli/src/CLI.go

func (cli CLI) Run() {
--
flagSendBlockVerify := transferBlockCmd.Bool("mine", false, "Whether to verify now")
--
if transferBlockCmd.Parsed() {
		if *flagFrom == "" || *flagTo == "" || *flagAmount == "" {
			printUsage()
			os.Exit(1)
		}

		from := JSONToArray(*flagFrom)
		to := JSONToArray(*flagTo)

		//verify the validity of address before transaction occurs
		for index, fromAddress := range from {
			if IsValidForAddress([]byte(fromAddress)) == false || IsValidForAddress([]byte(to[index])) == false {
				fmt.Println("Address is invalid")
				printUsage()
				os.Exit(1)
			}
		}

		amount := JSONToArray(*flagAmount)
		cli.send(from, to, amount, nodeID, *flagSendBlockVerify)
	}
	--
}

/Users/qinjianquan/publicChain/Part72-Net-Mine_Cli/src/CLI_transfer.go

func (cli CLI) send(from []string, to []string, amount []string, nodeID string, mineNow bool) {

	blockchain := BlockChainObject(nodeID)
	defer blockchain.DB.Close()
	if mineNow {
		//mine a new clock
		blockchain.MineNewBlock(from, to, amount, nodeID)

		//when finished the transaction, update the data
		utxoSet := &UTXOSet{blockchain}
		utxoSet.Update()
	} else {
		//send transaction to miner verify
		fmt.Println("handled by miner node...")
	}
}

重新编译并运行,在不设置是否要验证参数时,默认不验证

./bc transfer -from '["1JSZG4ggQYYxR3LidBz3wEe8b1HmmfkARm"]' -to '["1JRTE3YTLV7UTpYDK53H5ahEyeUXb7DMx4"]' -amount '["6"]' 
NODE_ID:3000
handled by miner node...

若验证时,则会继续mine

./bc transfer -from '["1JSZG4ggQYYxR3LidBz3wEe8b1HmmfkARm"]' -to '["1JRTE3YTLV7UTpYDK53H5ahEyeUXb7DMx4"]' -amount '["6"]' -mine 
NODE_ID:3000
Block hash:000057e1b9e955d844e73919ced9f4b2fa30d9088ba448acd9475902ef46f9c3

13.4 客户端服务器使用

本节我们创建一个客户端和服务器

新建文件

/Users/qinjianquan/publicChain/Part73-Net-TCP

新建两个文件

server

/Users/qinjianquan/publicChain/Part73-Net-TCP/server.go

func main() {

   //server
   fmt.Println("Server has started...")

   ln, err := net.Listen("tcp", ":8080")
   if err != nil {
      log.Panic(err)
   }
   defer ln.Close()

   for {

      //receive data from client
      conn, err1 := ln.Accept()
      if err != nil {
         log.Panic(err1)
      }
      //read data from client
      request, err2 := ioutil.ReadAll(conn)
      if err2 != nil {
         log.Panic(err2)
      }
      fmt.Printf("received a message:%v\n", request)
   }
}

client

/Users/qinjianquan/publicChain/Part73-Net-TCP/client.go

func main() {

	sendMessage()
}
func sendMessage() {

	fmt.Println("a client sends message to the server...")
	conn, err := net.Dial("tcp", "127.0.0.1:8080")
	if err != nil {
		log.Panic(err)
	}
	defer conn.Close()

	//attach message
	_, err1 := io.Copy(conn, bytes.NewReader([]byte("version")))
	if err1 != nil {
		log.Panic(err1)
	}
}

编译生成可以执行的二进制文件

client		client.go	server		server.go

13.5 startnode-cli

复制72…重命名

/Users/qinjianquan/publicChain/Part74-Net-TCP

增加命令

func printUsage() {
	--
	fmt.Println("\tstartNode -miner ADDRESS -- start the node server,and specify the mining reward address")
}
func (cli CLI) Run() { 
--
startNodeCmd := flag.NewFlagSet("startNode", flag.ExitOnError)
--
flagMiner := startNodeCmd.String("miner", "", "reward address")
...
}

启动节点

func (cli *CLI) StartNode(nodeID string, mineAddress string) {

	if IsValidForAddress([]byte(mineAddress)) || mineAddress == "" {

		//start server
		fmt.Printf("start the server, localhost:%s", nodeID)
		//StartServer(nodeID,mineAddress)

	} else {
		fmt.Println("reward address is invalid")
	}
}

记得编译测试

13.6 项目集成客户端和服务器代码

惯例复制重命名

/Users/qinjianquan/publicChain/Part75-Net-Server

/main node address

var konwNodes = []string{"localhost:3000"}
var nodeAddress string

func StartServer(nodeID string, mineAddress string) {

	//current node address
	nodeAddress = fmt.Sprintf("localhost:%s", nodeID)

	ln, err := net.Listen("tcp", nodeAddress)
	if err != nil {
		log.Panic(err)
	}
	defer ln.Close()
	if nodeAddress != konwNodes[0] {
		//konwNodes[0] is main node
		SendMessage(konwNodes[0], nodeAddress)
	}

	for {

		//receive data from client
		conn, err1 := ln.Accept()
		if err != nil {
			log.Panic(err1)
		}
		//read data from client
		request, err2 := ioutil.ReadAll(conn)
		if err2 != nil {
			log.Panic(err2)
		}
		fmt.Printf("received a message:%s\n", request)
	}
}

func SendMessage(to string, from string) {

	fmt.Println("a client sends message to the server...")
	conn, err := net.Dial("tcp", to)
	if err != nil {
		log.Panic(err)
	}
	defer conn.Close()

	//attach message
	_, err1 := io.Copy(conn, bytes.NewReader([]byte(from)))
	if err1 != nil {
		log.Panic(err1)
	}

}

启动主节点和钱包节点

注意:请在两个终端中操作

启动主节点:

cd /Users/qinjianquan/publicChain/Part75-Net-Server
qinjianquan@MacBook-Pro-10 Part75-Net-Server % go build -o bc main.go
qinjianquan@MacBook-Pro-10 Part75-Net-Server % export NODE_ID=3000
qinjianquan@MacBook-Pro-10 Part75-Net-Server % ./bc startNode
NODE_ID:3000
start the server, localhost:3000

启动钱包节点:

 cd /Users/qinjianquan/publicChain/Part75-Net-Server
qinjianquan@MacBook-Pro-10 Part75-Net-Server % go build -o bc1 main.go
qinjianquan@MacBook-Pro-10 Part75-Net-Server % export NODE_ID=3001
qinjianquan@MacBook-Pro-10 Part75-Net-Server % ./bc1 startNode
NODE_ID:3001
start the server, localhost:3001
a client sends message to the server...

此时主节点会收到来自钱包节点的请求

received a message:localhost:3001

13.7 Version信息处理

/Users/qinjianquan/publicChain/Part76-Net-Version

更新Server文件,主要是将一些信息打包然后发给主节点

/Users/qinjianquan/publicChain/Part76-Net-Version/src/Server.go

type GetData struct {
	AddFrom string
	Type    string
	ID      []byte
}

type Inv struct {
	AddFrom string
	Type    string
	Items   [][]byte
}

type Tx struct {
	AddFrom     string
	Transaction []byte
}

type Verzion struct {
	Version    int
	BestHeight int    //the block height of current node
	AddrFrom   string //the address of current node
}

//main node address

var konwNodes = []string{"localhost:3000"}
var nodeAddress string

func StartServer(nodeID string, mineAddress string) {

	//current node address
	nodeAddress = fmt.Sprintf("localhost:%s", nodeID)

	ln, err := net.Listen(PROTOCOL, nodeAddress)
	if err != nil {
		log.Panic(err)
	}
	defer ln.Close()

	bc := BlockChainObject(nodeID)

	if nodeAddress != konwNodes[0] {
		//konwNodes[0] is main node
		SendVersion(konwNodes[0], bc)
	}

	for {

		//receive data from client
		conn, err1 := ln.Accept()
		if err != nil {
			log.Panic(err1)
		}
		//read data from client
		request, err2 := ioutil.ReadAll(conn)
		if err2 != nil {
			log.Panic(err2)
		}
		fmt.Printf("received a message:%s\n", request[:COMMANDLENGTH])
	}
}

func SendVersion(toAddress string, bc *Blockchain) {
	//bestHeight := bc.GetBestHeight()
	bestHeight := 1
	payload := GobEncode(Verzion{NODE_VERSION, bestHeight, nodeAddress})

	request := append(CommandTOBytes(VERSION), payload...)

	SendData(toAddress, request)
}

func SendData(to string, data []byte) {

	fmt.Println("a client sends message to the server...")
	conn, err := net.Dial("tcp", to)
	if err != nil {
		log.Panic(err)
	}
	defer conn.Close()

	//attach message
	_, err1 := io.Copy(conn, bytes.NewReader(data))
	if err1 != nil {
		log.Panic(err1)
	}
}

/Users/qinjianquan/publicChain/Part76-Net-Version/src/Constant.go

const PROTOCOL = "tcp"
const COMMANDLENGTH = 12
const NODE_VERSION = 1

const VERSION = "version"

/Users/qinjianquan/publicChain/Part76-Net-Version/src/Utils.go

func bytesToCommand(bytes []byte) string {
	var command []byte
	for _, b := range bytes {
		if b != 0x0 {
			command = append(command, b)
		}
	}
	return fmt.Sprintf("%s", command)
}

func GobEncode(data interface{}) []byte {

	var buff bytes.Buffer

	enc := gob.NewEncoder(&buff)
	err := enc.Encode(data)
	if err != nil {
		log.Panic(err)
	}
	return buff.Bytes()
}

在两个节点中分别运行3000节点和3001节点,3000节点能够查阅到version信息

/bc startNode
NODE_ID:3001
start the server, localhost:3001
a client sends message to the server...
./bc startNode
NODE_ID:3000
start the server, localhost:3000
received a message:version

13.8 获取节点区块链高度

func (blockchain *Blockchain)GetBestHeight() int64{
	
	block := blockchain.Iterator().Next()
	
	return block.Height 
}
举报

相关推荐

0 条评论