Example #1
0
func main() {

	// Input parameters
	var coin = flag.String("coin", "", "Coin to sweep")
	var inMPKPubKey = flag.String("in_mpk", "", "Input MPK pubkey")
	var minInput = flag.Uint64("min_input", 0, "Minimum permittable input amount")
	var maxInput = flag.Uint64("max_input", math.MaxInt64, "Maximum permittable input amount")
	var maxTotal = flag.Uint64("max_total", 0, "Maximum amount of coins to move")
	var maxNumInputs = flag.Int("max_num_inputs", 10, "Maximum number of inputs per sweep transaction")
	var dryRun = flag.Bool("dry", true, "Run a dry run")
	// Output parameters
	var outMPKPubKey = flag.String("out_mpk", "", "Output MPK pubkey")
	var minOutput = flag.Uint64("min_output", 0, "Minimum permittable output amount")
	var maxOutput = flag.Uint64("max_output", math.MaxInt64, "Maximum permittable output amount")
	var maxNumOutputs = flag.Int("max_num_outputs", 10, "Maximum number of outputs per sweep transaction")

	flag.Parse()

	fmt.Printf(`Input parameters:
    coin:           %v
    in_mpk:         %v
    min_input:      %v
    max_input:      %v
    max_total:      %v
    max_num_inputs: %v
    dry:            %v

Output parameters:
    out_mpk:        %v
    min_output:     %v
    max_output:     %v
    max_num_outputs %v
    
`, *coin, *inMPKPubKey, *minInput, *maxInput, *maxTotal, *maxNumInputs, *dryRun,
		*outMPKPubKey, *minOutput, *maxOutput, *maxNumOutputs)

	if *coin == "" {
		log.Panicf("Invalid coin. Wanted: BTC, LTC, etc.")
	}
	inMPK := bitcoin.LoadMPKByPubKey(*inMPKPubKey)
	if inMPK == nil {
		log.Panicf("Invalid in_mpk_pubkey")
	}
	outMPK := bitcoin.LoadMPKByPubKey(*outMPKPubKey)
	if outMPK == nil {
		log.Panicf("Invalid out_mpk_pubkey")
	}

	fmt.Println("Enter in_mpk_privkey:")
	inMPKPrivKey := string(gopass.GetPasswd())
	treasury.StorePrivateKeyForMPKPubKey(*inMPKPubKey, inMPKPrivKey)

	// Select a bunch of inputs for sweeping.
	inputs, total, err := treasury.CollectSweepInputs(*coin, inMPK, *minInput, *maxInput, *maxTotal, *maxNumInputs)
	if err != nil {
		log.Panicf("Error in CollectSweepInputs: %v", err)
	}
	inputIds := Map(inputs, "Id")

	fmt.Printf("Found %v inputs (total: %v)\n", len(inputs), UI64ToF64(total))
	for _, input := range inputs {
		fmt.Printf("  %v\t%v %v\n", input.Address, input.Coin, UI64ToF64(input.Amount))
	}

	// Create sweep transaction.
	signedTx, _, minerFee, outputs, err := treasury.ComputeSweepTransaction(inputs, outMPK, *minOutput, *maxOutput, *maxNumOutputs, *dryRun)
	if err != nil {
		log.Panicf("Error in ComputeSweepTransaction: %v", err)
	}

	fmt.Printf("Computed signed sweep transaction (minerFee: %v)\n", UI64ToF64(minerFee))
	sum := uint64(minerFee)
	for addr, amount := range outputs {
		fmt.Printf("  %v:\t%v\n", addr, UI64ToF64(amount))
		sum += amount
	}
	fmt.Printf("Total: %v\n", UI64ToF64(sum))

	if *dryRun {
		fmt.Println("Dry run complete")
		return
	}

	// DRY RUN ENDS HERE
	// DRY RUN ENDS HERE

	// save WithdrawalTx for bookkeeping
	wthTx := treasury.SaveWithdrawalTx(&treasury.WithdrawalTx{
		Coin:     *coin,
		Type:     treasury.WITHDRAWAL_TX_TYPE_SWEEP,
		Amount:   total,
		MinerFee: minerFee,
		RawTx:    signedTx,
		TxId:     bitcoin.ComputeTxId(signedTx),
	})

	// checkout those payments.
	bitcoin.CheckoutPaymentsToSpend(inputIds, wthTx.Id)

	// broadcast transaction.
	err = bitcoin.SendRawTransaction(*coin, signedTx)
	if err != nil {
		panic(err)
	}

	// update payments as spent.
	bitcoin.MarkPaymentsAsSpent(inputIds, wthTx.Id)

	fmt.Println("Success! Expected TxId: %v (but may change due to malleability)", wthTx.TxId)

}
Example #2
0
// Returns false if no withdrawals are available to process.
func ProcessUserWithdrawals(coin string) (bool, error) {

	// Checkout withdrawals
	// TODO: Gather multiple small withdrawals.
	wths := account.CheckoutWithdrawals(coin, 1)
	if len(wths) == 0 {
		return false, nil
	}
	wthIds := Map(wths, "Id")
	amounts := map[string]uint64{}
	amountSum := uint64(0)
	for _, wth := range wths {
		if wth.Amount <= 0 {
			panic(NewError("Invalid send amount %v", wth.Amount))
		}
		amounts[wth.ToAddress] += uint64(wth.Amount)
		amountSum += uint64(wth.Amount)
	}

	// figure out which payments to use.
	signedTx, payments, minerFees, chgAddress, err := ComputeWithdrawalTransaction(coin, amounts)
	if err != nil {
		account.StallWithdrawals(wthIds)
		return false, err
	}
	paymentIds := Map(payments, "Id")

	// save withdrawal info for bookkeeping.
	wthTx := SaveWithdrawalTx(&WithdrawalTx{
		Coin:       coin,
		Type:       WITHDRAWAL_TX_TYPE_WITHDRAWAL,
		Amount:     amountSum,
		MinerFee:   minerFees,
		ChgAddress: chgAddress,
		RawTx:      signedTx,
		TxId:       bitcoin.ComputeTxId(signedTx),
	})

	// checkout those payments.
	bitcoin.CheckoutPaymentsToSpend(paymentIds, wthTx.Id)

	// TODO: the Tx should go out to our partners who sign them for us.
	// TODO: receive the signed Tx.

	// deduct change amount from system user's "change" wallet.
	// this creates a negative balance, which will revert to zero
	// when the change is received.
	if chgAddress != "" {
		changeAmount := amounts[chgAddress]
		err := db.DoBeginSerializable(func(tx *db.ModelTx) {
			account.UpdateBalanceByWallet(tx, 0, account.WALLET_CHANGE, coin, -int64(changeAmount), false)
		})
		if err != nil {
			panic(err)
		}
	}

	// broadcast transaction.
	rpc.SendRawTransaction(coin, signedTx)

	// update payments as spent.
	bitcoin.MarkPaymentsAsSpent(paymentIds, wthTx.Id)

	// update withdrawals as complete.
	account.CompleteWithdrawals(wths, wthTx.Id)

	return true, nil
}