// This example demonstrates creating a script which pays to a decred address. // It also prints the created script hex and uses the DisasmString function to // display the disassembled script. func ExamplePayToAddrScript() { // Parse the address to send the coins to into a dcrutil.Address // which is useful to ensure the accuracy of the address and determine // the address type. It is also required for the upcoming call to // PayToAddrScript. addressStr := "DsSej1qR3Fyc8kV176DCh9n9cY9nqf9Quxk" address, err := dcrutil.DecodeAddress(addressStr, &chaincfg.MainNetParams) if err != nil { fmt.Println(err) return } // Create a public key script that pays to the address. script, err := txscript.PayToAddrScript(address) if err != nil { fmt.Println(err) return } fmt.Printf("Script Hex: %x\n", script) disasm, err := txscript.DisasmString(script) if err != nil { fmt.Println(err) return } fmt.Println("Script Disassembly:", disasm) // Output: // Script Hex: 76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac // Script Disassembly: OP_DUP OP_HASH160 128004ff2fcaf13b2b91eb654b1dc2b674f7ec61 OP_EQUALVERIFY OP_CHECKSIG }
// TstCreateSeriesCredits creates a new credit for every item in the amounts // slice, locked to the given series' address with branch==1 and index==0. func TstCreateSeriesCredits(t *testing.T, pool *Pool, seriesID uint32, amounts []int64) []credit { addr := TstNewWithdrawalAddress(t, pool, seriesID, Branch(1), Index(0)) pkScript, err := txscript.PayToAddrScript(addr.addr) if err != nil { t.Fatal(err) } msgTx := createMsgTx(pkScript, amounts) txSha := msgTx.TxSha() credits := make([]credit, len(amounts)) for i := range msgTx.TxOut { c := wtxmgr.Credit{ OutPoint: wire.OutPoint{ Hash: txSha, Index: uint32(i), }, BlockMeta: wtxmgr.BlockMeta{ Block: wtxmgr.Block{Height: TstInputsBlock}, }, Amount: dcrutil.Amount(msgTx.TxOut[i].Value), PkScript: msgTx.TxOut[i].PkScript, } credits[i] = newCredit(c, *addr) } return credits }
// makeDestinationScriptSource creates a ChangeSource which is used to receive // all correlated previous input value. A non-change address is created by this // function. func makeDestinationScriptSource(rpcClient *dcrrpcclient.Client, accountName string) txauthor.ChangeSource { return func() ([]byte, error) { destinationAddress, err := rpcClient.GetNewAddress(accountName) if err != nil { return nil, err } return txscript.PayToAddrScript(destinationAddress) } }
func TestFakeTxs(t *testing.T) { // First we need a wallet. w, err := keystore.NewStore("banana wallet", "", []byte("banana"), wire.MainNet, &keystore.BlockStamp{}, 100) if err != nil { t.Errorf("Can not create encrypted wallet: %s", err) return } a := &Wallet{ Wallet: w, lockedOutpoints: map[wire.OutPoint]struct{}{}, } w.Unlock([]byte("banana")) // Create and add a fake Utxo so we have some funds to spend. // // This will pass validation because txcscript is unaware of invalid // tx inputs, however, this example would fail in dcrd. utxo := &tx.Utxo{} addr, err := w.NextChainedAddress(&keystore.BlockStamp{}, 100) if err != nil { t.Errorf("Cannot get next address: %s", err) return } copy(utxo.AddrHash[:], addr.ScriptAddress()) ophash := (chainhash.Hash)([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}) out := wire.NewOutPoint(&ophash, 0) utxo.Out = tx.OutPoint(*out) ss, err := txscript.PayToAddrScript(addr) if err != nil { t.Errorf("Could not create utxo PkScript: %s", err) return } utxo.Subscript = tx.PkScript(ss) utxo.Amt = 1000000 utxo.Height = 12345 a.UtxoStore = append(a.UtxoStore, utxo) // Fake our current block height so dcrd doesn't need to be queried. curBlock.BlockStamp.Height = 12346 // Create the transaction. pairs := map[string]int64{ "17XhEvq9Nahdj7Xe1nv6oRe1tEmaHUuynH": 5000, } _, err = a.txToPairs(pairs, 1) if err != nil { t.Errorf("Tx creation failed: %s", err) return } }
func TstCreatePkScript(t *testing.T, p *Pool, seriesID uint32, branch Branch, idx Index) []byte { script := TstEnsureUsedAddr(t, p, seriesID, branch, idx) addr, err := p.addressFor(script) if err != nil { t.Fatal(err) } pkScript, err := txscript.PayToAddrScript(addr) if err != nil { t.Fatal(err) } return pkScript }
func TestSignMultiSigUTXOPkScriptNotP2SH(t *testing.T) { tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() mgr := pool.Manager() tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{}) addr, _ := dcrutil.DecodeAddress("1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX", mgr.ChainParams()) pubKeyHashPkScript, _ := txscript.PayToAddrScript(addr.(*dcrutil.AddressPubKeyHash)) msgtx := tx.toMsgTx() err := signMultiSigUTXO(mgr, msgtx, 0, pubKeyHashPkScript, []RawSig{RawSig{}}) TstCheckError(t, "", err, ErrTxSigning) }
// finalizeCurrentTx finalizes the transaction in w.current, moves it to the // list of finalized transactions and replaces w.current with a new empty // transaction. func (w *withdrawal) finalizeCurrentTx() error { log.Debug("Finalizing current transaction") tx := w.current if len(tx.outputs) == 0 { log.Debug("Current transaction has no outputs, doing nothing") return nil } pkScript, err := txscript.PayToAddrScript(w.status.nextChangeAddr.addr) if err != nil { return newError(ErrWithdrawalProcessing, "failed to generate pkScript for change address", err) } if tx.addChange(pkScript) { var err error w.status.nextChangeAddr, err = nextChangeAddress(w.status.nextChangeAddr) if err != nil { return newError(ErrWithdrawalProcessing, "failed to get next change address", err) } } ntxid := tx.ntxid() for i, txOut := range tx.outputs { outputStatus := w.status.outputs[txOut.request.outBailmentID()] outputStatus.addOutpoint( OutBailmentOutpoint{ntxid: ntxid, index: uint32(i), amount: txOut.amount}) } // Check that WithdrawalOutput entries with status==success have the sum of // their outpoint amounts matching the requested amount. for _, txOut := range tx.outputs { // Look up the original request we received because txOut.request may // represent a split request and thus have a different amount from the // original one. outputStatus := w.status.outputs[txOut.request.outBailmentID()] origRequest := outputStatus.request amtFulfilled := dcrutil.Amount(0) for _, outpoint := range outputStatus.outpoints { amtFulfilled += outpoint.amount } if outputStatus.status == statusSuccess && amtFulfilled != origRequest.Amount { msg := fmt.Sprintf("%s was not completely fulfilled; only %v fulfilled", origRequest, amtFulfilled) return newError(ErrWithdrawalProcessing, msg, nil) } } w.transactions = append(w.transactions, tx) w.current = newWithdrawalTx(w.txOptions) return nil }
func (s *walletServer) FundTransaction(ctx context.Context, req *pb.FundTransactionRequest) ( *pb.FundTransactionResponse, error) { policy := wallet.OutputSelectionPolicy{ Account: req.Account, RequiredConfirmations: req.RequiredConfirmations, } unspentOutputs, err := s.wallet.UnspentOutputs(policy) if err != nil { return nil, translateError(err) } selectedOutputs := make([]*pb.FundTransactionResponse_PreviousOutput, 0, len(unspentOutputs)) var totalAmount dcrutil.Amount for _, output := range unspentOutputs { selectedOutputs = append(selectedOutputs, &pb.FundTransactionResponse_PreviousOutput{ TransactionHash: output.OutPoint.Hash[:], OutputIndex: output.OutPoint.Index, Amount: output.Output.Value, PkScript: output.Output.PkScript, ReceiveTime: output.ReceiveTime.Unix(), FromCoinbase: output.OutputKind == wallet.OutputKindCoinbase, Tree: int32(output.OutPoint.Tree), }) totalAmount += dcrutil.Amount(output.Output.Value) if req.TargetAmount != 0 && totalAmount > dcrutil.Amount(req.TargetAmount) { break } } var changeScript []byte if req.IncludeChangeScript && totalAmount > dcrutil.Amount(req.TargetAmount) { changeAddr, err := s.wallet.NewAddress(req.Account, waddrmgr.InternalBranch) if err != nil { return nil, translateError(err) } changeScript, err = txscript.PayToAddrScript(changeAddr) if err != nil { return nil, translateError(err) } } return &pb.FundTransactionResponse{ SelectedOutputs: selectedOutputs, TotalAmount: int64(totalAmount), ChangePkScript: changeScript, }, nil }
func TstNewOutputRequest(t *testing.T, transaction uint32, address string, amount dcrutil.Amount, net *chaincfg.Params) OutputRequest { addr, err := dcrutil.DecodeAddress(address, net) if err != nil { t.Fatalf("Unable to decode address %s", address) } pkScript, err := txscript.PayToAddrScript(addr) if err != nil { t.Fatalf("Unable to generate pkScript for %v", addr) } return OutputRequest{ PkScript: pkScript, Address: addr, Amount: amount, Server: "server", Transaction: transaction, } }
func TestSignMultiSigUTXORedeemScriptNotFound(t *testing.T) { tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() mgr := pool.Manager() tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{}) // This is a P2SH address for which the addr manager doesn't have the redeem // script. addr, _ := dcrutil.DecodeAddress("3Hb4xcebcKg4DiETJfwjh8sF4uDw9rqtVC", mgr.ChainParams()) if _, err := mgr.Address(addr); err == nil { t.Fatalf("Address %s found in manager when it shouldn't", addr) } msgtx := tx.toMsgTx() pkScript, _ := txscript.PayToAddrScript(addr.(*dcrutil.AddressScriptHash)) err := signMultiSigUTXO(mgr, msgtx, 0, pkScript, []RawSig{RawSig{}}) TstCheckError(t, "", err, ErrTxSigning) }
// checkOutputsMatch checks that the outputs in the tx match the expected ones. func checkOutputsMatch(t *testing.T, msgtx *wire.MsgTx, expected map[string]dcrutil.Amount) { // This is a bit convoluted because the index of the change output is randomized. for addrStr, v := range expected { addr, err := dcrutil.DecodeAddress(addrStr, &chaincfg.TestNetParams) if err != nil { t.Fatalf("Cannot decode address: %v", err) } pkScript, err := txscript.PayToAddrScript(addr) if err != nil { t.Fatalf("Cannot create pkScript: %v", err) } found := false for _, txout := range msgtx.TxOut { if reflect.DeepEqual(txout.PkScript, pkScript) && txout.Value == int64(v) { found = true break } } if !found { t.Fatalf("PkScript %v not found in msgtx.TxOut: %v", pkScript, msgtx.TxOut) } } }
// makeTx generates a transaction spending outputs to a single address. func makeTx(params *chaincfg.Params, inputs []*extendedOutPoint, addr dcrutil.Address, txFee int64) (*wire.MsgTx, error) { mtx := wire.NewMsgTx() allInAmts := int64(0) for _, input := range inputs { txIn := wire.NewTxIn(input.op, []byte{}) mtx.AddTxIn(txIn) allInAmts += input.amt } pkScript, err := txscript.PayToAddrScript(addr) if err != nil { return nil, err } txOut := wire.NewTxOut(allInAmts-txFee, pkScript) txOut.Version = txscript.DefaultScriptVersion mtx.AddTxOut(txOut) return mtx, nil }
// TestCheckTransactionStandard tests the checkTransactionStandard API. func TestCheckTransactionStandard(t *testing.T) { // Create some dummy, but otherwise standard, data for transactions. prevOutHash, err := chainhash.NewHashFromStr("01") if err != nil { t.Fatalf("NewShaHashFromStr: unexpected error: %v", err) } dummyPrevOut := wire.OutPoint{Hash: *prevOutHash, Index: 1, Tree: 0} dummySigScript := bytes.Repeat([]byte{0x00}, 65) dummyTxIn := wire.TxIn{ PreviousOutPoint: dummyPrevOut, Sequence: wire.MaxTxInSequenceNum, ValueIn: 0, BlockHeight: 0, BlockIndex: 0, SignatureScript: dummySigScript, } addrHash := [20]byte{0x01} addr, err := dcrutil.NewAddressPubKeyHash(addrHash[:], &chaincfg.TestNetParams, chainec.ECTypeSecp256k1) if err != nil { t.Fatalf("NewAddressPubKeyHash: unexpected error: %v", err) } dummyPkScript, err := txscript.PayToAddrScript(addr) if err != nil { t.Fatalf("PayToAddrScript: unexpected error: %v", err) } dummyTxOut := wire.TxOut{ Value: 100000000, // 1 BTC Version: 0, PkScript: dummyPkScript, } tests := []struct { name string tx wire.MsgTx height int64 isStandard bool code wire.RejectCode }{ { name: "Typical pay-to-pubkey-hash transaction", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{&dummyTxOut}, LockTime: 0, }, height: 300000, isStandard: true, }, { name: "Transaction version too high", tx: wire.MsgTx{ Version: int32(wire.TxVersion + 1), TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{&dummyTxOut}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Transaction is not finalized", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: dummyPrevOut, SignatureScript: dummySigScript, Sequence: 0, }}, TxOut: []*wire.TxOut{&dummyTxOut}, LockTime: 300001, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Transaction size is too large", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{{ Value: 0, PkScript: bytes.Repeat([]byte{0x00}, maxStandardTxSize+1), }}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Signature script size is too large", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: dummyPrevOut, SignatureScript: bytes.Repeat([]byte{0x00}, maxStandardSigScriptSize+1), Sequence: wire.MaxTxInSequenceNum, }}, TxOut: []*wire.TxOut{&dummyTxOut}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Signature script that does more than push data", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: dummyPrevOut, SignatureScript: []byte{ txscript.OP_CHECKSIGVERIFY}, Sequence: wire.MaxTxInSequenceNum, }}, TxOut: []*wire.TxOut{&dummyTxOut}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Valid but non standard public key script", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{{ Value: 100000000, PkScript: []byte{txscript.OP_TRUE}, }}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "More than four nulldata outputs", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{{ Value: 0, PkScript: []byte{txscript.OP_RETURN}, }, { Value: 0, PkScript: []byte{txscript.OP_RETURN}, }, { Value: 0, PkScript: []byte{txscript.OP_RETURN}, }, { Value: 0, PkScript: []byte{txscript.OP_RETURN}, }, { Value: 0, PkScript: []byte{txscript.OP_RETURN}, }}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Dust output", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{{ Value: 0, PkScript: dummyPkScript, }}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectDust, }, { name: "One nulldata output with 0 amount (standard)", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{{ Value: 0, PkScript: []byte{txscript.OP_RETURN}, }}, LockTime: 0, }, height: 300000, isStandard: true, }, } timeSource := blockchain.NewMedianTime() for _, test := range tests { // Ensure standardness is as expected. tx := dcrutil.NewTx(&test.tx) err := checkTransactionStandard(tx, stake.DetermineTxType(tx), test.height, timeSource, defaultMinRelayTxFee) if err == nil && test.isStandard { // Test passes since function returned standard for a // transaction which is intended to be standard. continue } if err == nil && !test.isStandard { t.Errorf("checkTransactionStandard (%s): standard when "+ "it should not be", test.name) continue } if err != nil && test.isStandard { t.Errorf("checkTransactionStandard (%s): nonstandard "+ "when it should not be: %v", test.name, err) continue } // Ensure error type is a TxRuleError inside of a RuleError. rerr, ok := err.(RuleError) if !ok { t.Errorf("checkTransactionStandard (%s): unexpected "+ "error type - got %T", test.name, err) continue } txrerr, ok := rerr.Err.(TxRuleError) if !ok { t.Errorf("checkTransactionStandard (%s): unexpected "+ "error type - got %T", test.name, rerr.Err) continue } // Ensure the reject code is the expected one. if txrerr.RejectCode != test.code { t.Errorf("checkTransactionStandard (%s): unexpected "+ "error code - got %v, want %v", test.name, txrerr.RejectCode, test.code) continue } } }
// This example demonstrates how to use the Pool.StartWithdrawal method. func Example_startWithdrawal() { // Create the address manager and votingpool DB namespace. See the example // for the Create() function for more info on how this is done. mgr, vpNamespace, tearDownFunc, err := exampleCreateMgrAndDBNamespace() if err != nil { fmt.Println(err) return } defer tearDownFunc() // Create a pool and a series. See the DepositAddress example for more info // on how this is done. pool, seriesID, err := exampleCreatePoolAndSeries(mgr, vpNamespace) if err != nil { fmt.Println(err) return } // Unlock the manager if err := mgr.Unlock(privPassphrase); err != nil { fmt.Println(err) return } defer mgr.Lock() addr, _ := dcrutil.DecodeAddress("1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX", mgr.ChainParams()) pkScript, _ := txscript.PayToAddrScript(addr) requests := []votingpool.OutputRequest{ votingpool.OutputRequest{ PkScript: pkScript, Address: addr, Amount: 1e6, Server: "server-id", Transaction: 123}, } changeStart, err := pool.ChangeAddress(seriesID, votingpool.Index(0)) if err != nil { fmt.Println(err) return } // This is only needed because we have not used any deposit addresses from // the series, and we cannot create a WithdrawalAddress for an unused // branch/idx pair. if err = pool.EnsureUsedAddr(seriesID, votingpool.Branch(1), votingpool.Index(0)); err != nil { fmt.Println(err) return } startAddr, err := pool.WithdrawalAddress(seriesID, votingpool.Branch(1), votingpool.Index(0)) if err != nil { fmt.Println(err) return } lastSeriesID := seriesID dustThreshold := dcrutil.Amount(1e4) currentBlock := int32(19432) roundID := uint32(0) txstore, tearDownFunc, err := exampleCreateTxStore() if err != nil { fmt.Println(err) return } _, err = pool.StartWithdrawal( roundID, requests, *startAddr, lastSeriesID, *changeStart, txstore, currentBlock, dustThreshold) if err != nil { fmt.Println(err) } // Output: // }
// This example demonstrates manually creating and signing a redeem transaction. func ExampleSignTxOutput() { // Ordinarily the private key would come from whatever storage mechanism // is being used, but for this example just hard code it. privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" + "d4f8720ee63e502ee2869afab7de234b80c") if err != nil { fmt.Println(err) return } privKey, pubKey := chainec.Secp256k1.PrivKeyFromBytes(privKeyBytes) pubKeyHash := dcrutil.Hash160(pubKey.SerializeCompressed()) addr, err := dcrutil.NewAddressPubKeyHash(pubKeyHash, &chaincfg.MainNetParams, chainec.ECTypeSecp256k1) if err != nil { fmt.Println(err) return } // For this example, create a fake transaction that represents what // would ordinarily be the real transaction that is being spent. It // contains a single output that pays to address in the amount of 1 DCR. originTx := wire.NewMsgTx() prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0), dcrutil.TxTreeRegular) txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}) originTx.AddTxIn(txIn) pkScript, err := txscript.PayToAddrScript(addr) if err != nil { fmt.Println(err) return } txOut := wire.NewTxOut(100000000, pkScript) originTx.AddTxOut(txOut) originTxHash := originTx.TxSha() // Create the transaction to redeem the fake transaction. redeemTx := wire.NewMsgTx() // Add the input(s) the redeeming transaction will spend. There is no // signature script at this point since it hasn't been created or signed // yet, hence nil is provided for it. prevOut = wire.NewOutPoint(&originTxHash, 0, dcrutil.TxTreeRegular) txIn = wire.NewTxIn(prevOut, nil) redeemTx.AddTxIn(txIn) // Ordinarily this would contain that actual destination of the funds, // but for this example don't bother. txOut = wire.NewTxOut(0, nil) redeemTx.AddTxOut(txOut) // Sign the redeeming transaction. lookupKey := func(a dcrutil.Address) (chainec.PrivateKey, bool, error) { // Ordinarily this function would involve looking up the private // key for the provided address, but since the only thing being // signed in this example uses the address associated with the // private key from above, simply return it with the compressed // flag set since the address is using the associated compressed // public key. // // NOTE: If you want to prove the code is actually signing the // transaction properly, uncomment the following line which // intentionally returns an invalid key to sign with, which in // turn will result in a failure during the script execution // when verifying the signature. // // privKey.D.SetInt64(12345) // return privKey, true, nil } // Notice that the script database parameter is nil here since it isn't // used. It must be specified when pay-to-script-hash transactions are // being signed. sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams, redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll, txscript.KeyClosure(lookupKey), nil, nil, secp) if err != nil { fmt.Println(err) return } redeemTx.TxIn[0].SignatureScript = sigScript // Prove that the transaction has been validly signed by executing the // script pair. flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | txscript.ScriptDiscourageUpgradableNops vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, flags, 0) if err != nil { fmt.Println(err) return } if err := vm.Execute(); err != nil { fmt.Println(err) return } fmt.Println("Transaction successfully signed") // Output: // Transaction successfully signed }
func TestLimitAndSkipFetchTxsForAddr(t *testing.T) { testDb, err := setUpTestDb(t, "tstdbtxaddr") if err != nil { t.Errorf("Failed to open test database %v", err) return } defer testDb.cleanUpFunc() _, err = testDb.db.InsertBlock(testDb.blocks[0]) if err != nil { t.Fatalf("failed to insert initial block") } // Insert a block with some fake test transactions. The block will have // 10 copies of a fake transaction involving same address. addrString := "DsZEAobx6qJ7K2qaHZBA2vBn66Nor8KYAKk" targetAddr, err := dcrutil.DecodeAddress(addrString, &chaincfg.MainNetParams) if err != nil { t.Fatalf("Unable to decode test address: %v", err) } outputScript, err := txscript.PayToAddrScript(targetAddr) if err != nil { t.Fatalf("Unable make test pkScript %v", err) } fakeTxOut := wire.NewTxOut(10, outputScript) var emptyHash chainhash.Hash fakeHeader := wire.NewBlockHeader(0, &emptyHash, &emptyHash, &emptyHash, 1, [6]byte{}, 1, 1, 1, 1, 1, 1, 1, 1, 1, [36]byte{}) msgBlock := wire.NewMsgBlock(fakeHeader) for i := 0; i < 10; i++ { mtx := wire.NewMsgTx() mtx.AddTxOut(fakeTxOut) msgBlock.AddTransaction(mtx) } lastBlock := testDb.blocks[0] msgBlock.Header.PrevBlock = *lastBlock.Sha() // Insert the test block into the DB. testBlock := dcrutil.NewBlock(msgBlock) newheight, err := testDb.db.InsertBlock(testBlock) if err != nil { t.Fatalf("Unable to insert block into db: %v", err) } // Create and insert an address index for out test addr. txLoc, _, _ := testBlock.TxLoc() index := make(database.BlockAddrIndex, len(txLoc)) for i := range testBlock.Transactions() { var hash160 [ripemd160.Size]byte scriptAddr := targetAddr.ScriptAddress() copy(hash160[:], scriptAddr[:]) txAddrIndex := &database.TxAddrIndex{ Hash160: hash160, Height: uint32(newheight), TxOffset: uint32(txLoc[i].TxStart), TxLen: uint32(txLoc[i].TxLen), } index[i] = txAddrIndex } blkSha := testBlock.Sha() err = testDb.db.UpdateAddrIndexForBlock(blkSha, newheight, index) if err != nil { t.Fatalf("UpdateAddrIndexForBlock: failed to index"+ " addrs for block #%d (%s) "+ "err %v", newheight, blkSha, err) return } // Try skipping the first 4 results, should get 6 in return. txReply, err := testDb.db.FetchTxsForAddr(targetAddr, 4, 100000) if err != nil { t.Fatalf("Unable to fetch transactions for address: %v", err) } if len(txReply) != 6 { t.Fatalf("Did not correctly skip forward in txs for address reply"+ " got %v txs, expected %v", len(txReply), 6) } // Limit the number of results to 3. txReply, err = testDb.db.FetchTxsForAddr(targetAddr, 0, 3) if err != nil { t.Fatalf("Unable to fetch transactions for address: %v", err) } if len(txReply) != 3 { t.Fatalf("Did not correctly limit in txs for address reply"+ " got %v txs, expected %v", len(txReply), 3) } // Skip 1, limit 5. txReply, err = testDb.db.FetchTxsForAddr(targetAddr, 1, 5) if err != nil { t.Fatalf("Unable to fetch transactions for address: %v", err) } if len(txReply) != 5 { t.Fatalf("Did not correctly limit in txs for address reply"+ " got %v txs, expected %v", len(txReply), 5) } }
// TestPayToAddrScript ensures the PayToAddrScript function generates the // correct scripts for the various types of addresses. func TestPayToAddrScript(t *testing.T) { t.Parallel() // 1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX p2pkhMain, err := dcrutil.NewAddressPubKeyHash(decodeHex("e34cce70c863"+ "73273efcc54ce7d2a491bb4a0e84"), &chaincfg.MainNetParams, secp) if err != nil { t.Errorf("Unable to create public key hash address: %v", err) return } // Taken from transaction: // b0539a45de13b3e0403909b8bd1a555b8cbe45fd4e3f3fda76f3a5f52835c29d p2shMain, _ := dcrutil.NewAddressScriptHashFromHash(decodeHex("e8c300"+ "c87986efa84c37c0519929019ef86eb5b4"), &chaincfg.MainNetParams) if err != nil { t.Errorf("Unable to create script hash address: %v", err) return } // mainnet p2pk 13CG6SJ3yHUXo4Cr2RY4THLLJrNFuG3gUg p2pkCompressedMain, err := dcrutil.NewAddressSecpPubKey(decodeHex("02192d74"+ "d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"), &chaincfg.MainNetParams) if err != nil { t.Errorf("Unable to create pubkey address (compressed): %v", err) return } p2pkCompressed2Main, err := dcrutil.NewAddressSecpPubKey(decodeHex("03b0bd"+ "634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65"), &chaincfg.MainNetParams) if err != nil { t.Errorf("Unable to create pubkey address (compressed 2): %v", err) return } p2pkUncompressedMain, err := dcrutil.NewAddressSecpPubKey(decodeHex("0411db"+ "93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2"+ "e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3"), &chaincfg.MainNetParams) if err != nil { t.Errorf("Unable to create pubkey address (uncompressed): %v", err) return } tests := []struct { in dcrutil.Address expected string err error }{ // pay-to-pubkey-hash address on mainnet 0 { p2pkhMain, "DUP HASH160 DATA_20 0xe34cce70c86373273efcc54ce7d2a4" + "91bb4a0e8488 CHECKSIG", nil, }, // pay-to-script-hash address on mainnet 1 { p2shMain, "HASH160 DATA_20 0xe8c300c87986efa84c37c0519929019ef8" + "6eb5b4 EQUAL", nil, }, // pay-to-pubkey address on mainnet. compressed key. 2 { p2pkCompressedMain, "DATA_33 0x02192d74d0cb94344c9569c2e77901573d8d7903c3" + "ebec3a957724895dca52c6b4 CHECKSIG", nil, }, // pay-to-pubkey address on mainnet. compressed key (other way). 3 { p2pkCompressed2Main, "DATA_33 0x03b0bd634234abbb1ba1e986e884185c61cf43e001" + "f9137f23c2c409273eb16e65 CHECKSIG", nil, }, // pay-to-pubkey address on mainnet. for decred this would // be uncompressed, but standard for decred is 33 byte // compressed public keys. { p2pkUncompressedMain, "DATA_33 0x0311db93e1dcdb8a016b49840f8c53bc1eb68a382e97b" + "1482ecad7b148a6909a5cac", nil, }, // Supported address types with nil pointers. {(*dcrutil.AddressPubKeyHash)(nil), "", txscript.ErrUnsupportedAddress}, {(*dcrutil.AddressScriptHash)(nil), "", txscript.ErrUnsupportedAddress}, {(*dcrutil.AddressSecpPubKey)(nil), "", txscript.ErrUnsupportedAddress}, // Unsupported address type. {&bogusAddress{}, "", txscript.ErrUnsupportedAddress}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { pkScript, err := txscript.PayToAddrScript(test.in) if err != test.err { t.Errorf("PayToAddrScript #%d unexpected error - "+ "got %v, want %v", i, err, test.err) continue } expected := mustParseShortForm(test.expected) if !bytes.Equal(pkScript, expected) { t.Errorf("PayToAddrScript #%d got: %x\nwant: %x", i, pkScript, expected) continue } } }
// deserializeWithdrawal deserializes the given byte slice into a dbWithdrawalRow, // converts it into an withdrawalInfo and returns it. This function must run // with the address manager unlocked. func deserializeWithdrawal(p *Pool, serialized []byte) (*withdrawalInfo, error) { var row dbWithdrawalRow if err := gob.NewDecoder(bytes.NewReader(serialized)).Decode(&row); err != nil { return nil, newError(ErrWithdrawalStorage, "cannot deserialize withdrawal information", err) } wInfo := &withdrawalInfo{ lastSeriesID: row.LastSeriesID, dustThreshold: row.DustThreshold, } chainParams := p.Manager().ChainParams() wInfo.requests = make([]OutputRequest, len(row.Requests)) // A map of requests indexed by OutBailmentID; needed to populate // WithdrawalStatus.Outputs later on. requestsByOID := make(map[OutBailmentID]OutputRequest) for i, req := range row.Requests { addr, err := dcrutil.DecodeAddress(req.Addr, chainParams) if err != nil { return nil, newError(ErrWithdrawalStorage, "cannot deserialize addr for requested output", err) } pkScript, err := txscript.PayToAddrScript(addr) if err != nil { return nil, newError(ErrWithdrawalStorage, "invalid addr for requested output", err) } request := OutputRequest{ Address: addr, Amount: req.Amount, PkScript: pkScript, Server: req.Server, Transaction: req.Transaction, } wInfo.requests[i] = request requestsByOID[request.outBailmentID()] = request } startAddr := row.StartAddress wAddr, err := p.WithdrawalAddress(startAddr.SeriesID, startAddr.Branch, startAddr.Index) if err != nil { return nil, newError(ErrWithdrawalStorage, "cannot deserialize startAddress", err) } wInfo.startAddress = *wAddr cAddr, err := p.ChangeAddress(row.ChangeStart.SeriesID, row.ChangeStart.Index) if err != nil { return nil, newError(ErrWithdrawalStorage, "cannot deserialize changeStart", err) } wInfo.changeStart = *cAddr // TODO: Copy over row.Status.nextInputAddr. Not done because StartWithdrawal // does not update that yet. nextChangeAddr := row.Status.NextChangeAddr cAddr, err = p.ChangeAddress(nextChangeAddr.SeriesID, nextChangeAddr.Index) if err != nil { return nil, newError(ErrWithdrawalStorage, "cannot deserialize nextChangeAddress for withdrawal", err) } wInfo.status = WithdrawalStatus{ nextChangeAddr: *cAddr, fees: row.Status.Fees, outputs: make(map[OutBailmentID]*WithdrawalOutput, len(row.Status.Outputs)), sigs: row.Status.Sigs, transactions: make(map[Ntxid]changeAwareTx, len(row.Status.Transactions)), } for oid, output := range row.Status.Outputs { outpoints := make([]OutBailmentOutpoint, len(output.Outpoints)) for i, outpoint := range output.Outpoints { outpoints[i] = OutBailmentOutpoint{ ntxid: outpoint.Ntxid, index: outpoint.Index, amount: outpoint.Amount, } } wInfo.status.outputs[oid] = &WithdrawalOutput{ request: requestsByOID[output.OutBailmentID], status: output.Status, outpoints: outpoints, } } for ntxid, tx := range row.Status.Transactions { msgtx := wire.NewMsgTx() if err := msgtx.Deserialize(bytes.NewBuffer(tx.SerializedMsgTx)); err != nil { return nil, newError(ErrWithdrawalStorage, "cannot deserialize transaction", err) } wInfo.status.transactions[ntxid] = changeAwareTx{ MsgTx: msgtx, changeIdx: tx.ChangeIdx, } } return wInfo, nil }