Ejemplo n.º 1
0
// verifyKeysSiag_1_0 is a copy-pasted version of the verifyKeys method
// from siag 1.0.
func verifyKeysSiag_1_0(uc types.UnlockConditions, folder string, keyname string) error {
	keysRequired := uc.SignaturesRequired
	totalKeys := uint64(len(uc.PublicKeys))
	loadedKeys := make([]KeyPairSiag_1_0, totalKeys)
	for i := 0; i < len(loadedKeys); i++ {
		err := encoding.ReadFile(filepath.Join(folder, keyname+"_Key"+strconv.Itoa(i)+".siakey"), &loadedKeys[i])
		if err != nil {
			return err
		}
	}
	for _, loadedKey := range loadedKeys {
		if loadedKey.UnlockConditions.UnlockHash() != uc.UnlockHash() {
			return errors.New("ErrCorruptedKey")
		}
	}
	txn := types.Transaction{
		SiafundInputs: []types.SiafundInput{
			types.SiafundInput{
				UnlockConditions: loadedKeys[0].UnlockConditions,
			},
		},
	}
	var i uint64
	for i != totalKeys {
		if i+keysRequired > totalKeys {
			i = totalKeys - keysRequired
		}
		var j uint64
		for j < keysRequired {
			txn.TransactionSignatures = append(txn.TransactionSignatures, types.TransactionSignature{
				PublicKeyIndex: i,
				CoveredFields:  types.CoveredFields{WholeTransaction: true},
			})
			sigHash := txn.SigHash(int(j))
			sig, err := crypto.SignHash(sigHash, loadedKeys[i].SecretKey)
			if err != nil {
				return err
			}
			txn.TransactionSignatures[j].Signature = types.Signature(sig[:])
			i++
			j++
		}
		err := txn.StandaloneValid(0)
		if err != nil {
			return err
		}
		txn.TransactionSignatures = nil
	}
	return nil
}
Ejemplo n.º 2
0
// FundTransaction adds siacoins to a transaction that the wallet knows how to
// spend. The exact amount of coins are always added, and this is achieved by
// creating two transactions. The first transaciton, the parent, spends a set
// of outputs that add up to at least the desired amount, and then creates a
// single output of the exact amount and a second refund output.
func (w *Wallet) FundTransaction(id string, amount types.Currency) (t types.Transaction, err error) {
	counter := w.mu.Lock()
	defer w.mu.Unlock(counter)

	// Create a parent transaction and supply it with enough inputs to cover
	// 'amount'.
	parentTxn := types.Transaction{}
	fundingOutputs, fundingTotal, err := w.findOutputs(amount)
	if err != nil {
		return
	}
	for _, output := range fundingOutputs {
		output.age = w.age
		key := w.keys[output.output.UnlockHash]
		newInput := types.SiacoinInput{
			ParentID:         output.id,
			UnlockConditions: key.unlockConditions,
		}
		parentTxn.SiacoinInputs = append(parentTxn.SiacoinInputs, newInput)
	}

	// Create and add the output that will be used to fund the standard
	// transaction.
	parentDest, parentSpendConds, err := w.coinAddress(false) // false indicates that the address should not be visible to the user
	exactOutput := types.SiacoinOutput{
		Value:      amount,
		UnlockHash: parentDest,
	}
	parentTxn.SiacoinOutputs = append(parentTxn.SiacoinOutputs, exactOutput)

	// Create a refund output if needed.
	if amount.Cmp(fundingTotal) != 0 {
		var refundDest types.UnlockHash
		refundDest, _, err = w.coinAddress(false) // false indicates that the address should not be visible to the user
		if err != nil {
			return
		}
		refundOutput := types.SiacoinOutput{
			Value:      fundingTotal.Sub(amount),
			UnlockHash: refundDest,
		}
		parentTxn.SiacoinOutputs = append(parentTxn.SiacoinOutputs, refundOutput)
	}

	// Sign all of the inputs to the parent trancstion.
	coveredFields := types.CoveredFields{WholeTransaction: true}
	for _, input := range parentTxn.SiacoinInputs {
		sig := types.TransactionSignature{
			ParentID:       crypto.Hash(input.ParentID),
			CoveredFields:  coveredFields,
			PublicKeyIndex: 0,
		}
		parentTxn.TransactionSignatures = append(parentTxn.TransactionSignatures, sig)

		// Hash the transaction according to the covered fields.
		coinAddress := input.UnlockConditions.UnlockHash()
		sigIndex := len(parentTxn.TransactionSignatures) - 1
		secKey := w.keys[coinAddress].secretKey
		sigHash := parentTxn.SigHash(sigIndex)

		// Get the signature.
		var encodedSig crypto.Signature
		encodedSig, err = crypto.SignHash(sigHash, secKey)
		if err != nil {
			return
		}
		parentTxn.TransactionSignatures[sigIndex].Signature = types.Signature(encodedSig[:])
	}

	// Add the exact output to the wallet's knowledgebase before releasing the
	// lock, to prevent the wallet from using the exact output elsewhere.
	key := w.keys[parentSpendConds.UnlockHash()]
	key.outputs[parentTxn.SiacoinOutputID(0)] = &knownOutput{
		id:     parentTxn.SiacoinOutputID(0),
		output: exactOutput,

		spendable: true,
		age:       w.age,
	}

	// Send the transaction to the transaction pool.
	err = w.tpool.AcceptTransaction(parentTxn)
	if err != nil {
		return
	}

	// Get the transaction that was originally meant to be funded.
	openTxn, exists := w.transactions[id]
	if !exists {
		err = ErrInvalidID
		return
	}
	txn := openTxn.transaction

	// Add the exact output.
	newInput := types.SiacoinInput{
		ParentID:         parentTxn.SiacoinOutputID(0),
		UnlockConditions: parentSpendConds,
	}
	openTxn.inputs = append(openTxn.inputs, len(txn.SiacoinInputs))
	txn.SiacoinInputs = append(txn.SiacoinInputs, newInput)
	t = *txn
	return
}
Ejemplo n.º 3
0
// SignTransaction signs the transaction, then deletes the transaction from the
// wallet's internal memory, then returns the transaction.
func (w *Wallet) SignTransaction(id string, wholeTransaction bool) (txn types.Transaction, err error) {
	counter := w.mu.Lock()
	defer w.mu.Unlock(counter)

	// Fetch the transaction.
	openTxn, exists := w.transactions[id]
	if !exists {
		err = ErrInvalidID
		return
	}
	txn = *openTxn.transaction

	// Get the coveredfields struct.
	var coveredFields types.CoveredFields
	if wholeTransaction {
		coveredFields = types.CoveredFields{WholeTransaction: true}
	} else {
		for i := range txn.MinerFees {
			coveredFields.MinerFees = append(coveredFields.MinerFees, uint64(i))
		}
		for i := range txn.SiacoinInputs {
			coveredFields.SiacoinInputs = append(coveredFields.SiacoinInputs, uint64(i))
		}
		for i := range txn.SiacoinOutputs {
			coveredFields.SiacoinOutputs = append(coveredFields.SiacoinOutputs, uint64(i))
		}
		for i := range txn.FileContracts {
			coveredFields.FileContracts = append(coveredFields.FileContracts, uint64(i))
		}
		for i := range txn.StorageProofs {
			coveredFields.StorageProofs = append(coveredFields.StorageProofs, uint64(i))
		}
		for i := range txn.ArbitraryData {
			coveredFields.ArbitraryData = append(coveredFields.ArbitraryData, uint64(i))
		}
		for i := range txn.TransactionSignatures {
			coveredFields.TransactionSignatures = append(coveredFields.TransactionSignatures, uint64(i))
		}
	}

	// For each input in the transaction that we added, provide a signature.
	for _, inputIndex := range openTxn.inputs {
		input := txn.SiacoinInputs[inputIndex]
		sig := types.TransactionSignature{
			ParentID:       crypto.Hash(input.ParentID),
			CoveredFields:  coveredFields,
			PublicKeyIndex: 0,
		}
		txn.TransactionSignatures = append(txn.TransactionSignatures, sig)

		// Hash the transaction according to the covered fields.
		coinAddress := input.UnlockConditions.UnlockHash()
		sigIndex := len(txn.TransactionSignatures) - 1
		secKey := w.keys[coinAddress].secretKey
		sigHash := txn.SigHash(sigIndex)

		// Get the signature.
		var encodedSig crypto.Signature
		encodedSig, err = crypto.SignHash(sigHash, secKey)
		if err != nil {
			return
		}
		txn.TransactionSignatures[sigIndex].Signature = types.Signature(encodedSig[:])
	}

	// Delete the open transaction.
	delete(w.transactions, id)

	return
}
Ejemplo n.º 4
0
Archivo: keys.go Proyecto: mm3/Sia
// verifyKeys checks a set of keys on disk to see that they can spend funds
// sent to their address.
func verifyKeys(uc types.UnlockConditions, folder string, keyname string) error {
	keysRequired := uc.SignaturesRequired
	totalKeys := uint64(len(uc.PublicKeys))

	// Load the keys from disk back into memory, then verify that the keys on
	// disk are able to sign outputs in transactions.
	loadedKeys := make([]KeyPair, totalKeys)
	for i := 0; i < len(loadedKeys); i++ {
		err := encoding.ReadFile(filepath.Join(folder, keyname+"_Key"+strconv.Itoa(i)+FileExtension), &loadedKeys[i])
		if err != nil {
			return err
		}
		if loadedKeys[i].Header != FileHeader {
			return ErrUnknownHeader
		}
		if loadedKeys[i].Version != FileVersion {
			return ErrUnknownVersion
		}
	}

	// Check that the keys can be used to spend transactions.
	for _, loadedKey := range loadedKeys {
		if loadedKey.UnlockConditions.UnlockHash() != uc.UnlockHash() {
			return ErrCorruptedKey
		}
	}
	// Create a transaction for the keys to sign.
	txn := types.Transaction{
		SiafundInputs: []types.SiafundInput{
			types.SiafundInput{
				UnlockConditions: loadedKeys[0].UnlockConditions,
			},
		},
	}
	// Loop through and sign the transaction multiple times. All keys will be
	// used at least once by the time the loop terminates.
	var i uint64
	for i != totalKeys {
		// i tracks which key is next to be used. If i + RequiredKeys results
		// in going out-of-bounds, reduce i so that the last key will be used
		// for the final signature.
		if i+keysRequired > totalKeys {
			i = totalKeys - keysRequired
		}
		var j uint64
		for j < keysRequired {
			txn.TransactionSignatures = append(txn.TransactionSignatures, types.TransactionSignature{
				PublicKeyIndex: i,
				CoveredFields:  types.CoveredFields{WholeTransaction: true},
			})
			sigHash := txn.SigHash(int(j))
			sig, err := crypto.SignHash(sigHash, loadedKeys[i].SecretKey)
			if err != nil {
				return err
			}
			txn.TransactionSignatures[j].Signature = types.Signature(sig[:])
			i++
			j++
		}
		// Check that the signature is valid.
		err := txn.StandaloneValid(0)
		if err != nil {
			return err
		}
		// Delete all of the signatures for the next iteration.
		txn.TransactionSignatures = nil
	}
	return nil
}
Ejemplo n.º 5
0
Archivo: siag.go Proyecto: mm3/Sia
// SendSiagSiafunds sends siafunds to another address. The siacoins stored in
// the siafunds are sent to an address in the wallet.
func (w *Wallet) SendSiagSiafunds(amount types.Currency, dest types.UnlockHash, keyfiles []string) (types.Transaction, error) {
	if len(keyfiles) < 1 {
		return types.Transaction{}, ErrNoKeyfile
	}

	// Load the siafund keys and verify they are sufficient to sign the
	// transaction.
	skps := make([]SiagKeyPair, len(keyfiles))
	for i, keyfile := range keyfiles {
		err := encoding.ReadFile(keyfile, &skps[i])
		if err != nil {
			return types.Transaction{}, err
		}

		if skps[i].Header != SiagFileHeader {
			return types.Transaction{}, ErrUnknownHeader
		}
		if skps[i].Version != SiagFileVersion {
			return types.Transaction{}, ErrUnknownVersion
		}
	}

	// Check that all of the loaded files have the same address, and that there
	// are enough to create the transaction.
	baseUnlockHash := skps[0].UnlockConditions.UnlockHash()
	for _, skp := range skps {
		if skp.UnlockConditions.UnlockHash() != baseUnlockHash {
			return types.Transaction{}, ErrInconsistentKeys
		}
	}
	if uint64(len(skps)) < skps[0].UnlockConditions.SignaturesRequired {
		return types.Transaction{}, ErrInsufficientKeys
	}

	// Check that there are enough siafunds in the key to complete the spend.
	lockID := w.mu.RLock()
	var availableSiafunds types.Currency
	var sfoids []types.SiafundOutputID
	for sfoid, sfo := range w.siafundOutputs {
		if sfo.UnlockHash == baseUnlockHash {
			availableSiafunds = availableSiafunds.Add(sfo.Value)
			sfoids = append(sfoids, sfoid)
		}
		if availableSiafunds.Cmp(amount) >= 0 {
			break
		}
	}
	w.mu.RUnlock(lockID)
	if availableSiafunds.Cmp(amount) < 0 {
		return types.Transaction{}, ErrInsufficientSiafunds
	}

	// Truncate the keys to exactly the number needed.
	skps = skps[:skps[0].UnlockConditions.SignaturesRequired]

	// Assemble the base transction, including a 10 siacoin fee if possible.
	id, err := w.RegisterTransaction(types.Transaction{})
	if err != nil {
		return types.Transaction{}, err
	}
	// Add a miner fee - if funding the transaction fails, we'll just send a
	// transaction with no fee.
	txn, err := w.FundTransaction(id, types.NewCurrency64(TransactionFee))
	if err == nil {
		txn, _, err = w.AddMinerFee(id, types.NewCurrency64(TransactionFee))
		if err != nil {
			return types.Transaction{}, err
		}
	}
	// Add the siafund inputs to the transcation.
	for _, sfoid := range sfoids {
		// Get an address for the siafund claims.
		lockID := w.mu.Lock()
		claimDest, _, err := w.coinAddress(false)
		w.mu.Unlock(lockID)
		if err != nil {
			return types.Transaction{}, err
		}

		// Assemble the SiafundInput to spend this output.
		sfi := types.SiafundInput{
			ParentID:         sfoid,
			UnlockConditions: skps[0].UnlockConditions,
			ClaimUnlockHash:  claimDest,
		}
		txn, _, err = w.AddSiafundInput(id, sfi)
		if err != nil {
			return types.Transaction{}, err
		}
	}
	// Add the siafund output to the transaction.
	sfo := types.SiafundOutput{
		Value:      amount,
		UnlockHash: dest,
	}
	txn, _, err = w.AddSiafundOutput(id, sfo)
	if err != nil {
		return types.Transaction{}, err
	}
	// Add a refund siafund output if needed.
	if amount.Cmp(availableSiafunds) != 0 {
		refund := availableSiafunds.Sub(amount)
		sfo := types.SiafundOutput{
			Value:      refund,
			UnlockHash: baseUnlockHash,
		}
		txn, _, err = w.AddSiafundOutput(id, sfo)
		if err != nil {
			return types.Transaction{}, err
		}
	}
	// Add signatures for the siafund inputs.
	sigIndex := 0
	for _, sfoid := range sfoids {
		for _, key := range skps {
			txnSig := types.TransactionSignature{
				ParentID:       crypto.Hash(sfoid),
				CoveredFields:  types.CoveredFields{WholeTransaction: true},
				PublicKeyIndex: uint64(key.Index),
			}
			txn.TransactionSignatures = append(txn.TransactionSignatures, txnSig)
			sigHash := txn.SigHash(sigIndex)
			encodedSig, err := crypto.SignHash(sigHash, key.SecretKey)
			if err != nil {
				return types.Transaction{}, err
			}
			txn.TransactionSignatures[sigIndex].Signature = types.Signature(encodedSig[:])

			txn, _, err = w.AddTransactionSignature(id, txn.TransactionSignatures[sigIndex])
			if err != nil {
				return types.Transaction{}, err
			}

			sigIndex++
		}
	}

	// Sign the transaction.
	txn, err = w.SignTransaction(id, true)
	if err != nil {
		return types.Transaction{}, err
	}

	err = w.tpool.AcceptTransaction(txn)
	if err != nil {
		return types.Transaction{}, err
	}
	return txn, nil
}