mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00

* Fix block verbosedata population from skipping transaction verbosedata population when BlockInfo errorenously reports block as Header only, but domainBlock is still fount with GetBlockEvenIfHeaderOnly. * Update app/rpc/rpccontext/verbosedata.go --------- Co-authored-by: Toni Lukkaroinen <toni.lukkaroinen@hoosat.fi> Co-authored-by: Michael Sutton <mikisiton2@gmail.com> Co-authored-by: Ori Newman <orinewman1@gmail.com>
177 lines
6.1 KiB
Go
177 lines
6.1 KiB
Go
package rpccontext
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"math"
|
|
"math/big"
|
|
|
|
difficultyPackage "github.com/kaspanet/kaspad/util/difficulty"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
|
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
|
|
|
"github.com/kaspanet/kaspad/app/appmessage"
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
|
)
|
|
|
|
// ErrBuildBlockVerboseDataInvalidBlock indicates that a block that was given to BuildBlockVerboseData is invalid.
|
|
var ErrBuildBlockVerboseDataInvalidBlock = errors.New("ErrBuildBlockVerboseDataInvalidBlock")
|
|
|
|
// GetDifficultyRatio returns the proof-of-work difficulty as a multiple of the
|
|
// minimum difficulty using the passed bits field from the header of a block.
|
|
func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) float64 {
|
|
// The minimum difficulty is the max possible proof-of-work limit bits
|
|
// converted back to a number. Note this is not the same as the proof of
|
|
// work limit directly because the block difficulty is encoded in a block
|
|
// with the compact form which loses precision.
|
|
target := difficultyPackage.CompactToBig(bits)
|
|
|
|
difficulty := new(big.Rat).SetFrac(params.PowMax, target)
|
|
diff, _ := difficulty.Float64()
|
|
|
|
roundingPrecision := float64(100)
|
|
diff = math.Round(diff*roundingPrecision) / roundingPrecision
|
|
|
|
return diff
|
|
}
|
|
|
|
// PopulateBlockWithVerboseData populates the given `block` with verbose
|
|
// data from `domainBlockHeader` and optionally from `domainBlock`
|
|
func (ctx *Context) PopulateBlockWithVerboseData(block *appmessage.RPCBlock, domainBlockHeader externalapi.BlockHeader,
|
|
domainBlock *externalapi.DomainBlock, includeTransactionVerboseData bool) error {
|
|
|
|
blockHash := consensushashing.HeaderHash(domainBlockHeader)
|
|
|
|
blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(blockHash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if blockInfo.BlockStatus == externalapi.StatusInvalid {
|
|
return errors.Wrap(ErrBuildBlockVerboseDataInvalidBlock, "cannot build verbose data for "+
|
|
"invalid block")
|
|
}
|
|
|
|
_, childrenHashes, err := ctx.Domain.Consensus().GetBlockRelations(blockHash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
isChainBlock, err := ctx.Domain.Consensus().IsChainBlock(blockHash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
block.VerboseData = &appmessage.RPCBlockVerboseData{
|
|
Hash: blockHash.String(),
|
|
Difficulty: ctx.GetDifficultyRatio(domainBlockHeader.Bits(), ctx.Config.ActiveNetParams),
|
|
ChildrenHashes: hashes.ToStrings(childrenHashes),
|
|
IsHeaderOnly: blockInfo.BlockStatus == externalapi.StatusHeaderOnly,
|
|
BlueScore: blockInfo.BlueScore,
|
|
MergeSetBluesHashes: hashes.ToStrings(blockInfo.MergeSetBlues),
|
|
MergeSetRedsHashes: hashes.ToStrings(blockInfo.MergeSetReds),
|
|
IsChainBlock: isChainBlock,
|
|
}
|
|
// selectedParentHash will be nil in the genesis block
|
|
if blockInfo.SelectedParent != nil {
|
|
block.VerboseData.SelectedParentHash = blockInfo.SelectedParent.String()
|
|
}
|
|
|
|
// Get the block if we didn't receive it previously
|
|
if domainBlock == nil {
|
|
domainBlock, err = ctx.Domain.Consensus().GetBlockEvenIfHeaderOnly(blockHash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(domainBlock.Transactions) == 0 {
|
|
return nil
|
|
}
|
|
|
|
transactionIDs := make([]string, len(domainBlock.Transactions))
|
|
for i, transaction := range domainBlock.Transactions {
|
|
transactionIDs[i] = consensushashing.TransactionID(transaction).String()
|
|
}
|
|
block.VerboseData.TransactionIDs = transactionIDs
|
|
|
|
if includeTransactionVerboseData {
|
|
for _, transaction := range block.Transactions {
|
|
err := ctx.PopulateTransactionWithVerboseData(transaction, domainBlockHeader)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PopulateTransactionWithVerboseData populates the given `transaction` with
|
|
// verbose data from `domainTransaction`
|
|
func (ctx *Context) PopulateTransactionWithVerboseData(
|
|
transaction *appmessage.RPCTransaction, domainBlockHeader externalapi.BlockHeader) error {
|
|
|
|
domainTransaction, err := appmessage.RPCTransactionToDomainTransaction(transaction)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx.Domain.Consensus().PopulateMass(domainTransaction)
|
|
|
|
transaction.VerboseData = &appmessage.RPCTransactionVerboseData{
|
|
TransactionID: consensushashing.TransactionID(domainTransaction).String(),
|
|
Hash: consensushashing.TransactionHash(domainTransaction).String(),
|
|
Mass: domainTransaction.Mass,
|
|
}
|
|
if domainBlockHeader != nil {
|
|
transaction.VerboseData.BlockHash = consensushashing.HeaderHash(domainBlockHeader).String()
|
|
transaction.VerboseData.BlockTime = uint64(domainBlockHeader.TimeInMilliseconds())
|
|
}
|
|
for _, input := range transaction.Inputs {
|
|
ctx.populateTransactionInputWithVerboseData(input)
|
|
}
|
|
for _, output := range transaction.Outputs {
|
|
err := ctx.populateTransactionOutputWithVerboseData(output)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ctx *Context) populateTransactionInputWithVerboseData(transactionInput *appmessage.RPCTransactionInput) {
|
|
transactionInput.VerboseData = &appmessage.RPCTransactionInputVerboseData{}
|
|
}
|
|
|
|
func (ctx *Context) populateTransactionOutputWithVerboseData(transactionOutput *appmessage.RPCTransactionOutput) error {
|
|
scriptPublicKey, err := hex.DecodeString(transactionOutput.ScriptPublicKey.Script)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
domainScriptPublicKey := &externalapi.ScriptPublicKey{
|
|
Script: scriptPublicKey,
|
|
Version: transactionOutput.ScriptPublicKey.Version,
|
|
}
|
|
|
|
// Ignore the error here since an error means the script
|
|
// couldn't be parsed and there's no additional information about
|
|
// it anyways
|
|
scriptPublicKeyType, scriptPublicKeyAddress, _ := txscript.ExtractScriptPubKeyAddress(
|
|
domainScriptPublicKey, ctx.Config.ActiveNetParams)
|
|
|
|
var encodedScriptPublicKeyAddress string
|
|
if scriptPublicKeyAddress != nil {
|
|
encodedScriptPublicKeyAddress = scriptPublicKeyAddress.EncodeAddress()
|
|
}
|
|
transactionOutput.VerboseData = &appmessage.RPCTransactionOutputVerboseData{
|
|
ScriptPublicKeyType: scriptPublicKeyType.String(),
|
|
ScriptPublicKeyAddress: encodedScriptPublicKeyAddress,
|
|
}
|
|
return nil
|
|
}
|