Ejemplo n.º 1
0
// GenerateRevocation generates a revocation (SSRtx), signs it, and
// submits it by SendRawTransaction. It also stores a record of it
// in the local database.
func (s *StakeStore) generateRevocation(blockHash *chainhash.Hash, height int64,
	sstxHash *chainhash.Hash) (*StakeNotification, error) {
	var revocationFee int64
	switch {
	case s.Params == &chaincfg.MainNetParams:
		revocationFee = revocationFeeMainNet
	case s.Params == &chaincfg.TestNetParams:
		revocationFee = revocationFeeTestNet
	default:
		revocationFee = revocationFeeTestNet
	}

	// 1. Fetch the SStx, then calculate all the values we'll need later for
	// the generation of the SSRtx tx outputs.
	sstxRecord, err := s.getSStx(sstxHash)
	if err != nil {
		return nil, err
	}
	sstx := sstxRecord.tx

	// Store the sstx pubkeyhashes and amounts as found in the transaction
	// outputs.
	// TODO Get information on the allowable fee range for the revocation
	// and check to make sure we don't overflow that.
	sstxPayTypes, sstxPkhs, sstxAmts, _, _, _ :=
		stake.GetSStxStakeOutputInfo(sstx)

	ssrtxCalcAmts := stake.GetStakeRewards(sstxAmts, sstx.MsgTx().TxOut[0].Value,
		int64(0))

	// 2. Add the only input.
	msgTx := wire.NewMsgTx()

	// SStx tagged output as an OutPoint; reference this as
	// the only input.
	prevOut := wire.NewOutPoint(sstxHash,
		0, // Index 0
		1) // Tree stake
	txIn := wire.NewTxIn(prevOut, []byte{})
	msgTx.AddTxIn(txIn)

	// 3. Add all the OP_SSRTX tagged outputs.

	// Add all the SSRtx-tagged transaction outputs to the transaction after
	// performing some validity checks.
	feeAdded := false
	for i, sstxPkh := range sstxPkhs {
		// Create a new script which pays to the provided address specified in
		// the original ticket tx.
		var ssrtxOutScript []byte
		switch sstxPayTypes[i] {
		case false: // P2PKH
			ssrtxOutScript, err = txscript.PayToSSRtxPKHDirect(sstxPkh)
			if err != nil {
				return nil, err
			}
		case true: // P2SH
			ssrtxOutScript, err = txscript.PayToSSRtxSHDirect(sstxPkh)
			if err != nil {
				return nil, err
			}
		}

		// Add a fee from an output that has enough.
		amt := ssrtxCalcAmts[i]
		if !feeAdded && ssrtxCalcAmts[i] >= revocationFee {
			amt -= revocationFee
			feeAdded = true
		}

		// Add the txout to our SSRtx tx.
		txOut := wire.NewTxOut(amt, ssrtxOutScript)
		msgTx.AddTxOut(txOut)
	}

	// Check to make sure our SSRtx was created correctly.
	ssrtxTx := dcrutil.NewTx(msgTx)
	ssrtxTx.SetTree(dcrutil.TxTreeStake)
	_, err = stake.IsSSRtx(ssrtxTx)
	if err != nil {
		return nil, err
	}

	// Sign the transaction.
	err = s.SignVRTransaction(msgTx, sstx, false)
	if err != nil {
		return nil, err
	}

	// Send the transaction.
	ssrtxSha, err := s.chainSvr.SendRawTransaction(msgTx, false)
	if err != nil {
		return nil, err
	}

	// Store the information about the SSRtx.
	err = s.insertSSRtx(blockHash,
		height,
		ssrtxSha,
		sstx.Sha())
	if err != nil {
		return nil, err
	}

	log.Debugf("Generated SSRtx %v. "+
		"The ticket used to generate the SSRtx was %v.",
		ssrtxSha, sstx.Sha())

	// Generate a notification to return.
	ntfn := &StakeNotification{
		TxType:    int8(stake.TxTypeSSRtx),
		TxHash:    *ssrtxSha,
		BlockHash: chainhash.Hash{},
		Height:    0,
		Amount:    0,
		SStxIn:    *sstx.Sha(),
		VoteBits:  0,
	}

	return ntfn, nil
}
Ejemplo n.º 2
0
// DebugMsgTxString dumps a verbose message containing information about the
// contents of a transaction.
func DebugMsgTxString(msgTx *wire.MsgTx) string {
	tx := dcrutil.NewTx(msgTx)
	isSStx, _ := stake.IsSStx(tx)
	isSSGen, _ := stake.IsSSGen(tx)
	var sstxType []bool
	var sstxPkhs [][]byte
	var sstxAmts []int64
	var sstxRules [][]bool
	var sstxLimits [][]uint16

	if isSStx {
		sstxType, sstxPkhs, sstxAmts, _, sstxRules, sstxLimits =
			stake.GetSStxStakeOutputInfo(tx)
	}

	var buffer bytes.Buffer

	hash := msgTx.TxSha()
	str := fmt.Sprintf("Transaction hash: %v, Version %v, Locktime: %v, "+
		"Expiry %v\n\n", hash, msgTx.Version, msgTx.LockTime, msgTx.Expiry)
	buffer.WriteString(str)

	str = fmt.Sprintf("==INPUTS==\nNumber of inputs: %v\n\n",
		len(msgTx.TxIn))
	buffer.WriteString(str)

	for i, input := range msgTx.TxIn {
		str = fmt.Sprintf("Input number: %v\n", i)
		buffer.WriteString(str)

		str = fmt.Sprintf("Previous outpoint hash: %v, ",
			input.PreviousOutPoint.Hash)
		buffer.WriteString(str)

		str = fmt.Sprintf("Previous outpoint index: %v, ",
			input.PreviousOutPoint.Index)
		buffer.WriteString(str)

		str = fmt.Sprintf("Previous outpoint tree: %v \n",
			input.PreviousOutPoint.Tree)
		buffer.WriteString(str)

		str = fmt.Sprintf("Sequence: %v \n",
			input.Sequence)
		buffer.WriteString(str)

		str = fmt.Sprintf("ValueIn: %v \n",
			input.ValueIn)
		buffer.WriteString(str)

		str = fmt.Sprintf("BlockHeight: %v \n",
			input.BlockHeight)
		buffer.WriteString(str)

		str = fmt.Sprintf("BlockIndex: %v \n",
			input.BlockIndex)
		buffer.WriteString(str)

		str = fmt.Sprintf("Raw signature script: %x \n", input.SignatureScript)
		buffer.WriteString(str)

		sigScr, _ := txscript.DisasmString(input.SignatureScript)
		str = fmt.Sprintf("Disasmed signature script: %v \n\n",
			sigScr)
		buffer.WriteString(str)
	}

	str = fmt.Sprintf("==OUTPUTS==\nNumber of outputs: %v\n\n",
		len(msgTx.TxOut))
	buffer.WriteString(str)

	for i, output := range msgTx.TxOut {
		str = fmt.Sprintf("Output number: %v\n", i)
		buffer.WriteString(str)

		coins := float64(output.Value) / 1e8
		str = fmt.Sprintf("Output amount: %v atoms or %v coins\n", output.Value,
			coins)
		buffer.WriteString(str)

		// SStx OP_RETURNs, dump pkhs and amts committed
		if isSStx && i != 0 && i%2 == 1 {
			coins := float64(sstxAmts[i/2]) / 1e8
			str = fmt.Sprintf("SStx commit amount: %v atoms or %v coins\n",
				sstxAmts[i/2], coins)
			buffer.WriteString(str)
			str = fmt.Sprintf("SStx commit address: %x\n",
				sstxPkhs[i/2])
			buffer.WriteString(str)
			str = fmt.Sprintf("SStx address type is P2SH: %v\n",
				sstxType[i/2])
			buffer.WriteString(str)

			str = fmt.Sprintf("SStx all address types is P2SH: %v\n",
				sstxType)
			buffer.WriteString(str)

			str = fmt.Sprintf("Voting is fee limited: %v\n",
				sstxLimits[i/2][0])
			buffer.WriteString(str)
			if sstxRules[i/2][0] {
				str = fmt.Sprintf("Voting limit imposed: %v\n",
					sstxLimits[i/2][0])
				buffer.WriteString(str)
			}

			str = fmt.Sprintf("Revoking is fee limited: %v\n",
				sstxRules[i/2][1])
			buffer.WriteString(str)

			if sstxRules[i/2][1] {
				str = fmt.Sprintf("Voting limit imposed: %v\n",
					sstxLimits[i/2][1])
				buffer.WriteString(str)
			}
		}

		// SSGen block/block height OP_RETURN.
		if isSSGen && i == 0 {
			blkHash, blkHeight, _ := stake.GetSSGenBlockVotedOn(tx)
			str = fmt.Sprintf("SSGen block hash voted on: %v, height: %v\n",
				blkHash, blkHeight)
			buffer.WriteString(str)
		}

		if isSSGen && i == 1 {
			vb := stake.GetSSGenVoteBits(tx)
			str = fmt.Sprintf("SSGen vote bits: %v\n", vb)
			buffer.WriteString(str)
		}

		str = fmt.Sprintf("Raw script: %x \n", output.PkScript)
		buffer.WriteString(str)

		scr, _ := txscript.DisasmString(output.PkScript)
		str = fmt.Sprintf("Disasmed script: %v \n\n", scr)
		buffer.WriteString(str)
	}

	return buffer.String()
}
Ejemplo n.º 3
0
// GenerateVote creates a new SSGen given a header hash, height, sstx
// tx hash, and votebits.
func (s *StakeStore) generateVote(blockHash *chainhash.Hash, height int64,
	sstxHash *chainhash.Hash, voteBits uint16) (*StakeNotification, error) {
	// 1. Fetch the SStx, then calculate all the values we'll need later for
	// the generation of the SSGen tx outputs.
	sstxRecord, err := s.getSStx(sstxHash)
	if err != nil {
		return nil, err
	}
	sstx := sstxRecord.tx
	sstxMsgTx := sstx.MsgTx()

	// Store the sstx pubkeyhashes and amounts as found in the transaction
	// outputs.
	// TODO Get information on the allowable fee range for the vote
	// and check to make sure we don't overflow that.
	ssgenPayTypes, ssgenPkhs, sstxAmts, _, _, _ :=
		stake.GetSStxStakeOutputInfo(sstx)

	// Get the current reward.
	stakeVoteSubsidy := blockchain.CalcStakeVoteSubsidy(height,
		s.Params)

	// Calculate the output values from this data.
	ssgenCalcAmts := stake.GetStakeRewards(sstxAmts,
		sstxMsgTx.TxOut[0].Value,
		stakeVoteSubsidy)

	// 2. Add all transaction inputs to a new transaction after performing
	// some validity checks. First, add the stake base, then the OP_SSTX
	// tagged output.
	msgTx := wire.NewMsgTx()

	// Stakebase.
	stakeBaseOutPoint := wire.NewOutPoint(&chainhash.Hash{},
		uint32(0xFFFFFFFF),
		dcrutil.TxTreeRegular)
	txInStakeBase := wire.NewTxIn(stakeBaseOutPoint, []byte{})
	msgTx.AddTxIn(txInStakeBase)

	// Add the subsidy amount into the input.
	msgTx.TxIn[0].ValueIn = stakeVoteSubsidy

	// SStx tagged output as an OutPoint.
	prevOut := wire.NewOutPoint(sstxHash,
		0, // Index 0
		1) // Tree stake
	txIn := wire.NewTxIn(prevOut, []byte{})
	msgTx.AddTxIn(txIn)

	// 3. Add the OP_RETURN null data pushes of the block header hash,
	// the block height, and votebits, then add all the OP_SSGEN tagged
	// outputs.
	//
	// Block reference output.
	blockRefScript, err := txscript.GenerateSSGenBlockRef(*blockHash,
		uint32(height))
	if err != nil {
		return nil, err
	}
	blockRefOut := wire.NewTxOut(0, blockRefScript)
	msgTx.AddTxOut(blockRefOut)

	// Votebits output.
	blockVBScript, err := txscript.GenerateSSGenVotes(voteBits)
	if err != nil {
		return nil, err
	}
	blockVBOut := wire.NewTxOut(0, blockVBScript)
	msgTx.AddTxOut(blockVBOut)

	// Add all the SSGen-tagged transaction outputs to the transaction after
	// performing some validity checks.
	for i, ssgenPkh := range ssgenPkhs {
		// Create a new script which pays to the provided address specified in
		// the original ticket tx.
		var ssgenOutScript []byte
		switch ssgenPayTypes[i] {
		case false: // P2PKH
			ssgenOutScript, err = txscript.PayToSSGenPKHDirect(ssgenPkh)
			if err != nil {
				return nil, err
			}
		case true: // P2SH
			ssgenOutScript, err = txscript.PayToSSGenSHDirect(ssgenPkh)
			if err != nil {
				return nil, err
			}
		}

		// Add the txout to our SSGen tx.
		txOut := wire.NewTxOut(ssgenCalcAmts[i], ssgenOutScript)

		msgTx.AddTxOut(txOut)
	}

	// Check to make sure our SSGen was created correctly.
	ssgenTx := dcrutil.NewTx(msgTx)
	ssgenTx.SetTree(dcrutil.TxTreeStake)
	_, err = stake.IsSSGen(ssgenTx)
	if err != nil {
		return nil, err
	}

	// Sign the transaction.
	err = s.SignVRTransaction(msgTx, sstx, true)
	if err != nil {
		return nil, err
	}

	// Send the transaction.
	ssgenSha, err := s.chainSvr.SendRawTransaction(msgTx, false)
	if err != nil {
		return nil, err
	}

	// Store the information about the SSGen.
	err = s.insertSSGen(blockHash,
		height,
		ssgenSha,
		voteBits,
		sstx.Sha())
	if err != nil {
		return nil, err
	}

	log.Debugf("Generated SSGen %v , voting on block %v at height %v. "+
		"The ticket used to generate the SSGen was %v.",
		ssgenSha, blockHash, height, sstxHash)

	// Generate a notification to return.
	ntfn := &StakeNotification{
		TxType:    int8(stake.TxTypeSSGen),
		TxHash:    *ssgenSha,
		BlockHash: *blockHash,
		Height:    int32(height),
		Amount:    0,
		SStxIn:    *sstx.Sha(),
		VoteBits:  voteBits,
	}

	return ntfn, nil
}