// RegisterTransaction takes a transaction and its parents and returns a // TransactionBuilder which can be used to expand the transaction. The most // typical call is 'RegisterTransaction(types.Transaction{}, nil)', which // registers a new transaction without parents. func (w *Wallet) RegisterTransaction(t types.Transaction, parents []types.Transaction) modules.TransactionBuilder { // Create a deep copy of the transaction and parents by encoding them. A // deep copy ensures that there are no pointer or slice related errors - // the builder will be working directly on the transaction, and the // transaction may be in use elsewhere (in this case, the host is using the // transaction. pBytes := encoding.Marshal(parents) var pCopy []types.Transaction err := encoding.Unmarshal(pBytes, &pCopy) if err != nil { panic(err) } tBytes := encoding.Marshal(t) var tCopy types.Transaction err = encoding.Unmarshal(tBytes, &tCopy) if err != nil { panic(err) } return &transactionBuilder{ parents: pCopy, transaction: tCopy, wallet: w, } }
// Returns the transaction with the given id func (db *explorerDB) getTransaction(id crypto.Hash) (modules.TransactionResponse, error) { var tr modules.TransactionResponse // Look up the transaction's location tBytes, err := db.GetFromBucket("Transactions", encoding.Marshal(id)) if err != nil { return tr, err } var tLocation txInfo err = encoding.Unmarshal(tBytes, &tLocation) if err != nil { return tr, err } // Look up the block specified by the location and extract the transaction bBytes, err := db.GetFromBucket("Blocks", encoding.Marshal(tLocation.BlockID)) if err != nil { return tr, err } var block types.Block err = encoding.Unmarshal(bBytes, &block) if err != nil { return tr, err } tr.Tx = block.Transactions[tLocation.TxNum] tr.ParentID = tLocation.BlockID tr.TxNum = tLocation.TxNum tr.ResponseType = responseTransaction return tr, nil }
// dbGetTransactionIDSet returns a 'func(*bolt.Tx) error' that decodes a // bucket of transaction IDs into a slice. If the bucket is nil, // dbGetTransactionIDSet returns errNotExist. func dbGetTransactionIDSet(bucket []byte, key interface{}, ids *[]types.TransactionID) func(*bolt.Tx) error { return func(tx *bolt.Tx) error { b := tx.Bucket(bucket).Bucket(encoding.Marshal(key)) if b == nil { return errNotExist } // decode into a local slice var txids []types.TransactionID err := b.ForEach(func(txid, _ []byte) error { var id types.TransactionID err := encoding.Unmarshal(txid, &id) if err != nil { return err } txids = append(txids, id) return nil }) if err != nil { return err } // set pointer *ids = txids return nil } }
// findHostAnnouncements returns a list of the host announcements found within // a given block. No check is made to see that the ip address found in the // announcement is actually a valid ip address. func findHostAnnouncements(b types.Block) (announcements []modules.HostSettings) { for _, t := range b.Transactions { // the HostAnnouncement must be prefaced by the standard host // announcement string var prefix types.Specifier for _, arb := range t.ArbitraryData { copy(prefix[:], arb) if prefix != modules.PrefixHostAnnouncement { continue } // decode the HostAnnouncement var ha modules.HostAnnouncement err := encoding.Unmarshal(arb[types.SpecifierLen:], &ha) if err != nil { continue } // Add the announcement to the slice being returned. announcements = append(announcements, modules.HostSettings{ NetAddress: ha.IPAddress, }) } } return }
// applySiacoinInputs takes all of the siacoin inputs in a transaction and // applies them to the state, updating the diffs in the processed block. func (cs *ConsensusSet) applySiacoinInputs(tx *bolt.Tx, pb *processedBlock, t types.Transaction) error { // Remove all siacoin inputs from the unspent siacoin outputs list. scoBucket := tx.Bucket(SiacoinOutputs) for _, sci := range t.SiacoinInputs { scoBytes := scoBucket.Get(sci.ParentID[:]) if build.DEBUG && scoBytes == nil { panic(ErrMisuseApplySiacoinInput) } var sco types.SiacoinOutput err := encoding.Unmarshal(scoBytes, &sco) if err != nil { return err } scod := modules.SiacoinOutputDiff{ Direction: modules.DiffRevert, ID: sci.ParentID, SiacoinOutput: sco, } pb.SiacoinOutputDiffs = append(pb.SiacoinOutputDiffs, scod) err = cs.commitBucketSiacoinOutputDiff(scoBucket, scod, modules.DiffApply) if err != nil { return err } } return nil }
func forEachDSCO(tx *bolt.Tx, bh types.BlockHeight, fn func(id types.SiacoinOutputID, sco types.SiacoinOutput) error) error { bucketID := append(prefix_dsco, encoding.Marshal(bh)...) return forEach(tx, bucketID, func(kb, vb []byte) error { var key types.SiacoinOutputID var value types.SiacoinOutput err := encoding.Unmarshal(kb, &key) if err != nil { return err } err = encoding.Unmarshal(vb, &value) if err != nil { return err } return fn(key, value) }) }
// forEachSiacoinOutputs applies a function to every siacoin output and ID func (db *setDB) forEachSiacoinOutputs(fn func(k types.SiacoinOutputID, v types.SiacoinOutput)) { db.forEachItem(SiacoinOutputs, func(kb, vb []byte) error { var key types.SiacoinOutputID var value types.SiacoinOutput err := encoding.Unmarshal(kb, &key) if err != nil { return err } err = encoding.Unmarshal(vb, &value) if err != nil { return err } fn(key, value) return nil }) }
// TestAnnouncement has a host announce itself to the blockchain and then // checks that the announcement makes it correctly. func TestAnnouncement(t *testing.T) { ht := CreateHostTester("TestAnnouncement", t) // Place the announcement. err := ht.host.ForceAnnounce() if err != nil { t.Fatal(err) } ht.tpUpdateWait() // Check that the announcement made it into the transaction pool correctly. txns := ht.tpool.TransactionSet() if len(txns) != 1 { t.Error("Expecting 1 transaction in transaction pool, instead there was", len(txns)) } encodedAnnouncement := txns[0].ArbitraryData[0][types.SpecifierLen:] var ha modules.HostAnnouncement err = encoding.Unmarshal([]byte(encodedAnnouncement), &ha) if err != nil { t.Error(err) } // TODO: Need to check that the host announcement gets the host into the // hostdb. }
// validSiacoins checks that the siacoin inputs and outputs are valid in the // context of the current consensus set. func validSiacoins(tx *bolt.Tx, t types.Transaction) error { scoBucket := tx.Bucket(SiacoinOutputs) var inputSum types.Currency for _, sci := range t.SiacoinInputs { // Check that the input spends an existing output. scoBytes := scoBucket.Get(sci.ParentID[:]) if scoBytes == nil { return errMissingSiacoinOutput } // Check that the unlock conditions match the required unlock hash. var sco types.SiacoinOutput err := encoding.Unmarshal(scoBytes, &sco) if build.DEBUG && err != nil { panic(err) } if sci.UnlockConditions.UnlockHash() != sco.UnlockHash { return errWrongUnlockConditions } inputSum = inputSum.Add(sco.Value) } if inputSum.Cmp(t.SiacoinOutputSum()) != 0 { return errSiacoinInputOutputMismatch } return nil }
// GetHashInfo returns sufficient data about the hash that was // provided to do more extensive lookups func (e *Explorer) GetHashInfo(hash []byte) (interface{}, error) { if len(hash) < crypto.HashSize { return nil, errors.New("requested hash not long enough") } lockID := e.mu.RLock() defer e.mu.RUnlock(lockID) // Perform a lookup to tell which type of hash it is typeBytes, err := e.db.GetFromBucket("Hashes", hash[:crypto.HashSize]) if err != nil { return nil, err } var hashType int err = encoding.Unmarshal(typeBytes, &hashType) if err != nil { return nil, err } switch hashType { case hashBlock: var id types.BlockID copy(id[:], hash[:]) return e.db.getBlock(types.BlockID(id)) case hashTransaction: var id crypto.Hash copy(id[:], hash[:]) return e.db.getTransaction(id) case hashFilecontract: var id types.FileContractID copy(id[:], hash[:]) return e.db.getFileContract(id) case hashCoinOutputID: var id types.SiacoinOutputID copy(id[:], hash[:]) return e.db.getSiacoinOutput(id) case hashFundOutputID: var id types.SiafundOutputID copy(id[:], hash[:]) return e.db.getSiafundOutput(id) case hashUnlockHash: // Check that the address is valid before doing a lookup if len(hash) != crypto.HashSize+types.UnlockHashChecksumSize { return nil, errors.New("address does not have a valid checksum") } var id types.UnlockHash copy(id[:], hash[:crypto.HashSize]) uhChecksum := crypto.HashObject(id) givenChecksum := hash[crypto.HashSize : crypto.HashSize+types.UnlockHashChecksumSize] if !bytes.Equal(givenChecksum, uhChecksum[:types.UnlockHashChecksumSize]) { return nil, errors.New("address does not have a valid checksum") } return e.db.getAddressTransactions(id) default: return nil, errors.New("bad hash type") } }
// findHostAnnouncements returns a list of the host announcements found within // a given block. No check is made to see that the ip address found in the // announcement is actually a valid ip address. func findHostAnnouncements(b types.Block) (announcements []modules.HostSettings) { for _, t := range b.Transactions { for _, data := range t.ArbitraryData { // the HostAnnouncement must be prefaced by the standard host announcement string if !strings.HasPrefix(data, modules.PrefixHostAnnouncement) { continue } // decode the HostAnnouncement var ha modules.HostAnnouncement encAnnouncement := []byte(strings.TrimPrefix(data, modules.PrefixHostAnnouncement)) err := encoding.Unmarshal(encAnnouncement, &ha) if err != nil { continue } // Add the announcement to the slice being returned. announcements = append(announcements, modules.HostSettings{ IPAddress: ha.IPAddress, }) } } return }
// EarliestChildTimestamp returns the earliest timestamp that the next block can // have in order for it to be considered valid. func (cs *ConsensusSet) EarliestChildTimestamp(bid types.BlockID) (timestamp types.Timestamp, exists bool) { // Lock is not needed because the values being read will not change once // they have been created. err := cs.db.View(func(tx *bolt.Tx) error { // Check that the parent exists. blockMap := tx.Bucket(BlockMap) // The identifier for the BlockMap is the sia encoding of the parent // id. The sia encoding is the same as ParentID[:]. var parent processedBlock parentBytes := blockMap.Get(bid[:]) if parentBytes == nil { return ErrOrphan } err := encoding.Unmarshal(parentBytes, &parent) if err != nil { return err } timestamp = earliestChildTimestamp(blockMap, &parent) return nil }) if err != nil { return 0, false } return timestamp, true }
// forEachFileContracts applies a function to each (file contract id, filecontract) // pair in the consensus set func (db *setDB) forEachFileContracts(fn func(k types.FileContractID, v types.FileContract)) { db.forEachItem(FileContracts, func(kb, vb []byte) error { var key types.FileContractID var value types.FileContract err := encoding.Unmarshal(kb, &key) if err != nil { return err } err = encoding.Unmarshal(vb, &value) if err != nil { return err } fn(key, value) return nil }) }
// initUnseededKeys loads all of the unseeded keys into the wallet after the // wallet gets unlocked. func (w *Wallet) initUnseededKeys(masterKey crypto.TwofishKey) error { for _, uk := range w.persist.UnseededKeys { // Verify that the decryption key is correct. encKey := uidEncryptionKey(masterKey, uk.UID) expectedDecryptedVerification := make([]byte, crypto.EntropySize) decryptedVerification, err := encKey.DecryptBytes(uk.EncryptionVerification) if err != nil { return err } if !bytes.Equal(expectedDecryptedVerification, decryptedVerification) { return modules.ErrBadEncryptionKey } // Decrypt the spendable key and add it to the wallet. encodedKey, err := encKey.DecryptBytes(uk.SpendableKey) if err != nil { return err } var sk spendableKey err = encoding.Unmarshal(encodedKey, &sk) if err != nil { return err } w.keys[sk.UnlockConditions.UnlockHash()] = sk } return nil }
// Returns an array of block summaries. Bounds checking should be done elsewhere func (db *explorerDB) dbBlockSummaries(start types.BlockHeight, finish types.BlockHeight) ([]modules.ExplorerBlockData, error) { summaries := make([]modules.ExplorerBlockData, int(finish-start)) err := db.View(func(tx *bolt.Tx) error { heights := tx.Bucket([]byte("Heights")) // Iterate over each block height, constructing a // summary data structure for each block for i := start; i < finish; i++ { bSummaryBytes := heights.Get(encoding.Marshal(i)) if bSummaryBytes == nil { return errors.New("Block not found in height bucket") } err := encoding.Unmarshal(bSummaryBytes, &summaries[i-start]) if err != nil { return err } } return nil }) if err != nil { return nil, err } return summaries, nil }
// inconsistencyDetected indicates whether inconsistency has been detected // within the database. func inconsistencyDetected(tx *bolt.Tx) (detected bool) { inconsistencyBytes := tx.Bucket(Consistency).Get(Consistency) err := encoding.Unmarshal(inconsistencyBytes, &detected) if build.DEBUG && err != nil { panic(err) } return }
// forEachDelayedSiacoinOutputsHeight applies a function to every siacoin output at a given height func (db *setDB) forEachDelayedSiacoinOutputsHeight(h types.BlockHeight, fn func(k types.SiacoinOutputID, v types.SiacoinOutput)) { bucketID := append(prefix_dsco, encoding.Marshal(h)...) db.forEachItem(bucketID, func(kb, vb []byte) error { var key types.SiacoinOutputID var value types.SiacoinOutput err := encoding.Unmarshal(kb, &key) if err != nil { return err } err = encoding.Unmarshal(vb, &value) if err != nil { return err } fn(key, value) return nil }) }
// dbGetAndDecode returns a 'func(*bolt.Tx) error' that retrieves and decodes // a value from the specified bucket. If the value does not exist, // dbGetAndDecode returns errNotExist. func dbGetAndDecode(bucket []byte, key, val interface{}) func(*bolt.Tx) error { return func(tx *bolt.Tx) error { valBytes := tx.Bucket(bucket).Get(encoding.Marshal(key)) if valBytes == nil { return errNotExist } return encoding.Unmarshal(valBytes, val) } }
// blockHeight returns the height of the blockchain. func blockHeight(tx *bolt.Tx) types.BlockHeight { var height types.BlockHeight bh := tx.Bucket(BlockHeight) err := encoding.Unmarshal(bh.Get(BlockHeight), &height) if build.DEBUG && err != nil { panic(err) } return height }
// TestUnitSignatureEncoding creates and encodes a public key, and verifies // that it decodes correctly, does the same with a signature. func TestUnitSignatureEncoding(t *testing.T) { // Create a dummy key pair. var sk SecretKey sk[0] = 4 sk[32] = 5 pk := sk.PublicKey() // Marshal and unmarshal the public key. marshalledPK := encoding.Marshal(pk) var unmarshalledPK PublicKey err := encoding.Unmarshal(marshalledPK, &unmarshalledPK) if err != nil { t.Fatal(err) } // Test the public keys for equality. if pk != unmarshalledPK { t.Error("pubkey not the same after marshalling and unmarshalling") } // Create a signature using the secret key. var signedData Hash rand.Read(signedData[:]) sig, err := SignHash(signedData, sk) if err != nil { t.Fatal(err) } // Marshal and unmarshal the signature. marshalledSig := encoding.Marshal(sig) var unmarshalledSig Signature err = encoding.Unmarshal(marshalledSig, &unmarshalledSig) if err != nil { t.Fatal(err) } // Test signatures for equality. if sig != unmarshalledSig { t.Error("signature not same after marshalling and unmarshalling") } }
// getPath returns the block id at 'height' in the block path. func getPath(tx *bolt.Tx, height types.BlockHeight) (id types.BlockID) { idBytes, err := getItem(tx, BlockPath, height) if build.DEBUG && err != nil { panic(err) } err = encoding.Unmarshal(idBytes, &id) if build.DEBUG && err != nil { panic(err) } return id }
// BenchmarkDecodeEmptyBlock benchmarks decoding an empty block. // // i7-4770, b0b162d: 38 MB/s // i5-4670K, 9a90f86: 55 MB/s func BenchmarkDecodeEmptyBlock(b *testing.B) { var block Block encodedBlock := encoding.Marshal(block) b.SetBytes(int64(len(encodedBlock))) for i := 0; i < b.N; i++ { err := encoding.Unmarshal(encodedBlock, &block) if err != nil { b.Fatal(err) } } }
func forEachFCExpiration(tx *bolt.Tx, bh types.BlockHeight, fn func(types.FileContractID) error) error { bucketID := append(prefix_fcex, encoding.Marshal(bh)...) return forEach(tx, bucketID, func(kb, bv []byte) error { var id types.FileContractID err := encoding.Unmarshal(kb, &id) if err != nil { return err } return fn(id) }) }
// getFileContract fetches a file contract from the database, returning an // error if it is not there. func getFileContract(tx *bolt.Tx, id types.FileContractID) (fc types.FileContract, err error) { fcBytes := tx.Bucket(FileContracts).Get(id[:]) if fcBytes == nil { return types.FileContract{}, errNilItem } err = encoding.Unmarshal(fcBytes, &fc) if err != nil { return types.FileContract{}, err } return fc, nil }
func getFileContract(tx *bolt.Tx, id types.FileContractID) (fc types.FileContract, err error) { fcBytes, err := getItem(tx, FileContracts, id) if err != nil { return types.FileContract{}, err } err = encoding.Unmarshal(fcBytes, &fc) if err != nil { return types.FileContract{}, err } return fc, nil }
// getPath retreives the block id of a block at a given hegiht from the path // // DEPRECATED func (db *setDB) getPath(h types.BlockHeight) (id types.BlockID) { idBytes, err := db.getItem(BlockPath, h) if err != nil { panic(err) } err = encoding.Unmarshal(idBytes, &id) if err != nil { panic(err) } return }
// getSiafundPool returns the current value of the siafund pool. No error is // returned as the siafund pool should always be available. func getSiafundPool(tx *bolt.Tx) (pool types.Currency) { bucket := tx.Bucket(SiafundPool) poolBytes := bucket.Get(SiafundPool) // An error should only be returned if the object stored in the siafund // pool bucket is either unavailable or otherwise malformed. As this is a // developer error, a panic is appropriate. err := encoding.Unmarshal(poolBytes, &pool) if build.DEBUG && err != nil { panic(err) } return pool }
// TestCurrencyEncoding checks that a currency can encode and decode without // error. func TestCurrencyEncoding(t *testing.T) { c := NewCurrency64(351) cMar := encoding.Marshal(c) var cUmar Currency err := encoding.Unmarshal(cMar, &cUmar) if err != nil { t.Error("Error unmarshalling a currency:", err) } if cUmar.Cmp(c) != 0 { t.Error("Marshalling and Unmarshalling a currency did not work correctly") } }
// getPath returns the block id at 'height' in the block path. func getPath(tx *bolt.Tx, height types.BlockHeight) (id types.BlockID, err error) { idBytes := tx.Bucket(BlockPath).Get(encoding.Marshal(height)) if idBytes == nil { return types.BlockID{}, errNilItem } err = encoding.Unmarshal(idBytes, &id) if build.DEBUG && err != nil { panic(err) } return id, nil }
// getSiafundOutput fetches a siafund output from the database. An error is // returned if the siafund output does not exist. func getSiafundOutput(tx *bolt.Tx, id types.SiafundOutputID) (types.SiafundOutput, error) { sfoBytes := tx.Bucket(SiafundOutputs).Get(id[:]) if sfoBytes == nil { return types.SiafundOutput{}, errNilItem } var sfo types.SiafundOutput err := encoding.Unmarshal(sfoBytes, &sfo) if err != nil { return types.SiafundOutput{}, err } return sfo, nil }