github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/contexts/governance_context.go (about)

     1  package contexts
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  
     7  	"github.com/hyperledger/burrow/acm"
     8  	"github.com/hyperledger/burrow/acm/acmstate"
     9  	"github.com/hyperledger/burrow/acm/validator"
    10  	"github.com/hyperledger/burrow/crypto"
    11  	"github.com/hyperledger/burrow/execution/errors"
    12  	"github.com/hyperledger/burrow/execution/exec"
    13  	"github.com/hyperledger/burrow/genesis/spec"
    14  	"github.com/hyperledger/burrow/logging"
    15  	"github.com/hyperledger/burrow/permission"
    16  	"github.com/hyperledger/burrow/txs/payload"
    17  )
    18  
    19  type GovernanceContext struct {
    20  	State        acmstate.ReaderWriter
    21  	ValidatorSet validator.ReaderWriter
    22  	Logger       *logging.Logger
    23  	tx           *payload.GovTx
    24  	txe          *exec.TxExecution
    25  }
    26  
    27  // GovTx provides a set of TemplateAccounts and GovernanceContext tries to alter the chain state to match the
    28  // specification given
    29  func (ctx *GovernanceContext) Execute(txe *exec.TxExecution, p payload.Payload) error {
    30  	var ok bool
    31  	ctx.txe = txe
    32  	ctx.tx, ok = p.(*payload.GovTx)
    33  	if !ok {
    34  		return fmt.Errorf("payload must be NameTx, but is: %v", txe.Envelope.Tx.Payload)
    35  	}
    36  	// Nothing down with any incoming funds at this point
    37  	accounts, _, err := getInputs(ctx.State, ctx.tx.Inputs)
    38  	if err != nil {
    39  		return err
    40  	}
    41  
    42  	// ensure all inputs have root permissions
    43  	err = allHavePermission(ctx.State, permission.Root, accounts, ctx.Logger)
    44  	if err != nil {
    45  		return errors.Wrap(err, "at least one input lacks permission for GovTx")
    46  	}
    47  
    48  	for _, i := range ctx.tx.Inputs {
    49  		txe.Input(i.Address, nil)
    50  	}
    51  
    52  	for _, update := range ctx.tx.AccountUpdates {
    53  		err := VerifyIdentity(ctx.State, update)
    54  		if err != nil {
    55  			return fmt.Errorf("GovTx: %v", err)
    56  		}
    57  		account, err := getOrMakeOutput(ctx.State, accounts, *update.Address, ctx.Logger)
    58  		if err != nil {
    59  			return err
    60  		}
    61  		governAccountEvent, err := ctx.UpdateAccount(account, update)
    62  		if err != nil {
    63  			txe.GovernAccount(governAccountEvent, errors.AsException(err))
    64  			return err
    65  		}
    66  		txe.GovernAccount(governAccountEvent, nil)
    67  	}
    68  	return nil
    69  }
    70  
    71  func (ctx *GovernanceContext) UpdateAccount(account *acm.Account, update *spec.TemplateAccount) (ev *exec.GovernAccountEvent, err error) {
    72  	ev = &exec.GovernAccountEvent{
    73  		AccountUpdate: update,
    74  	}
    75  	if update.Balances().HasNative() {
    76  		account.Balance = update.Balances().GetNative(0)
    77  	}
    78  	if update.Balances().HasPower() {
    79  		if update.PublicKey == nil {
    80  			err = fmt.Errorf("updateAccount should have PublicKey by this point but appears not to for "+
    81  				"template account: %v", update)
    82  			return
    83  		}
    84  		power := new(big.Int).SetUint64(update.Balances().GetPower(0))
    85  		_, err := ctx.ValidatorSet.SetPower(update.PublicKey, power)
    86  		if err != nil {
    87  			return ev, err
    88  		}
    89  	}
    90  	if update.Code != nil {
    91  		account.EVMCode = *update.Code
    92  		if err != nil {
    93  			return ev, err
    94  		}
    95  	}
    96  	perms := account.Permissions
    97  	if len(update.Permissions) > 0 {
    98  		perms.Base, err = permission.BasePermissionsFromStringList(update.Permissions)
    99  		if err != nil {
   100  			return
   101  		}
   102  	}
   103  	if len(update.Roles) > 0 {
   104  		perms.Roles = update.Roles
   105  	}
   106  	account.Permissions = perms
   107  	if err != nil {
   108  		return
   109  	}
   110  	err = ctx.State.UpdateAccount(account)
   111  	return
   112  }
   113  
   114  func VerifyIdentity(sw acmstate.ReaderWriter, account *spec.TemplateAccount) (err error) {
   115  	if account.Address == nil && account.PublicKey == nil {
   116  		// We do not want to generate a key
   117  		return fmt.Errorf("could not execute Tx since account template %v contains neither "+
   118  			"address or public key", account)
   119  	}
   120  	if account.PublicKey == nil {
   121  		account.PublicKey, err = MaybeGetPublicKey(sw, *account.Address)
   122  		if err != nil {
   123  			return err
   124  		}
   125  	}
   126  	// Check address
   127  	if account.PublicKey != nil {
   128  		address := account.PublicKey.GetAddress()
   129  		if account.Address != nil && address != *account.Address {
   130  			return fmt.Errorf("supplied public key %v whose address %v does not match %v provided by"+
   131  				"GovTx", account.PublicKey, address, account.Address)
   132  		}
   133  		account.Address = &address
   134  	} else if account.Balances().HasPower() {
   135  		// If we are updating power we will need the key
   136  		return fmt.Errorf("must be provided with public key when updating validator power")
   137  	}
   138  	return nil
   139  }
   140  
   141  func MaybeGetPublicKey(sw acmstate.ReaderWriter, address crypto.Address) (*crypto.PublicKey, error) {
   142  	// First try state in case chain has received input previously
   143  	acc, err := sw.GetAccount(address)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	if acc != nil && acc.PublicKey.IsSet() {
   148  		publicKey := acc.PublicKey
   149  		return publicKey, nil
   150  	}
   151  	return nil, nil
   152  }