mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-14 00:59:33 +00:00
[NOD-385] Change confirmation calculation to be relative to the selected tip (#455)
* [NOD-385] Make confirmations be calculated as dag.selectedTip().blueScore - acceptingBlock.blueScore + 2 * [NOD-385] Fix comments * [NOD-385] Make more explicit check in accepting block for selected tip * [NOD-385] Put only non accepted transactions in areTxsInBlock * [NOD-385] fetchSelectedTip only if needed
This commit is contained in:
parent
770dfd147d
commit
24305cda68
@ -110,21 +110,42 @@ func GetTransactionsByAddressHandler(address string, skip uint64, limit uint64)
|
|||||||
return txResponses, nil
|
return txResponses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchSelectedTipBlueScore() (uint64, error) {
|
func fetchSelectedTip() (*dbmodels.Block, error) {
|
||||||
db, err := database.DB()
|
db, err := database.DB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
block := &dbmodels.Block{}
|
block := &dbmodels.Block{}
|
||||||
dbResult := db.Order("blue_score DESC").
|
dbResult := db.Order("blue_score DESC").
|
||||||
Where(&dbmodels.Block{IsChainBlock: true}).
|
Where(&dbmodels.Block{IsChainBlock: true}).
|
||||||
Select("blue_score").
|
|
||||||
First(block)
|
First(block)
|
||||||
dbErrors := dbResult.GetErrors()
|
dbErrors := dbResult.GetErrors()
|
||||||
if httpserverutils.HasDBError(dbErrors) {
|
if httpserverutils.HasDBError(dbErrors) {
|
||||||
return 0, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading transactions from the database:", dbErrors)
|
return nil, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading transactions from the database:", dbErrors)
|
||||||
}
|
}
|
||||||
return block.BlueScore, nil
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func areTxsInBlock(blockID uint64, txIDs []uint64) (map[uint64]bool, error) {
|
||||||
|
db, err := database.DB()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
transactionBlocks := []*dbmodels.TransactionBlock{}
|
||||||
|
dbErrors := db.
|
||||||
|
Where(&dbmodels.TransactionBlock{BlockID: blockID}).
|
||||||
|
Where("transaction_id in (?)", txIDs).
|
||||||
|
Find(&transactionBlocks).GetErrors()
|
||||||
|
|
||||||
|
if len(dbErrors) > 0 {
|
||||||
|
return nil, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading UTXOs from the database:", dbErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
isInBlock := make(map[uint64]bool)
|
||||||
|
for _, transactionBlock := range transactionBlocks {
|
||||||
|
isInBlock[transactionBlock.TransactionID] = true
|
||||||
|
}
|
||||||
|
return isInBlock, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUTXOsByAddressHandler searches for all UTXOs that belong to a certain address.
|
// GetUTXOsByAddressHandler searches for all UTXOs that belong to a certain address.
|
||||||
@ -145,9 +166,25 @@ func GetUTXOsByAddressHandler(address string) (interface{}, error) {
|
|||||||
return nil, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading UTXOs from the database:", dbErrors)
|
return nil, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading UTXOs from the database:", dbErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedTipBlueScore, err := fetchSelectedTipBlueScore()
|
nonAcceptedTxIds := make([]uint64, len(transactionOutputs))
|
||||||
if err != nil {
|
for i, txOut := range transactionOutputs {
|
||||||
return nil, err
|
if txOut.Transaction.AcceptingBlock == nil {
|
||||||
|
nonAcceptedTxIds[i] = txOut.TransactionID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedTip *dbmodels.Block
|
||||||
|
var isTxInSelectedTip map[uint64]bool
|
||||||
|
if len(nonAcceptedTxIds) != 0 {
|
||||||
|
selectedTip, err = fetchSelectedTip()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
isTxInSelectedTip, err = areTxsInBlock(selectedTip.ID, nonAcceptedTxIds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UTXOsResponses := make([]*apimodels.TransactionOutputResponse, len(transactionOutputs))
|
UTXOsResponses := make([]*apimodels.TransactionOutputResponse, len(transactionOutputs))
|
||||||
@ -160,10 +197,12 @@ func GetUTXOsByAddressHandler(address string) (interface{}, error) {
|
|||||||
var acceptingBlockHash *string
|
var acceptingBlockHash *string
|
||||||
var confirmations uint64
|
var confirmations uint64
|
||||||
acceptingBlockBlueScore := blockdag.UnacceptedBlueScore
|
acceptingBlockBlueScore := blockdag.UnacceptedBlueScore
|
||||||
if transactionOutput.Transaction.AcceptingBlock != nil {
|
if isTxInSelectedTip[transactionOutput.ID] {
|
||||||
|
confirmations = 1
|
||||||
|
} else if transactionOutput.Transaction.AcceptingBlock != nil {
|
||||||
acceptingBlockHash = btcjson.String(transactionOutput.Transaction.AcceptingBlock.BlockHash)
|
acceptingBlockHash = btcjson.String(transactionOutput.Transaction.AcceptingBlock.BlockHash)
|
||||||
acceptingBlockBlueScore = transactionOutput.Transaction.AcceptingBlock.BlueScore
|
acceptingBlockBlueScore = transactionOutput.Transaction.AcceptingBlock.BlueScore
|
||||||
confirmations = selectedTipBlueScore - acceptingBlockBlueScore
|
confirmations = selectedTip.BlueScore - acceptingBlockBlueScore + 2
|
||||||
}
|
}
|
||||||
UTXOsResponses[i] = &apimodels.TransactionOutputResponse{
|
UTXOsResponses[i] = &apimodels.TransactionOutputResponse{
|
||||||
TransactionID: transactionOutput.Transaction.TransactionID,
|
TransactionID: transactionOutput.Transaction.TransactionID,
|
||||||
|
@ -1362,9 +1362,13 @@ func (dag *BlockDAG) UTXOCommitment() string {
|
|||||||
|
|
||||||
// blockConfirmations returns the current confirmations number of the given node
|
// blockConfirmations returns the current confirmations number of the given node
|
||||||
// The confirmations number is defined as follows:
|
// The confirmations number is defined as follows:
|
||||||
// * If the node is red -> 0
|
// * If the node is in the selected tip red set -> 0
|
||||||
// * Otherwise -> virtual.blueScore - acceptingBlock.blueScore + 1
|
// * If the node is the selected tip -> 1
|
||||||
|
// * Otherwise -> selectedTip.blueScore - acceptingBlock.blueScore + 2
|
||||||
func (dag *BlockDAG) blockConfirmations(node *blockNode) (uint64, error) {
|
func (dag *BlockDAG) blockConfirmations(node *blockNode) (uint64, error) {
|
||||||
|
if node == dag.selectedTip() {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
acceptingBlock, err := dag.acceptingBlock(node)
|
acceptingBlock, err := dag.acceptingBlock(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -1375,36 +1379,23 @@ func (dag *BlockDAG) blockConfirmations(node *blockNode) (uint64, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return dag.virtual.blueScore - acceptingBlock.blueScore + 1, nil
|
return dag.selectedTip().blueScore - acceptingBlock.blueScore + 2, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// acceptingBlock finds the node in the selected-parent chain that had accepted
|
// acceptingBlock finds the node in the selected-parent chain that had accepted
|
||||||
// the given node
|
// the given node
|
||||||
func (dag *BlockDAG) acceptingBlock(node *blockNode) (*blockNode, error) {
|
func (dag *BlockDAG) acceptingBlock(node *blockNode) (*blockNode, error) {
|
||||||
// Explicitly handle the DAG tips
|
|
||||||
if dag.virtual.tips().contains(node) {
|
|
||||||
// Return the virtual block if the node is one of the DAG blues
|
|
||||||
for _, tip := range dag.virtual.blues {
|
|
||||||
if tip == node {
|
|
||||||
return &dag.virtual.blockNode, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, this tip is red and doesn't have an accepting block
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return an error if the node is the virtual block
|
// Return an error if the node is the virtual block
|
||||||
if len(node.children) == 0 {
|
if node == &dag.virtual.blockNode {
|
||||||
if node == &dag.virtual.blockNode {
|
return nil, errors.New("cannot get acceptingBlock for virtual")
|
||||||
return nil, errors.New("cannot get acceptingBlock for virtual")
|
|
||||||
}
|
|
||||||
// A childless block that isn't a tip or the virtual can never happen. Panicking
|
|
||||||
panic(errors.Errorf("got childless block %s that is neither a tip nor the virtual", node.hash))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the node is a chain-block itself, the accepting block is its chain-child
|
// If the node is a chain-block itself, the accepting block is its chain-child
|
||||||
if dag.IsInSelectedParentChain(node.hash) {
|
if dag.IsInSelectedParentChain(node.hash) {
|
||||||
|
if len(node.children) == 0 {
|
||||||
|
// If the node is the selected tip, it doesn't have an accepting block
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
for _, child := range node.children {
|
for _, child := range node.children {
|
||||||
if dag.IsInSelectedParentChain(child.hash) {
|
if dag.IsInSelectedParentChain(child.hash) {
|
||||||
return child, nil
|
return child, nil
|
||||||
@ -1416,6 +1407,13 @@ func (dag *BlockDAG) acceptingBlock(node *blockNode) (*blockNode, error) {
|
|||||||
// Find the only chain block that may contain the node in its blues
|
// Find the only chain block that may contain the node in its blues
|
||||||
candidateAcceptingBlock := dag.oldestChainBlockWithBlueScoreGreaterThan(node.blueScore)
|
candidateAcceptingBlock := dag.oldestChainBlockWithBlueScoreGreaterThan(node.blueScore)
|
||||||
|
|
||||||
|
// if no candidate is found, it means that the node has same or more
|
||||||
|
// blue score than the selected tip and is found in its anticone, so
|
||||||
|
// it doesn't have an accepting block
|
||||||
|
if candidateAcceptingBlock == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// candidateAcceptingBlock is the accepting block only if it actually contains
|
// candidateAcceptingBlock is the accepting block only if it actually contains
|
||||||
// the node in its blues
|
// the node in its blues
|
||||||
for _, blue := range candidateAcceptingBlock.blues {
|
for _, blue := range candidateAcceptingBlock.blues {
|
||||||
@ -1424,7 +1422,8 @@ func (dag *BlockDAG) acceptingBlock(node *blockNode) (*blockNode, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, the node is red and doesn't have an accepting block
|
// Otherwise, the node is red or in the selected tip anticone, and
|
||||||
|
// doesn't have an accepting block
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1042,13 +1042,9 @@ func TestConfirmations(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestConfirmations: confirmations for tip unexpectedly failed: %s", err)
|
t.Fatalf("TestConfirmations: confirmations for tip unexpectedly failed: %s", err)
|
||||||
}
|
}
|
||||||
acceptingBlock, err := dag.acceptingBlock(tip)
|
expectedConfirmations := uint64(0)
|
||||||
if err != nil {
|
if tip == dag.selectedTip() {
|
||||||
t.Fatalf("TestConfirmations: dag.acceptingBlock unexpectedly failed: %s", err)
|
expectedConfirmations = 1
|
||||||
}
|
|
||||||
expectedConfirmations := uint64(1)
|
|
||||||
if acceptingBlock == nil {
|
|
||||||
expectedConfirmations = 0
|
|
||||||
}
|
}
|
||||||
if tipConfirmations != expectedConfirmations {
|
if tipConfirmations != expectedConfirmations {
|
||||||
t.Fatalf("TestConfirmations: unexpected confirmations for tip. "+
|
t.Fatalf("TestConfirmations: unexpected confirmations for tip. "+
|
||||||
@ -1090,7 +1086,7 @@ func TestConfirmations(t *testing.T) {
|
|||||||
func TestAcceptingBlock(t *testing.T) {
|
func TestAcceptingBlock(t *testing.T) {
|
||||||
// Create a new database and DAG instance to run tests against.
|
// Create a new database and DAG instance to run tests against.
|
||||||
params := dagconfig.SimNetParams
|
params := dagconfig.SimNetParams
|
||||||
params.K = 1
|
params.K = 3
|
||||||
dag, teardownFunc, err := DAGSetup("TestAcceptingBlock", Config{
|
dag, teardownFunc, err := DAGSetup("TestAcceptingBlock", Config{
|
||||||
DAGParams: ¶ms,
|
DAGParams: ¶ms,
|
||||||
})
|
})
|
||||||
@ -1105,15 +1101,16 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestAcceptingBlock: acceptingBlock for genesis block unexpectedly failed: %s", err)
|
t.Fatalf("TestAcceptingBlock: acceptingBlock for genesis block unexpectedly failed: %s", err)
|
||||||
}
|
}
|
||||||
if genesisAcceptingBlock != &dag.virtual.blockNode {
|
if genesisAcceptingBlock != nil {
|
||||||
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for genesis block. "+
|
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for genesis block. "+
|
||||||
"Want: virtual, got: %s", genesisAcceptingBlock.hash)
|
"Want: nil, got: %s", genesisAcceptingBlock.hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
chainBlocks := make([]*blockNode, 5)
|
numChainBlocks := uint32(10)
|
||||||
|
chainBlocks := make([]*blockNode, numChainBlocks)
|
||||||
chainBlocks[0] = dag.genesis
|
chainBlocks[0] = dag.genesis
|
||||||
buildNode := buildNodeGenerator(dag.dagParams.K, true)
|
buildNode := buildNodeGenerator(dag.dagParams.K, true)
|
||||||
for i := uint32(1); i <= 4; i++ {
|
for i := uint32(1); i <= numChainBlocks-1; i++ {
|
||||||
chainBlocks[i] = buildNode(setFromSlice(chainBlocks[i-1]))
|
chainBlocks[i] = buildNode(setFromSlice(chainBlocks[i-1]))
|
||||||
dag.virtual.AddTip(chainBlocks[i])
|
dag.virtual.AddTip(chainBlocks[i])
|
||||||
}
|
}
|
||||||
@ -1131,16 +1128,65 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
branchingBlocks := make([]*blockNode, 2)
|
// Make sure that the selected tip doesn't have an accepting
|
||||||
// Add two branching blocks
|
tipAcceptingBlock, err := dag.acceptingBlock(chainBlocks[len(chainBlocks)-1])
|
||||||
branchingBlocks[0] = buildNode(setFromSlice(chainBlocks[1]))
|
if err != nil {
|
||||||
dag.virtual.AddTip(branchingBlocks[0])
|
t.Fatalf("TestAcceptingBlock: acceptingBlock for tip unexpectedly failed: %s", err)
|
||||||
branchingBlocks[1] = buildNode(setFromSlice(branchingBlocks[0]))
|
}
|
||||||
dag.virtual.AddTip(branchingBlocks[1])
|
if tipAcceptingBlock != nil {
|
||||||
|
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for tip. "+
|
||||||
|
"Want: nil, got: %s", tipAcceptingBlock.hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate side-chain of dag.dagParams.K + 1 blocks so its tip
|
||||||
|
// will be in the blues of the virtual but in the anticone of
|
||||||
|
// the selected tip.
|
||||||
|
branchingChainTip := chainBlocks[len(chainBlocks)-2]
|
||||||
|
for i := uint32(0); i < dag.dagParams.K+1; i++ {
|
||||||
|
nextBranchingChainTip := buildNode(setFromSlice(branchingChainTip))
|
||||||
|
dag.virtual.AddTip(nextBranchingChainTip)
|
||||||
|
branchingChainTip = nextBranchingChainTip
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that branchingChainTip is not in the selected parent chain
|
||||||
|
if dag.IsInSelectedParentChain(branchingChainTip.hash) {
|
||||||
|
t.Fatalf("TestAcceptingBlock: branchingChainTip wasn't expected to be in the selected parent chain")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that branchingChainTip is in the virtual blues
|
||||||
|
isVirtualBlue := false
|
||||||
|
for _, virtualBlue := range dag.virtual.blues {
|
||||||
|
if branchingChainTip == virtualBlue {
|
||||||
|
isVirtualBlue = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isVirtualBlue {
|
||||||
|
t.Fatalf("TestAcceptingBlock: redChainBlock was expected to be a virtual blue")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that a block that is in the anticone of the selected tip and
|
||||||
|
// in the blues of the virtual doesn't have an accepting block
|
||||||
|
branchingChainTipAcceptionBlock, err := dag.acceptingBlock(branchingChainTip)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain block unexpectedly failed: %s", err)
|
||||||
|
}
|
||||||
|
if branchingChainTipAcceptionBlock != nil {
|
||||||
|
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for branchingChainTipAcceptionBlock. "+
|
||||||
|
"Want: nil, got: %s", branchingChainTipAcceptionBlock.hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add K + 1 branching blocks
|
||||||
|
intersectionBlock := chainBlocks[1]
|
||||||
|
sideChainTip := buildNode(setFromSlice(intersectionBlock))
|
||||||
|
i := uint32(0)
|
||||||
|
for ; i < dag.dagParams.K+1; sideChainTip = buildNode(setFromSlice(sideChainTip)) {
|
||||||
|
dag.virtual.AddTip(sideChainTip)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure that the accepting block of the parent of the branching block didn't change
|
// Make sure that the accepting block of the parent of the branching block didn't change
|
||||||
expectedAcceptingBlock := chainBlocks[2]
|
expectedAcceptingBlock := chainBlocks[2]
|
||||||
intersectionBlock := chainBlocks[1]
|
|
||||||
intersectionAcceptingBlock, err := dag.acceptingBlock(intersectionBlock)
|
intersectionAcceptingBlock, err := dag.acceptingBlock(intersectionBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestAcceptingBlock: acceptingBlock for intersection block unexpectedly failed: %s", err)
|
t.Fatalf("TestAcceptingBlock: acceptingBlock for intersection block unexpectedly failed: %s", err)
|
||||||
@ -1150,44 +1196,18 @@ func TestAcceptingBlock(t *testing.T) {
|
|||||||
"Want: %s, got: %s", expectedAcceptingBlock.hash, intersectionAcceptingBlock.hash)
|
"Want: %s, got: %s", expectedAcceptingBlock.hash, intersectionAcceptingBlock.hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the accepting block of the chain tips is the virtual block
|
// Make sure that a block that is found in the red set of the selected tip
|
||||||
tipAcceptingBlock, err := dag.acceptingBlock(chainBlocks[len(chainBlocks)-1])
|
// doesn't have an accepting block
|
||||||
if err != nil {
|
newTip := buildNode(setFromSlice(sideChainTip, chainBlocks[len(chainBlocks)-1]))
|
||||||
t.Fatalf("TestAcceptingBlock: acceptingBlock for tip unexpectedly failed: %s", err)
|
dag.virtual.AddTip(newTip)
|
||||||
}
|
|
||||||
if tipAcceptingBlock != &dag.virtual.blockNode {
|
|
||||||
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for tip. "+
|
|
||||||
"Want: Virtual, got: %s", tipAcceptingBlock.hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate 100 blocks to force the "main" chain to become red
|
sideChainTipAcceptingBlock, err := dag.acceptingBlock(sideChainTip)
|
||||||
branchingChainTip := branchingBlocks[1]
|
|
||||||
for i := uint32(0); i < 100; i++ {
|
|
||||||
nextBranchingChainTip := buildNode(setFromSlice(branchingChainTip))
|
|
||||||
dag.virtual.AddTip(nextBranchingChainTip)
|
|
||||||
branchingChainTip = nextBranchingChainTip
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that a red block returns nil
|
|
||||||
redChainBlock := chainBlocks[2]
|
|
||||||
redChainBlockAcceptionBlock, err := dag.acceptingBlock(redChainBlock)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain block unexpectedly failed: %s", err)
|
t.Fatalf("TestAcceptingBlock: acceptingBlock for sideChainTip unexpectedly failed: %s", err)
|
||||||
}
|
}
|
||||||
if redChainBlockAcceptionBlock != nil {
|
if sideChainTipAcceptingBlock != nil {
|
||||||
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for red chain block. "+
|
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for sideChainTip. "+
|
||||||
"Want: nil, got: %s", redChainBlockAcceptionBlock.hash)
|
"Want: nil, got: %s", intersectionAcceptingBlock.hash)
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that a red tip returns nil
|
|
||||||
redChainTip := chainBlocks[len(chainBlocks)-1]
|
|
||||||
redChainTipAcceptingBlock, err := dag.acceptingBlock(redChainTip)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("TestAcceptingBlock: acceptingBlock for red chain tip unexpectedly failed: %s", err)
|
|
||||||
}
|
|
||||||
if redChainTipAcceptingBlock != nil {
|
|
||||||
t.Fatalf("TestAcceptingBlock: unexpected acceptingBlock for red tip block. "+
|
|
||||||
"Want: nil, got: %s", redChainTipAcceptingBlock.hash)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user