Пример #1
0
// TestVerifyKeysSiag_1_0 loads some keys generated by siag1.0.
// Verification must still work.
func TestVerifyKeysSiag_1_0(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	var kp KeyPairSiag_1_0

	// 1 of 1
	err := encoding.ReadFile("siag_1.0_1of1_Key0.siakey", &kp)
	if err != nil {
		t.Fatal(err)
	}
	err = verifyKeysSiag_1_0(kp.UnlockConditions, "", "siag_1.0_1of1")
	if err != nil {
		t.Fatal(err)
	}

	// 1 of 2
	err = encoding.ReadFile("siag_1.0_1of2_Key0.siakey", &kp)
	if err != nil {
		t.Fatal(err)
	}
	err = verifyKeysSiag_1_0(kp.UnlockConditions, "", "siag_1.0_1of2")
	if err != nil {
		t.Fatal(err)
	}

	// 2 of 3
	err = encoding.ReadFile("siag_1.0_2of3_Key0.siakey", &kp)
	if err != nil {
		t.Fatal(err)
	}
	err = verifyKeysSiag_1_0(kp.UnlockConditions, "", "siag_1.0_2of3")
	if err != nil {
		t.Fatal(err)
	}

	// 3 of 3
	err = encoding.ReadFile("siag_1.0_3of3_Key0.siakey", &kp)
	if err != nil {
		t.Fatal(err)
	}
	err = verifyKeysSiag_1_0(kp.UnlockConditions, "", "siag_1.0_3of3")
	if err != nil {
		t.Fatal(err)
	}

	// 4 of 9
	err = encoding.ReadFile("siag_1.0_4of9_Key0.siakey", &kp)
	if err != nil {
		t.Fatal(err)
	}
	err = verifyKeysSiag_1_0(kp.UnlockConditions, "", "siag_1.0_4of9")
	if err != nil {
		t.Fatal(err)
	}
}
Пример #2
0
Файл: persist.go Проект: mm3/Sia
// loadWallet pulls a wallet from disk into memory, merging it with whatever
// wallet is already in memory. The result is a combined wallet that has all of
// the addresses.
func (w *Wallet) loadWallet(filepath string) error {
	var savedKeys []savedKey
	err := encoding.ReadFile(filepath, &savedKeys)
	if err != nil {
		return err
	}
	err = w.loadKeys(savedKeys)
	if err != nil {
		return err
	}
	return nil
}
Пример #3
0
// loadSiagKeys loads a set of siag keyfiles into the wallet, so that the
// wallet may spend the siafunds.
func (w *Wallet) loadSiagKeys(masterKey crypto.TwofishKey, keyfiles []string) error {
	// Load the keyfiles from disk.
	if len(keyfiles) < 1 {
		return ErrNoKeyfile
	}
	skps := make([]SiagKeyPair, len(keyfiles))
	for i, keyfile := range keyfiles {
		err := encoding.ReadFile(keyfile, &skps[i])
		if err != nil {
			return err
		}

		if skps[i].Header != SiagFileHeader {
			return ErrUnknownHeader
		}
		if skps[i].Version != SiagFileVersion {
			return 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 ErrInconsistentKeys
		}
	}
	if uint64(len(skps)) < skps[0].UnlockConditions.SignaturesRequired {
		return ErrInsufficientKeys
	}
	// Drop all unneeded keys.
	skps = skps[0:skps[0].UnlockConditions.SignaturesRequired]

	// Merge the keys into a single spendableKey and save it to the wallet.
	var sk spendableKey
	sk.UnlockConditions = skps[0].UnlockConditions
	for _, skp := range skps {
		sk.SecretKeys = append(sk.SecretKeys, skp.SecretKey)
	}
	err := w.loadSpendableKey(masterKey, sk)
	if err != nil {
		return err
	}
	err = w.saveSettingsSync()
	if err != nil {
		return err
	}
	return w.createBackup(filepath.Join(w.persistDir, "Sia Wallet Encrypted Backup - "+persist.RandomSuffix()+settingsFileSuffix))
}
Пример #4
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 = sig[:]
			i++
			j++
		}
		err := txn.StandaloneValid(0)
		if err != nil {
			return err
		}
		txn.TransactionSignatures = nil
	}
	return nil
}
Пример #5
0
Файл: persist.go Проект: mm3/Sia
// loadSiafundTracking loads siafund addresses for tracking.
func (w *Wallet) loadSiafundTracking(filepath string) error {
	// Load the siafunds file, which is intentionally called 'outputs.dat'.
	var siafundAddresses []types.UnlockHash
	err := encoding.ReadFile(filepath, &siafundAddresses)
	if err != nil {
		return err
	}

	// Load the addresses into the wallet.
	for _, sa := range siafundAddresses {
		w.siafundAddresses[sa] = struct{}{}
	}
	return nil
}
Пример #6
0
// printKeyInfo opens a keyfile and prints the contents, returning an error if
// there's a problem.
func printKeyInfo(filename string) error {
	var kp KeyPair
	err := encoding.ReadFile(filename, &kp)
	if err != nil {
		return err
	}
	if kp.Header != FileHeader {
		return ErrUnknownHeader
	}
	if kp.Version != FileVersion {
		return ErrUnknownVersion
	}

	fmt.Printf("Found a key for a %v of %v address.\n", kp.UnlockConditions.SignaturesRequired, len(kp.UnlockConditions.PublicKeys))
	fmt.Printf("The address is: %v\n", kp.UnlockConditions.UnlockHash())
	return nil
}
Пример #7
0
// WatchSiagSiafundAddress loads a siafund address from a siag key. The private
// key is NOT loaded.
func (w *Wallet) WatchSiagSiafundAddress(keyfile string) error {
	lockID := w.mu.Lock()
	defer w.mu.Unlock(lockID)

	var skp SiagKeyPair
	err := encoding.ReadFile(keyfile, &skp)
	if err != nil {
		return err
	}
	if skp.Header != SiagFileHeader {
		return ErrUnknownHeader
	}
	if skp.Version != SiagFileVersion {
		return ErrUnknownVersion
	}
	w.siafundAddresses[skp.UnlockConditions.UnlockHash()] = struct{}{}
	// Janky println... but it's important to get this message to the user.
	println("Loaded a siafund address. Please note that the private key was not loaded, you must KEEP the original keyfile. You must restart siad before your balance can be displayed.")
	w.save()
	return nil
}
Пример #8
0
// Load033xWallet loads a v0.3.3.x wallet as an unseeded key, such that the
// funds become spendable to the current wallet.
func (w *Wallet) Load033xWallet(masterKey crypto.TwofishKey, filepath033x string) error {
	if err := w.tg.Add(); err != nil {
		return err
	}
	defer w.tg.Done()
	w.mu.Lock()
	defer w.mu.Unlock()
	err := w.checkMasterKey(masterKey)
	if err != nil {
		return err
	}

	var savedKeys []SavedKey033x
	err = encoding.ReadFile(filepath033x, &savedKeys)
	if err != nil {
		return err
	}
	var seedsLoaded int
	for _, savedKey := range savedKeys {
		spendKey := spendableKey{
			UnlockConditions: savedKey.UnlockConditions,
			SecretKeys:       []crypto.SecretKey{savedKey.SecretKey},
		}
		err = w.loadSpendableKey(masterKey, spendKey)
		if err != nil && err != errDuplicateSpendableKey {
			return err
		}
		if err == nil {
			seedsLoaded++
		}
	}
	err = w.saveSettingsSync()
	if err != nil {
		return err
	}
	if seedsLoaded == 0 {
		return errAllDuplicates
	}
	return w.createBackup(filepath.Join(w.persistDir, "Sia Wallet Encrypted Backup - "+persist.RandomSuffix()+settingsFileSuffix))
}
Пример #9
0
// TestPrintKeyInfo probes the printKeyInfo function.
func TestPrintKeyInfo(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	testDir := build.TempDir("siakg", "TestPrintKeyInfo")

	// Check that a corrupted header or version will trigger an error.
	keyname := "headerCheck"
	_, err := generateKeys(1, 1, testDir, keyname)
	if err != nil {
		t.Fatal(err)
	}
	var kp, badKP KeyPair
	keyfile := filepath.Join(testDir, keyname+"_Key0"+FileExtension)
	err = encoding.ReadFile(keyfile, &kp)
	if err != nil {
		t.Fatal(err)
	}
	badKP = kp
	badKP.Header = "bad"
	err = encoding.WriteFile(keyfile, badKP)
	if err != nil {
		t.Fatal(err)
	}
	err = printKeyInfo(keyfile)
	if err != ErrUnknownHeader {
		t.Error("Expected ErrUnknownHeader:", err)
	}
	badKP = kp
	badKP.Version = "bad"
	err = encoding.WriteFile(keyfile, badKP)
	if err != nil {
		t.Fatal(err)
	}
	err = printKeyInfo(keyfile)
	if err != ErrUnknownVersion {
		t.Error("Expected ErrUnknownVersion:", err)
	}
}
Пример #10
0
// TestVerifyKeys proves the verifyKeys function.
func TestVerifyKeys(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	testDir := build.TempDir("siag", "TestVerifyKeys")

	// Check that a corrupted header or version will trigger an error.
	keyname := "headerCheck"
	uc, err := generateKeys(1, 1, testDir, keyname)
	if err != nil {
		t.Fatal(err)
	}
	var kp, badKP KeyPair
	keyfile := filepath.Join(testDir, keyname+"_Key0"+FileExtension)
	err = encoding.ReadFile(keyfile, &kp)
	if err != nil {
		t.Fatal(err)
	}
	badKP = kp
	badKP.Header = "bad"
	err = encoding.WriteFile(keyfile, badKP)
	if err != nil {
		t.Fatal(err)
	}
	err = verifyKeys(uc, testDir, keyname)
	if err != ErrUnknownHeader {
		t.Error("Expected ErrUnknownHeader:", err)
	}
	badKP = kp
	badKP.Version = "bad"
	err = encoding.WriteFile(keyfile, badKP)
	if err != nil {
		t.Fatal(err)
	}
	err = verifyKeys(uc, testDir, keyname)
	if err != ErrUnknownVersion {
		t.Error("Expected ErrUnknownVersion:", err)
	}

	// Create sets of keys that cover all boundaries from 0 of 1 to 5 of 9.
	// This is to check for errors in the keycheck calculations.
	for i := 1; i < 5; i++ {
		for j := i; j < 9; j++ {
			keyname := "genuine" + strconv.Itoa(i) + strconv.Itoa(j)
			uc, err := generateKeys(i, j, testDir, keyname)
			if err != nil {
				t.Fatal(err)
			}

			// Check that the validate under standard conditions.
			err = verifyKeys(uc, testDir, keyname)
			if err != nil {
				t.Error(err)
			}

			// Provide the wrong keyname to simulate a file does not exist error.
			err = verifyKeys(uc, testDir, "wrongName")
			if err == nil {
				t.Error("Expecting an error")
			}

			// Corrupt the unlock conditions of the files 1 by 1, and see that each
			// file is checked for validity.
			for k := 0; k < j; k++ {
				// Load, corrupt, and then save the keypair. This corruption
				// alters the UnlockConditions.
				var originalKP, badKP KeyPair
				keyfile := filepath.Join(testDir, keyname+"_Key"+strconv.Itoa(k)+FileExtension)
				err := encoding.ReadFile(keyfile, &originalKP)
				if err != nil {
					t.Fatal(err)
				}
				badKP = originalKP
				badKP.UnlockConditions.PublicKeys = nil
				err = encoding.WriteFile(keyfile, badKP)
				if err != nil {
					t.Fatal(err)
				}

				// Run verifyKeys with the corrupted file.
				err = verifyKeys(uc, testDir, keyname)
				if err == nil {
					t.Error("Expecting error after corrupting unlock conditions")
				}

				// Restore the original keyfile.
				err = encoding.WriteFile(keyfile, originalKP)
				if err != nil {
					t.Fatal(err)
				}

				// Verify that things work again.
				err = verifyKeys(uc, testDir, keyname)
				if err != nil {
					t.Fatal(err)
				}
			}

			// Corrupt the secret keys of the files 1 by 1, and see that each secret
			// key is checked for validity.
			for k := 0; k < j; k++ {
				// Load, corrupt, and then save the keypair. This corruption
				// alters the secret key.
				var originalKP, badKP KeyPair
				keyfile := filepath.Join(testDir, keyname+"_Key"+strconv.Itoa(k)+FileExtension)
				err := encoding.ReadFile(keyfile, &originalKP)
				if err != nil {
					t.Fatal(err)
				}
				badKP = originalKP
				badKP.SecretKey[0]++
				err = encoding.WriteFile(keyfile, badKP)
				if err != nil {
					t.Fatal(err)
				}

				// Run verifyKeys with the corrupted file.
				err = verifyKeys(uc, testDir, keyname)
				if err == nil {
					t.Error("Expecting error after corrupting unlock conditions")
				}

				// Restore the original keyfile.
				err = encoding.WriteFile(keyfile, originalKP)
				if err != nil {
					t.Fatal(err)
				}

				// Verify that things work again.
				err = verifyKeys(uc, testDir, keyname)
				if err != nil {
					t.Fatal(err)
				}
			}
		}
	}
}
Пример #11
0
// 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 = 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
}
Пример #12
0
// 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 = 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
}