Пример #1
0
// targetAdjustmentBase returns the magnitude that the target should be
// adjusted by before a clamp is applied.
func (cs *ConsensusSet) targetAdjustmentBase(blockMap *bolt.Bucket, pb *processedBlock) *big.Rat {
	// Grab the block that was generated 'TargetWindow' blocks prior to the
	// parent. If there are not 'TargetWindow' blocks yet, stop at the genesis
	// block.
	var windowSize types.BlockHeight
	parent := pb.Block.ParentID
	current := pb.Block.ID()
	for windowSize = 0; windowSize < types.TargetWindow && parent != (types.BlockID{}); windowSize++ {
		current = parent
		copy(parent[:], blockMap.Get(parent[:])[:32])
	}
	timestamp := types.Timestamp(encoding.DecUint64(blockMap.Get(current[:])[40:48]))

	// The target of a child is determined by the amount of time that has
	// passed between the generation of its immediate parent and its
	// TargetWindow'th parent. The expected amount of seconds to have passed is
	// TargetWindow*BlockFrequency. The target is adjusted in proportion to how
	// time has passed vs. the expected amount of time to have passed.
	//
	// The target is converted to a big.Rat to provide infinite precision
	// during the calculation. The big.Rat is just the int representation of a
	// target.
	timePassed := pb.Block.Timestamp - timestamp
	expectedTimePassed := types.BlockFrequency * windowSize
	return big.NewRat(int64(timePassed), int64(expectedTimePassed))
}
Пример #2
0
// ReceiveUpdatedUnconfirmedTransactions updates the wallet's unconfirmed
// transaction set.
func (w *Wallet) ReceiveUpdatedUnconfirmedTransactions(txns []types.Transaction, _ modules.ConsensusChange) {
	// There are two different situations under which a subscribee calls
	// ProcessConsensusChange. The first is when w.subscribed is set to false
	// AND the mutex is already locked. The other situation is that subscribed
	// is set to true and is not going to be changed. Therefore there is no
	// race condition here. If w.subscribed is set to false, trying to grab the
	// lock would cause a deadlock.
	if w.subscribed {
		lockID := w.mu.Lock()
		defer w.mu.Unlock(lockID)
	}

	w.unconfirmedProcessedTransactions = nil
	for _, txn := range txns {
		// To save on  code complexity, relveancy is determined while building
		// up the wallet transaction.
		relevant := false
		pt := modules.ProcessedTransaction{
			Transaction:           txn,
			TransactionID:         txn.ID(),
			ConfirmationHeight:    types.BlockHeight(math.MaxUint64),
			ConfirmationTimestamp: types.Timestamp(math.MaxUint64),
		}
		for _, sci := range txn.SiacoinInputs {
			_, exists := w.keys[sci.UnlockConditions.UnlockHash()]
			if exists {
				relevant = true
			}
			pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
				FundType:       types.SpecifierSiacoinInput,
				WalletAddress:  exists,
				RelatedAddress: sci.UnlockConditions.UnlockHash(),
				Value:          w.historicOutputs[types.OutputID(sci.ParentID)],
			})
		}
		for i, sco := range txn.SiacoinOutputs {
			_, exists := w.keys[sco.UnlockHash]
			if exists {
				relevant = true
			}
			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
				FundType:       types.SpecifierSiacoinOutput,
				MaturityHeight: types.BlockHeight(math.MaxUint64),
				WalletAddress:  exists,
				RelatedAddress: sco.UnlockHash,
				Value:          sco.Value,
			})
			w.historicOutputs[types.OutputID(txn.SiacoinOutputID(i))] = sco.Value
		}
		for _, fee := range txn.MinerFees {
			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
				FundType: types.SpecifierMinerFee,
				Value:    fee,
			})
		}
		if relevant {
			w.unconfirmedProcessedTransactions = append(w.unconfirmedProcessedTransactions, pt)
		}
	}
}
Пример #3
0
// minimumValidChildTimestamp returns the earliest timestamp that a child node
// can have while still being valid. See section 'Block Timestamps' in
// Consensus.md.
//
// To boost performance, minimumValidChildTimestamp is passed a bucket that it
// can use from inside of a boltdb transaction.
func (rh stdBlockRuleHelper) minimumValidChildTimestamp(blockMap dbBucket, pb *processedBlock) types.Timestamp {
	// Get the previous MedianTimestampWindow timestamps.
	windowTimes := make(types.TimestampSlice, types.MedianTimestampWindow)
	windowTimes[0] = pb.Block.Timestamp
	parent := pb.Block.ParentID
	for i := uint64(1); i < types.MedianTimestampWindow; i++ {
		// If the genesis block is 'parent', use the genesis block timestamp
		// for all remaining times.
		if parent == (types.BlockID{}) {
			windowTimes[i] = windowTimes[i-1]
			continue
		}

		// Get the next parent's bytes. Because the ordering is specific, the
		// parent does not need to be decoded entirely to get the desired
		// information. This provides a performance boost. The id of the next
		// parent lies at the first 32 bytes, and the timestamp of the block
		// lies at bytes 40-48.
		parentBytes := blockMap.Get(parent[:])
		copy(parent[:], parentBytes[:32])
		windowTimes[i] = types.Timestamp(encoding.DecUint64(parentBytes[40:48]))
	}
	sort.Sort(windowTimes)

	// Return the median of the sorted timestamps.
	return windowTimes[len(windowTimes)/2]
}
Пример #4
0
// testDependentUpdates adds a parent transaction and a dependent transaction
// to the unconfirmed set. Then the parent transaction is added to the
// confirmed set but the dependent is not. A check is made to see that the
// dependent is still in the unconfirmed set.
func (tpt *tpoolTester) testDependentUpdates() {
	// Put two transactions, a parent and a dependent, into the transaction
	// pool. Then create a transaction that is in conflict with the parent.
	parent := tpt.emptyUnlockTransaction()
	dependent := types.Transaction{
		SiacoinInputs: []types.SiacoinInput{
			types.SiacoinInput{
				ParentID: parent.SiacoinOutputID(0),
			},
		},
		MinerFees: []types.Currency{
			parent.SiacoinOutputs[0].Value,
		},
	}
	err := tpt.tpool.AcceptTransaction(parent)
	if err != nil {
		tpt.t.Fatal(err)
	}
	tpt.tpUpdateWait()
	err = tpt.tpool.AcceptTransaction(dependent)
	if err != nil {
		tpt.t.Fatal(err)
	}
	tpt.tpUpdateWait()

	// Mine a block to put the parent into the confirmed set.
	tset := tpt.tpool.TransactionSet()
	tset = tset[:len(tset)-1] // strip 'dependent'
	target, exists := tpt.cs.ChildTarget(tpt.cs.CurrentBlock().ID())
	if !exists {
		tpt.t.Fatal("unable to recover child target")
	}
	block := types.Block{
		ParentID:  tpt.cs.CurrentBlock().ID(),
		Timestamp: types.Timestamp(time.Now().Unix()),
		MinerPayouts: []types.SiacoinOutput{
			types.SiacoinOutput{Value: types.CalculateCoinbase(tpt.cs.Height() + 1)},
		},
		Transactions: tset,
	}
	for {
		var found bool
		block, found = tpt.miner.SolveBlock(block, target)
		if found {
			err = tpt.cs.AcceptBlock(block)
			if err != nil {
				tpt.t.Fatal(err)
			}
			break
		}
	}
	tpt.csUpdateWait()

	// Check that 'parent' and 'dependent' have been removed from the
	// transaction set, since conflict has made the confirmed set.
	if len(tpt.tpool.TransactionSet()) != 1 {
		tpt.t.Error("dependent transaction does not remain unconfirmed after parent has been confirmed:", len(tset))
	}
}
Пример #5
0
// ReceiveUpdatedUnconfirmedTransactions updates the wallet's unconfirmed
// transaction set.
func (w *Wallet) ReceiveUpdatedUnconfirmedTransactions(txns []types.Transaction, _ modules.ConsensusChange) {
	if err := w.tg.Add(); err != nil {
		// Gracefully reject transactions if the wallet's Close method has
		// closed the wallet's ThreadGroup already.
		return
	}
	defer w.tg.Done()
	w.mu.Lock()
	defer w.mu.Unlock()

	w.unconfirmedProcessedTransactions = nil
	for _, txn := range txns {
		// To save on code complexity, relevancy is determined while building
		// up the wallet transaction.
		relevant := false
		pt := modules.ProcessedTransaction{
			Transaction:           txn,
			TransactionID:         txn.ID(),
			ConfirmationHeight:    types.BlockHeight(math.MaxUint64),
			ConfirmationTimestamp: types.Timestamp(math.MaxUint64),
		}
		for _, sci := range txn.SiacoinInputs {
			_, exists := w.keys[sci.UnlockConditions.UnlockHash()]
			if exists {
				relevant = true
			}
			pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
				FundType:       types.SpecifierSiacoinInput,
				WalletAddress:  exists,
				RelatedAddress: sci.UnlockConditions.UnlockHash(),
				Value:          w.historicOutputs[types.OutputID(sci.ParentID)],
			})
		}
		for i, sco := range txn.SiacoinOutputs {
			_, exists := w.keys[sco.UnlockHash]
			if exists {
				relevant = true
			}
			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
				FundType:       types.SpecifierSiacoinOutput,
				MaturityHeight: types.BlockHeight(math.MaxUint64),
				WalletAddress:  exists,
				RelatedAddress: sco.UnlockHash,
				Value:          sco.Value,
			})
			w.historicOutputs[types.OutputID(txn.SiacoinOutputID(uint64(i)))] = sco.Value
		}
		for _, fee := range txn.MinerFees {
			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
				FundType: types.SpecifierMinerFee,
				Value:    fee,
			})
		}
		if relevant {
			w.unconfirmedProcessedTransactions = append(w.unconfirmedProcessedTransactions, pt)
		}
	}
}
Пример #6
0
// TestSetChildTarget probes the setChildTarget method of the block node type.
func TestSetChildTarget(t *testing.T) {
	// Create a genesis node and a child that took 2x as long as expected.
	genesisNode := &blockNode{
		block: types.Block{Timestamp: 10000},
	}
	genesisNode.childTarget[0] = 64
	doubleTimeNode := &blockNode{
		block: types.Block{Timestamp: types.Timestamp(10000 + types.BlockFrequency*2)},
	}
	doubleTimeNode.parent = genesisNode

	// Check the resulting childTarget of the new node and see that the clamp
	// was applied.
	doubleTimeNode.setChildTarget()
	if doubleTimeNode.childTarget.Cmp(genesisNode.childTarget) <= 0 {
		t.Error("double time node target did not increase")
	}
	fullAdjustment := genesisNode.childTarget.MulDifficulty(big.NewRat(1, 2))
	if doubleTimeNode.childTarget.Cmp(fullAdjustment) >= 0 {
		t.Error("clamp was not applied when adjusting target")
	}
}
Пример #7
0
// Returns many pieces of readily available information
func (e *Explorer) ExplorerStatus() modules.ExplorerStatus {
	lockID := e.mu.RLock()
	defer e.mu.RUnlock(lockID)

	// No reason that consensus should broadcast a block that it
	// doesn't have information on
	var currentTarget types.Target
	if e.currentBlock.ID() == e.genesisBlockID {
		currentTarget = types.RootDepth
	} else {
		var exists bool
		currentTarget, exists = e.cs.ChildTarget(e.currentBlock.ParentID)
		if build.DEBUG {
			if !exists {
				panic("The state of the current block cannot be found")
			}
		}
	}

	// Find the seen time of the block 144 ago in the list
	matureBlockTime := e.seenTimes[(e.blockchainHeight-144)%types.BlockHeight(len(e.seenTimes))]

	return modules.ExplorerStatus{
		Height:              e.blockchainHeight,
		Block:               e.currentBlock,
		Target:              currentTarget,
		MatureTime:          types.Timestamp(matureBlockTime.Unix()),
		TotalCurrency:       totalCurrency(e.blockchainHeight),
		ActiveContractCount: e.activeContracts,
		ActiveContractCosts: e.activeContractCost,
		ActiveContractSize:  e.activeContractSize,
		TotalContractCount:  e.totalContracts,
		TotalContractCosts:  e.totalContractCost,
		TotalContractSize:   e.totalContractSize,
	}
}
Пример #8
0
// TestNewChild probes the newChild method of the block node type.
func TestNewChild(t *testing.T) {
	parent := &blockNode{
		height: 12,
	}
	parent.depth[0] = 45
	parent.block.Timestamp = 100
	parent.childTarget[0] = 90

	child := parent.newChild(types.Block{Timestamp: types.Timestamp(100 + types.BlockFrequency)})
	if child.parent != parent {
		t.Error("parent-child relationship incorrect")
	}
	if child.height != 13 {
		t.Error("child height set incorrectly")
	}
	var expectedDepth types.Target
	expectedDepth[0] = 30
	if child.depth.Cmp(expectedDepth) != 0 {
		t.Error("child depth did not adjust correctly")
	}
	if child.childTarget.Cmp(parent.childTarget) != 0 {
		t.Error("child childTarget not adjusted correctly")
	}
}
Пример #9
0
// testBlockConflicts adds a transaction to the unconfirmed set, and then adds
// a conflicting transaction to the confirmed set, checking that the conflict
// is properly handled by the pool.
func (tpt *tpoolTester) testBlockConflicts() {
	// Put two transactions, a parent and a dependent, into the transaction
	// pool. Then create a transaction that is in conflict with the parent.
	parent := tpt.emptyUnlockTransaction()
	dependent := types.Transaction{
		SiacoinInputs: []types.SiacoinInput{
			types.SiacoinInput{
				ParentID: parent.SiacoinOutputID(0),
			},
		},
		MinerFees: []types.Currency{
			parent.SiacoinOutputs[0].Value,
		},
	}
	err := tpt.tpool.AcceptTransaction(parent)
	if err != nil {
		tpt.t.Fatal(err)
	}
	tpt.tpUpdateWait()
	err = tpt.tpool.AcceptTransaction(dependent)
	if err != nil {
		tpt.t.Fatal(err)
	}
	tpt.tpUpdateWait()

	// Create a transaction that is in conflict with the parent.
	parentValue := parent.SiacoinOutputSum()
	conflict := types.Transaction{
		SiacoinInputs: parent.SiacoinInputs,
		MinerFees: []types.Currency{
			parentValue,
		},
	}

	// Mine a block to put the conflict into the confirmed set. 'parent' has
	// dependencies of it's own, and 'conflict' has the same dependencies as
	// 'parent'. So the block we mine needs to include all of the dependencies
	// without including 'parent' or 'dependent'.
	tset := tpt.tpool.TransactionSet()
	tset = tset[:len(tset)-2]     // strip 'parent' and 'dependent'
	tset = append(tset, conflict) // add 'conflict'
	target, exists := tpt.cs.ChildTarget(tpt.cs.CurrentBlock().ID())
	if !exists {
		tpt.t.Fatal("unable to recover child target")
	}
	block := types.Block{
		ParentID:  tpt.cs.CurrentBlock().ID(),
		Timestamp: types.Timestamp(time.Now().Unix()),
		MinerPayouts: []types.SiacoinOutput{
			types.SiacoinOutput{Value: parentValue.Add(types.CalculateCoinbase(tpt.cs.Height() + 1))},
		},
		Transactions: tset,
	}
	for {
		block, found := tpt.miner.SolveBlock(block, target)
		if found {
			err = tpt.cs.AcceptBlock(block)
			if err != nil {
				tpt.t.Fatal(err)
			}
			break
		}
	}
	tpt.csUpdateWait()

	// Check that 'parent' and 'dependent' have been removed from the
	// transaction set, since conflict has made the confirmed set.
	if len(tpt.tpool.TransactionSet()) != 0 {
		tpt.t.Error("parent and dependent transaction are still in the pool after a conflict has been introduced, have", len(tset))
	}
}
Пример #10
0
// testRewinding adds transactions in a block, then removes the block and
// verifies that the transaction pool adds the block transactions.
func (tpt *tpoolTester) testRewinding() {
	// Put some transactions into the unconfirmed set.
	tpt.addSiacoinTransactionToPool()
	if len(tpt.tpool.TransactionSet()) == 0 {
		tpt.t.Fatal("transaction pool has no transactions")
	}

	// Prepare an empty block to cause a rewind (by forking).
	target, exists := tpt.cs.ChildTarget(tpt.cs.CurrentBlock().ID())
	if !exists {
		tpt.t.Fatal("unable to recover child target")
	}
	forkStart := types.Block{
		ParentID:  tpt.cs.CurrentBlock().ID(),
		Timestamp: types.Timestamp(time.Now().Unix()),
		MinerPayouts: []types.SiacoinOutput{
			types.SiacoinOutput{Value: types.CalculateCoinbase(tpt.cs.Height() + 1)},
		},
	}
	for {
		var found bool
		forkStart, found = tpt.miner.SolveBlock(forkStart, target)
		if found {
			break
		}
	}

	// Mine a block with the transaction.
	b, _ := tpt.miner.FindBlock()
	err := tpt.cs.AcceptBlock(b)
	if err != nil {
		tpt.t.Fatal(err)
	}
	tpt.csUpdateWait()
	if len(tpt.tpool.TransactionSet()) != 0 {
		tpt.t.Fatal("tset should be empty after FindBlock()")
	}

	// Fork around the block with the transaction.
	err = tpt.cs.AcceptBlock(forkStart)
	if err != nil && err != modules.ErrNonExtendingBlock {
		tpt.t.Fatal(err)
	}
	target, exists = tpt.cs.ChildTarget(tpt.cs.CurrentBlock().ID())
	if !exists {
		tpt.t.Fatal("unable to recover child target")
	}
	forkCommit := types.Block{
		ParentID:  forkStart.ID(),
		Timestamp: types.Timestamp(time.Now().Unix()),
		MinerPayouts: []types.SiacoinOutput{
			types.SiacoinOutput{Value: types.CalculateCoinbase(tpt.cs.Height() + 1)},
		},
	}
	for {
		var found bool
		forkCommit, found = tpt.miner.SolveBlock(forkCommit, target)
		if found {
			tpt.cs.AcceptBlock(forkCommit)
			break
		}
	}
	tpt.csUpdateWait()

	// Check that the transaction which was once confirmed but no longer is
	// confirmed is now unconfirmed.
	if len(tpt.tpool.TransactionSet()) == 0 {
		tpt.t.Error("tset should contain transactions that used to be confirmed but no longer are")
	}
}
Пример #11
0
// Now returns mockClock's pre-defined Timestamp.
func (c mockClock) Now() types.Timestamp {
	return c.now
}

var validateBlockTests = []struct {
	now            types.Timestamp
	minTimestamp   types.Timestamp
	blockTimestamp types.Timestamp
	blockSize      uint64
	errWant        error
	msg            string
}{
	{
		minTimestamp:   types.Timestamp(5),
		blockTimestamp: types.Timestamp(4),
		errWant:        errEarlyTimestamp,
		msg:            "ValidateBlock should reject blocks with timestamps that are too early",
	},
	{
		blockSize: types.BlockSizeLimit + 1,
		errWant:   errLargeBlock,
		msg:       "ValidateBlock should reject excessively large blocks",
	},
	{
		now:            types.Timestamp(50),
		blockTimestamp: types.Timestamp(50) + types.ExtremeFutureThreshold + 1,
		errWant:        errExtremeFutureTimestamp,
		msg:            "ValidateBlock should reject blocks timestamped in the extreme future",
	},
Пример #12
0
// TestTargetAdjustmentBase probes the targetAdjustmentBase method of the block
// node type.
func TestTargetAdjustmentBase(t *testing.T) {
	// Create a genesis node at timestamp 10,000
	genesisNode := &blockNode{
		block: types.Block{Timestamp: 10000},
	}
	exactTimeNode := &blockNode{
		block: types.Block{Timestamp: types.Timestamp(10000 + types.BlockFrequency)},
	}
	exactTimeNode.parent = genesisNode

	// Base adjustment for the exactTimeNode should be 1.
	adjustment, exact := exactTimeNode.targetAdjustmentBase().Float64()
	if !exact {
		t.Fatal("did not get an exact target adjustment")
	}
	if adjustment != 1 {
		t.Error("block did not adjust itself to the same target")
	}

	// Create a double-speed node and get the base adjustment.
	doubleSpeedNode := &blockNode{
		block: types.Block{Timestamp: types.Timestamp(10000 + types.BlockFrequency)},
	}
	doubleSpeedNode.parent = exactTimeNode
	adjustment, exact = doubleSpeedNode.targetAdjustmentBase().Float64()
	if !exact {
		t.Fatal("did not get an exact adjustment")
	}
	if adjustment != 0.5 {
		t.Error("double speed node did not get a base to halve the target")
	}

	// Create a half-speed node and get the base adjustment.
	halfSpeedNode := &blockNode{
		block: types.Block{Timestamp: types.Timestamp(10000 + types.BlockFrequency*6)},
	}
	halfSpeedNode.parent = doubleSpeedNode
	adjustment, exact = halfSpeedNode.targetAdjustmentBase().Float64()
	if !exact {
		t.Fatal("did not get an exact adjustment")
	}
	if adjustment != 2 {
		t.Error("double speed node did not get a base to halve the target")
	}

	if testing.Short() {
		t.SkipNow()
	}
	// Create a chain of nodes so that the genesis node is no longer the point
	// of comparison.
	comparisonNode := &blockNode{
		block: types.Block{Timestamp: 125000},
	}
	comparisonNode.parent = halfSpeedNode
	startingNode := comparisonNode
	for i := types.BlockHeight(0); i < types.TargetWindow; i++ {
		newNode := new(blockNode)
		newNode.parent = startingNode
		startingNode = newNode
	}
	startingNode.block.Timestamp = types.Timestamp(125000 + types.BlockFrequency*types.TargetWindow)
	adjustment, exact = startingNode.targetAdjustmentBase().Float64()
	if !exact {
		t.Error("failed to get exact result")
	}
	if adjustment != 1 {
		t.Error("got wrong long-range adjustment")
	}
	startingNode.block.Timestamp = types.Timestamp(125000 + 2*types.BlockFrequency*types.TargetWindow)
	adjustment, exact = startingNode.targetAdjustmentBase().Float64()
	if !exact {
		t.Error("failed to get exact result")
	}
	if adjustment != 2 {
		t.Error("got wrong long-range adjustment")
	}
}