github.com/datachainlab/burrow@v0.25.0/execution/contexts/permissions_context.go (about)

     1  package contexts
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hyperledger/burrow/acm"
     7  	"github.com/hyperledger/burrow/acm/acmstate"
     8  	"github.com/hyperledger/burrow/crypto"
     9  	"github.com/hyperledger/burrow/execution/errors"
    10  	"github.com/hyperledger/burrow/execution/exec"
    11  	"github.com/hyperledger/burrow/logging"
    12  	"github.com/hyperledger/burrow/permission"
    13  	"github.com/hyperledger/burrow/txs/payload"
    14  )
    15  
    16  type PermissionsContext struct {
    17  	StateWriter acmstate.ReaderWriter
    18  	Logger      *logging.Logger
    19  	tx          *payload.PermsTx
    20  }
    21  
    22  func (ctx *PermissionsContext) Execute(txe *exec.TxExecution, p payload.Payload) error {
    23  	var ok bool
    24  	ctx.tx, ok = p.(*payload.PermsTx)
    25  	if !ok {
    26  		return fmt.Errorf("payload must be PermsTx, but is: %v", txe.Envelope.Tx.Payload)
    27  	}
    28  	// Validate input
    29  	inAcc, err := ctx.StateWriter.GetAccount(ctx.tx.Input.Address)
    30  	if err != nil {
    31  		return err
    32  	}
    33  	if inAcc == nil {
    34  		ctx.Logger.InfoMsg("Cannot find input account",
    35  			"tx_input", ctx.tx.Input)
    36  		return errors.ErrorCodeInvalidAddress
    37  	}
    38  
    39  	err = ctx.tx.PermArgs.EnsureValid()
    40  	if err != nil {
    41  		return fmt.Errorf("PermsTx received containing invalid PermArgs: %v", err)
    42  	}
    43  
    44  	permFlag := ctx.tx.PermArgs.Action
    45  	// check permission
    46  	if !HasPermission(ctx.StateWriter, inAcc, permFlag, ctx.Logger) {
    47  		return fmt.Errorf("account %s does not have moderator permission %s (%b)", ctx.tx.Input.Address,
    48  			permFlag.String(), permFlag)
    49  	}
    50  
    51  	value := ctx.tx.Input.Amount
    52  
    53  	ctx.Logger.TraceMsg("New PermsTx",
    54  		"perm_args", ctx.tx.PermArgs.String())
    55  
    56  	var permAcc *acm.Account
    57  	switch ctx.tx.PermArgs.Action {
    58  	case permission.HasBase:
    59  		// this one doesn't make sense from txs
    60  		return fmt.Errorf("HasBase is for contracts, not humans. Just look at the blockchain")
    61  	case permission.SetBase:
    62  		permAcc, err = mutatePermissions(ctx.StateWriter, *ctx.tx.PermArgs.Target,
    63  			func(perms *permission.AccountPermissions) error {
    64  				return perms.Base.Set(*ctx.tx.PermArgs.Permission, *ctx.tx.PermArgs.Value)
    65  			})
    66  	case permission.UnsetBase:
    67  		permAcc, err = mutatePermissions(ctx.StateWriter, *ctx.tx.PermArgs.Target,
    68  			func(perms *permission.AccountPermissions) error {
    69  				return perms.Base.Unset(*ctx.tx.PermArgs.Permission)
    70  			})
    71  	case permission.SetGlobal:
    72  		permAcc, err = mutatePermissions(ctx.StateWriter, acm.GlobalPermissionsAddress,
    73  			func(perms *permission.AccountPermissions) error {
    74  				return perms.Base.Set(*ctx.tx.PermArgs.Permission, *ctx.tx.PermArgs.Value)
    75  			})
    76  	case permission.HasRole:
    77  		return fmt.Errorf("HasRole is for contracts, not humans. Just look at the blockchain")
    78  	case permission.AddRole:
    79  		permAcc, err = mutatePermissions(ctx.StateWriter, *ctx.tx.PermArgs.Target,
    80  			func(perms *permission.AccountPermissions) error {
    81  				if !perms.AddRole(*ctx.tx.PermArgs.Role) {
    82  					return fmt.Errorf("role (%s) already exists for account %s",
    83  						*ctx.tx.PermArgs.Role, *ctx.tx.PermArgs.Target)
    84  				}
    85  				return nil
    86  			})
    87  	case permission.RemoveRole:
    88  		permAcc, err = mutatePermissions(ctx.StateWriter, *ctx.tx.PermArgs.Target,
    89  			func(perms *permission.AccountPermissions) error {
    90  				if !perms.RemoveRole(*ctx.tx.PermArgs.Role) {
    91  					return fmt.Errorf("role (%s) does not exist for account %s",
    92  						*ctx.tx.PermArgs.Role, *ctx.tx.PermArgs.Target)
    93  				}
    94  				return nil
    95  			})
    96  	default:
    97  		return fmt.Errorf("invalid permission function: %v", permFlag)
    98  	}
    99  
   100  	// TODO: maybe we want to take funds on error and allow txs in that don't do anythingi?
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	// Good!
   106  	inAcc.Balance -= value
   107  	err = inAcc.SubtractFromBalance(value)
   108  	if err != nil {
   109  		return errors.ErrorCodef(errors.ErrorCodeInsufficientFunds,
   110  			"Input account does not have sufficient balance to cover input amount: %v", ctx.tx.Input)
   111  	}
   112  	err = ctx.StateWriter.UpdateAccount(inAcc)
   113  	if err != nil {
   114  		return err
   115  	}
   116  	if permAcc != nil {
   117  		err = ctx.StateWriter.UpdateAccount(permAcc)
   118  		if err != nil {
   119  			return err
   120  		}
   121  	}
   122  
   123  	txe.Input(ctx.tx.Input.Address, nil)
   124  	txe.Permission(&ctx.tx.PermArgs)
   125  	return nil
   126  }
   127  
   128  func mutatePermissions(stateReader acmstate.Reader, address crypto.Address,
   129  	mutator func(*permission.AccountPermissions) error) (*acm.Account, error) {
   130  
   131  	account, err := stateReader.GetAccount(address)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	if account == nil {
   136  		return nil, fmt.Errorf("could not get account at address %s in order to alter permissions", address)
   137  	}
   138  	return account, mutator(&account.Permissions)
   139  }