Ejemplo n.º 1
0
// Collects up to maxTotal coins of tx outs between minInput and maxInput coins in size.
// - inMPK: Set inMPK to hotMPK to move funds offline from the hot wallet.
// - minInput, maxInput: Range of permittable input transaction sizes.
// - maxTotal: Maximum amount of coins to collect.
// NOTE: should be deterministic, because of dryRun options.
func CollectSweepInputs(coin string, inMPK *bitcoin.MPK, minInput, maxInput, maxTotal uint64, maxNumInputs int) ([]*bitcoin.Payment, uint64, error) {
	reqHeight := bitcoin.ReqHeight(coin)

	if minInput < bitcoin.MinerFee(coin) {
		return nil, 0, NewError("minInput must be at least as large as the miner fee for %v: %v", coin, maxMinerFeeForCoin(coin))
	}

	// Gather oldest spendable inputs in range until maxTotal.
	candidates := bitcoin.LoadOldestSpendablePaymentsBetween(inMPK.Id, coin, minInput, maxInput, maxNumInputs, reqHeight)
	inputs := []*bitcoin.Payment{}
	total := uint64(0)
	for _, payment := range candidates {
		if total+payment.Amount > maxTotal {
			continue
		}
		total += payment.Amount
		inputs = append(inputs, payment)
	}

	// total must meet some threshold for sweep to be worth it.
	if total < maxMinerFeeForCoin(coin) {
		return nil, 0, NewError("Could not gather enough inputs. Needed at least %v, only got %v", maxMinerFeeForCoin(coin), total)
	}

	return inputs, total, nil
}
Ejemplo n.º 2
0
// Finds payments & constructs transaction to satisfy the given output amounts.
// This function could fail, in which case we'll call it again later.
// This means that this function should be largely side-effect free.
// However, note that 'outputs' may be modified to account for
// fees & change addresses.
func ComputeWithdrawalTransaction(coin string, outputs map[string]uint64) (string, []*bitcoin.Payment, uint64, string, error) {
	returnErr := func(err error) (string, []*bitcoin.Payment, uint64, string, error) { return "", nil, 0, "", err }

	reqHeight := bitcoin.ReqHeight(coin)
	payments := []*bitcoin.Payment{}
	changeAddress := createNewChangeAddress(coin)

	sumAmount := int64(0)
	for _, amount := range outputs {
		sumAmount += int64(amount)
	}
	// Add base fees to sumAmount. Be generous, we'll adjust later.
	sumAmount += int64(bitcoin.MinerFee(coin)) * MAX_BASE_FEES
	sumAmountCopy := sumAmount
	// Then load payments greater than remainder
	for sumAmountCopy > 0 {
		// We shouldn't use too many inputs.
		if len(payments) > len(outputs)*2 {
			return returnErr(NewError("[%v] Too many inputs required for %v", coin, sumAmount))
		}
		// Try to do it with one input
		payment := bitcoin.LoadSmallestSpendablePaymentGreaterThan(hotMPK.Id, coin, uint64(sumAmountCopy), reqHeight, payments)
		if payment == nil {
			// Try to fill it as much as possible
			payment = bitcoin.LoadLargestSpendablePaymentLessThan(hotMPK.Id, coin, uint64(sumAmountCopy), reqHeight, payments)
		}
		if payment == nil {
			return returnErr(NewError("[%v] Unable to gather enough inputs for %v", coin, sumAmount))
		}
		sumAmountCopy -= int64(payment.Amount)
		payments = append(payments, payment)
	}
	// If we need to create a change address, do so.
	if sumAmountCopy != 0 {
		outputs[changeAddress] = uint64(-1 * sumAmountCopy)
	}
	// Adjust miner fees & collect private keys
	privKeys := map[string]string{}
	minerFee, err := adjustMinerFee(coin, payments, outputs, changeAddress, privKeys)
	if err != nil {
		return returnErr(err)
	}
	// Sign transaction
	s := rpc.CreateSignedRawTransaction(coin, bitcoin.ToRPCPayments(payments), outputs, privKeys)
	return s, payments, minerFee, changeAddress, nil
}
Ejemplo n.º 3
0
func maxMinerFeeForCoin(coin string) uint64 {
	return uint64(bitcoin.MinerFee(coin)) * MAX_BASE_FEES
}