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