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 }
// 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 (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) }
// 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 }
// 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 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)) }
func toHash(txHash *wire.ShaHash) *common.Hash { h := new(common.Hash) h.SetBytes(txHash.Bytes()) return h }
func shaSpentTxToKey(sha *wire.ShaHash) []byte { shaB := sha.Bytes() shaB = append(shaB, "sx"...) return shaB }
func shaBlkToKey(sha *wire.ShaHash) []byte { shaB := sha.Bytes() return shaB }
// AddShaHash adds the passed wire.ShaHash to the Filter. // // This function is safe for concurrent access. func (bf *Filter) AddShaHash(sha *wire.ShaHash) { bf.mtx.Lock() bf.add(sha.Bytes()) bf.mtx.Unlock() }