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