Beispiel #1
0
// updateSSRtxRecord updates an SSRtx record in the SSRtx records bucket.
func updateSSRtxRecord(tx walletdb.Tx, hash *chainhash.Hash,
	record *ssrtxRecord) error {
	// Fetch the current content of the key.
	// Possible buggy behaviour: If deserialization fails,
	// we won't detect it here. We assume we're throwing
	// ErrSSRtxsNotFound.
	oldRecords, _ := fetchSSRtxRecords(tx, hash)

	// Don't reinsert records we already have.
	if ssrtxRecordExistsInRecords(record, oldRecords) {
		return nil
	}

	bucket := tx.RootBucket().Bucket(ssrtxRecordsBucketName)

	var records []*ssrtxRecord
	// Either create a slice if currently nothing exists for this
	// key in the db, or append the entry to the slice.
	if oldRecords == nil {
		records = make([]*ssrtxRecord, 1)
		records[0] = record
	} else {
		records = append(oldRecords, record)
	}

	// Write the serialized SSRtxs keyed by the sstx hash.
	serializedSSRtxsRecords := serializeSSRtxRecords(records)

	err := bucket.Put(hash.Bytes(), serializedSSRtxsRecords)
	if err != nil {
		str := fmt.Sprintf("failed to store ssrtx records '%s'", hash)
		return stakeStoreError(ErrDatabase, str, err)
	}
	return nil
}
Beispiel #2
0
func assertAddrIndexTipIsUpdated(db database.Db, t *testing.T, newestSha *chainhash.Hash, newestBlockIdx int64) {
	// Safe to ignore error, since height will be < 0 in "error" case.
	sha, height, _ := db.FetchAddrIndexTip()
	if newestBlockIdx != height {
		t.Fatalf("Height of address index tip failed to update, "+
			"expected %v, got %v", newestBlockIdx, height)
	}
	if !bytes.Equal(newestSha.Bytes(), sha.Bytes()) {
		t.Fatalf("Sha of address index tip failed to update, "+
			"expected %v, got %v", newestSha, sha)
	}
}
Beispiel #3
0
// fetchSStxRecord retrieves a tx record from the sstx records bucket
// with the given hash.
func fetchSStxRecord(tx walletdb.Tx, hash *chainhash.Hash) (*sstxRecord, error) {
	bucket := tx.RootBucket().Bucket(sstxRecordsBucketName)

	key := hash.Bytes()
	val := bucket.Get(key)
	if val == nil {
		str := fmt.Sprintf("missing sstx record for hash '%s'", hash.String())
		return nil, stakeStoreError(ErrSStxNotFound, str, nil)
	}

	return deserializeSStxRecord(val)
}
Beispiel #4
0
// fetchSSRtxRecords retrieves SSRtx records from the SSRtxRecords bucket with
// the given hash.
func fetchSSRtxRecords(ns walletdb.ReadBucket, hash *chainhash.Hash) ([]*ssrtxRecord,
	error) {

	bucket := ns.NestedReadBucket(ssrtxRecordsBucketName)

	key := hash.Bytes()
	val := bucket.Get(key)
	if val == nil {
		str := fmt.Sprintf("missing ssrtx records for hash '%s'", hash.String())
		return nil, stakeStoreError(ErrSSRtxsNotFound, str, nil)
	}

	return deserializeSSRtxRecords(val)
}
Beispiel #5
0
// fetchSStxRecordSStxTicketHash160 retrieves a ticket 0th output script or
// pubkeyhash from the sstx records bucket with the given hash.
func fetchSStxRecordSStxTicketHash160(ns walletdb.ReadBucket,
	hash *chainhash.Hash) (hash160 []byte, p2sh bool, err error) {

	bucket := ns.NestedReadBucket(sstxRecordsBucketName)

	key := hash.Bytes()
	val := bucket.Get(key)
	if val == nil {
		str := fmt.Sprintf("missing sstx record for hash '%s'", hash.String())
		return nil, false, stakeStoreError(ErrSStxNotFound, str, nil)
	}

	return deserializeSStxTicketHash160(val)
}
Beispiel #6
0
// updateSStxRecordVoteBits updates an individual ticket's intended voteBits
// which are used to override the default voteBits when voting.
func updateSStxRecordVoteBits(ns walletdb.ReadWriteBucket, hash *chainhash.Hash,
	voteBits stake.VoteBits) error {
	if len(voteBits.ExtendedBits) > stake.MaxSingleBytePushLength-2 {
		str := fmt.Sprintf("voteBitsExt too long (got %v bytes, want max %v)",
			len(voteBits.ExtendedBits), stake.MaxSingleBytePushLength-2)
		return stakeStoreError(ErrData, str, nil)
	}

	bucket := ns.NestedReadWriteBucket(sstxRecordsBucketName)

	key := hash.Bytes()
	val := bucket.Get(key)
	if val == nil {
		str := fmt.Sprintf("missing sstx record for hash '%s'", hash.String())
		return stakeStoreError(ErrSStxNotFound, str, nil)
	}
	valLen := len(val)
	valCopy := make([]byte, valLen, valLen)
	copy(valCopy, val)

	// Move the cursor to the voteBits position and rewrite it.
	curPos := 0
	curPos += int64Size
	curPos += int32Size

	// Write the intended votebits length (uint8).
	valCopy[curPos] = byte(int16Size + len(voteBits.ExtendedBits))
	curPos += int8Size

	// Write the first two bytes for the intended votebits.
	byteOrder.PutUint16(valCopy[curPos:curPos+int16Size], voteBits.Bits)
	curPos += int16Size

	// Copy the remaining data from voteBitsExt.
	copy(valCopy[curPos:], voteBits.ExtendedBits[:])

	err := bucket.Put(key, valCopy)
	if err != nil {
		str := fmt.Sprintf("failed to update sstxrecord votebits for '%s'", hash)
		return stakeStoreError(ErrDatabase, str, err)
	}
	return nil
}
Beispiel #7
0
// fetchSStxRecordVoteBits fetches an individual ticket's intended voteBits
// which are used to override the default voteBits when voting.
func fetchSStxRecordVoteBits(ns walletdb.ReadBucket, hash *chainhash.Hash) (bool,
	stake.VoteBits, error) {

	bucket := ns.NestedReadBucket(sstxRecordsBucketName)

	key := hash.Bytes()
	val := bucket.Get(key)
	if val == nil {
		str := fmt.Sprintf("missing sstx record for hash '%s'", hash.String())
		return false, stake.VoteBits{}, stakeStoreError(ErrSStxNotFound, str, nil)
	}
	valLen := len(val)
	valCopy := make([]byte, valLen, valLen)
	copy(valCopy, val)

	// Move the cursor to the voteBits position and rewrite it.
	curPos := 0
	curPos += int64Size
	curPos += int32Size

	// Read the intended votebits length (uint8). If it is unset, return now.
	// Check it for sanity.
	voteBitsLen := uint8(val[curPos])
	if voteBitsLen == 0 {
		return false, stake.VoteBits{}, nil
	}
	if voteBitsLen < 2 || voteBitsLen > stake.MaxSingleBytePushLength {
		str := fmt.Sprintf("corrupt votebits length '%v'", voteBitsLen)
		return false, stake.VoteBits{}, stakeStoreError(ErrData, str, nil)
	}
	curPos += int8Size

	// Read the first two bytes for the intended votebits.
	voteBits := byteOrder.Uint16(valCopy[curPos : curPos+int16Size])
	curPos += int16Size

	// Retrieve the extended vote bits.
	voteBitsExt := make([]byte, voteBitsLen-int16Size)
	copy(voteBitsExt[:], valCopy[curPos:(curPos+int(voteBitsLen)-int16Size)])

	return true, stake.VoteBits{Bits: voteBits, ExtendedBits: voteBitsExt}, nil
}
Beispiel #8
0
// GenerateSSGenBlockRef generates an OP_RETURN push for the block header hash and
// height which the block votes on.
func GenerateSSGenBlockRef(blockHash chainhash.Hash, height uint32) ([]byte,
	error) {
	// Prefix
	dataPushes := []byte{
		0x6a, // OP_RETURN
		0x24, // OP_DATA_36
	}

	// Serialize the block hash and height
	blockHashBytes := blockHash.Bytes()
	blockHeightBytes := make([]byte, 4)
	binary.LittleEndian.PutUint32(blockHeightBytes, height)

	blockData := append(blockHashBytes, blockHeightBytes...)

	// Concatenate the prefix and block data
	blockDataOut := append(dataPushes, blockData...)

	return blockDataOut, nil
}
Beispiel #9
0
// calcSignatureHash will, given a script and hash type for the current script
// engine instance, calculate the signature hash to be used for signing and
// verification.
func calcSignatureHash(script []parsedOpcode, hashType SigHashType,
	tx *wire.MsgTx, idx int, cachedPrefix *chainhash.Hash) ([]byte, error) {
	// The SigHashSingle signature type signs only the corresponding input
	// and output (the output with the same index number as the input).
	//
	// Since transactions can have more inputs than outputs, this means it
	// is improper to use SigHashSingle on input indices that don't have a
	// corresponding output.
	//
	// A bug in the original Satoshi client implementation means specifying
	// an index that is out of range results in a signature hash of 1 (as a
	// uint256 little endian).  The original intent appeared to be to
	// indicate failure, but unfortunately, it was never checked and thus is
	// treated as the actual signature hash.  This buggy behavior is now
	// part of the consensus and a hard fork would be required to fix it.
	//
	// Due to this, care must be taken by software that creates transactions
	// which make use of SigHashSingle because it can lead to an extremely
	// dangerous situation where the invalid inputs will end up signing a
	// hash of 1.  This in turn presents an opportunity for attackers to
	// cleverly construct transactions which can steal those coins provided
	// they can reuse signatures.
	//
	// Decred mitigates this by actually returning an error instead.
	if hashType&sigHashMask == SigHashSingle && idx >= len(tx.TxOut) {
		return nil, ErrSighashSingleIdx
	}

	// Remove all instances of OP_CODESEPARATOR from the script.
	script = removeOpcode(script, OP_CODESEPARATOR)

	// Make a deep copy of the transaction, zeroing out the script for all
	// inputs that are not currently being processed.
	txCopy := tx.Copy()
	for i := range txCopy.TxIn {
		if i == idx {
			// UnparseScript cannot fail here because removeOpcode
			// above only returns a valid script.
			sigScript, _ := unparseScript(script)
			txCopy.TxIn[idx].SignatureScript = sigScript
		} else {
			txCopy.TxIn[i].SignatureScript = nil
		}
	}

	switch hashType & sigHashMask {
	case SigHashNone:
		txCopy.TxOut = txCopy.TxOut[0:0] // Empty slice.
		for i := range txCopy.TxIn {
			if i != idx {
				txCopy.TxIn[i].Sequence = 0
			}
		}

	case SigHashSingle:
		// Resize output array to up to and including requested index.
		txCopy.TxOut = txCopy.TxOut[:idx+1]

		// All but current output get zeroed out.
		for i := 0; i < idx; i++ {
			txCopy.TxOut[i].Value = -1
			txCopy.TxOut[i].PkScript = nil
		}

		// Sequence on all other inputs is 0, too.
		for i := range txCopy.TxIn {
			if i != idx {
				txCopy.TxIn[i].Sequence = 0
			}
		}

	default:
		// Consensus treats undefined hashtypes like normal SigHashAll
		// for purposes of hash generation.
		fallthrough
	case SigHashOld:
		fallthrough
	case SigHashAllValue:
		fallthrough
	case SigHashAll:
		// Nothing special here.
	}
	if hashType&SigHashAnyOneCanPay != 0 {
		txCopy.TxIn = txCopy.TxIn[idx : idx+1]
		idx = 0
	}

	// The final hash (message to sign) is the hash of:
	// 1) hash of the prefix ||
	// 2) hash of the witness for signing ||
	// 3) the hash type (encoded as a 4-byte little-endian value)
	var wbuf bytes.Buffer
	binary.Write(&wbuf, binary.LittleEndian, uint32(hashType))

	// Optimization for SIGHASH_ALL. In this case, the prefix hash is
	// the same as the transaction hash because only the inputs have
	// been modified, so don't bother to do the wasteful O(N^2) extra
	// hash here.
	// The caching only works if the "anyone can pay flag" is also
	// disabled.
	var prefixHash chainhash.Hash
	if cachedPrefix != nil &&
		(hashType&sigHashMask == SigHashAll) &&
		(hashType&SigHashAnyOneCanPay == 0) &&
		chaincfg.SigHashOptimization {
		prefixHash = *cachedPrefix
	} else {
		prefixHash = txCopy.TxSha()
	}

	// If the ValueIn is to be included in what we're signing, sign
	// the witness hash that includes it. Otherwise, just sign the
	// prefix and signature scripts.
	var witnessHash chainhash.Hash
	if hashType&sigHashMask != SigHashAllValue {
		witnessHash = txCopy.TxShaWitnessSigning()
	} else {
		witnessHash = txCopy.TxShaWitnessValueSigning()
	}
	wbuf.Write(prefixHash.Bytes())
	wbuf.Write(witnessHash.Bytes())
	return chainhash.HashFuncB(wbuf.Bytes()), nil
}
Beispiel #10
0
// AddShaHash adds the passed chainhash.Hash to the Filter.
//
// This function is safe for concurrent access.
func (bf *Filter) AddShaHash(sha *chainhash.Hash) {
	bf.mtx.Lock()
	bf.add(sha.Bytes())
	bf.mtx.Unlock()
}