Example #1
0
// TimelockedCoinAddress returns an address that can only be spent after block
// `unlockHeight`.
func (w *Wallet) timelockedCoinAddress(unlockHeight types.BlockHeight, visible bool) (coinAddress types.UnlockHash, unlockConditions types.UnlockConditions, err error) {
	// Create the address + spend conditions.
	sk, pk, err := crypto.GenerateSignatureKeys()
	if err != nil {
		return
	}
	unlockConditions = types.UnlockConditions{
		Timelock:           unlockHeight,
		SignaturesRequired: 1,
		PublicKeys: []types.SiaPublicKey{
			types.SiaPublicKey{
				Algorithm: types.SignatureEd25519,
				Key:       encoding.Marshal(pk),
			},
		},
	}
	coinAddress = unlockConditions.UnlockHash()

	// Create a spendableAddress for the keys and add it to the
	// timelockedUnlockableAddresses map. If the address has already been
	// unlocked, also add it to the list of currently spendable addresses. It
	// needs to go in both though in case there is a reorganization of the
	// blockchain.
	w.keys[coinAddress] = &key{
		spendable:        w.consensusHeight >= unlockHeight,
		unlockConditions: unlockConditions,
		secretKey:        sk,

		outputs: make(map[types.SiacoinOutputID]*knownOutput),
	}

	// Add this key to the list of addresses that get unlocked at
	// `unlockHeight`
	w.timelockedKeys[unlockHeight] = append(w.timelockedKeys[unlockHeight], coinAddress)

	// Put the address in the list of visible addresses if 'visible' is set.
	if visible {
		w.visibleAddresses[coinAddress] = struct{}{}
	}

	// Save the wallet state, which now includes the new address.
	err = w.save()
	if err != nil {
		return
	}

	return
}
// BenchmarkStandaloneValid times how long it takes to verify a single
// large transaction, with a certain number of signatures
func BenchmarkStandaloneValid(b *testing.B) {
	numSigs := 7
	// make a transaction numSigs with valid inputs with valid signatures
	b.ReportAllocs()
	txn := Transaction{}
	sk := make([]crypto.SecretKey, numSigs)
	pk := make([]crypto.PublicKey, numSigs)
	for i := 0; i < numSigs; i++ {
		s, p, err := crypto.GenerateSignatureKeys()
		if err != nil {
			b.Fatal(err)
		}
		sk[i] = s
		pk[i] = p

		uc := UnlockConditions{
			PublicKeys: []SiaPublicKey{
				{Algorithm: SignatureEd25519, Key: pk[i][:]},
			},
			SignaturesRequired: 1,
		}
		txn.SiacoinInputs = append(txn.SiacoinInputs, SiacoinInput{
			UnlockConditions: uc,
		})
		copy(txn.SiacoinInputs[i].ParentID[:], encoding.Marshal(i))
		txn.TransactionSignatures = append(txn.TransactionSignatures, TransactionSignature{
			CoveredFields: CoveredFields{WholeTransaction: true},
		})
		copy(txn.TransactionSignatures[i].ParentID[:], encoding.Marshal(i))
	}
	// Transaction must be constructed before signing
	for i := 0; i < numSigs; i++ {
		sigHash := txn.SigHash(i)
		sig0, err := crypto.SignHash(sigHash, sk[i])
		if err != nil {
			b.Fatal(err)
		}
		txn.TransactionSignatures[i].Signature = sig0[:]
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		err := txn.StandaloneValid(10)
		if err != nil {
			b.Fatal(err)
		}
	}
}
Example #3
0
// coinAddress returns a new address for receiving coins.
func (w *Wallet) coinAddress(visible bool) (coinAddress types.UnlockHash, unlockConditions types.UnlockConditions, err error) {
	// Create the keys and address.
	sk, pk, err := crypto.GenerateSignatureKeys()
	if err != nil {
		return
	}
	unlockConditions = types.UnlockConditions{
		SignaturesRequired: 1,
		PublicKeys: []types.SiaPublicKey{
			types.SiaPublicKey{
				Algorithm: types.SignatureEd25519,
				Key:       encoding.Marshal(pk),
			},
		},
	}
	coinAddress = unlockConditions.UnlockHash()

	// Add the address to the set of spendable addresses.
	newKey := &key{
		spendable:        true,
		unlockConditions: unlockConditions,
		secretKey:        sk,

		outputs: make(map[types.SiacoinOutputID]*knownOutput),
	}
	w.keys[coinAddress] = newKey

	if visible {
		w.visibleAddresses[coinAddress] = struct{}{}
	}

	// Save the wallet state, which now includes the new address.
	err = w.save()
	if err != nil {
		return
	}

	return
}
Example #4
0
// TestTransactionValidSignatures probes the validSignatures method of the
// Transaction type.
func TestTransactionValidSignatures(t *testing.T) {
	// Create keys for use in signing and verifying.
	sk, pk, err := crypto.GenerateSignatureKeys()
	if err != nil {
		t.Fatal(err)
	}

	// Create UnlockConditions with 3 keys, 2 of which are required. The first
	// possible key is a standard signature. The second key is an unknown
	// signature type, which should always be accepted. The final type is an
	// entropy type, which should never be accepted.
	uc := UnlockConditions{
		PublicKeys: []SiaPublicKey{
			{Algorithm: SignatureEd25519, Key: pk[:]},
			{},
			{Algorithm: SignatureEntropy},
		},
		SignaturesRequired: 2,
	}

	// Create a transaction with each type of unlock condition.
	txn := Transaction{
		SiacoinInputs: []SiacoinInput{
			{UnlockConditions: uc},
		},
		FileContractRevisions: []FileContractRevision{
			{UnlockConditions: uc},
		},
		SiafundInputs: []SiafundInput{
			{UnlockConditions: uc},
		},
	}
	txn.FileContractRevisions[0].ParentID[0] = 1 // can't overlap with other objects
	txn.SiafundInputs[0].ParentID[0] = 2         // can't overlap with other objects

	// Create the signatures that spend the output.
	txn.TransactionSignatures = []TransactionSignature{
		// First signatures use cryptography.
		{
			Timelock:      5,
			CoveredFields: CoveredFields{WholeTransaction: true},
		},
		{
			CoveredFields: CoveredFields{WholeTransaction: true},
		},
		{
			CoveredFields: CoveredFields{WholeTransaction: true},
		},

		// The second signatures should always work for being unrecognized
		// types.
		{PublicKeyIndex: 1},
		{PublicKeyIndex: 1},
		{PublicKeyIndex: 1},
	}
	txn.TransactionSignatures[1].ParentID[0] = 1
	txn.TransactionSignatures[2].ParentID[0] = 2
	txn.TransactionSignatures[4].ParentID[0] = 1
	txn.TransactionSignatures[5].ParentID[0] = 2
	sigHash0 := txn.SigHash(0)
	sigHash1 := txn.SigHash(1)
	sigHash2 := txn.SigHash(2)
	sig0, err := crypto.SignHash(sigHash0, sk)
	if err != nil {
		t.Fatal(err)
	}
	sig1, err := crypto.SignHash(sigHash1, sk)
	if err != nil {
		t.Fatal(err)
	}
	sig2, err := crypto.SignHash(sigHash2, sk)
	if err != nil {
		t.Fatal(err)
	}
	txn.TransactionSignatures[0].Signature = sig0[:]
	txn.TransactionSignatures[1].Signature = sig1[:]
	txn.TransactionSignatures[2].Signature = sig2[:]

	// Check that the signing was successful.
	err = txn.validSignatures(10)
	if err != nil {
		t.Error(err)
	}

	// Corrupt one of the sigantures.
	sig0[0]++
	txn.TransactionSignatures[0].Signature = sig0[:]
	err = txn.validSignatures(10)
	if err == nil {
		t.Error("Corrupted a signature but the txn was still accepted as valid!")
	}
	sig0[0]--
	txn.TransactionSignatures[0].Signature = sig0[:]

	// Fail the validCoveredFields check.
	txn.TransactionSignatures[0].CoveredFields.SiacoinInputs = []uint64{33}
	err = txn.validSignatures(10)
	if err == nil {
		t.Error("failed to flunk the validCoveredFields check")
	}
	txn.TransactionSignatures[0].CoveredFields.SiacoinInputs = nil

	// Double spend a SiacoinInput, FileContractTermination, and SiafundInput.
	txn.SiacoinInputs = append(txn.SiacoinInputs, SiacoinInput{UnlockConditions: UnlockConditions{}})
	err = txn.validSignatures(10)
	if err == nil {
		t.Error("failed to double spend a siacoin input")
	}
	txn.SiacoinInputs = txn.SiacoinInputs[:len(txn.SiacoinInputs)-1]
	txn.FileContractRevisions = append(txn.FileContractRevisions, FileContractRevision{UnlockConditions: UnlockConditions{}})
	err = txn.validSignatures(10)
	if err == nil {
		t.Error("failed to double spend a file contract termination")
	}
	txn.FileContractRevisions = txn.FileContractRevisions[:len(txn.FileContractRevisions)-1]
	txn.SiafundInputs = append(txn.SiafundInputs, SiafundInput{UnlockConditions: UnlockConditions{}})
	err = txn.validSignatures(10)
	if err == nil {
		t.Error("failed to double spend a siafund input")
	}
	txn.SiafundInputs = txn.SiafundInputs[:len(txn.SiafundInputs)-1]

	// Add a frivilous signature
	txn.TransactionSignatures = append(txn.TransactionSignatures, TransactionSignature{})
	err = txn.validSignatures(10)
	if err != ErrFrivilousSignature {
		t.Error(err)
	}
	txn.TransactionSignatures = txn.TransactionSignatures[:len(txn.TransactionSignatures)-1]

	// Replace one of the cryptography signatures with an always-accepted
	// signature. This should get rejected because the always-accepted
	// signature has already been used.
	tmpTxn0 := txn.TransactionSignatures[0]
	txn.TransactionSignatures[0] = TransactionSignature{PublicKeyIndex: 1}
	err = txn.validSignatures(10)
	if err != ErrPublicKeyOveruse {
		t.Error(err)
	}
	txn.TransactionSignatures[0] = tmpTxn0

	// Fail the timelock check for signatures.
	err = txn.validSignatures(4)
	if err != ErrPrematureSignature {
		t.Error(err)
	}

	// Try to spend an entropy signature.
	txn.TransactionSignatures[0] = TransactionSignature{PublicKeyIndex: 2}
	err = txn.validSignatures(10)
	if err != ErrEntropyKey {
		t.Error(err)
	}
	txn.TransactionSignatures[0] = tmpTxn0

	// Try to point to a nonexistant public key.
	txn.TransactionSignatures[0] = TransactionSignature{PublicKeyIndex: 5}
	err = txn.validSignatures(10)
	if err != ErrInvalidPubKeyIndex {
		t.Error(err)
	}
	txn.TransactionSignatures[0] = tmpTxn0

	// Insert a malformed public key into the transaction.
	txn.SiacoinInputs[0].UnlockConditions.PublicKeys[0].Key = []byte{'b', 'a', 'd'}
	err = txn.validSignatures(10)
	if err == nil {
		t.Error(err)
	}
	txn.SiacoinInputs[0].UnlockConditions.PublicKeys[0].Key = pk[:]

	// Insert a malformed signature into the transaction.
	txn.TransactionSignatures[0].Signature = []byte{'m', 'a', 'l'}
	err = txn.validSignatures(10)
	if err == nil {
		t.Error(err)
	}
	txn.TransactionSignatures[0] = tmpTxn0

	// Try to spend a transaction when not every required signature is
	// available.
	txn.TransactionSignatures = txn.TransactionSignatures[1:]
	err = txn.validSignatures(10)
	if err != ErrMissingSignatures {
		t.Error(err)
	}
}
Example #5
0
func TestReviseContract(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	rt, err := newRenterTester("TestNegotiateContract")
	if err != nil {
		t.Fatal(err)
	}

	// get an address
	ourAddr, err := rt.wallet.NextAddress()
	if err != nil {
		t.Fatal(err)
	}

	// generate keys
	sk, pk, err := crypto.GenerateSignatureKeys()
	if err != nil {
		t.Fatal(err)
	}
	renterPubKey := types.SiaPublicKey{
		Algorithm: types.SignatureEd25519,
		Key:       pk[:],
	}

	uc := types.UnlockConditions{
		PublicKeys:         []types.SiaPublicKey{renterPubKey, renterPubKey},
		SignaturesRequired: 1,
	}

	// create file contract
	payout := types.NewCurrency64(1e16)

	fc := types.FileContract{
		FileSize:       0,
		FileMerkleRoot: crypto.Hash{}, // no proof possible without data
		WindowStart:    100,
		WindowEnd:      1000,
		Payout:         payout,
		UnlockHash:     uc.UnlockHash(),
		RevisionNumber: 0,
	}
	// outputs need account for tax
	fc.ValidProofOutputs = []types.SiacoinOutput{
		{Value: payout.Sub(fc.Tax()), UnlockHash: ourAddr.UnlockHash()},
		{Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}}, // no collateral
	}
	fc.MissedProofOutputs = []types.SiacoinOutput{
		// same as above
		fc.ValidProofOutputs[0],
		// goes to the void, not the renter
		{Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}},
	}

	txnBuilder := rt.wallet.StartTransaction()
	err = txnBuilder.FundSiacoins(fc.Payout)
	if err != nil {
		t.Fatal(err)
	}
	txnBuilder.AddFileContract(fc)
	signedTxnSet, err := txnBuilder.Sign(true)
	if err != nil {
		t.Fatal(err)
	}

	// submit contract
	err = rt.tpool.AcceptTransactionSet(signedTxnSet)
	if err != nil {
		t.Fatal(err)
	}

	// create revision
	fcid := signedTxnSet[len(signedTxnSet)-1].FileContractID(0)
	rev := types.FileContractRevision{
		ParentID:              fcid,
		UnlockConditions:      uc,
		NewFileSize:           10,
		NewWindowStart:        100,
		NewWindowEnd:          1000,
		NewRevisionNumber:     1,
		NewValidProofOutputs:  fc.ValidProofOutputs,
		NewMissedProofOutputs: fc.MissedProofOutputs,
	}

	// create transaction containing the revision
	signedTxn := types.Transaction{
		FileContractRevisions: []types.FileContractRevision{rev},
		TransactionSignatures: []types.TransactionSignature{{
			ParentID:       crypto.Hash(fcid),
			CoveredFields:  types.CoveredFields{FileContractRevisions: []uint64{0}},
			PublicKeyIndex: 0, // renter key is always first -- see negotiateContract
		}},
	}

	// sign the transaction
	encodedSig, err := crypto.SignHash(signedTxn.SigHash(0), sk)
	if err != nil {
		t.Fatal(err)
	}
	signedTxn.TransactionSignatures[0].Signature = encodedSig[:]

	err = signedTxn.StandaloneValid(rt.renter.blockHeight)
	if err != nil {
		t.Fatal(err)
	}

	// submit revision
	err = rt.tpool.AcceptTransactionSet([]types.Transaction{signedTxn})
	if err != nil {
		t.Fatal(err)
	}
}
Example #6
0
// generateKeys generates a set of keys and saves them to disk.
func generateKeys(requiredKeys int, totalKeys int, folder string, keyname string) (types.UnlockConditions, error) {
	// Check that the inputs have sane values.
	if requiredKeys < 1 {
		return types.UnlockConditions{}, ErrInsecureAddress
	}
	if totalKeys < requiredKeys {
		return types.UnlockConditions{}, ErrUnspendableAddress
	}

	// Generate 'TotalKeys', filling out everything except the unlock
	// conditions.
	keys := make([]KeyPair, totalKeys)
	pubKeys := make([]crypto.PublicKey, totalKeys)
	for i := range keys {
		var err error
		keys[i].Header = FileHeader
		keys[i].Version = FileVersion
		keys[i].Index = i
		keys[i].SecretKey, pubKeys[i], err = crypto.GenerateSignatureKeys()
		if err != nil {
			return types.UnlockConditions{}, err
		}
	}

	// Generate the unlock conditions and add them to each KeyPair object. This
	// must be done second because the keypairs can't be given unlock
	// conditions until the PublicKeys have all been added.
	unlockConditions := types.UnlockConditions{
		Timelock:           0,
		SignaturesRequired: uint64(requiredKeys),
	}
	for i := range keys {
		unlockConditions.PublicKeys = append(unlockConditions.PublicKeys, types.SiaPublicKey{
			Algorithm: types.SignatureEd25519,
			Key:       pubKeys[i][:],
		})
	}
	for i := range keys {
		keys[i].UnlockConditions = unlockConditions
	}

	// Save the KeyPairs to disk.
	if folder != "" {
		err := os.MkdirAll(folder, 0700)
		if err != nil {
			return types.UnlockConditions{}, err
		}
	}
	for i, key := range keys {
		keyFilename := filepath.Join(folder, keyname+"_Key"+strconv.Itoa(i)+FileExtension)
		_, err := os.Stat(keyFilename)
		if !os.IsNotExist(err) {
			if err != nil {
				return types.UnlockConditions{}, err
			}
			return types.UnlockConditions{}, ErrOverwrite
		}
		err = encoding.WriteFile(keyFilename, key)
		if err != nil {
			return types.UnlockConditions{}, err
		}
	}

	return unlockConditions, nil
}
Example #7
0
// New returns an initialized Host.
func New(cs *consensus.ConsensusSet, hdb modules.HostDB, tpool modules.TransactionPool, wallet modules.Wallet, addr string, saveDir string) (*Host, error) {
	if cs == nil {
		return nil, errors.New("host cannot use a nil state")
	}
	if hdb == nil {
		return nil, errors.New("host cannot use a nil hostdb")
	}
	if tpool == nil {
		return nil, errors.New("host cannot use a nil tpool")
	}
	if wallet == nil {
		return nil, errors.New("host cannot use a nil wallet")
	}

	// Create host directory if it does not exist.
	err := os.MkdirAll(saveDir, 0700)
	if err != nil {
		return nil, err
	}

	h := &Host{
		cs:     cs,
		hostdb: hdb,
		tpool:  tpool,
		wallet: wallet,

		// default host settings
		HostSettings: modules.HostSettings{
			TotalStorage: 10e9,                        // 10 GB
			MaxFilesize:  5 * 1024 * 1024 * 1024,      // 5 GiB
			MaxDuration:  144 * 60,                    // 60 days
			WindowSize:   288,                         // 48 hours
			Price:        types.NewCurrency64(100e12), // 0.1 siacoin / mb / week
			Collateral:   types.NewCurrency64(0),
		},

		saveDir: saveDir,

		obligationsByID:     make(map[types.FileContractID]contractObligation),
		obligationsByHeight: make(map[types.BlockHeight][]contractObligation),

		mu: safesync.New(modules.SafeMutexDelay, 1),
	}
	h.spaceRemaining = h.TotalStorage

	// Generate signing key, for revising contracts.
	sk, pk, err := crypto.GenerateSignatureKeys()
	if err != nil {
		return nil, err
	}
	h.secretKey = sk
	h.publicKey = types.SiaPublicKey{
		Algorithm: types.SignatureEd25519,
		Key:       pk[:],
	}

	// Load the old host data.
	err = h.load()
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	// Create listener and set address.
	h.listener, err = net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}
	_, port, _ := net.SplitHostPort(h.listener.Addr().String())
	h.myAddr = modules.NetAddress(net.JoinHostPort("::1", port))

	// Learn our external IP.
	go h.learnHostname()

	// Forward the hosting port, if possible.
	go h.forwardPort(port)

	// spawn listener
	go h.listen()

	h.cs.ConsensusSetSubscribe(h)

	return h, nil
}