github.com/datachainlab/burrow@v0.25.0/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  	StateWriter  acmstate.ReaderWriter
    21  	ValidatorSet validator.Alterer
    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.StateWriter, ctx.tx.Inputs)
    38  	if err != nil {
    39  		return err
    40  	}
    41  
    42  	// ensure all inputs have root permissions
    43  	err = allHavePermission(ctx.StateWriter, 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  		if update.Address == nil && update.PublicKey == nil {
    54  			// We do not want to generate a key
    55  			return fmt.Errorf("could not execution GovTx since account template %v contains neither "+
    56  				"address or public key", update)
    57  		}
    58  		if update.PublicKey == nil {
    59  			update.PublicKey, err = ctx.MaybeGetPublicKey(*update.Address)
    60  			if err != nil {
    61  				return err
    62  			}
    63  		}
    64  		// Check address
    65  		if update.PublicKey != nil {
    66  			address := update.PublicKey.GetAddress()
    67  			if update.Address != nil && address != *update.Address {
    68  				return fmt.Errorf("supplied public key %v whose address %v does not match %v provided by"+
    69  					"GovTx", update.PublicKey, address, update.Address)
    70  			}
    71  			update.Address = &address
    72  		} else if update.Balances().HasPower() {
    73  			// If we are updating power we will need the key
    74  			return fmt.Errorf("GovTx must be provided with public key when updating validator power")
    75  		}
    76  		account, err := getOrMakeOutput(ctx.StateWriter, accounts, *update.Address, ctx.Logger)
    77  		if err != nil {
    78  			return err
    79  		}
    80  		governAccountEvent, err := ctx.UpdateAccount(account, update)
    81  		if err != nil {
    82  			txe.GovernAccount(governAccountEvent, errors.AsException(err))
    83  			return err
    84  		}
    85  		txe.GovernAccount(governAccountEvent, nil)
    86  	}
    87  	return nil
    88  }
    89  
    90  func (ctx *GovernanceContext) UpdateAccount(account *acm.Account, update *spec.TemplateAccount) (ev *exec.GovernAccountEvent, err error) {
    91  	ev = &exec.GovernAccountEvent{
    92  		AccountUpdate: update,
    93  	}
    94  	if update.Balances().HasNative() {
    95  		account.Balance = update.Balances().GetNative(0)
    96  	}
    97  	if update.NodeAddress != nil {
    98  		// TODO: can we do something useful if provided with a NodeAddress for an account about to become a validator
    99  		// like add it to persistent peers or pre gossip so it gets inbound connections? If so under which circumstances?
   100  	}
   101  	if update.Balances().HasPower() {
   102  		if update.PublicKey == nil {
   103  			err = fmt.Errorf("updateAccount should have PublicKey by this point but appears not to for "+
   104  				"template account: %v", update)
   105  			return
   106  		}
   107  		power := new(big.Int).SetUint64(update.Balances().GetPower(0))
   108  		if !power.IsInt64() {
   109  			err = fmt.Errorf("power supplied in update to validator power for %v does not fit into int64 and "+
   110  				"so is not supported by Tendermint", update.Address)
   111  		}
   112  		_, err := ctx.ValidatorSet.AlterPower(*update.PublicKey, power)
   113  		if err != nil {
   114  			return ev, err
   115  		}
   116  	}
   117  	if update.Code != nil {
   118  		account.Code = *update.Code
   119  		if err != nil {
   120  			return ev, err
   121  		}
   122  	}
   123  	perms := account.Permissions
   124  	if len(update.Permissions) > 0 {
   125  		perms.Base, err = permission.BasePermissionsFromStringList(update.Permissions)
   126  		if err != nil {
   127  			return
   128  		}
   129  	}
   130  	if len(update.Roles) > 0 {
   131  		perms.Roles = update.Roles
   132  	}
   133  	account.Permissions = perms
   134  	if err != nil {
   135  		return
   136  	}
   137  	err = ctx.StateWriter.UpdateAccount(account)
   138  	return
   139  }
   140  
   141  func (ctx *GovernanceContext) MaybeGetPublicKey(address crypto.Address) (*crypto.PublicKey, error) {
   142  	// First try state in case chain has received input previously
   143  	acc, err := ctx.StateWriter.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  }