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 }