// 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 }
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) } }
// 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) }
// 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) }
// 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) }
// 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 }
// 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 }
// 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 }
// 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 }
// 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() }