当前位置:网站首页>Packaging and uplink of btcd transaction process (III)
Packaging and uplink of btcd transaction process (III)
2022-06-13 00:16:00 【Ethan97】
Overview of transaction packaging and chain
btcd First, get the transaction from the memory pool , Fill blocks with transactions , And fill in the necessary information on the block . Subsequent block POW Calculation . After calculating the block hash matching the difficulty value ,btcd Perform the final step of verification for the new block , Connect the new block to the local main chain , And broadcast the new block to the peer .
BitCoin RPCs
To understand how transactions are packaged into blocks , Last published , First of all, from the Bitcoin API Reference as well as btcd API Reference To view related RPC call .
getblocktemplate
getblocktemplate ( "template_request" )
Its description is as follows :
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
The data needed to construct a block will be returned .
generateblock
generateblock "output" ["rawtx/txid",...]
Its description is as follows :
Mine a block with a set of ordered transactions immediately to a specified address or descriptor (before the RPC call returns)
generateblock RPC
Mining with a given ordered transaction , The beneficiary's address needs to be specified .
submitblock
submitblock "hexdata" ( "dummy" )
Its description is as follows :
Attempts to submit new block to network.
Try to submit a new block to the 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 Set up server Whether to start mining . If open , The server will use CPU dig . Need to use –miningaddr Specify beneficiary's address .
mining Overall process analysis
With these three RPC, We can roughly determine the process from packaging transactions to publishing blocks .
First, through getblocktemplate RPC
Get the information you need to assemble a block , And then through generateblock RPC
Generate a block , Finally through submitblock RPC
Publish blocks to the network .
It can also be done through setgenerate RPC
Set the server on mining, Complete the generation of blocks on the server 、 dig 、 Submit .
Select below getblocktemplate RPC
and setgenerate RPC
Analyze .
getblocktemplate
from Client
issue RPC Start . The RPC be located rpcclient/mining.go
, The code is as follows :
// GetBlockTemplate returns a new block template for mining.
func (c *Client) GetBlockTemplate(req *btcjson.TemplateRequest) (*btcjson.GetBlockTemplateResult, error) {
return c.GetBlockTemplateAsync(req).Receive()
}
It actually calls the asynchronous version GetBlockTemplateAsync
, And call it Receive
Method blocks waiting for the result to return :
// 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)
}
Called Client
Of SendCmd
Method :
// 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
}
This method declares a capacity of 1 The channel used for reply , Describe the caller GetBlockTemplateAsync
Will not block waiting for the method to return results .SendCmd
Called further Client
Of sendRequest
Method , After that, we use it POST Method to send the request to the server .
On the server ,RPC The processing method of is located in rpcserver.go
, The treatment is handleGetBlockTemplate
, The code is as follows :
// 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",
}
}
This method first determines the requested mode mode
What is it? , The default is template
. With template
For example , It calls for handleGetBlockTemplateRequest
Method , The code is as follows :
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)
}
This method calls blockTemplateResult
Method . Check this method to see blockTemplateResult
Returns the current and state
Associated block template .
First of all to see state
What is it? , Its structure is defined as follows :
// 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
Many of them are preserved getblocktemplate
RPC The state between calls . among BlockTemplate
The domain holds the block template we need . So this template *mining.BlockTemplate
When was it set up ? Actually in handleGetBlockTemplateRequest
in , Called updateBlockTemplate
Method sets this template
:
func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bool) error {
blkTemplate, err := generator.NewBlockTemplate(payAddr)
...
template = blkTemplate
...
state.template = template
...
}
NewBlockTemplate
Method actually creates a new block template . This block template uses transactions from the memory pool to create blocks . among , Incoming payToAddress
Used to create coinbase transaction .
NewBlockTemplate Choose a trading strategy
The transactions selected and included are prioritized according to several factors :
- Each transaction has a value based 、 Enter the priority of time and size calculation . By a larger amount 、 Transactions consisting of older inputs and smaller sizes have the highest priority ;
- Calculate the cost per kilobyte per transaction . Transactions with higher cost per kilobyte are preferred .
- Take into account all policy settings related to block generation .
Transactions that use only the output of other transactions already in the block chain will be immediately added to the priority queue , The queue is based on priority ( Then there is the cost per kilobyte ) Or the cost per kilobyte ( Then there is the priority ) Prioritization depends on BlockPrioritySize
Policy setting whether to allocate space for high priority transactions . Transactions that use the output of other transactions in the source pool will be added to the dependency mapping , So that after including the transactions on which they depend , You can add them to the priority queue . Once the high priority area ( If configured ) Transactions filled , Or the priority is lower than the high priority , The priority queue will be updated to cost per kilobyte ( Then there is the priority ) Prioritize . When the cost per kilobyte is less than TxMinFreeFee
When setting policy , The transaction will be skipped , Unless BlockMinSize
Policy set to non-zero , under these circumstances , The block will be populated at a low cost / Free trade , Until the block size reaches the minimum size . Skipping anything will cause the block to exceed BlockMaxSize
Policy settings 、 Transactions that exceed the maximum number of signature operations allowed per block or otherwise invalidate the block .
thus ,NewBlockTemplate
Method returns a new block template :
return &BlockTemplate{
Block: &msgBlock,
Fees: txFees,
SigOpCosts: txSigOpCosts,
Height: nextBlockHeight,
ValidPayAddress: payToAddress != nil,
WitnessCommitment: witnessCommitment,
}, nil
setgenerate
It was said that ,setgenerate Set up server Whether to start mining . If open , The server will use CPU dig . Need to use –miningaddr Specify beneficiary's address .
First, check the processing setgennerate Methods handleSetGenerate
:
// handleSetGenerate implements the setgenerate command.
func handleSetGenerate(s *rpcServer, cmd interface{
}, closeChan <-chan struct{
}) (interface{
}, error) {
...
// It's safe to call start even if it's already started.
s.cfg.CPUMiner.SetNumWorkers(int32(genProcLimit))
s.cfg.CPUMiner.Start()
...
}
handleSetGenerate
First set up the mining worker Number , And then call CPUMiner
Of Start
Methods start mining . Keep looking at Start
Method :
// Start begins the CPU mining process as well as the speed monitor used to
// track hashing metrics. Calling this function when the CPU miner has
// already been started will have no effect.
//
// This function is safe for concurrent access.
func (m *CPUMiner) Start() {
m.Lock()
defer m.Unlock()
// Nothing to do if the miner is already running or if running in
// discrete mode (using GenerateNBlocks).
if m.started || m.discreteMining {
return
}
m.quit = make(chan struct{
})
m.speedMonitorQuit = make(chan struct{
})
m.wg.Add(2)
go m.speedMonitor()
go m.miningWorkerController()
m.started = true
log.Infof("CPU miner started")
}
Start
Method enables a speed monitor speedMonitor
Coroutine and a worker thread controller miningWorkerController
coroutines .
Here's the main thing miningWorkerController
The implementation of the :
// miningWorkerController launches the worker goroutines that are used to
// generate block templates and solve them. It also provides the ability to
// dynamically adjust the number of running worker goroutines.
func (m *CPUMiner) miningWorkerController() {
// launchWorkers groups common code to launch a specified number of
// workers for generating blocks.
var runningWorkers []chan struct{
}
launchWorkers := func(numWorkers uint32) {
for i := uint32(0); i < numWorkers; i++ {
quit := make(chan struct{
})
runningWorkers = append(runningWorkers, quit)
m.workerWg.Add(1)
go m.generateBlocks(quit)
}
}
// Launch the current number of workers by default.
runningWorkers = make([]chan struct{
}, 0, m.numWorkers)
launchWorkers(m.numWorkers)
out:
for {
select {
// Update the number of running workers.
case <-m.updateNumWorkers:
// No change.
numRunning := uint32(len(runningWorkers))
if m.numWorkers == numRunning {
continue
}
// Add new workers.
if m.numWorkers > numRunning {
launchWorkers(m.numWorkers - numRunning)
continue
}
// Signal the most recently created goroutines to exit.
for i := numRunning - 1; i >= m.numWorkers; i-- {
close(runningWorkers[i])
runningWorkers[i] = nil
runningWorkers = runningWorkers[:i]
}
case <-m.quit:
for _, quit := range runningWorkers {
close(quit)
}
break out
}
}
// Wait until all workers shut down to stop the speed monitor since
// they rely on being able to send updates to it.
m.workerWg.Wait()
close(m.speedMonitorQuit)
m.wg.Done()
}
stay for
In circulation , adopt go m.generateBlocks(quit)
Open all worker coroutines . And then miningWorkerController
Monitor mining worker Have the numbers changed , Yes, mining worker Adjust the number , And listen for exit messages , And then quit mining .
generateBlocks
It's called worker, Keep looking at generateBlocks
The method is how to mine :
// generateBlocks is a worker that is controlled by the miningWorkerController.
// It is self contained in that it creates block templates and attempts to solve
// them while detecting when it is performing stale work and reacting
// accordingly by generating a new block template. When a block is solved, it
// is submitted.
//
// It must be run as a goroutine.
func (m *CPUMiner) generateBlocks(quit chan struct{
}) {
log.Tracef("Starting generate blocks worker")
// Start a ticker which is used to signal checks for stale work and
// updates to the speed monitor.
ticker := time.NewTicker(time.Second * hashUpdateSecs)
defer ticker.Stop()
out:
for {
// Quit when the miner is stopped.
select {
case <-quit:
break out
default:
// Non-blocking select to fall through
}
// Wait until there is a connection to at least one other peer
// since there is no way to relay a found block or receive
// transactions to work on when there are no connected peers.
if m.cfg.ConnectedCount() == 0 {
time.Sleep(time.Second)
continue
}
// No point in searching for a solution before the chain is
// synced. Also, grab the same lock as used for block
// submission, since the current block will be changing and
// this would otherwise end up building a new block template on
// a block that is in the process of becoming stale.
m.submitBlockLock.Lock()
curHeight := m.g.BestSnapshot().Height
if curHeight != 0 && !m.cfg.IsCurrent() {
m.submitBlockLock.Unlock()
time.Sleep(time.Second)
continue
}
// Choose a payment address at random.
rand.Seed(time.Now().UnixNano())
payToAddr := m.cfg.MiningAddrs[rand.Intn(len(m.cfg.MiningAddrs))]
// Create a new block template using the available transactions
// in the memory pool as a source of transactions to potentially
// include in the block.
template, err := m.g.NewBlockTemplate(payToAddr)
m.submitBlockLock.Unlock()
if err != nil {
errStr := fmt.Sprintf("Failed to create new block "+
"template: %v", err)
log.Errorf(errStr)
continue
}
// Attempt to solve the block. The function will exit early
// with false when conditions that trigger a stale block, so
// a new block template can be generated. When the return is
// true a solution was found, so submit the solved block.
if m.solveBlock(template.Block, curHeight+1, ticker, quit) {
block := btcutil.NewBlock(template.Block)
m.submitBlock(block)
}
}
m.workerWg.Done()
log.Tracef("Generate blocks worker done")
}
generateBlocks
Did these things :
- monitor
quit
passageway , If the channel is closed , be worker sign out ; - If there is no connected peer , Is blocked for one second ;
- Check whether the main chain has been synchronized , Otherwise, it will block for one second and wait for the main chain synchronization to complete ;
- Choose one of the beneficiary's designated addresses ;
- Call the preceding
NewBlockTemplate
Get a block template ; - call
solveBlock
To solve the problem ofnonce
value ; - Successfully find qualified
nonce
after , callNewBlock
Assemble blocks , callsubmitBlock
Submit block .
solveBlock
Is really doing POW The place of , The code is as follows :
// solveBlock attempts to find some combination of a nonce, extra nonce, and
// current timestamp which makes the passed block hash to a value less than the
// target difficulty. The timestamp is updated periodically and the passed
// block is modified with all tweaks during this process. This means that
// when the function returns true, the block is ready for submission.
//
// This function will return early with false when conditions that trigger a
// stale block such as a new block showing up or periodically when there are
// new transactions and enough time has elapsed without finding a solution.
func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32,
ticker *time.Ticker, quit chan struct{
}) bool {
// Choose a random extra nonce offset for this block template and
// worker.
enOffset, err := wire.RandomUint64()
if err != nil {
log.Errorf("Unexpected error while generating random "+
"extra nonce offset: %v", err)
enOffset = 0
}
// Create some convenience variables.
header := &msgBlock.Header
targetDifficulty := blockchain.CompactToBig(header.Bits)
// Initial state.
lastGenerated := time.Now()
lastTxUpdate := m.g.TxSource().LastUpdated()
hashesCompleted := uint64(0)
// Note that the entire extra nonce range is iterated and the offset is
// added relying on the fact that overflow will wrap around 0 as
// provided by the Go spec.
for extraNonce := uint64(0); extraNonce < maxExtraNonce; extraNonce++ {
// Update the extra nonce in the block template with the
// new value by regenerating the coinbase script and
// setting the merkle root to the new value.
m.g.UpdateExtraNonce(msgBlock, blockHeight, extraNonce+enOffset)
// Search through the entire nonce range for a solution while
// periodically checking for early quit and stale block
// conditions along with updates to the speed monitor.
for i := uint32(0); i <= maxNonce; i++ {
select {
case <-quit:
return false
case <-ticker.C:
m.updateHashes <- hashesCompleted
hashesCompleted = 0
// The current block is stale if the best block
// has changed.
best := m.g.BestSnapshot()
if !header.PrevBlock.IsEqual(&best.Hash) {
return false
}
// The current block is stale if the memory pool
// has been updated since the block template was
// generated and it has been at least one
// minute.
if lastTxUpdate != m.g.TxSource().LastUpdated() &&
time.Now().After(lastGenerated.Add(time.Minute)) {
return false
}
m.g.UpdateBlockTime(msgBlock)
default:
// Non-blocking select to fall through
}
// Update the nonce and hash the block header. Each
// hash is actually a double sha256 (two hashes), so
// increment the number of hashes completed for each
// attempt accordingly.
header.Nonce = i
hash := header.BlockHash()
hashesCompleted += 2
// The block is solved when the new block hash is less
// than the target difficulty. Yay!
if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
m.updateHashes <- hashesCompleted
return true
}
}
}
return false
}
solveBlock
Trying to set nonce
,extra nonce
and timestamp
To make the hash of a given block smaller than the target difficulty value . When a new block appears 、 Or when there is a new transaction and a certain period of time has passed , This method returns early .solveBlock
First, set the loop in the outer layer extraNonce
, Each loop pair extraNonce
Add one more ; Set the loop in the inner layer nonce
, Each loop pair nonce
Add one more ; Inside the loop ,solveBlock
Be able to receive exit messages , And it will periodically check whether new blocks appear ; If a new transaction occurs , And a minute has passed , The exit ;hash := header.BlockHash()
The block header is hashed . If the hash meets the requirements , Will hashesCompleted Send to m.updateHashes
, And back to true
.( take hashesCompleted
Send to m.updateHashes
This is to allow the speed monitor to monitor the hash number per second )
go back to generateBlocks
, Successfully find qualified nonce
, extra nonce
, timestamp
After combination ,solveBlock
return true
, caller generateBlocks
Continue to call NewBlock
Yes template.Block
Call after simply encapsulating the block submitBlock
Submit the block .
submitBlock
The code is as follows :
// submitBlock submits the passed block to network after ensuring it passes all
// of the consensus validation rules.
func (m *CPUMiner) submitBlock(block *btcutil.Block) bool {
m.submitBlockLock.Lock()
defer m.submitBlockLock.Unlock()
// Ensure the block is not stale since a new block could have shown up
// while the solution was being found. Typically that condition is
// detected and all work on the stale block is halted to start work on
// a new block, but the check only happens periodically, so it is
// possible a block was found and submitted in between.
msgBlock := block.MsgBlock()
if !msgBlock.Header.PrevBlock.IsEqual(&m.g.BestSnapshot().Hash) {
log.Debugf("Block submitted via CPU miner with previous "+
"block %s is stale", msgBlock.Header.PrevBlock)
return false
}
// Process this block using the same rules as blocks coming from other
// nodes. This will in turn relay it to the network like normal.
isOrphan, err := m.cfg.ProcessBlock(block, blockchain.BFNone)
if err != nil {
// Anything other than a rule violation is an unexpected error,
// so log that error as an internal error.
if _, ok := err.(blockchain.RuleError); !ok {
log.Errorf("Unexpected error while processing "+
"block submitted via CPU miner: %v", err)
return false
}
log.Debugf("Block submitted via CPU miner rejected: %v", err)
return false
}
if isOrphan {
log.Debugf("Block submitted via CPU miner is an orphan")
return false
}
// The block was accepted.
coinbaseTx := block.MsgBlock().Transactions[0].TxOut[0]
log.Infof("Block submitted via CPU miner accepted (hash %s, "+
"amount %v)", block.Hash(), btcutil.Amount(coinbaseTx.Value))
return true
}
submitBlock
Finally, check whether the newly excavated block has expired ( By comparing the hash of the last block of the newly excavated block with the latest block hash of the current main chain ).
adopt ProcessBlock
Method , Broadcast blocks to peers , This ProcessBlock
Method by cpuminer
Medium Config
Structs hold as method variables , In fact, this method variable is assigned SyncManager
Of ProcessBlock
Method , The code is as follows :
// ProcessBlock makes use of ProcessBlock on an internal instance of a block
// chain.
func (sm *SyncManager) ProcessBlock(block *btcutil.Block, flags blockchain.BehaviorFlags) (bool, error) {
reply := make(chan processBlockResponse, 1)
sm.msgChan <- processBlockMsg{
block: block, flags: flags, reply: reply}
response := <-reply
return response.isOrphan, response.err
}
It will processBlockMsg
Send to msgChan
passageway , Then try to start from processBlockMsg
The attached reply channel reads the reply message , And return it to the caller . At this time, I returned to the familiar blockHandler
Method ( It was mentioned in the section of submitting the transaction that ), receive processBlockMsg
The code for is as follows :
out:
for {
select {
case m := <-sm.msgChan:
switch msg := m.(type) {
...
case processBlockMsg:
_, isOrphan, err := sm.chain.ProcessBlock(
msg.block, msg.flags)
if err != nil {
msg.reply <- processBlockResponse{
isOrphan: false,
err: err,
}
}
msg.reply <- processBlockResponse{
isOrphan: isOrphan,
err: nil,
}
...
...
}
}
When from msgChan
The message received was processBlockMsg
Type , call ProcessBlock
Method , And return the response .BlockChain
Object's ProcessBlock
Method is the last step of blockchain . It includes blockExists
Reject duplicate blocks 、checkBlockSanity
Ensure that the block complies with all rules 、addOrphanBlock
Orphan treatment 、maybeAcceptBlock
Insert blocks into the blockchain .
maybeAcceptBlock
Basically, a block is placed on the blockchain , It will return whether a block is finally on the main chain .
- It will perform several validation checks depending on its location ( The height of the new block is the height of the previous block referenced plus one , Whether the difficulty meets the requirements, etc …), Before trying to add it to the main chain ;
- Save the block to the database ;
- Create a
blockNode
object , And index it ; connectBestChain
Connect blocks to the main chain .
connectBestChain
Connect blocks to the main chain . Typically , Just connect the new block to the main chain . However, it may extend a side chain , It can become the main chain , It may not be the main chain .connectBestChain
Database that updates the block index ;
Perform several checks again to verify that the block can be connected to the main chain ;
The main concern is when to update UTXO:
if fastAdd {
err := view.fetchInputUtxos(b.db, block)
if err != nil {
return false, err
}
err = view.connectTransactions(block, &stxos)
if err != nil {
return false, err
}
}
The transactions in the block inputs Contains UTXOs Bring it here , And the transaction in the block outputs As new UTXOs.
call connectBlock
Connect blocks to the main chain . stay connectBlock
There are the following codes in :
err := b.indexManager.ConnectBlock(dbTx, block, stxos)
ConnectBlock
Performed a series of updates , Finally, the new block is connected to the main chain .
Although the new block is connected to the main chain , But this is only a local blockchain , Other nodes in the network do not know that a new block has been generated . Generally speaking , A node connects a new block , As many nodes as possible should be notified as soon as possible , Otherwise, other nodes may also find qualified nonce
, Also connected to a new block , Then the block connected by the current node may become a side chain . Let's go back to maybeAcceptBlock
Method :
When the new block is successfully connected to the main chain , The following code follows :
// Notify the caller that the new block was accepted into the block
// chain. The caller would typically want to react by relaying the
// inventory to other peers.
b.chainLock.Unlock()
b.sendNotification(NTBlockAccepted, block)
b.chainLock.Lock()
maybeAcceptBlock
By calling sendNotification
Notify the caller , The new block has been accepted . The code is as follows :
// sendNotification sends a notification with the passed type and data if the
// caller requested notifications by providing a callback function in the call
// to New.
func (b *BlockChain) sendNotification(typ NotificationType, data interface{
}) {
// Generate and send the notification.
n := Notification{
Type: typ, Data: data}
b.notificationsLock.RLock()
for _, callback := range b.notifications {
callback(&n)
}
b.notificationsLock.RUnlock()
}
This method first constructs a Notification
, Then traverse BlockChain
Object's notifications
, Constructed with Notification
As a parameter callback notifications
Every function in , among ,Subscribe
Method can subscribe to this notification :
// Subscribe to block chain notifications. Registers a callback to be executed
// when various events take place. See the documentation on Notification and
// NotificationType for details on the types and contents of notifications.
func (b *BlockChain) Subscribe(callback NotificationCallback) {
b.notificationsLock.Lock()
b.notifications = append(b.notifications, callback)
b.notificationsLock.Unlock()
}
Check out the Usage
You know ,RPCServer
and SyncManager
All subscribed to this message , And pass in their respective handleBlockchainNotification
Method as a callback function .
To look at first rpcServer
Of handleBlockchainNotification
Corresponding branch :
case blockchain.NTBlockAccepted:
block, ok := notification.Data.(*btcutil.Block)
if !ok {
rpcsLog.Warnf("Chain accepted notification is not a block.")
break
}
// Allow any clients performing long polling via the
// getblocktemplate RPC to be notified when the new block causes
// their old block template to become stale.
s.gbtWorkState.NotifyBlockConnected(block.Hash())
When new blocks expire their old block templates , Through NotifyBlockConnected
Method pair “ adopt getblocktemplate RPC
perform long polling
The client of ” Notice .
In the view SyncManager
Of handleBlockchainNotification
Corresponding branch :
// A block has been accepted into the block chain. Relay it to other
// peers.
case blockchain.NTBlockAccepted:
// Don't relay if we are not current. Other peers that are
// current should already know about it.
if !sm.current() {
return
}
block, ok := notification.Data.(*btcutil.Block)
if !ok {
log.Warnf("Chain accepted notification is not a block.")
break
}
// Generate the inventory vector and relay it.
iv := wire.NewInvVect(wire.InvTypeBlock, block.Hash())
sm.peerNotifier.RelayInventory(iv, block.MsgBlock().Header)
Here, the new block is passed to other peers . Generate a Inventory vector And pass it on .
// RelayInventory relays the passed inventory vector to all connected peers
// that are not already known to have it.
func (s *server) RelayInventory(invVect *wire.InvVect, data interface{
}) {
s.relayInv <- relayMsg{
invVect: invVect, data: data}
}
RelayInventory
The inventory vector is passed to server
Of relayInv
.
This passage is server Of peerHandler
Read :
// New inventory to potentially be relayed to other peers.
case invMsg := <-s.relayInv:
s.handleRelayInvMsg(state, invMsg)
handleRelayInvMsg
Implemented a closure :
state.forAllPeers(func(sp *serverPeer) {
if !sp.Connected() {
return
}
// If the inventory is a block and the peer prefers headers,
// generate and send a headers message instead of an inventory
// message.
if msg.invVect.Type == wire.InvTypeBlock && sp.WantsHeaders() {
blockHeader, ok := msg.data.(wire.BlockHeader)
if !ok {
peerLog.Warnf("Underlying data for headers" +
" is not a block header")
return
}
msgHeaders := wire.NewMsgHeaders()
if err := msgHeaders.AddBlockHeader(&blockHeader); err != nil {
peerLog.Errorf("Failed to add block"+
" header: %v", err)
return
}
sp.QueueMessage(msgHeaders, nil)
return
}
...
})
Finally, the encapsulated information is put into the peer sending queue .QueueMessage
The code is as follows :
// QueueMessage adds the passed bitcoin message to the peer send queue.
//
// This function is safe for concurrent access.
func (p *Peer) QueueMessage(msg wire.Message, doneChan chan<- struct{
}) {
p.QueueMessageWithEncoding(msg, doneChan, wire.BaseEncoding)
}
Finally, step by step , Leave it to infrastruture
The code in the package completes sending the new block to all known peers .
thus , Completed the source code analysis from the creation of blocks to the transfer of blocks to peers . The peer receives the new block , It will be verified , And decide whether to connect it to the main chain . Whether the block can finally be linked , It depends on whether most nodes in the network finally accept it . If there are new blocks excavated by other nodes in the network at the same time , The success or failure of the uplink will continue to be decided by the next block of the new block .
边栏推荐
- 【Matlab】矩阵变换与矩阵求值
- ucore lab3
- PMP training organization
- June 13, 2022 Daily: Turing prize winner: what should we pay attention to if we want to succeed in our academic career?
- How to quickly query the mobile phone number home and operator
- Day 3 of jsbom and DOM learning
- [matlab] two dimensional curve
- 在 Golang 中构建 CRUD 应用程序
- Divicon est toujours utilisé dans le leaflet de l'ère H5?
- 6.824 Lab 1: MapReduce
猜你喜欢
进程间通信-共享内存shmat
ik分词器的安装
Free lottery --- PMP renewal PDU | PMP knowledge map
String类中split()方法的使用
How to gracefully solve the offset problem of Baidu and Gaode maps in leaflet
PMP test difficulty and pass rate
How to load 100000 pieces of data in leaflet
Tsinghua Bosch joint ml center, thbi lab:cheng Yang Ying | realize safety reinforcement learning through the value at risk of constraints
浏览器缓存的执行流程
【HCIE论述】STP-A
随机推荐
The difference between caching and buffering
Why does the PMP certificate need to be renewed and the renewal process?
Accelerating with Dali modules
PLC也能制作小游戏----Codesys编写猜数字小游戏
【Matlab】二维曲线
Pytorch loading model error resolution
June 11, 2022 diary: Mr. Wang's spring, mixed in
PLC也能制作小遊戲----Codesys編寫猜數字小遊戲
分公司能与员工签劳动合同么
Do you have to read for PMP?
How leaflet gracefully displays the bubble window of overlapping points
The whole process from entering URL to displaying page (interview)
Vscode实现PHP在浏览器实时预览
Video tracker error troubleshooting
[matlab] two dimensional curve
[hcie discussion] STP-A
Is the revised PMP worth testing?
Day 3 of jsbom and DOM learning
Kaust:deyao Zhu | value memory map: a graph structured world model based on off-line reinforcement learning
2022 constructor - Equipment direction - General Foundation (constructor) operation certificate examination questions and simulation examination