@@ -227,6 +227,21 @@ func TestDerivativeWallet(t *testing.T) {
227227 })
228228 })
229229
230+ t .Run ("Send" , func (t * testing.T ) {
231+ t .Run ("Success" , func (t * testing.T ) { testSend (t , rskAccount , existingAddressInfo ) })
232+ t .Run ("Error handling on btc tx sending" , func (t * testing.T ) {
233+ cases := derivativeWalletSendErrorSetups (rskAccount )
234+ for _ , testCase := range cases {
235+ client := & mocks.ClientAdapterMock {}
236+ client .On ("GetWalletInfo" ).Return (& btcjson.GetWalletInfoResult {WalletName : bitcoin .DerivativeWalletId , Scanning : btcjson.ScanningOrFalse {Value : false }}, nil ).Twice ()
237+ client .On ("GetAddressInfo" , btcAddress ).Return (existingAddressInfo , nil ).Once ()
238+ t .Run (testCase .description , func (t * testing.T ) {
239+ testCase .setup (t , client )
240+ })
241+ }
242+ })
243+ })
244+
230245 t .Run ("SendWithOpReturn" , func (t * testing.T ) {
231246 t .Run ("Success" , func (t * testing.T ) { testSendWithOpReturn (t , rskAccount , existingAddressInfo ) })
232247 t .Run ("Error handling on btc tx sending" , func (t * testing.T ) {
@@ -468,6 +483,192 @@ func testEstimateFeesExtra(t *testing.T, rskAccount *account.RskAccount, address
468483 client .AssertExpectations (t )
469484}
470485
486+ func testSend (t * testing.T , rskAccount * account.RskAccount , addressInfo * btcjson.GetAddressInfoResult ) {
487+ client := & mocks.ClientAdapterMock {}
488+ value := entities .NewWei (600000000000000000 )
489+ satoshis , _ := value .ToSatoshi ().Float64 ()
490+ address , err := btcutil .DecodeAddress (testnetAddress , & chaincfg .TestNet3Params )
491+ require .NoError (t , err )
492+ client .On ("GetWalletInfo" ).Return (& btcjson.GetWalletInfoResult {WalletName : bitcoin .DerivativeWalletId , Scanning : btcjson.ScanningOrFalse {Value : false }}, nil ).Twice ()
493+ client .On ("GetAddressInfo" , btcAddress ).Return (addressInfo , nil ).Once ()
494+ client .On ("CreateRawTransaction" ,
495+ ([]btcjson.TransactionInput )(nil ),
496+ mock .MatchedBy (func (outputs map [btcutil.Address ]btcutil.Amount ) bool {
497+ for k , v := range outputs {
498+ require .Equal (t , address , k )
499+ require .Equal (t , btcutil .Amount (satoshis ), v )
500+ }
501+ return len (outputs ) == 1
502+ }),
503+ (* int64 )(nil ),
504+ ).Return (& wire.MsgTx {
505+ Version : 0 ,
506+ TxIn : nil ,
507+ TxOut : []* wire.TxOut {{Value : int64 (satoshis ), PkScript : []byte (paymentScriptMock )}},
508+ LockTime : 0 ,
509+ }, nil ).Once ()
510+ tx := & wire.MsgTx {
511+ Version : 0 ,
512+ TxIn : nil ,
513+ TxOut : []* wire.TxOut {{Value : int64 (satoshis ), PkScript : []byte (paymentScriptMock )}},
514+ LockTime : 0 ,
515+ }
516+ client .On ("EstimateSmartFee" , int64 (1 ), & btcjson .EstimateModeEconomical ).Return (& btcjson.EstimateSmartFeeResult {FeeRate : btcjson .Float64 (feeRate ), Blocks : 2 }, nil ).Once ()
517+ client .On ("FundRawTransaction" , tx , btcjson.FundRawTransactionOpts {
518+ ChangeAddress : btcjson .String (btcAddress ),
519+ ChangePosition : btcjson .Int (1 ),
520+ IncludeWatching : btcjson .Bool (true ),
521+ LockUnspents : btcjson .Bool (true ),
522+ FeeRate : btcjson .Float64 (feeRate ),
523+ Replaceable : btcjson .Bool (true ),
524+ }, (* bool )(nil )).Return (& btcjson.FundRawTransactionResult {Transaction : tx , Fee : 50 , ChangePosition : 1 }, nil ).Once ()
525+ client .On ("SendRawTransaction" , tx , false ).Return (chainhash .NewHashFromStr (testnetTestTxHash )).Once ()
526+ client .On ("SignRawTransactionWithKey" , tx , mock .MatchedBy (func (pks []string ) bool {
527+ return len (pks ) == 1 && pks [0 ] != ""
528+ })).Return (tx , true , nil ).Once ()
529+ wallet , err := bitcoin .NewDerivativeWallet (bitcoin .NewWalletConnection (& chaincfg .TestNet3Params , client , bitcoin .DerivativeWalletId ), rskAccount )
530+ require .NoError (t , err )
531+ result , err := wallet .Send (testnetAddress , value )
532+ require .NoError (t , err )
533+ assert .NotEmpty (t , result .Hash )
534+ assert .NotNil (t , result .Fee )
535+ expectedFee := entities .SatoshiToWei (50 )
536+ assert .Equal (t , expectedFee , result .Fee )
537+ client .AssertExpectations (t )
538+ }
539+
540+ // nolint:funlen
541+ func derivativeWalletSendErrorSetups (rskAccount * account.RskAccount ) []struct {
542+ description string
543+ setup func (t * testing.T , client * mocks.ClientAdapterMock )
544+ } {
545+ rawTx := & wire.MsgTx {TxOut : []* wire.TxOut {{Value : int64 (50000000 ), PkScript : []byte (paymentScriptMock )}}}
546+ return []struct {
547+ description string
548+ setup func (t * testing.T , client * mocks.ClientAdapterMock )
549+ }{
550+ {
551+ description : "error parsing address" ,
552+ setup : func (t * testing.T , client * mocks.ClientAdapterMock ) {
553+ wallet , err := bitcoin .NewDerivativeWallet (bitcoin .NewWalletConnection (& chaincfg .TestNet3Params , client , bitcoin .DerivativeWalletId ), rskAccount )
554+ require .NoError (t , err )
555+ result , err := wallet .Send (test .AnyString , entities .NewWei (1 ))
556+ require .Error (t , err )
557+ assert .Empty (t , result .Hash )
558+ assert .Nil (t , result .Fee )
559+ client .AssertExpectations (t )
560+ },
561+ },
562+ {
563+ description : "error creating raw tx" ,
564+ setup : func (t * testing.T , client * mocks.ClientAdapterMock ) {
565+ client .On ("CreateRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (nil , assert .AnError ).Once ()
566+ wallet , err := bitcoin .NewDerivativeWallet (bitcoin .NewWalletConnection (& chaincfg .TestNet3Params , client , bitcoin .DerivativeWalletId ), rskAccount )
567+ require .NoError (t , err )
568+ result , err := wallet .Send (testnetAddress , entities .NewWei (1 ))
569+ require .Error (t , err )
570+ assert .Empty (t , result .Hash )
571+ assert .Nil (t , result .Fee )
572+ client .AssertExpectations (t )
573+ },
574+ },
575+ {
576+ description : "error estimating fees" ,
577+ setup : func (t * testing.T , client * mocks.ClientAdapterMock ) {
578+ client .On ("CreateRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (rawTx , nil ).Once ()
579+ client .On ("EstimateSmartFee" , mock .Anything , mock .Anything ).Return (nil , assert .AnError ).Once ()
580+ wallet , err := bitcoin .NewDerivativeWallet (bitcoin .NewWalletConnection (& chaincfg .TestNet3Params , client , bitcoin .DerivativeWalletId ), rskAccount )
581+ require .NoError (t , err )
582+ result , err := wallet .Send (testnetAddress , entities .NewWei (1 ))
583+ require .Error (t , err )
584+ assert .Empty (t , result .Hash )
585+ assert .Nil (t , result .Fee )
586+ client .AssertExpectations (t )
587+ },
588+ },
589+ {
590+ description : "error estimating fees (RPC error)" ,
591+ setup : func (t * testing.T , client * mocks.ClientAdapterMock ) {
592+ client .On ("CreateRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (rawTx , nil ).Once ()
593+ client .On ("EstimateSmartFee" , mock .Anything , mock .Anything ).Return (& btcjson.EstimateSmartFeeResult {
594+ Errors : []string {assert .AnError .Error ()},
595+ }, nil ).Once ()
596+ wallet , err := bitcoin .NewDerivativeWallet (bitcoin .NewWalletConnection (& chaincfg .TestNet3Params , client , bitcoin .DerivativeWalletId ), rskAccount )
597+ require .NoError (t , err )
598+ result , err := wallet .Send (testnetAddress , entities .NewWei (1 ))
599+ require .Error (t , err )
600+ assert .Empty (t , result .Hash )
601+ assert .Nil (t , result .Fee )
602+ client .AssertExpectations (t )
603+ },
604+ },
605+ {
606+ description : "error funding raw tx" ,
607+ setup : func (t * testing.T , client * mocks.ClientAdapterMock ) {
608+ client .On ("CreateRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (rawTx , nil ).Once ()
609+ client .On ("EstimateSmartFee" , mock .Anything , mock .Anything ).Return (& btcjson.EstimateSmartFeeResult {FeeRate : btcjson .Float64 (feeRate ), Blocks : 1 }, nil ).Once ()
610+ client .On ("FundRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (nil , assert .AnError ).Once ()
611+ wallet , err := bitcoin .NewDerivativeWallet (bitcoin .NewWalletConnection (& chaincfg .TestNet3Params , client , bitcoin .DerivativeWalletId ), rskAccount )
612+ require .NoError (t , err )
613+ result , err := wallet .Send (testnetAddress , entities .NewWei (1 ))
614+ require .Error (t , err )
615+ assert .Empty (t , result .Hash )
616+ assert .Nil (t , result .Fee )
617+ client .AssertExpectations (t )
618+ },
619+ },
620+ {
621+ description : "error signing tx" ,
622+ setup : func (t * testing.T , client * mocks.ClientAdapterMock ) {
623+ client .On ("CreateRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (rawTx , nil ).Once ()
624+ client .On ("EstimateSmartFee" , mock .Anything , mock .Anything ).Return (& btcjson.EstimateSmartFeeResult {FeeRate : btcjson .Float64 (feeRate ), Blocks : 1 }, nil ).Once ()
625+ client .On ("FundRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (& btcjson.FundRawTransactionResult {Transaction : rawTx , Fee : 50 , ChangePosition : 1 }, nil ).Once ()
626+ client .On ("SignRawTransactionWithKey" , mock .Anything , mock .Anything ).Return (nil , false , assert .AnError ).Once ()
627+ wallet , err := bitcoin .NewDerivativeWallet (bitcoin .NewWalletConnection (& chaincfg .TestNet3Params , client , bitcoin .DerivativeWalletId ), rskAccount )
628+ require .NoError (t , err )
629+ result , err := wallet .Send (testnetAddress , entities .NewWei (1 ))
630+ require .Error (t , err )
631+ assert .Empty (t , result .Hash )
632+ assert .Nil (t , result .Fee )
633+ client .AssertExpectations (t )
634+ },
635+ },
636+ {
637+ description : "error signing tx (incomplete signatures)" ,
638+ setup : func (t * testing.T , client * mocks.ClientAdapterMock ) {
639+ client .On ("CreateRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (rawTx , nil ).Once ()
640+ client .On ("EstimateSmartFee" , mock .Anything , mock .Anything ).Return (& btcjson.EstimateSmartFeeResult {FeeRate : btcjson .Float64 (feeRate ), Blocks : 1 }, nil ).Once ()
641+ client .On ("FundRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (& btcjson.FundRawTransactionResult {Transaction : rawTx , Fee : 50 , ChangePosition : 1 }, nil ).Once ()
642+ client .On ("SignRawTransactionWithKey" , mock .Anything , mock .Anything ).Return (rawTx , false , nil ).Once ()
643+ wallet , err := bitcoin .NewDerivativeWallet (bitcoin .NewWalletConnection (& chaincfg .TestNet3Params , client , bitcoin .DerivativeWalletId ), rskAccount )
644+ require .NoError (t , err )
645+ result , err := wallet .Send (testnetAddress , entities .NewWei (1 ))
646+ require .Error (t , err )
647+ assert .Empty (t , result .Hash )
648+ assert .Nil (t , result .Fee )
649+ client .AssertExpectations (t )
650+ },
651+ },
652+ {
653+ description : "error sending tx" ,
654+ setup : func (t * testing.T , client * mocks.ClientAdapterMock ) {
655+ client .On ("CreateRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (rawTx , nil ).Once ()
656+ client .On ("EstimateSmartFee" , mock .Anything , mock .Anything ).Return (& btcjson.EstimateSmartFeeResult {FeeRate : btcjson .Float64 (feeRate ), Blocks : 1 }, nil ).Once ()
657+ client .On ("FundRawTransaction" , mock .Anything , mock .Anything , mock .Anything ).Return (& btcjson.FundRawTransactionResult {Transaction : rawTx , Fee : 50 , ChangePosition : 1 }, nil ).Once ()
658+ client .On ("SignRawTransactionWithKey" , mock .Anything , mock .Anything ).Return (rawTx , true , nil ).Once ()
659+ client .On ("SendRawTransaction" , mock .Anything , mock .Anything ).Return (nil , assert .AnError ).Once ()
660+ wallet , err := bitcoin .NewDerivativeWallet (bitcoin .NewWalletConnection (& chaincfg .TestNet3Params , client , bitcoin .DerivativeWalletId ), rskAccount )
661+ require .NoError (t , err )
662+ result , err := wallet .Send (testnetAddress , entities .NewWei (1 ))
663+ require .Error (t , err )
664+ assert .Empty (t , result .Hash )
665+ assert .Nil (t , result .Fee )
666+ client .AssertExpectations (t )
667+ },
668+ },
669+ }
670+ }
671+
471672func testSendWithOpReturn (t * testing.T , rskAccount * account.RskAccount , addressInfo * btcjson.GetAddressInfoResult ) {
472673 client := & mocks.ClientAdapterMock {}
473674 value := entities .NewWei (600000000000000000 )
0 commit comments