// 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(ns walletdb.ReadWriteBucket, waddrmgrNs walletdb.ReadBucket, blockHash *chainhash.Hash, height int64, sstxHash *chainhash.Hash, allowHighFees bool) (*StakeNotification, error) { // 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(ns, 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.TxSStxStakeOutputInfo(sstx.MsgTx()) ssrtxCalcAmts := stake.CalculateRewards(sstxAmts, sstx.MsgTx().TxOut[0].Value, int64(0)) // Calculate the fee to use for this revocation based on the fee // per KB that is standard for mainnet. revocationSizeEst := estimateSSRtxTxSize(1, len(sstxPkhs)) revocationFee := txrules.FeeForSerializeSize(revocationFeePerKB, revocationSizeEst) // 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] >= int64(revocationFee) { amt -= int64(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. _, err = stake.IsSSRtx(msgTx) if err != nil { return nil, err } // Sign the transaction. err = s.SignVRTransaction(waddrmgrNs, msgTx, sstx, false) if err != nil { return nil, err } // Store the information about the SSRtx. hash := msgTx.TxSha() err = s.insertSSRtx(ns, blockHash, height, &hash, sstx.Sha()) if err != nil { return nil, err } // Send the transaction. ssrtxSha, err := s.chainSvr.SendRawTransaction(msgTx, allowHighFees) 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(ns walletdb.ReadWriteBucket, waddrmgrNs walletdb.ReadBucket, blockHash *chainhash.Hash, height int64, sstxHash *chainhash.Hash, defaultVoteBits stake.VoteBits, allowHighFees bool) (*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(ns, sstxHash) if err != nil { return nil, err } sstx := sstxRecord.tx sstxMsgTx := sstx.MsgTx() // The legacy wallet didn't store anything about the voteBits to use. // In the case we're loading a legacy wallet and the voteBits are // unset, just use the default voteBits as set by the user. voteBits := defaultVoteBits if sstxRecord.voteBitsSet { voteBits.Bits = sstxRecord.voteBits voteBits.ExtendedBits = sstxRecord.voteBitsExt } // 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.TxSStxStakeOutputInfo(sstxMsgTx) // Get the current reward. initSudsidyCacheOnce.Do(func() { subsidyCache = blockchain.NewSubsidyCache(height, s.Params) }) stakeVoteSubsidy := blockchain.CalcStakeVoteSubsidy(subsidyCache, height, s.Params) // Calculate the output values from this data. ssgenCalcAmts := stake.CalculateRewards(sstxAmts, sstxMsgTx.TxOut[0].Value, stakeVoteSubsidy) subsidyCache = blockchain.NewSubsidyCache(height, s.Params) // 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), wire.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 := generateVoteScript(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. _, err = stake.IsSSGen(msgTx) if err != nil { return nil, err } // Sign the transaction. err = s.SignVRTransaction(waddrmgrNs, msgTx, sstx, true) if err != nil { return nil, err } // Store the information about the SSGen. hash := msgTx.TxSha() err = s.insertSSGen(ns, blockHash, height, &hash, voteBits.Bits, sstx.Sha()) if err != nil { return nil, err } // Send the transaction. ssgenSha, err := s.chainSvr.SendRawTransaction(msgTx, allowHighFees) 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.Bits, } return ntfn, nil }