func (w *Wallet) extendMainChain(dbtx walletdb.ReadWriteTx, block *wtxmgr.BlockHeaderData, transactions [][]byte) error { txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey) log.Infof("Connecting block %v, height %v", block.BlockHash, block.SerializedHeader.Height()) err := w.TxStore.ExtendMainChain(txmgrNs, block) if err != nil { return err } // Notify interested clients of the connected block. var header wire.BlockHeader header.Deserialize(bytes.NewReader(block.SerializedHeader[:])) w.NtfnServer.notifyAttachedBlock(dbtx, &header, &block.BlockHash) blockMeta, err := w.TxStore.GetBlockMetaForHash(txmgrNs, &block.BlockHash) if err != nil { return err } for _, serializedTx := range transactions { err = w.processTransaction(dbtx, serializedTx, &block.SerializedHeader, &blockMeta) if err != nil { return err } } return nil }
// FetchBlockHeaderBySha - return a ShaHash func (db *LevelDb) FetchBlockHeaderBySha(sha *chainhash.Hash) (bh *wire.BlockHeader, err error) { db.dbLock.Lock() defer db.dbLock.Unlock() // Read the raw block from the database. buf, _, err := db.fetchSha(sha) if err != nil { return nil, err } // Only deserialize the header portion and ensure the transaction count // is zero since this is a standalone header. var blockHeader wire.BlockHeader err = blockHeader.Deserialize(bytes.NewReader(buf)) if err != nil { return nil, err } bh = &blockHeader return bh, err }
func TestBlockHeaderHashing(t *testing.T) { dummyHeader := "0000000049e0b48ade043f729d60095ed92642d96096fe6aba42f2eda" + "632d461591a152267dc840ff27602ce1968a81eb30a43423517207617a0150b56c4f72" + "b803e497f00000000000000000000000000000000000000000000000000000000000000" + "00010000000000000000000000b7000000ffff7f20204e0000000000005800000060010" + "0008b990956000000000000000000000000000000000000000000000000000000000000" + "0000000000000000ABCD" // This hash has reversed endianness compared to what chainhash spits out. hashStr := "0d40d58703482d81d711be0ffc1b313788d3c3937e1617e4876661d33a8c4c41" hashB, _ := hex.DecodeString(hashStr) hash, _ := chainhash.NewHash(hashB) vecH, _ := hex.DecodeString(dummyHeader) r := bytes.NewReader(vecH) var bh wire.BlockHeader bh.Deserialize(r) hash2 := bh.BlockSha() if !hash2.IsEqual(hash) { t.Errorf("wrong block sha returned (want %v, got %v)", hash, hash2) } }
// TestBlockHeaderSerialize tests BlockHeader serialize and deserialize. func TestBlockHeaderSerialize(t *testing.T) { nonce := uint32(123123) // 0x1e0f3 // baseBlockHdr is used in the various tests as a baseline BlockHeader. bits := uint32(0x1d00ffff) baseBlockHdr := &wire.BlockHeader{ Version: 1, PrevBlock: mainNetGenesisHash, MerkleRoot: mainNetGenesisMerkleRoot, StakeRoot: mainNetGenesisMerkleRoot, VoteBits: uint16(0x0000), FinalState: [6]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, Voters: uint16(0x0000), FreshStake: uint8(0x00), Revocations: uint8(0x00), Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST Bits: bits, SBits: int64(0x0000000000000000), Nonce: nonce, Height: uint32(0), Size: uint32(0), } // baseBlockHdrEncoded is the wire encoded bytes of baseBlockHdr. baseBlockHdrEncoded := []byte{ 0x01, 0x00, 0x00, 0x00, // Version 1 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, // MerkleRoot 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, // StakeRoot 0x00, 0x00, // VoteBits 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FinalState 0x00, 0x00, // Voters 0x00, // FreshStake 0x00, // Revocations 0x00, 0x00, 0x00, 0x00, //Poolsize 0xff, 0xff, 0x00, 0x1d, // Bits 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SBits 0x00, 0x00, 0x00, 0x00, // Height 0x00, 0x00, 0x00, 0x00, // Size 0x29, 0xab, 0x5f, 0x49, // Timestamp 0xf3, 0xe0, 0x01, 0x00, // Nonce 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ExtraData 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } tests := []struct { in *wire.BlockHeader // Data to encode out *wire.BlockHeader // Expected decoded data buf []byte // Serialized data }{ { baseBlockHdr, baseBlockHdr, baseBlockHdrEncoded, }, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Serialize the block header. var buf bytes.Buffer err := test.in.Serialize(&buf) if err != nil { t.Errorf("Serialize #%d error %v", i, err) continue } if !bytes.Equal(buf.Bytes(), test.buf) { t.Errorf("Serialize #%d\n got: %s want: %s", i, spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) continue } // Deserialize the block header. var bh wire.BlockHeader rbuf := bytes.NewReader(test.buf) err = bh.Deserialize(rbuf) if err != nil { t.Errorf("Deserialize #%d error %v", i, err) continue } if !reflect.DeepEqual(&bh, test.out) { t.Errorf("Deserialize #%d\n got: %s want: %s", i, spew.Sdump(&bh), spew.Sdump(test.out)) continue } } }
// onBlockConnected is the entry point for processing chain server // blockconnected notifications. func (w *Wallet) onBlockConnected(dbtx walletdb.ReadWriteTx, serializedBlockHeader []byte, transactions [][]byte) error { var blockHeader wire.BlockHeader err := blockHeader.Deserialize(bytes.NewReader(serializedBlockHeader)) if err != nil { return err } block := wtxmgr.BlockHeaderData{BlockHash: blockHeader.BlockSha()} err = copyHeaderSliceToArray(&block.SerializedHeader, serializedBlockHeader) if err != nil { return err } w.reorganizingLock.Lock() reorg, reorgToHash := w.reorganizing, w.reorganizeToHash w.reorganizingLock.Unlock() if reorg { // add to side chain scBlock := sideChainBlock{ transactions: transactions, headerData: block, } w.sideChain = append(w.sideChain, scBlock) log.Infof("Adding block %v (height %v) to sidechain", block.BlockHash, block.SerializedHeader.Height()) if block.BlockHash != reorgToHash { // Nothing left to do until the later blocks are // received. return nil } err = w.switchToSideChain(dbtx) if err != nil { return err } w.sideChain = nil w.reorganizingLock.Lock() w.reorganizing = false w.reorganizingLock.Unlock() log.Infof("Wallet reorganization to block %v complete", reorgToHash) } else { err = w.extendMainChain(dbtx, &block, transactions) if err != nil { return err } } height := int32(blockHeader.Height) // Handle automatic ticket purchasing if enabled. This function should // not error due to an error purchasing tickets (several tickets may be // have been purhcased and successfully published, as well as addresses // created and used), so just log it instead. err = w.handleTicketPurchases(dbtx, height) switch err.(type) { case nil: case txauthor.InsufficientFundsError: log.Debugf("Insufficient funds to auto-purchase maximum number " + "of tickets") default: log.Errorf("Failed to perform automatic picket purchasing: %v", err) } // Prune all expired transactions and all stake tickets that no longer // meet the minimum stake difficulty. txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey) err = w.TxStore.PruneUnconfirmed(txmgrNs, height, blockHeader.SBits) if err != nil { log.Errorf("Failed to prune unconfirmed transactions when "+ "connecting block height %v: %s", height, err.Error()) } return nil }