github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/deploy/jobs/jobs_proposal.go (about) 1 package jobs 2 3 import ( 4 "encoding/json" 5 "fmt" 6 7 "github.com/hyperledger/burrow/execution/contexts" 8 9 "github.com/hyperledger/burrow/acm/acmstate" 10 "github.com/hyperledger/burrow/binary" 11 "github.com/hyperledger/burrow/crypto" 12 "github.com/hyperledger/burrow/deploy/def" 13 "github.com/hyperledger/burrow/deploy/proposals" 14 "github.com/hyperledger/burrow/deploy/util" 15 "github.com/hyperledger/burrow/logging" 16 "github.com/hyperledger/burrow/txs" 17 "github.com/hyperledger/burrow/txs/payload" 18 ) 19 20 func getAccountSequence(seq string, addressStr string, seqCache *acmstate.Cache, client *def.Client, logger *logging.Logger) (string, error) { 21 if seq != "" { 22 return seq, nil 23 } 24 address, err := client.ParseAddress(addressStr, logger) 25 if err != nil { 26 return "", err 27 } 28 acc, err := seqCache.GetAccount(address) 29 if err != nil { 30 return "", err 31 } 32 acc.Sequence++ 33 err = seqCache.UpdateAccount(acc) 34 return fmt.Sprintf("%d", acc.Sequence), err 35 } 36 37 func recurseJobs(proposeBatch *payload.BatchTx, jobs []*def.Job, prop *def.Proposal, do *def.DeployArgs, parentScript *def.Playbook, client *def.Client, seqCache *acmstate.Cache, logger *logging.Logger) error { 38 script := def.Playbook{Jobs: jobs, Account: FirstOf(prop.Source, parentScript.Account), Parent: parentScript, BinPath: parentScript.BinPath} 39 40 for _, job := range script.Jobs { 41 load, err := job.Payload() 42 if err != nil { 43 return fmt.Errorf("could not get Job payload: %v", load) 44 } 45 46 err = util.PreProcessFields(load, do, &script, client, logger) 47 if err != nil { 48 return err 49 } 50 // Revalidate with possible replacements 51 err = load.Validate() 52 if err != nil { 53 return fmt.Errorf("error validating job %s after pre-processing variables: %v", job.Name, err) 54 } 55 56 switch load.(type) { 57 case *def.Meta: 58 announceProposalJob(job.Name, "Meta", logger) 59 // load the package 60 err = recurseJobs(proposeBatch, job.Meta.Playbook.Jobs, prop, do, &script, client, seqCache, logger) 61 if err != nil { 62 return err 63 } 64 65 case *def.UpdateAccount: 66 announceProposalJob(job.Name, "UpdateAccount", logger) 67 job.UpdateAccount.Source = FirstOf(job.UpdateAccount.Source, script.Account) 68 job.UpdateAccount.Sequence, err = getAccountSequence(job.UpdateAccount.Sequence, job.UpdateAccount.Source, seqCache, client, logger) 69 if err != nil { 70 return err 71 } 72 tx, _, err := FormulateUpdateAccountJob(job.UpdateAccount, script.Account, client, logger) 73 if err != nil { 74 return err 75 } 76 proposeBatch.Txs = append(proposeBatch.Txs, &payload.Any{GovTx: tx}) 77 78 case *def.RegisterName: 79 announceProposalJob(job.Name, "RegisterName", logger) 80 job.RegisterName.Source = FirstOf(job.RegisterName.Source, script.Account) 81 job.RegisterName.Sequence, err = getAccountSequence(job.RegisterName.Sequence, job.RegisterName.Source, seqCache, client, logger) 82 if err != nil { 83 return err 84 } 85 txs, err := FormulateRegisterNameJob(job.RegisterName, do, &script, client, logger) 86 if err != nil { 87 return err 88 } 89 for _, tx := range txs { 90 proposeBatch.Txs = append(proposeBatch.Txs, &payload.Any{NameTx: tx}) 91 } 92 case *def.Call: 93 announceProposalJob(job.Name, "Call", logger) 94 job.Call.Source = FirstOf(job.Call.Source, script.Account) 95 job.Call.Sequence, err = getAccountSequence(job.Call.Sequence, job.Call.Source, seqCache, client, logger) 96 if err != nil { 97 return err 98 } 99 tx, err := FormulateCallJob(job.Call, do, &script, client, logger) 100 if err != nil { 101 return err 102 } 103 proposeBatch.Txs = append(proposeBatch.Txs, &payload.Any{CallTx: tx}) 104 case *def.Deploy: 105 announceProposalJob(job.Name, "Deploy", logger) 106 job.Deploy.Source = FirstOf(job.Deploy.Source, script.Account) 107 job.Deploy.Sequence, err = getAccountSequence(job.Deploy.Sequence, job.Deploy.Source, seqCache, client, logger) 108 if err != nil { 109 return err 110 } 111 deployTxs, _, err := FormulateDeployJob(job.Deploy, do, &script, client, job.Intermediate, logger) 112 if err != nil { 113 return err 114 } 115 var deployAddress crypto.Address 116 // Predict address 117 callee, err := client.ParseAddress(job.Deploy.Source, logger) 118 if err != nil { 119 return err 120 } 121 for _, tx := range deployTxs { 122 proposeBatch.Txs = append(proposeBatch.Txs, &payload.Any{CallTx: tx}) 123 txEnv := txs.NewTx(tx) 124 125 deployAddress = crypto.NewContractAddress(callee, txEnv.Hash()) 126 } 127 job.Result = deployAddress.String() 128 case *def.Permission: 129 announceProposalJob(job.Name, "Permission", logger) 130 job.Permission.Source = FirstOf(job.Permission.Source, script.Account) 131 job.Permission.Sequence, err = getAccountSequence(job.Permission.Sequence, job.Permission.Source, seqCache, client, logger) 132 if err != nil { 133 return err 134 } 135 tx, err := FormulatePermissionJob(job.Permission, script.Account, client, logger) 136 if err != nil { 137 return err 138 } 139 proposeBatch.Txs = append(proposeBatch.Txs, &payload.Any{PermsTx: tx}) 140 case *def.Send: 141 announceProposalJob(job.Name, "Send", logger) 142 job.Send.Source = FirstOf(job.Send.Source, script.Account) 143 job.Send.Sequence, err = getAccountSequence(job.Send.Sequence, job.Send.Source, seqCache, client, logger) 144 if err != nil { 145 return err 146 } 147 tx, err := FormulateSendJob(job.Send, script.Account, client, logger) 148 if err != nil { 149 return err 150 } 151 proposeBatch.Txs = append(proposeBatch.Txs, &payload.Any{SendTx: tx}) 152 case *def.QueryContract: 153 announceProposalJob(job.Name, "Query Contract", logger) 154 logger.InfoMsg("Query Contract jobs are IGNORED in proposals") 155 156 case *def.Assert: 157 announceProposalJob(job.Name, "Assert", logger) 158 logger.InfoMsg("Assert jobs are IGNORED in proposals") 159 default: 160 return fmt.Errorf("jobs %s illegal job type for proposal", job.Name) 161 } 162 } 163 164 return nil 165 } 166 167 func ProposalJob(prop *def.Proposal, do *def.DeployArgs, parentScript *def.Playbook, client *def.Client, logger *logging.Logger) (string, error) { 168 var proposeBatch payload.BatchTx 169 170 seqCache := acmstate.NewCache(client) 171 172 err := recurseJobs(&proposeBatch, prop.Jobs, prop, do, parentScript, client, seqCache, logger) 173 if err != nil { 174 return "", err 175 } 176 177 proposal := payload.Proposal{Name: prop.Name, Description: prop.Description, BatchTx: &proposeBatch} 178 179 proposalInput, err := client.TxInput(prop.ProposalAddress, "", prop.ProposalSequence, false, logger) 180 if err != nil { 181 return "", err 182 } 183 proposal.BatchTx.Inputs = []*payload.TxInput{proposalInput} 184 proposalHash := contexts.HashProposal(&proposal) 185 186 var proposalTx *payload.ProposalTx 187 if do.ProposeVerify { 188 ballot, err := client.GetProposal(proposalHash, logger) 189 if err != nil { 190 logger.InfoMsg("Proposal could NOT be verified", "error", err) 191 return "", err 192 } 193 194 err = proposals.ProposalExpired(ballot.Proposal, client, logger) 195 if err != nil { 196 logger.InfoMsg("Proposal verify FAILED", "error", err) 197 return "", err 198 } 199 200 logger.InfoMsg("Proposal VERIFY SUCCESSFUL", "votes count", len(ballot.Votes)) 201 202 for i, v := range ballot.Votes { 203 logger.InfoMsg("Vote", "no", i+1, "address", v.Address.String()) 204 } 205 206 return "", err 207 } else if do.ProposeVote { 208 ballot, err := client.GetProposal(proposalHash, logger) 209 if err != nil { 210 logger.InfoMsg("Proposal could not be found", "error", err) 211 return "", err 212 } 213 214 err = proposals.ProposalExpired(ballot.Proposal, client, logger) 215 if err != nil { 216 logger.InfoMsg("Proposal error", "error", err) 217 return "", err 218 } 219 220 // proposal is there and current, let's vote for it 221 input, err := client.TxInput(parentScript.Account, "", prop.Sequence, true, logger) 222 if err != nil { 223 return "", err 224 } 225 226 logger.InfoMsg("Voting for proposal", "hash", fmt.Sprintf("%X", proposalHash)) 227 228 h := binary.HexBytes(proposalHash) 229 proposalTx = &payload.ProposalTx{ProposalHash: &h, VotingWeight: 1, Input: input} 230 } else if do.ProposeCreate { 231 input, err := client.TxInput(FirstOf(prop.Source, parentScript.Account), "", prop.Sequence, true, logger) 232 if err != nil { 233 return "", err 234 } 235 logger.InfoMsg("Creating Proposal", "hash", fmt.Sprintf("%X", proposalHash)) 236 237 bs, _ := json.Marshal(proposal) 238 logger.TraceMsg("Proposal json", "json", string(bs)) 239 proposalTx = &payload.ProposalTx{VotingWeight: 1, Input: input, Proposal: &proposal} 240 } else { 241 logger.InfoMsg("please specify one of --proposal-create, --proposal-vote, --proposal-verify") 242 return "", nil 243 } 244 245 txe, err := client.SignAndBroadcast(proposalTx, logger) 246 if err != nil { 247 return "", fmt.Errorf("error in ProposalJob with proposal %v: %w", prop, err) 248 } 249 250 result := fmt.Sprintf("%X", txe.Receipt.TxHash) 251 252 return result, nil 253 }