// Uses the txid of the target funding transaction and asks blockchain.info's // api for information (in json) relaated to that transaction. func lookupTxid(hash *wire.ShaHash) *blockChainInfoTx { url := "https://blockchain.info/rawtx/" + hash.String() resp, err := http.Get(url) if err != nil { log.Fatal(fmt.Errorf("Tx Lookup failed: %v", err)) } b, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(fmt.Errorf("TxInfo read failed: %s", err)) } //fmt.Printf("%s\n", b) txinfo := &blockChainInfoTx{} err = json.Unmarshal(b, txinfo) if err != nil { log.Fatal(err) } if txinfo.Ver != 1 { log.Fatal(fmt.Errorf("Blockchain.info's response seems bad: %v", txinfo)) } return txinfo }
// FetchHeightRange looks up a range of blocks by the start and ending // heights. Fetch is inclusive of the start height and exclusive of the // ending height. To fetch all hashes from the start height until no // more are present, use the special id `AllShas'. func (db *LevelDb) FetchHeightRange(startHeight, endHeight int64) (rshalist []wire.ShaHash, err error) { db.dbLock.Lock() defer db.dbLock.Unlock() var endidx int64 if endHeight == database.AllShas { endidx = startHeight + 500 } else { endidx = endHeight } shalist := make([]wire.ShaHash, 0, endidx-startHeight) for height := startHeight; height < endidx; height++ { // TODO(drahn) fix blkFile from height key := int64ToKey(height) blkVal, lerr := db.lDb.Get(key, db.ro) if lerr != nil { break } var sha wire.ShaHash sha.SetBytes(blkVal[0:32]) shalist = append(shalist, sha) } if err != nil { return } //log.Tracef("FetchIdxRange idx %v %v returned %v shas err %v", startHeight, endHeight, len(shalist), err) return shalist, nil }
func parsesha(argstr string) (argtype int, height int64, psha *wire.ShaHash, err error) { var sha wire.ShaHash var hashbuf string switch len(argstr) { case 64: hashbuf = argstr case 66: if argstr[0:2] != "0x" { log.Infof("prefix is %v", argstr[0:2]) err = errBadShaPrefix return } hashbuf = argstr[2:] default: if len(argstr) <= 16 { // assume value is height argtype = argHeight var h int h, err = strconv.Atoi(argstr) if err == nil { height = int64(h) return } log.Infof("Unable to parse height %v, err %v", height, err) } err = errBadShaLen return } var buf [32]byte for idx, ch := range hashbuf { var val rune switch { case ch >= '0' && ch <= '9': val = ch - '0' case ch >= 'a' && ch <= 'f': val = ch - 'a' + rune(10) case ch >= 'A' && ch <= 'F': val = ch - 'A' + rune(10) default: err = errBadShaChar return } b := buf[31-idx/2] if idx&1 == 1 { b |= byte(val) } else { b |= (byte(val) << 4) } buf[31-idx/2] = b } sha.SetBytes(buf[0:32]) psha = &sha return }
// GetBlockAsync returns an instance of a type that can be used to get the // result of the RPC at some future time by invoking the Receive function on the // returned instance. // // See GetBlock for the blocking version and more details. func (c *Client) GetBlockAsync(blockHash *wire.ShaHash) FutureGetBlockResult { hash := "" if blockHash != nil { hash = blockHash.String() } cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(false), nil) return c.sendCmd(cmd) }
// GetRawTransactionAsync returns an instance of a type that can be used to get // the result of the RPC at some future time by invoking the Receive function on // the returned instance. // // See GetRawTransaction for the blocking version and more details. func (c *Client) GetRawTransactionAsync(txHash *wire.ShaHash) FutureGetRawTransactionResult { hash := "" if txHash != nil { hash = txHash.String() } cmd := btcjson.NewGetRawTransactionCmd(hash, btcjson.Int(0)) return c.sendCmd(cmd) }
// GetTxOutAsync returns an instance of a type that can be used to get // the result of the RPC at some future time by invoking the Receive function on // the returned instance. // // See GetTxOut for the blocking version and more details. func (c *Client) GetTxOutAsync(txHash *wire.ShaHash, index uint32, mempool bool) FutureGetTxOutResult { hash := "" if txHash != nil { hash = txHash.String() } cmd := btcjson.NewGetTxOutCmd(hash, index, &mempool) return c.sendCmd(cmd) }
// GetBlockVerboseAsync returns an instance of a type that can be used to get // the result of the RPC at some future time by invoking the Receive function on // the returned instance. // // See GetBlockVerbose for the blocking version and more details. func (c *Client) GetBlockVerboseAsync(blockHash *wire.ShaHash, verboseTx bool) FutureGetBlockVerboseResult { hash := "" if blockHash != nil { hash = blockHash.String() } cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), &verboseTx) return c.sendCmd(cmd) }
// add txid of interest func (t *TxStore) AddTxid(txid *wire.ShaHash, height int32) error { if txid == nil { return fmt.Errorf("tried to add nil txid") } log.Printf("added %s to OKTxids at height %d\n", txid.String(), height) t.OKMutex.Lock() t.OKTxids[*txid] = height t.OKMutex.Unlock() return nil }
// ShaHashToBig converts a wire.ShaHash into a big.Int that can be used to // perform math comparisons. func ShaHashToBig(hash *wire.ShaHash) *big.Int { // A ShaHash is in little-endian, but the big package wants the bytes // in big-endian. Reverse them. ShaHash.Bytes makes a copy, so it // is safe to modify the returned buffer. buf := hash.Bytes() blen := len(buf) for i := 0; i < blen/2; i++ { buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i] } return new(big.Int).SetBytes(buf) }
func assertAddrIndexTipIsUpdated(db database.Db, t *testing.T, newestSha *wire.ShaHash, newestBlockIdx int32) { // 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) } }
// HashMerkleBranches takes two hashes, treated as the left and right tree // nodes, and returns the hash of their concatenation. This is a helper // function used to aid in the generation of a merkle tree. func HashMerkleBranches(left *wire.ShaHash, right *wire.ShaHash) *wire.ShaHash { // Concatenate the left and right nodes. var sha [wire.HashSize * 2]byte copy(sha[:wire.HashSize], left.Bytes()) copy(sha[wire.HashSize:], right.Bytes()) // Create a new sha hash from the double sha 256. Ignore the error // here since SetBytes can't fail here due to the fact DoubleSha256 // always returns a []byte of the right size regardless of input. newSha, _ := wire.NewShaHash(wire.DoubleSha256(sha[:])) return newSha }
// fetchBlockShaByHeight returns a block hash based on its height in the // block chain. func (db *LevelDb) fetchBlockShaByHeight(height int64) (rsha *wire.ShaHash, err error) { key := int64ToKey(height) blkVal, err := db.lDb.Get(key, db.ro) if err != nil { log.Tracef("failed to find height %v", height) return // exists ??? } var sha wire.ShaHash sha.SetBytes(blkVal[0:32]) return &sha, nil }
// blockExists determines whether a block with the given hash exists either in // the main chain or any side chains. func (b *BlockChain) blockExists(hash *wire.ShaHash) (bool, error) { // Check memory chain first (could be main chain or side chain blocks). if _, ok := b.index[*hash]; ok { return true, nil } // Check if it's the latest checkpoint block if hash.IsEqual(b.chainParams.Checkpoints[len(b.chainParams.Checkpoints)-1].Hash) { return true, nil } // Check in database (rest of main chain not in memory). return b.db.ExistsSha(hash) }
func (db *LevelDb) setBlk(sha *wire.ShaHash, blkHeight int64, buf []byte) { // serialize var lw [8]byte binary.LittleEndian.PutUint64(lw[0:8], uint64(blkHeight)) shaKey := shaBlkToKey(sha) blkKey := int64ToKey(blkHeight) shaB := sha.Bytes() blkVal := make([]byte, len(shaB)+len(buf)) copy(blkVal[0:], shaB) copy(blkVal[len(shaB):], buf) db.lBatch().Put(shaKey, lw[:]) db.lBatch().Put(blkKey, blkVal) }
// fetchAddrIndexTip returns the last block height and block sha to be indexed. // Meta-data about the address tip is currently cached in memory, and will be // updated accordingly by functions that modify the state. This function is // used on start up to load the info into memory. Callers will use the public // version of this function below, which returns our cached copy. func (db *LevelDb) fetchAddrIndexTip() (*wire.ShaHash, int64, error) { db.dbLock.Lock() defer db.dbLock.Unlock() data, err := db.lDb.Get(addrIndexMetaDataKey, db.ro) if err != nil { return &wire.ShaHash{}, -1, database.ErrAddrIndexDoesNotExist } var blkSha wire.ShaHash blkSha.SetBytes(data[0:32]) blkHeight := binary.LittleEndian.Uint64(data[32:]) return &blkSha, int64(blkHeight), nil }
// newDummyCredit creates a new credit with the given hash and outpointIdx, // locked to the votingpool address identified by the given // series/index/branch. func newDummyCredit(t *testing.T, pool *Pool, series uint32, index Index, branch Branch, txSha []byte, outpointIdx uint32) credit { var hash wire.ShaHash if err := hash.SetBytes(txSha); err != nil { t.Fatal(err) } // Ensure the address defined by the given series/branch/index is present on // the set of used addresses as that's a requirement of WithdrawalAddress. TstEnsureUsedAddr(t, pool, series, branch, index) addr := TstNewWithdrawalAddress(t, pool, series, branch, index) c := wtxmgr.Credit{ OutPoint: wire.OutPoint{ Hash: hash, Index: outpointIdx, }, } return newCredit(c, *addr) }
// GetTx takes a txid and returns the transaction. If we have it. func (ts *TxStore) GetTx(txid *wire.ShaHash) (*wire.MsgTx, error) { rtx := wire.NewMsgTx() err := ts.StateDB.View(func(btx *bolt.Tx) error { txns := btx.Bucket(BKTTxns) if txns == nil { return fmt.Errorf("no transactions in db") } txbytes := txns.Get(txid.Bytes()) if txbytes == nil { return fmt.Errorf("tx %x not in db", txid.String()) } buf := bytes.NewBuffer(txbytes) return rtx.Deserialize(buf) }) if err != nil { return nil, err } return rtx, nil }
func (db *LevelDb) getBlkByHeight(blkHeight int64) (rsha *wire.ShaHash, rbuf []byte, err error) { var blkVal []byte key := int64ToKey(blkHeight) blkVal, err = db.lDb.Get(key, db.ro) if err != nil { log.Tracef("failed to find height %v", blkHeight) return // exists ??? } var sha wire.ShaHash sha.SetBytes(blkVal[0:32]) blockdata := make([]byte, len(blkVal[32:])) copy(blockdata[:], blkVal[32:]) return &sha, blockdata, nil }
// RescanEndBlockAsync returns an instance of a type that can be used to get // the result of the RPC at some future time by invoking the Receive function on // the returned instance. // // See RescanEndBlock for the blocking version and more details. // // NOTE: This is a btcd extension and requires a websocket connection. func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash, addresses []btcutil.Address, outpoints []*wire.OutPoint, endBlock *wire.ShaHash) FutureRescanResult { // Not supported in HTTP POST mode. if c.config.HTTPPostMode { return newFutureError(ErrNotificationsNotSupported) } // Ignore the notification if the client is not interested in // notifications. if c.ntfnHandlers == nil { return newNilFutureResult() } // Convert block hashes to strings. var startBlockShaStr, endBlockShaStr string if startBlock != nil { startBlockShaStr = startBlock.String() } if endBlock != nil { endBlockShaStr = endBlock.String() } // Convert addresses to strings. addrs := make([]string, 0, len(addresses)) for _, addr := range addresses { addrs = append(addrs, addr.String()) } // Convert outpoints. ops := make([]btcjson.OutPoint, 0, len(outpoints)) for _, op := range outpoints { ops = append(ops, newOutPointFromWire(op)) } cmd := btcjson.NewRescanCmd(startBlockShaStr, addrs, ops, &endBlockShaStr) return c.sendCmd(cmd) }
func (db *LevelDb) getBlkLoc(sha *wire.ShaHash) (int32, error) { key := shaBlkToKey(sha) checkpointSha, _ := wire.NewShaHashFromStr("00000000000000000a8dc6ed5b133d0eb2fd6af56203e4159789b092defd8ab2") if sha.IsEqual(checkpointSha) { return 382320, nil } data, err := db.lDb.Get(key, db.ro) if err != nil { if err == leveldb.ErrNotFound { err = database.ErrBlockShaMissing } return 0, err } // deserialize blkHeight := binary.LittleEndian.Uint64(data) return int32(blkHeight), nil }
// UpdateAddrIndexForBlock updates the stored addrindex with passed // index information for a particular block height. Additionally, it // will update the stored meta-data related to the curent tip of the // addr index. These two operations are performed in an atomic // transaction which is commited before the function returns. // Transactions indexed by address are stored with the following format: // * prefix || hash160 || blockHeight || txoffset || txlen // Indexes are stored purely in the key, with blank data for the actual value // in order to facilitate ease of iteration by their shared prefix and // also to allow limiting the number of returned transactions (RPC). // Alternatively, indexes for each address could be stored as an // append-only list for the stored value. However, this add unnecessary // overhead when storing and retrieving since the entire list must // be fetched each time. func (db *LevelDb) UpdateAddrIndexForBlock(blkSha *wire.ShaHash, blkHeight int64, addrIndex database.BlockAddrIndex) error { db.dbLock.Lock() defer db.dbLock.Unlock() var blankData []byte batch := db.lBatch() defer db.lbatch.Reset() // Write all data for the new address indexes in a single batch // transaction. for addrKey, indexes := range addrIndex { for _, txLoc := range indexes { index := &txAddrIndex{ hash160: addrKey, blkHeight: blkHeight, txoffset: txLoc.TxStart, txlen: txLoc.TxLen, } // The index is stored purely in the key. packedIndex := addrIndexToKey(index) batch.Put(packedIndex, blankData) } } // Update tip of addrindex. newIndexTip := make([]byte, 40, 40) copy(newIndexTip[:32], blkSha.Bytes()) binary.LittleEndian.PutUint64(newIndexTip[32:], uint64(blkHeight)) batch.Put(addrIndexMetaDataKey, newIndexTip) if err := db.lDb.Write(batch, db.wo); err != nil { return err } db.lastAddrIndexBlkIdx = blkHeight db.lastAddrIndexBlkSha = *blkSha return nil }
func MakeMerkleParent(left *wire.ShaHash, right *wire.ShaHash) *wire.ShaHash { // dupes can screw things up; CVE-2012-2459. check for them if left != nil && right != nil && left.IsEqual(right) { fmt.Printf("DUP HASH CRASH") return nil } // if left child is nil, output nil. Need this for hard mode. if left == nil { return nil } // if right is nil, hash left with itself if right == nil { right = left } // Concatenate the left and right nodes var sha [64]byte copy(sha[:32], left[:]) copy(sha[32:], right[:]) newSha := wire.DoubleSha256SH(sha[:]) return &newSha }
// MakeMerkleParent ... func MakeMerkleParent(left *wire.ShaHash, right *wire.ShaHash) *wire.ShaHash { // this can screw things up; CVE-2012-2459 if left != nil && right != nil && left.IsEqual(right) { fmt.Printf("DUP HASH CRASH") return nil } // if left chils is nil, output nil. Shouldn't need this? if left == nil { fmt.Printf("L CRASH") return nil } // if right is nil, has left with itself if right == nil { right = left } // Concatenate the left and right nodes var sha [wire.HashSize * 2]byte copy(sha[:wire.HashSize], left[:]) copy(sha[wire.HashSize:], right[:]) newSha := wire.DoubleSha256SH(sha[:]) return &newSha }
func testBasicWalletReservationWorkFlow(lnwallet *LightningWallet, t *testing.T) { // Create our test wallet, will have a total of 20 BTC available for bobNode, err := newBobNode() if err != nil { t.Fatalf("unable to create bob node: %v", err) } // Bob initiates a channel funded with 5 BTC for each side, so 10 // BTC total. He also generates 2 BTC in change. fundingAmount := btcutil.Amount(5 * 1e8) chanReservation, err := lnwallet.InitChannelReservation(fundingAmount, SIGHASH, bobNode.id, 4) if err != nil { t.Fatalf("unable to initialize funding reservation: %v", err) } // The channel reservation should now be populated with a multi-sig key // from our HD chain, a change output with 3 BTC, and 2 outputs selected // of 4 BTC each. Additionally, the rest of the items needed to fufill a // funding contribution should also have been filled in. ourContribution := chanReservation.OurContribution() if len(ourContribution.Inputs) != 2 { t.Fatalf("outputs for funding tx not properly selected, have %v "+ "outputs should have 2", len(ourContribution.Inputs)) } if ourContribution.ChangeOutputs[0].Value != 3e8 { t.Fatalf("coin selection failed, change output should be 3e8 "+ "satoshis, is instead %v", ourContribution.ChangeOutputs[0].Value) } if ourContribution.MultiSigKey == nil { t.Fatalf("alice's key for multi-sig not found") } if ourContribution.CommitKey == nil { t.Fatalf("alice's key for commit not found") } if ourContribution.DeliveryAddress == nil { t.Fatalf("alice's final delivery address not found") } if bytes.Equal(ourContribution.RevocationHash[:], zeroHash) { t.Fatalf("alice's revocation hash not found") } if ourContribution.CsvDelay == 0 { t.Fatalf("csv delay not set") } // Bob sends over his output, change addr, pub keys, initial revocation, // final delivery address, and his accepted csv delay for the commitmen // t transactions. if err := chanReservation.ProcessContribution(bobNode.Contribution()); err != nil { t.Fatalf("unable to add bob's funds to the funding tx: %v", err) } // At this point, the reservation should have our signatures, and a // partial funding transaction (missing bob's sigs). theirContribution := chanReservation.TheirContribution() ourFundingSigs, ourCommitSig := chanReservation.OurSignatures() if len(ourFundingSigs) != 2 { t.Fatalf("only %v of our sigs present, should have 2", len(ourFundingSigs)) } if ourCommitSig == nil { t.Fatalf("commitment sig not found") } // Additionally, the funding tx should have been populated. if chanReservation.partialState.FundingTx == nil { t.Fatalf("funding transaction never created!") } // Their funds should also be filled in. if len(theirContribution.Inputs) != 1 { t.Fatalf("bob's outputs for funding tx not properly selected, have %v "+ "outputs should have 2", len(theirContribution.Inputs)) } if theirContribution.ChangeOutputs[0].Value != 2e8 { t.Fatalf("bob should have one change output with value 2e8"+ "satoshis, is instead %v", theirContribution.ChangeOutputs[0].Value) } if theirContribution.MultiSigKey == nil { t.Fatalf("bob's key for multi-sig not found") } if theirContribution.CommitKey == nil { t.Fatalf("bob's key for commit tx not found") } if theirContribution.DeliveryAddress == nil { t.Fatalf("bob's final delivery address not found") } if bytes.Equal(theirContribution.RevocationHash[:], zeroHash) { t.Fatalf("bob's revocaiton hash not found") } // Alice responds with her output, change addr, multi-sig key and signatures. // Bob then responds with his signatures. bobsSigs, err := bobNode.signFundingTx(chanReservation.partialState.FundingTx) if err != nil { t.Fatalf("unable to sign inputs for bob: %v", err) } commitSig, err := bobNode.signCommitTx( chanReservation.partialState.OurCommitTx, chanReservation.partialState.FundingRedeemScript) if err != nil { t.Fatalf("bob is unable to sign alice's commit tx: %v", err) } if err := chanReservation.CompleteReservation(bobsSigs, commitSig); err != nil { t.Fatalf("unable to complete funding tx: %v", err) } // At this point, the channel can be considered "open" when the funding // txn hits a "comfortable" depth. fundingTx := chanReservation.FinalFundingTx() // The resulting active channel state should have been persisted to the DB. channel, err := lnwallet.ChannelDB.FetchOpenChannel(bobNode.id) if err != nil { t.Fatalf("unable to retrieve channel from DB: %v", err) } if channel.FundingTx.TxSha() != fundingTx.TxSha() { t.Fatalf("channel state not properly saved") } // The funding tx should now be valid and complete. // Check each input and ensure all scripts are fully valid. // TODO(roasbeef): remove this loop after nodetest hooked up. var zeroHash wire.ShaHash for i, input := range fundingTx.TxIn { var pkscript []byte // Bob's txin if bytes.Equal(input.PreviousOutPoint.Hash.Bytes(), zeroHash.Bytes()) { pkscript = bobNode.changeOutputs[0].PkScript } else { // Does the wallet know about the txin? txDetail, err := lnwallet.TxStore.TxDetails(&input.PreviousOutPoint.Hash) if txDetail == nil || err != nil { t.Fatalf("txstore can't find tx detail, err: %v", err) } prevIndex := input.PreviousOutPoint.Index pkscript = txDetail.TxRecord.MsgTx.TxOut[prevIndex].PkScript } vm, err := txscript.NewEngine(pkscript, fundingTx, i, txscript.StandardVerifyFlags, nil) if err != nil { // TODO(roasbeef): cancel at this stage if invalid sigs? t.Fatalf("cannot create script engine: %s", err) } if err = vm.Execute(); err != nil { t.Fatalf("cannot validate transaction: %s", err) } } }
// RightSha ... func RightSha(in wire.ShaHash) wire.ShaHash { return wire.DoubleSha256SH(append(in.Bytes(), 0x01)) // sha(sha(in, 1)) }
// LeftSha ... func LeftSha(in wire.ShaHash) wire.ShaHash { return wire.DoubleSha256SH(in.Bytes()) // left is sha(sha(in)) }
// blockLocatorFromHash returns a block locator for the passed block hash. // See BlockLocator for details on the algotirhm used to create a block locator. // // In addition to the general algorithm referenced above, there are a couple of // special cases which are handled: // // - If the genesis hash is passed, there are no previous hashes to add and // therefore the block locator will only consist of the genesis hash // - If the passed hash is not currently known, the block locator will only // consist of the passed hash // // This function MUST be called with the chain state lock held (for reads). func (b *BlockChain) blockLocatorFromHash(hash *wire.ShaHash) BlockLocator { // The locator contains the requested hash at the very least. locator := make(BlockLocator, 0, wire.MaxBlockLocatorsPerMsg) locator = append(locator, hash) // Nothing more to do if a locator for the genesis hash was requested. if hash.IsEqual(b.chainParams.GenesisHash) { return locator } // Attempt to find the height of the block that corresponds to the // passed hash, and if it's on a side chain, also find the height at // which it forks from the main chain. blockHeight := int32(-1) forkHeight := int32(-1) node, exists := b.index[*hash] if !exists { // Try to look up the height for passed block hash. Assume an // error means it doesn't exist and just return the locator for // the block itself. var height int32 err := b.db.View(func(dbTx database.Tx) error { var err error height, err = dbFetchHeightByHash(dbTx, hash) return err }) if err != nil { return locator } blockHeight = height } else { blockHeight = node.height // Find the height at which this node forks from the main chain // if the node is on a side chain. if !node.inMainChain { for n := node; n.parent != nil; n = n.parent { if n.inMainChain { forkHeight = n.height break } } } } // Generate the block locators according to the algorithm described in // in the BlockLocator comment and make sure to leave room for the final // genesis hash. // // The error is intentionally ignored here since the only way the code // could fail is if there is something wrong with the database which // will be caught in short order anyways and it's also safe to ignore // block locators. _ = b.db.View(func(dbTx database.Tx) error { iterNode := node increment := int32(1) for len(locator) < wire.MaxBlockLocatorsPerMsg-1 { // Once there are 10 locators, exponentially increase // the distance between each block locator. if len(locator) > 10 { increment *= 2 } blockHeight -= increment if blockHeight < 1 { break } // As long as this is still on the side chain, walk // backwards along the side chain nodes to each block // height. if forkHeight != -1 && blockHeight > forkHeight { // Intentionally use parent field instead of the // getPrevNodeFromNode function since we don't // want to dynamically load nodes when building // block locators. Side chain blocks should // always be in memory already, and if they // aren't for some reason it's ok to skip them. for iterNode != nil && blockHeight > iterNode.height { iterNode = iterNode.parent } if iterNode != nil && iterNode.height == blockHeight { locator = append(locator, iterNode.hash) } continue } // The desired block height is in the main chain, so // look it up from the main chain database. h, err := dbFetchHashByHeight(dbTx, blockHeight) if err != nil { // This shouldn't happen and it's ok to ignore // block locators, so just continue to the next // one. log.Warnf("Lookup of known valid height failed %v", blockHeight) continue } locator = append(locator, h) } return nil }) // Append the appropriate genesis block. locator = append(locator, b.chainParams.GenesisHash) return locator }
func shaBlkToKey(sha *wire.ShaHash) []byte { shaB := sha.Bytes() return shaB }
func shaSpentTxToKey(sha *wire.ShaHash) []byte { shaB := sha.Bytes() shaB = append(shaB, "sx"...) return shaB }
func toHash(txHash *wire.ShaHash) *common.Hash { h := new(common.Hash) h.SetBytes(txHash.Bytes()) return h }