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  }