package lnrpc import ( "encoding/hex" "errors" "sort" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/lightningnetwork/lnd/lnwallet" ) const ( // RegisterRPCMiddlewareURI is the full RPC method URI for the // middleware registration call. This is declared here rather than where // it's mainly used to avoid circular package dependencies. RegisterRPCMiddlewareURI = "/lnrpc.Lightning/RegisterRPCMiddleware" ) // RPCTransaction returns a rpc transaction. func RPCTransaction(tx *lnwallet.TransactionDetail) *Transaction { var destAddresses []string // Re-package destination output information. var outputDetails []*OutputDetail for _, o := range tx.OutputDetails { // Note: DestAddresses is deprecated but we keep // populating it with addresses for backwards // compatibility. for _, a := range o.Addresses { destAddresses = append(destAddresses, a.EncodeAddress()) } var address string if len(o.Addresses) == 1 { address = o.Addresses[0].EncodeAddress() } outputDetails = append(outputDetails, &OutputDetail{ OutputType: MarshallOutputType(o.OutputType), Address: address, PkScript: hex.EncodeToString(o.PkScript), OutputIndex: int64(o.OutputIndex), Amount: int64(o.Value), IsOurAddress: o.IsOurAddress, }) } previousOutpoints := make([]*PreviousOutPoint, len(tx.PreviousOutpoints)) for idx, previousOutPoint := range tx.PreviousOutpoints { previousOutpoints[idx] = &PreviousOutPoint{ Outpoint: previousOutPoint.OutPoint, IsOurOutput: previousOutPoint.IsOurOutput, } } // We also get unconfirmed transactions, so BlockHash can be nil. blockHash := "" if tx.BlockHash != nil { blockHash = tx.BlockHash.String() } return &Transaction{ TxHash: tx.Hash.String(), Amount: int64(tx.Value), NumConfirmations: tx.NumConfirmations, BlockHash: blockHash, BlockHeight: tx.BlockHeight, TimeStamp: tx.Timestamp, TotalFees: tx.TotalFees, DestAddresses: destAddresses, OutputDetails: outputDetails, RawTxHex: hex.EncodeToString(tx.RawTx), Label: tx.Label, PreviousOutpoints: previousOutpoints, } } // RPCTransactionDetails returns a set of rpc transaction details. func RPCTransactionDetails(txns []*lnwallet.TransactionDetail) *TransactionDetails { txDetails := &TransactionDetails{ Transactions: make([]*Transaction, len(txns)), } for i, tx := range txns { txDetails.Transactions[i] = RPCTransaction(tx) } // Sort transactions by number of confirmations rather than height so // that unconfirmed transactions (height =0; confirmations =-1) will // follow the most recently set of confirmed transactions. If we sort // by height, unconfirmed transactions will follow our oldest // transactions, because they have lower block heights. sort.Slice(txDetails.Transactions, func(i, j int) bool { return txDetails.Transactions[i].NumConfirmations < txDetails.Transactions[j].NumConfirmations }) return txDetails } // ExtractMinConfs extracts the minimum number of confirmations that each // output used to fund a transaction should satisfy. func ExtractMinConfs(minConfs int32, spendUnconfirmed bool) (int32, error) { switch { // Ensure that the MinConfs parameter is non-negative. case minConfs < 0: return 0, errors.New("minimum number of confirmations must " + "be a non-negative number") // The transaction should not be funded with unconfirmed outputs // unless explicitly specified by SpendUnconfirmed. We do this to // provide sane defaults to the OpenChannel RPC, as otherwise, if the // MinConfs field isn't explicitly set by the caller, we'll use // unconfirmed outputs without the caller being aware. case minConfs == 0 && !spendUnconfirmed: return 1, nil // In the event that the caller set MinConfs > 0 and SpendUnconfirmed to // true, we'll return an error to indicate the conflict. case minConfs > 0 && spendUnconfirmed: return 0, errors.New("SpendUnconfirmed set to true with " + "MinConfs > 0") // The funding transaction of the new channel to be created can be // funded with unconfirmed outputs. case spendUnconfirmed: return 0, nil // If none of the above cases matched, we'll return the value set // explicitly by the caller. default: return minConfs, nil } } // GetChanPointFundingTxid returns the given channel point's funding txid in // raw bytes. func GetChanPointFundingTxid(chanPoint *ChannelPoint) (*chainhash.Hash, error) { var txid []byte // A channel point's funding txid can be get/set as a byte slice or a // string. In the case it is a string, decode it. switch chanPoint.GetFundingTxid().(type) { case *ChannelPoint_FundingTxidBytes: txid = chanPoint.GetFundingTxidBytes() case *ChannelPoint_FundingTxidStr: s := chanPoint.GetFundingTxidStr() h, err := chainhash.NewHashFromStr(s) if err != nil { return nil, err } txid = h[:] } return chainhash.NewHash(txid) }