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 }