// 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 }
// 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 }