func WireBlockToRosetta(b, prev *wire.MsgBlock, fetchPrevOuts PrevOutputsFetcher,
	chainParams *chaincfg.Params) (*rtypes.Block, error) {
	approvesParent := VoteBitsApprovesParent(b.Header.VoteBits) || b.Header.Height == 0
	if !approvesParent && prev == nil {
		return nil, ErrNeedsPreviousBlock
	}
	var prevBlockHash chainhash.Hash
	if prev != nil {
		prevBlockHash = prev.BlockHash()
	}
	var txs []*rtypes.Transaction
	nbTxs := len(b.Transactions) + len(b.STransactions)
	if !approvesParent {
		nbTxs += len(prev.Transactions) + len(prev.STransactions)
	}
	txs = make([]*rtypes.Transaction, 0, nbTxs)
	var tx *rtypes.Transaction
	applyOp := func(op *Op) error {
		if op.OpIndex == 0 {
			var blockHash *chainhash.Hash
			if op.Status == OpStatusReversed {
				blockHash = &prevBlockHash
			}
			tx = txMetaToRosetta(op.Tx, &op.TxHash, blockHash)
			txs = append(txs, tx)
		}
		tx.Operations = append(tx.Operations, op.ROp())
		return nil
	}
	err := IterateBlockOps(b, prev, fetchPrevOuts, applyOp, chainParams)
	if err != nil {
		return nil, err
	}
	blockHash := b.Header.BlockHash()
	prevHeight := b.Header.Height - 1
	prevHash := b.Header.PrevBlock
	if b.Header.Height == 0 {
		prevHeight = 0
		prevHash = blockHash
	}
	r := &rtypes.Block{
		BlockIdentifier: &rtypes.BlockIdentifier{
			Index: int64(b.Header.Height),
			Hash:  blockHash.String(),
		},
		ParentBlockIdentifier: &rtypes.BlockIdentifier{
			Index: int64(prevHeight),
			Hash:  prevHash.String(),
		},
		Timestamp:    b.Header.Timestamp.Unix() * 1000,
		Transactions: txs,
		Metadata: map[string]interface{}{
			"block_version":   b.Header.Version,
			"merkle_root":     b.Header.MerkleRoot.String(),
			"stake_root":      b.Header.StakeRoot.String(),
			"approves_parent": approvesParent,
			"vote_bits":       b.Header.VoteBits,
			"bits":            b.Header.Bits,
			"sbits":           b.Header.SBits,
		},
	}
	return r, nil
}
WireBlockToRosetta converts the given block in wire representation to the
block in rosetta representation. The previous block is needed when the
current block disapproved the regular transactions of the previous one, in
which case it must be specified or this function errors.
package types
import (
	"errors"
	"fmt"
	rtypes "github.com/coinbase/rosetta-sdk-go/types"
	"github.com/decred/dcrd/blockchain/stake/v3"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/chaincfg/v3"
	"github.com/decred/dcrd/dcrutil/v3"
	"github.com/decred/dcrd/wire"
)
var (
	ErrNeedsPreviousBlock = errors.New("previous block required")
)
func VoteBitsApprovesParent(voteBits uint16) bool {
	return voteBits&0x01 == 0x01
}
type PrevOutput struct {
	PkScript []byte
	Version  uint16
	Amount   dcrutil.Amount
}
type PrevOutputsFetcher func(...*wire.OutPoint) (map[wire.OutPoint]*PrevOutput, error)
type Op struct {
	Tree           int8
	Status         OpStatus
	Tx             *wire.MsgTx
	TxHash         chainhash.Hash
	TxIndex        int
	IOIndex        int
	Account        string
	AccountVersion uint16
	Type           OpType
	OpIndex        int64
	Amount         dcrutil.Amount
	In             *wire.TxIn
	Out            *wire.TxOut
	PrevOutput     *PrevOutput
}
func (op *Op) ROp() *rtypes.Operation {
	account := &rtypes.AccountIdentifier{
		Address: op.Account,
		Metadata: map[string]interface{}{
			"script_version": op.AccountVersion,
		},
	}
	var meta map[string]interface{}
	var coinChange *rtypes.CoinChange
	if op.Type == OpTypeDebit {
		meta = map[string]interface{}{
			"input_index":      op.IOIndex,
			"prev_hash":        op.In.PreviousOutPoint.Hash.String(),
			"prev_index":       op.In.PreviousOutPoint.Index,
			"prev_tree":        op.In.PreviousOutPoint.Tree,
			"sequence":         op.In.Sequence,
			"block_height":     op.In.BlockHeight,
			"block_index":      op.In.BlockIndex,
			"signature_script": op.In.SignatureScript,
		}
		coinAction := rtypes.CoinSpent
		if op.Status == OpStatusReversed {
			coinAction = rtypes.CoinCreated
		}
		if op.Account != TreasuryAccountAdddress {
			coinChange = &rtypes.CoinChange{
				CoinAction: coinAction,
				CoinIdentifier: &rtypes.CoinIdentifier{
					Identifier: op.In.PreviousOutPoint.String(),
				},
			}
		}
	} else {
		meta = map[string]interface{}{
			"output_index": op.IOIndex,
		}
		outp := wire.OutPoint{
			Hash:  op.TxHash,
			Index: uint32(op.IOIndex),
			Tree:  op.Tree,
		}
		coinAction := rtypes.CoinCreated
		if op.Status == OpStatusReversed {
			coinAction = rtypes.CoinSpent
		}
		if op.Account != TreasuryAccountAdddress {
			coinChange = &rtypes.CoinChange{
				CoinAction: coinAction,
				CoinIdentifier: &rtypes.CoinIdentifier{
					Identifier: outp.String(),
				},
			}
		}
	}
	return &rtypes.Operation{
		OperationIdentifier: &rtypes.OperationIdentifier{
			Index: op.OpIndex,
		},
		Type:       op.Type.RType(),
		Status:     op.Status.Status(),
		Account:    account,
		Amount:     DcrAmountToRosetta(op.Amount),
		Metadata:   meta,
		CoinChange: coinChange,
	}
}
type BlockOpCb = func(op *Op) error
func iterateBlockOpsInTx(op *Op, fetchPrevOuts PrevOutputsFetcher, applyOp BlockOpCb,
	chainParams *chaincfg.Params) error {
	tx := op.Tx
	isStake := op.Tree == wire.TxTreeStake
	txType := stake.DetermineTxType(tx, true)
	isVote := txType == stake.TxTypeSSGen
	isCoinbase := !isStake && txType == stake.TxTypeRegular && op.TxIndex == 0
	isTBase := isStake && txType == stake.TxTypeTreasuryBase && op.TxIndex == 0
	isTAdd := isStake && txType == stake.TxTypeTAdd
	isTSpend := isStake && txType == stake.TxTypeTSpend
	prevOutpoints := make([]*wire.OutPoint, 0, len(tx.TxIn))
	for i, in := range tx.TxIn {
		if i == 0 && (isVote || isCoinbase || isTBase || isTSpend) {
			continue
		}
		prevOutpoints = append(prevOutpoints, &in.PreviousOutPoint)
	}
	prevOutputs, err := fetchPrevOuts(prevOutpoints...)
	if err != nil {
		return err
	}
	var ok bool
	addTxIns := func() error {
		op.Out = nil
		for i, in := range tx.TxIn {
			if i == 0 && (isVote || isCoinbase || isTBase) {
				continue
			}
			if isTSpend {
				op.PrevOutput = nil
				op.AccountVersion = 0
				op.Account = TreasuryAccountAdddress
				op.Amount = -dcrutil.Amount(in.ValueIn)
			} else {
				op.PrevOutput, ok = prevOutputs[in.PreviousOutPoint]
				if !ok {
					return fmt.Errorf("missing prev outpoint %s", in.PreviousOutPoint)
				}
				op.AccountVersion = op.PrevOutput.Version
				op.Account, err = dcrPkScriptToAccountAddr(op.PrevOutput.Version,
					op.PrevOutput.PkScript, chainParams)
				if err != nil {
					return err
				}
				if op.Account == "" {
					continue
				}
				op.Amount = -op.PrevOutput.Amount
			}
			op.IOIndex = i
			op.In = in
			op.Type = OpTypeDebit
			if op.Status == OpStatusReversed {
				op.Amount *= -1
			}
			if err := applyOp(op); err != nil {
				return err
			}
			op.OpIndex += 1
		}
		return nil
	}
	addTxOuts := func() error {
		op.In = nil
		op.PrevOutput = nil
		for i, out := range tx.TxOut {
			if out.Value == 0 {
				continue
			}
			op.AccountVersion = out.Version
			if (isTAdd || isTBase) && i == 0 {
				op.Account = TreasuryAccountAdddress
			} else {
				op.Account, err = dcrPkScriptToAccountAddr(out.Version,
					out.PkScript, chainParams)
				if err != nil {
					return err
				}
				if op.Account == "" {
					continue
				}
			}
			op.IOIndex = i
			op.Out = out
			op.Type = OpTypeCredit
			op.Amount = dcrutil.Amount(out.Value)
			if op.Status == OpStatusReversed {
				op.Amount *= -1
			}
			if err := applyOp(op); err != nil {
				return err
			}
			op.OpIndex += 1
		}
		return nil
	}
	if op.Status == OpStatusSuccess {
		if err := addTxIns(); err != nil {
			return err
		}
		if err := addTxOuts(); err != nil {
			return err
		}
	} else {
		if err := addTxOuts(); err != nil {
			return err
		}
		if err := addTxIns(); err != nil {
			return err
		}
	}
	return nil
}
func IterateBlockOps(b, prev *wire.MsgBlock, fetchPrevOuts PrevOutputsFetcher,
	applyOp BlockOpCb, chainParams *chaincfg.Params) error {
	approvesParent := VoteBitsApprovesParent(b.Header.VoteBits) || b.Header.Height == 0
	if !approvesParent && prev == nil {
		return ErrNeedsPreviousBlock
	}
	var op Op
	applyTxs := func(tree int8, status OpStatus, txs []*wire.MsgTx) error {
		op = Op{
			Tree:   tree,
			Status: status,
		}
		for i, tx := range txs {
			op.Tx = tx
			op.TxHash = tx.TxHash()
			op.TxIndex = i
			op.OpIndex = 0
			err := iterateBlockOpsInTx(&op, fetchPrevOuts, applyOp,
				chainParams)
			if err != nil {
				return err
			}
		}
		return nil
	}
	if !approvesParent {
		if err := applyTxs(wire.TxTreeRegular, OpStatusReversed, prev.Transactions); err != nil {
			return err
		}
	}
	if err := applyTxs(wire.TxTreeRegular, OpStatusSuccess, b.Transactions); err != nil {
		return err
	}
	if err := applyTxs(wire.TxTreeStake, OpStatusSuccess, b.STransactions); err != nil {
		return err
	}
	return nil
}
func txMetaToRosetta(tx *wire.MsgTx, txHash, blockHash *chainhash.Hash) *rtypes.Transaction {
	var txId string
	meta := map[string]interface{}{
		"version":  tx.Version,
		"expiry":   tx.Expiry,
		"locktime": tx.LockTime,
	}
	switch {
	case blockHash != nil:
		txId = fmt.Sprintf("%s:%s", blockHash.String(), txHash.String())
		meta["original_tx_hash"] = txHash.String()
	default:
		txId = txHash.String()
	}
	return &rtypes.Transaction{
		TransactionIdentifier: &rtypes.TransactionIdentifier{
			Hash: txId,
		},
		Operations: []*rtypes.Operation{},
		Metadata:   meta,
	}
}