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  }