@@ -56,7 +56,7 @@ type requestMethod string
56
56
57
57
const (
58
58
// https://bitcoin.org/en/developer-reference#getblock
59
- requestMethodGetBlock requestMethod = "getblock "
59
+ requestMethodGetBlock requestMethod = "getblockexpanded "
60
60
61
61
// https://bitcoin.org/en/developer-reference#getblockhash
62
62
requestMethodGetBlockHash requestMethod = "getblockhash"
@@ -221,24 +221,36 @@ func (b *Client) GetRawBlock(
221
221
coins := []string {}
222
222
blockTxHashes := []string {}
223
223
for txIndex , tx := range block .Txs {
224
- blockTxHashes = append (blockTxHashes , tx .Hash )
225
- for inputIndex , input := range tx .Inputs {
226
- txHash , vout , ok := b .getInputTxHash (input , txIndex , inputIndex )
227
- if ! ok {
228
- continue
229
- }
224
+ coins , blockTxHashes = addCoins (txIndex , blockTxHashes , tx .Hash , tx .Inputs , b , coins )
225
+ }
230
226
231
- // If any transactions spent in the same block they are created, don't include them
232
- // in previousTxHashes to fetch.
233
- if ! utils .ContainsString (blockTxHashes , txHash ) {
234
- coins = append (coins , CoinIdentifier (txHash , vout ))
235
- }
236
- }
227
+ blockCertTxHashes := []string {}
228
+ for certTxIndex , certTx := range block .Certs {
229
+ coins , blockCertTxHashes = addCoins (certTxIndex , blockCertTxHashes , certTx .Hash , certTx .Inputs , b , coins )
237
230
}
238
231
239
232
return block , coins , nil
240
233
}
241
234
235
+
236
+ func addCoins (txIndex int , blockTxHashes []string , hash string , inputs []* Input , b * Client , coins []string ) ([]string , []string ) {
237
+ blockTxHashes = append (blockTxHashes , hash )
238
+ for inputIndex , input := range inputs {
239
+ txHash , vout , ok := b .getInputTxHash (input , txIndex , inputIndex )
240
+ if ! ok {
241
+ continue
242
+ }
243
+
244
+ // If any transactions spent in the same block they are created, don't include them
245
+ // in previousTxHashes to fetch.
246
+ if ! utils .ContainsString (blockTxHashes , txHash ) {
247
+ coins = append (coins , CoinIdentifier (txHash , vout ))
248
+ }
249
+ }
250
+
251
+ return coins , blockTxHashes
252
+ }
253
+
242
254
// ParseBlock returns a parsed bitcoin block given a raw bitcoin
243
255
// block and a map of transactions containing inputs.
244
256
func (b * Client ) ParseBlock (
@@ -493,9 +505,9 @@ func (b *Client) parseTransactions(
493
505
if block == nil {
494
506
return nil , errors .New ("error parsing nil block" )
495
507
}
496
- txs := make ([]* types.Transaction , len (block .Txs ))
508
+ txs := make ([]* types.Transaction , len (block .Txs ) + len ( block . Certs ) )
497
509
for index , transaction := range block .Txs {
498
- txOps , err := b .parseTxOperations (transaction , index , coins )
510
+ txOps , err := b .parseTxOperations (transaction . Inputs , transaction . Outputs , transaction . Hash , index , coins , false )
499
511
if err != nil {
500
512
return nil , fmt .Errorf ("%w: error parsing transaction operations" , err )
501
513
}
@@ -515,41 +527,95 @@ func (b *Client) parseTransactions(
515
527
516
528
txs [index ] = tx
517
529
518
- // In some cases, a transaction will spent an output
519
- // from the same block.
520
- for _ , op := range tx .Operations {
521
- if op .CoinChange == nil {
522
- continue
523
- }
530
+ coins = addCoinsFromSameBlock (tx .Operations , coins )
531
+ }
524
532
525
- if op .CoinChange .CoinAction != types .CoinCreated {
526
- continue
527
- }
533
+ for index , certificate := range block .Certs {
534
+ txIndex := len (block .Txs ) + index ;
535
+ certTxOps , err := b .parseTxOperations (certificate .Inputs , certificate .Outputs , certificate .Hash , txIndex , coins , true )
536
+ if err != nil {
537
+ return nil , fmt .Errorf ("%w: error parsing certificate transaction operations" , err )
538
+ }
539
+
540
+ tx := & types.Transaction {
541
+ TransactionIdentifier : & types.TransactionIdentifier {
542
+ Hash : certificate .Hash ,
543
+ },
544
+ Operations : certTxOps ,
545
+ }
546
+
547
+ txs [txIndex ] = tx
548
+
549
+ coins = addCoinsFromSameBlock (tx .Operations , coins )
550
+ }
528
551
529
- coins [op .CoinChange .CoinIdentifier .Identifier ] = & storage.AccountCoin {
530
- Coin : & types.Coin {
531
- CoinIdentifier : op .CoinChange .CoinIdentifier ,
532
- Amount : op .Amount ,
533
- },
534
- Account : op .Account ,
552
+ for index , certificate := range block .MaturedCerts {
553
+ // For matured certificates, we only parse outputs that are backward transfers
554
+ backwardTransferOutputs := []* Output {}
555
+
556
+ for i := range certificate .Outputs {
557
+ if certificate .Outputs [i ].BackwardTransfer == true {
558
+ backwardTransferOutputs = append (backwardTransferOutputs , certificate .Outputs [i ])
535
559
}
536
560
}
561
+
562
+ certTxOps , err := b .parseTxOperations ([]* Input {}, backwardTransferOutputs , certificate .Hash , len (block .Txs ) + len (block .Certs ) + index , coins , false )
563
+ if err != nil {
564
+ return nil , fmt .Errorf ("%w: error parsing mature certificate transaction operations" , err )
565
+ }
566
+
567
+ tx := & types.Transaction {
568
+ TransactionIdentifier : & types.TransactionIdentifier {
569
+ Hash : certificate .Hash ,
570
+ },
571
+ Operations : certTxOps ,
572
+ }
573
+
574
+ txs = append (txs , tx )
575
+
576
+ coins = addCoinsFromSameBlock (tx .Operations , coins )
537
577
}
538
578
539
579
return txs , nil
580
+ }
540
581
582
+ func addCoinsFromSameBlock (operations []* types.Operation , coins map [string ]* storage.AccountCoin ) map [string ]* storage.AccountCoin {
583
+ // In some cases, a transaction will spent an output
584
+ // from the same block.
585
+ for _ , op := range operations {
586
+ if op .CoinChange == nil {
587
+ continue
588
+ }
589
+
590
+ if op .CoinChange .CoinAction != types .CoinCreated {
591
+ continue
592
+ }
593
+
594
+ coins [op .CoinChange .CoinIdentifier .Identifier ] = & storage.AccountCoin {
595
+ Coin : & types.Coin {
596
+ CoinIdentifier : op .CoinChange .CoinIdentifier ,
597
+ Amount : op .Amount ,
598
+ },
599
+ Account : op .Account ,
600
+ }
601
+ }
602
+
603
+ return coins
541
604
}
542
605
543
606
// parseTransactions returns the transaction operations for a specified transaction.
544
607
// It uses a map of previous transactions to properly hydrate the input operations.
545
608
func (b * Client ) parseTxOperations (
546
- tx * Transaction ,
609
+ inputs []* Input ,
610
+ outputs []* Output ,
611
+ hash string ,
547
612
txIndex int ,
548
613
coins map [string ]* storage.AccountCoin ,
614
+ isImmatureCertificate bool ,
549
615
) ([]* types.Operation , error ) {
550
616
txOps := []* types.Operation {}
551
617
552
- for networkIndex , input := range tx . Inputs {
618
+ for networkIndex , input := range inputs {
553
619
if bitcoinIsCoinbaseInput (input , txIndex , networkIndex ) {
554
620
txOp , err := b .coinbaseTxOperation (input , int64 (len (txOps )), int64 (networkIndex ))
555
621
if err != nil {
@@ -566,7 +632,7 @@ func (b *Client) parseTxOperations(
566
632
return nil , fmt .Errorf (
567
633
"error finding previous tx: %s, for tx: %s, input index: %d" ,
568
634
input .TxHash ,
569
- tx . Hash ,
635
+ hash ,
570
636
networkIndex ,
571
637
)
572
638
}
@@ -585,20 +651,26 @@ func (b *Client) parseTxOperations(
585
651
txOps = append (txOps , txOp )
586
652
}
587
653
588
- for networkIndex , output := range tx .Outputs {
654
+ for _ , output := range outputs {
655
+ if isImmatureCertificate == true && output .BackwardTransfer == true {
656
+ continue
657
+ }
658
+
659
+ outputIndex := int64 (output .Index )
660
+
589
661
txOp , err := b .parseOutputTransactionOperation (
590
662
output ,
591
- tx . Hash ,
663
+ hash ,
592
664
int64 (len (txOps )),
593
- int64 ( networkIndex ) ,
665
+ outputIndex ,
594
666
txIndex ,
595
667
)
596
668
if err != nil {
597
669
return nil , fmt .Errorf (
598
670
"%w: error parsing tx output, hash: %s, index: %d" ,
599
671
err ,
600
- tx . Hash ,
601
- networkIndex ,
672
+ hash ,
673
+ outputIndex ,
602
674
)
603
675
}
604
676
@@ -651,7 +723,7 @@ func (b *Client) parseOutputTransactionOperation(
651
723
account .Address = fmt .Sprintf ("%s:%d" , txHash , networkIndex )
652
724
}
653
725
654
- //if it's a coinbase output and we are not in regtest populate SubAccount field
726
+ // if it's a coinbase output and we are not in regtest populate SubAccount field
655
727
if txIndex == 0 && b .genesisBlockIdentifier .Hash != RegtestGenesisBlockIdentifier .Hash {
656
728
account .SubAccount = & types.SubAccountIdentifier {
657
729
Address : "coinbase" ,
0 commit comments