Example #1
0
// buildExplorerBlock takes a block and its height and uses it to construct an
// explorer block.
func (srv *Server) buildExplorerBlock(height types.BlockHeight, block types.Block) ExplorerBlock {
	var mpoids []types.SiacoinOutputID
	for i := range block.MinerPayouts {
		mpoids = append(mpoids, block.MinerPayoutID(uint64(i)))
	}

	var etxns []ExplorerTransaction
	for _, txn := range block.Transactions {
		etxns = append(etxns, srv.buildExplorerTransaction(height, block.ID(), txn))
	}

	facts, exists := srv.explorer.BlockFacts(height)
	if build.DEBUG && !exists {
		panic("incorrect request to buildExplorerBlock - block does not exist")
	}

	return ExplorerBlock{
		MinerPayoutIDs: mpoids,
		Transactions:   etxns,
		RawBlock:       block,

		BlockFacts: facts,
	}
}
Example #2
0
// New returns a new State, containing at least the genesis block. If there is
// an existing block database present in saveDir, it will be loaded. Otherwise,
// a new database will be created.
func New(gateway modules.Gateway, saveDir string) (*State, error) {
	if gateway == nil {
		return nil, ErrNilGateway
	}

	// Create the State object.
	cs := &State{
		blockMap:  make(map[types.BlockID]*blockNode),
		dosBlocks: make(map[types.BlockID]struct{}),

		currentPath: make([]types.BlockID, 1),

		siacoinOutputs:        make(map[types.SiacoinOutputID]types.SiacoinOutput),
		fileContracts:         make(map[types.FileContractID]types.FileContract),
		siafundOutputs:        make(map[types.SiafundOutputID]types.SiafundOutput),
		delayedSiacoinOutputs: make(map[types.BlockHeight]map[types.SiacoinOutputID]types.SiacoinOutput),

		gateway: gateway,

		mu: sync.New(modules.SafeMutexDelay, 1),
	}

	// Create the genesis block and add it as the BlockRoot.
	genesisBlock := types.Block{
		Timestamp: types.GenesisTimestamp,
		Transactions: []types.Transaction{
			{SiafundOutputs: types.GenesisSiafundAllocation},
		},
	}
	cs.blockRoot = &blockNode{
		block:       genesisBlock,
		childTarget: types.RootTarget,
		depth:       types.RootDepth,

		diffsGenerated: true,
	}
	cs.blockMap[genesisBlock.ID()] = cs.blockRoot

	// Fill out the consensus information for the genesis block.
	cs.currentPath[0] = genesisBlock.ID()
	cs.siacoinOutputs[genesisBlock.MinerPayoutID(0)] = types.SiacoinOutput{
		Value:      types.CalculateCoinbase(0),
		UnlockHash: types.ZeroUnlockHash,
	}

	// Allocate the Siafund addresses by putting them all in a big transaction
	// and applying the diffs.
	for i, siafundOutput := range genesisBlock.Transactions[0].SiafundOutputs {
		sfid := genesisBlock.Transactions[0].SiafundOutputID(i)
		sfod := modules.SiafundOutputDiff{
			Direction:     modules.DiffApply,
			ID:            sfid,
			SiafundOutput: siafundOutput,
		}
		cs.commitSiafundOutputDiff(sfod, modules.DiffApply)
		cs.blockRoot.siafundOutputDiffs = append(cs.blockRoot.siafundOutputDiffs, sfod)
	}

	// Send out genesis block update.
	cs.updateSubscribers(nil, []*blockNode{cs.blockRoot})

	// Create the consensus directory.
	err := os.MkdirAll(saveDir, 0700)
	if err != nil {
		return nil, err
	}

	// During short tests, use an in-memory database.
	if build.Release == "testing" && testing.Short() {
		cs.db = persist.NilDB
	} else {
		// Otherwise, try to load an existing database from disk.
		err = cs.load(saveDir)
		if err != nil {
			return nil, err
		}
	}

	// Register RPCs
	gateway.RegisterRPC("SendBlocks", cs.sendBlocks)
	gateway.RegisterRPC("RelayBlock", cs.RelayBlock)
	gateway.RegisterConnectCall("SendBlocks", cs.receiveBlocks)

	// Spawn resynchronize loop.
	go cs.threadedResynchronize()

	return cs, nil
}
Example #3
0
// addBlockDB parses a block and adds it to the database
func (e *Explorer) addBlockDB(b types.Block) error {
	// Special case for the genesis block, which does not have a
	// valid parent, and for testing, as tests will not always use
	// blocks in consensus
	var blocktarget types.Target
	if b.ID() == e.genesisBlockID {
		blocktarget = types.RootDepth
		e.blockchainHeight = 0
	} else {
		var exists bool
		blocktarget, exists = e.cs.ChildTarget(b.ParentID)
		if build.DEBUG {
			if build.Release == "testing" {
				blocktarget = types.RootDepth
			} else if !exists {
				panic("Applied block not in consensus")
			}

		}
	}

	// Check if the block exsts.
	var exists bool
	dbErr := e.db.View(func(tx *bolt.Tx) error {
		id := b.ID()
		block := tx.Bucket([]byte("Blocks")).Get(id[:])
		exists = block != nil
		return nil
	})
	if dbErr != nil {
		return dbErr
	}
	if exists {
		return nil
	}

	tx, err := newBoltTx(e.db)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	// Construct the struct that will be inside the heights map
	blockStruct := blockData{
		Block:  b,
		Height: e.blockchainHeight,
	}

	tx.addNewHash("Blocks", hashBlock, crypto.Hash(b.ID()), blockStruct)

	bSum := modules.ExplorerBlockData{
		ID:        b.ID(),
		Timestamp: b.Timestamp,
		Target:    blocktarget,
		Size:      uint64(len(encoding.Marshal(b))),
	}

	tx.putObject("Heights", e.blockchainHeight, bSum)
	tx.putObject("Hashes", crypto.Hash(b.ID()), hashBlock)

	// Insert the miner payouts as new outputs
	for i, payout := range b.MinerPayouts {
		tx.addAddress(payout.UnlockHash, types.TransactionID(b.ID()))
		tx.addNewOutput(b.MinerPayoutID(uint64(i)), types.TransactionID(b.ID()))
	}

	// Insert each transaction
	for i, txn := range b.Transactions {
		tx.addNewHash("Transactions", hashTransaction, crypto.Hash(txn.ID()), txInfo{b.ID(), i})
		tx.addTransaction(txn)
	}

	return tx.commit()
}
Example #4
0
// Add a couple blocks to the database, then perform lookups to see if
// they were added and crossed referenced correctly
func (et *explorerTester) testAddBlock(t *testing.T) error {
	// This block will *NOT* be valid, but should contain
	// addresses that can cross reference each other.
	b1 := types.Block{
		ParentID:  types.BlockID(genHashNum(1)),
		Nonce:     [8]byte{2, 2, 2, 2, 2, 2, 2, 2},
		Timestamp: 3,
		MinerPayouts: []types.SiacoinOutput{types.SiacoinOutput{
			Value:      types.NewCurrency64(4),
			UnlockHash: types.UnlockHash(genHashNum(5)),
		}},
		Transactions: nil,
	}

	// This should not error at least...
	lockID := et.explorer.mu.Lock()
	err := et.explorer.addBlockDB(b1)
	et.explorer.mu.Unlock(lockID)
	if err != nil {
		return errors.New("Error inserting basic block: " + err.Error())
	}

	// Again, not a valid block at all.
	b2 := types.Block{
		ParentID:     b1.ID(),
		Nonce:        [8]byte{7, 7, 7, 7, 7, 7, 7, 7},
		Timestamp:    8,
		MinerPayouts: nil,
		Transactions: []types.Transaction{types.Transaction{
			SiacoinInputs: []types.SiacoinInput{types.SiacoinInput{
				ParentID: b1.MinerPayoutID(0),
			}},
			FileContracts: []types.FileContract{types.FileContract{
				UnlockHash: types.UnlockHash(genHashNum(10)),
			}},
		}},
	}

	lockID = et.explorer.mu.Lock()
	err = et.explorer.addBlockDB(b2)
	et.explorer.mu.Unlock(lockID)
	if err != nil {
		return errors.New("Error inserting block 2: " + err.Error())
	}

	// Now query the database to see if it has been linked properly
	lockID = et.explorer.mu.RLock()
	bytes, err := et.explorer.db.GetFromBucket("Blocks", encoding.Marshal(b1.ID()))
	et.explorer.mu.RUnlock(lockID)
	var b types.Block
	err = encoding.Unmarshal(bytes, &b)
	if err != nil {
		return errors.New("Could not decode loaded block")
	}
	if b.ID() != b1.ID() {
		return errors.New("Block 1 not stored properly")
	}

	// Query to see if the input is added to the output field
	lockID = et.explorer.mu.RLock()
	bytes, err = et.explorer.db.GetFromBucket("SiacoinOutputs", encoding.Marshal(b1.MinerPayoutID(0)))
	et.explorer.mu.RUnlock(lockID)
	if err != nil {
		t.Fatal(err.Error())
	}
	if bytes == nil {
		return errors.New("Output is nil")
	}
	var ot outputTransactions
	err = encoding.Unmarshal(bytes, &ot)
	if err != nil {
		return errors.New("Could not decode loaded block")
	}
	if ot.InputTx == (types.TransactionID{}) {
		return errors.New("Input not added as output")
	}
	return nil
}