mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-09-13 13:00:10 +00:00
247 lines
7.9 KiB
Go
247 lines
7.9 KiB
Go
package rpccontext
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"github.com/kaspanet/kaspad/app/appmessage"
|
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
|
"github.com/kaspanet/kaspad/domain/txscript"
|
|
"github.com/kaspanet/kaspad/util"
|
|
"github.com/kaspanet/kaspad/util/daghash"
|
|
"github.com/kaspanet/kaspad/util/pointers"
|
|
"math/big"
|
|
"strconv"
|
|
)
|
|
|
|
// BuildBlockVerboseData builds a BlockVerboseData from the given block.
|
|
// This method must be called with the DAG lock held for reads
|
|
func (ctx *Context) BuildBlockVerboseData(block *util.Block, includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) {
|
|
hash := block.Hash()
|
|
params := ctx.DAG.Params
|
|
blockHeader := block.MsgBlock().Header
|
|
|
|
blockBlueScore, err := ctx.DAG.BlueScoreByBlockHash(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Get the hashes for the next blocks unless there are none.
|
|
childHashes, err := ctx.DAG.ChildHashesByHash(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
blockConfirmations, err := ctx.DAG.BlockConfirmationsByHashNoLock(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
selectedParentHash, err := ctx.DAG.SelectedParentHash(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
selectedParentHashStr := ""
|
|
if selectedParentHash != nil {
|
|
selectedParentHashStr = selectedParentHash.String()
|
|
}
|
|
|
|
isChainBlock, err := ctx.DAG.IsInSelectedParentChain(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
acceptedBlockHashes, err := ctx.DAG.BluesByBlockHash(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &appmessage.BlockVerboseData{
|
|
Hash: hash.String(),
|
|
Version: blockHeader.Version,
|
|
VersionHex: fmt.Sprintf("%08x", blockHeader.Version),
|
|
HashMerkleRoot: blockHeader.HashMerkleRoot.String(),
|
|
AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot.String(),
|
|
UTXOCommitment: blockHeader.UTXOCommitment.String(),
|
|
ParentHashes: daghash.Strings(blockHeader.ParentHashes),
|
|
SelectedParentHash: selectedParentHashStr,
|
|
Nonce: blockHeader.Nonce,
|
|
Time: blockHeader.Timestamp.UnixMilliseconds(),
|
|
Confirmations: blockConfirmations,
|
|
BlueScore: blockBlueScore,
|
|
IsChainBlock: isChainBlock,
|
|
Size: int32(block.MsgBlock().SerializeSize()),
|
|
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
|
|
Difficulty: ctx.GetDifficultyRatio(blockHeader.Bits, params),
|
|
ChildHashes: daghash.Strings(childHashes),
|
|
AcceptedBlockHashes: daghash.Strings(acceptedBlockHashes),
|
|
}
|
|
|
|
transactions := block.Transactions()
|
|
txIDs := make([]string, len(transactions))
|
|
for i, tx := range transactions {
|
|
txIDs[i] = tx.ID().String()
|
|
}
|
|
result.TxIDs = txIDs
|
|
|
|
if includeTransactionVerboseData {
|
|
transactions := block.Transactions()
|
|
transactionVerboseData := make([]*appmessage.TransactionVerboseData, len(transactions))
|
|
for i, tx := range transactions {
|
|
data, err := ctx.buildTransactionVerboseData(tx.MsgTx(), tx.ID().String(),
|
|
&blockHeader, hash.String(), nil, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
transactionVerboseData[i] = data
|
|
}
|
|
result.TransactionVerboseData = transactionVerboseData
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// 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 := util.CompactToBig(bits)
|
|
|
|
difficulty := new(big.Rat).SetFrac(params.PowMax, target)
|
|
outString := difficulty.FloatString(8)
|
|
diff, err := strconv.ParseFloat(outString, 64)
|
|
if err != nil {
|
|
log.Errorf("Cannot get difficulty: %s", err)
|
|
return 0
|
|
}
|
|
return diff
|
|
}
|
|
|
|
func (ctx *Context) buildTransactionVerboseData(mtx *appmessage.MsgTx,
|
|
txID string, blockHeader *appmessage.BlockHeader, blockHash string,
|
|
acceptingBlock *daghash.Hash, isInMempool bool) (*appmessage.TransactionVerboseData, error) {
|
|
|
|
mtxHex, err := msgTxToHex(mtx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var payloadHash string
|
|
if mtx.PayloadHash != nil {
|
|
payloadHash = mtx.PayloadHash.String()
|
|
}
|
|
|
|
txReply := &appmessage.TransactionVerboseData{
|
|
Hex: mtxHex,
|
|
TxID: txID,
|
|
Hash: mtx.TxHash().String(),
|
|
Size: int32(mtx.SerializeSize()),
|
|
TransactionVerboseInputs: ctx.buildTransactionVerboseInputs(mtx),
|
|
TransactionVerboseOutputs: ctx.buildTransactionVerboseOutputs(mtx, nil),
|
|
Version: mtx.Version,
|
|
LockTime: mtx.LockTime,
|
|
SubnetworkID: mtx.SubnetworkID.String(),
|
|
Gas: mtx.Gas,
|
|
PayloadHash: payloadHash,
|
|
Payload: hex.EncodeToString(mtx.Payload),
|
|
}
|
|
|
|
if blockHeader != nil {
|
|
txReply.Time = uint64(blockHeader.Timestamp.UnixMilliseconds())
|
|
txReply.BlockTime = uint64(blockHeader.Timestamp.UnixMilliseconds())
|
|
txReply.BlockHash = blockHash
|
|
}
|
|
|
|
txReply.IsInMempool = isInMempool
|
|
if acceptingBlock != nil {
|
|
txReply.AcceptedBy = acceptingBlock.String()
|
|
}
|
|
|
|
return txReply, nil
|
|
}
|
|
|
|
// msgTxToHex serializes a transaction using the latest protocol version and
|
|
// returns a hex-encoded string of the result.
|
|
func msgTxToHex(msgTx *appmessage.MsgTx) (string, error) {
|
|
var buf bytes.Buffer
|
|
err := msgTx.KaspaEncode(&buf, 0)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return hex.EncodeToString(buf.Bytes()), nil
|
|
}
|
|
|
|
func (ctx *Context) buildTransactionVerboseInputs(mtx *appmessage.MsgTx) []*appmessage.TransactionVerboseInput {
|
|
inputs := make([]*appmessage.TransactionVerboseInput, len(mtx.TxIn))
|
|
for i, txIn := range mtx.TxIn {
|
|
// The disassembled string will contain [error] inline
|
|
// if the script doesn't fully parse, so ignore the
|
|
// error here.
|
|
disbuf, _ := txscript.DisasmString(txIn.SignatureScript)
|
|
|
|
input := &appmessage.TransactionVerboseInput{}
|
|
input.TxID = txIn.PreviousOutpoint.TxID.String()
|
|
input.OutputIndex = txIn.PreviousOutpoint.Index
|
|
input.Sequence = txIn.Sequence
|
|
input.ScriptSig = &appmessage.ScriptSig{
|
|
Asm: disbuf,
|
|
Hex: hex.EncodeToString(txIn.SignatureScript),
|
|
}
|
|
inputs[i] = input
|
|
}
|
|
|
|
return inputs
|
|
}
|
|
|
|
// buildTransactionVerboseOutputs returns a slice of JSON objects for the outputs of the passed
|
|
// transaction.
|
|
func (ctx *Context) buildTransactionVerboseOutputs(mtx *appmessage.MsgTx, filterAddrMap map[string]struct{}) []*appmessage.TransactionVerboseOutput {
|
|
outputs := make([]*appmessage.TransactionVerboseOutput, len(mtx.TxOut))
|
|
for i, v := range mtx.TxOut {
|
|
// The disassembled string will contain [error] inline if the
|
|
// script doesn't fully parse, so ignore the error here.
|
|
disbuf, _ := txscript.DisasmString(v.ScriptPubKey)
|
|
|
|
// Ignore the error here since an error means the script
|
|
// couldn't parse and there is no additional information about
|
|
// it anyways.
|
|
scriptClass, addr, _ := txscript.ExtractScriptPubKeyAddress(
|
|
v.ScriptPubKey, ctx.DAG.Params)
|
|
|
|
// Encode the addresses while checking if the address passes the
|
|
// filter when needed.
|
|
passesFilter := len(filterAddrMap) == 0
|
|
var encodedAddr string
|
|
if addr != nil {
|
|
encodedAddr = *pointers.String(addr.EncodeAddress())
|
|
|
|
// If the filter doesn't already pass, make it pass if
|
|
// the address exists in the filter.
|
|
if _, exists := filterAddrMap[encodedAddr]; exists {
|
|
passesFilter = true
|
|
}
|
|
}
|
|
|
|
if !passesFilter {
|
|
continue
|
|
}
|
|
|
|
output := &appmessage.TransactionVerboseOutput{}
|
|
output.Index = uint32(i)
|
|
output.Value = v.Value
|
|
output.ScriptPubKey = &appmessage.ScriptPubKeyResult{
|
|
Address: encodedAddr,
|
|
Asm: disbuf,
|
|
Hex: hex.EncodeToString(v.ScriptPubKey),
|
|
Type: scriptClass.String(),
|
|
}
|
|
outputs[i] = output
|
|
}
|
|
|
|
return outputs
|
|
}
|