btcd交易流程之交易的打包上链(三)

交易打包上链的概述

btcd首先从内存池取得交易,用交易填充区块,并往区块上填入必要的信息。随后区块进行POW计算。当计算符合难度值的区块哈希后,btcd对新区块进行最后一步的验证,将新区块连接到本地的主链上,并广播这个新区块给对等方。

BitCoin RPCs

为了了解交易是怎么被打包到区块,最后发布的,首先从查看相关的RPC调用。

getblocktemplate
getblocktemplate ( "template_request" )

其描述如下:

If the request parameters include a ‘mode’ key, that is used to explicitly select between the default ‘template’ request or a ‘proposal’.
It returns data needed to construct a block to work on.

getblocktemplate RPC将返回构造一个区块所需要的数据。

generateblock
generateblock "output" ["rawtx/txid",...]

其描述如下:

Mine a block with a set of ordered transactions immediately to a specified address or descriptor (before the RPC call returns)

generateblock RPC用给定的排好序的交易挖矿,需要指定受益人的地址。

submitblock
submitblock "hexdata" ( "dummy" )

其描述如下:

Attempts to submit new block to network.

尝试提交一个新的区块到网络中。

setgenerate

Set the server to generate coins (mine) or not.
NOTE: Since btcd does not have the wallet integrated to provide payment addresses, btcd must be configured via the --miningaddr option to provide which payment addresses to pay created blocks to for this RPC to function.

setgenerate设置server是否开启mining。如果开启,服务器将动用CPU mining。需要用–miningaddr指定受益人地址。

mining总体流程分析

有了这三个RPC,我们可以大概确定从打包交易到发布区块的流程。
首先通过getblocktemplate RPC得到组装一个区块需要的信息,然后通过generateblock RPC产生一个区块,最后通过submitblock RPC将区块发布到网络上。
也可以通过setgenerate RPC设置服务器开启mining,在服务器上完成区块的生成、挖矿、提交。
以下选取getblocktemplate RPCsetgenerate RPC进行分析。

getblocktemplate

Client发出RPC开始。该RPC位于rpcclient/mining.go,其代码如下:

// GetBlockTemplate returns a new block template for mining.
func (c *Client) GetBlockTemplate(req *btcjson.TemplateRequest) (*btcjson.GetBlockTemplateResult, error) {
   
	return c.GetBlockTemplateAsync(req).Receive()
}

它实际调用了异步版本GetBlockTemplateAsync,并调用其Receive方法阻塞等待结果返回:

// GetBlockTemplateAsync returns an instance of a type that can be used to get the
// result of the RPC at some future time by invoking the Receive function on the
// returned instance.
//
// See GetBlockTemplate for the blocking version and more details.
func (c *Client) GetBlockTemplateAsync(req *btcjson.TemplateRequest) FutureGetBlockTemplateResponse {
   
	cmd := btcjson.NewGetBlockTemplateCmd(req)
	return c.SendCmd(cmd)
}

调用了ClientSendCmd方法:

// SendCmd sends the passed command to the associated server and returns a
// response channel on which the reply will be delivered at some point in the
// future.  It handles both websocket and HTTP POST mode depending on the
// configuration of the client.
func (c *Client) SendCmd(cmd interface{
   }) chan *Response {
   
	...
	responseChan := make(chan *Response, 1)
	jReq := &jsonRequest{
   
		id:             id,
		method:         method,
		cmd:            cmd,
		marshalledJSON: marshalledJSON,
		responseChan:   responseChan,
	}

	c.sendRequest(jReq)

	return responseChan
}

该方法声明了一个容量为1的回复用的通道,说明调用者GetBlockTemplateAsync不会阻塞在等待该方法返回结果。
SendCmd进一步调用了ClientsendRequest方法,之后就是用POST方法将请求发到服务器上。

在服务器上,RPC的处理方法位于rpcserver.go,处理方法为handleGetBlockTemplate,其代码如下:

// handleGetBlockTemplate implements the getblocktemplate command.
func handleGetBlockTemplate(s *rpcServer, cmd interface{
   }, closeChan <-chan struct{
   }) (interface{
   }, error) {
   
	c := cmd.(*btcjson.GetBlockTemplateCmd)
	request := c.Request

	// Set the default mode and override it if supplied.
	mode := "template"
	if request != nil && request.Mode != "" {
   
		mode = request.Mode
	}

	switch mode {
   
	case "template":
		return handleGetBlockTemplateRequest(s, request, closeChan)
	case "proposal":
		return handleGetBlockTemplateProposal(s, request)
	}

	return nil, &btcjson.RPCError{
   
		Code:    btcjson.ErrRPCInvalidParameter,
		Message: "Invalid mode",
	}
}

该方法先判断请求的模式mode是什么,默认为template。以template为例,它调用了handleGetBlockTemplateRequest方法,其代码如下:

func handleGetBlockTemplateRequest(s *rpcServer, request *btcjson.TemplateRequest, closeChan <-chan struct{
   }) (interface{
   }, error) {
   
	...

	// Get and return a block template.  A new block template will be
	// generated when the current best block has changed or the transactions
	// in the memory pool have been updated and it has been at least five
	// seconds since the last template was generated.  Otherwise, the
	// timestamp for the existing block template is updated (and possibly
	// the difficulty on testnet per the consesus rules).
	if err := state.updateBlockTemplate(s, useCoinbaseValue); err != nil {
   
		return nil, err
	}
	return state.blockTemplateResult(useCoinbaseValue, nil)
}

该方法调用了blockTemplateResult方法。查看该方法可知blockTemplateResult返回了目前和state关联的区块模板。
首先看state是什么,其结构体定义如下:

// gbtWorkState houses state that is used in between multiple RPC invocations to
// getblocktemplate.
type gbtWorkState struct {
   
	sync.Mutex
	lastTxUpdate  time.Time
	lastGenerated time.Time
	prevHash      *chainhash.Hash
	minTimestamp  time.Time
	template      *mining.BlockTemplate
	notifyMap     map[chainhash.Hash]map[int64]chan struct{
   }
	timeSource    blockchain.MedianTimeSource
}

gbtWorkState保存了多个getblocktemplate RPC调用请求间的状态。其中BlockTemplate域保存了我们需要的区块模板。那么这个template *mining.BlockTemplate是什么时候设置的呢?其实在handleGetBlockTemplateRequest中,调用的updateBlockTemplate方法设置了这个template

func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bool) error {
   
		
		blkTemplate, err := generator.NewBlockTemplate(payAddr)
		...
		template = blkTemplate
		...
		state.template = template
		...
}

NewBlockTemplate方法则真正创建了一个新的区块模板。该区块模板使用来自内存池的交易创建区块。其中,传入的payToAddress用于创建coinbase交易。

NewBlockTemplate选择交易的策略

选择和包含的交易根据几个因素进行优先级排序:

  1. 每个事务都有一个基于其值、输入时间和大小计算的优先级。由较大金额、较旧输入和较小规模组成的事务具有最高优先级;
  2. 计算每笔交易的每千字节费用。首选每千字节费用较高的交易。
  3. 将所有与块生成相关的策略设置都考虑在内。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值