github.com/datachainlab/burrow@v0.25.0/execution/contexts/shared.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/logging/structure"
    13  	"github.com/hyperledger/burrow/permission"
    14  	"github.com/hyperledger/burrow/txs/payload"
    15  )
    16  
    17  type Context interface {
    18  	Execute(txe *exec.TxExecution, p payload.Payload) error
    19  }
    20  
    21  // The accounts from the TxInputs must either already have
    22  // acm.PublicKey().(type) != nil, (it must be known),
    23  // or it must be specified in the TxInput.  If redeclared,
    24  // the TxInput is modified and input.PublicKey() set to nil.
    25  func getInputs(accountGetter acmstate.AccountGetter, ins []*payload.TxInput) (map[crypto.Address]*acm.Account, uint64, error) {
    26  	var total uint64
    27  	accounts := map[crypto.Address]*acm.Account{}
    28  	for _, in := range ins {
    29  		// Account shouldn't be duplicated
    30  		if _, ok := accounts[in.Address]; ok {
    31  			return nil, total, errors.ErrorCodeDuplicateAddress
    32  		}
    33  		acc, err := accountGetter.GetAccount(in.Address)
    34  		if err != nil {
    35  			return nil, total, err
    36  		}
    37  		if acc == nil {
    38  			return nil, total, errors.ErrorCodeInvalidAddress
    39  		}
    40  		accounts[in.Address] = acc
    41  		total += in.Amount
    42  	}
    43  	return accounts, total, nil
    44  }
    45  
    46  func getOrMakeOutputs(accountGetter acmstate.AccountGetter, accs map[crypto.Address]*acm.Account,
    47  	outs []*payload.TxOutput, logger *logging.Logger) (map[crypto.Address]*acm.Account, error) {
    48  	if accs == nil {
    49  		accs = make(map[crypto.Address]*acm.Account)
    50  	}
    51  	// we should err if an account is being created but the inputs don't have permission
    52  	var err error
    53  	for _, out := range outs {
    54  		accs[out.Address], err = getOrMakeOutput(accountGetter, accs, out.Address, logger)
    55  		if err != nil {
    56  			return nil, err
    57  		}
    58  	}
    59  	return accs, nil
    60  }
    61  
    62  func getOrMakeOutput(accountGetter acmstate.AccountGetter, accs map[crypto.Address]*acm.Account,
    63  	outputAddress crypto.Address, logger *logging.Logger) (*acm.Account, error) {
    64  
    65  	// Account shouldn't be duplicated
    66  	if _, ok := accs[outputAddress]; ok {
    67  		return nil, errors.ErrorCodeDuplicateAddress
    68  	}
    69  	acc, err := accountGetter.GetAccount(outputAddress)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	// output account may be nil (new)
    74  	if acc == nil {
    75  		if !hasCreateAccountPermission(accountGetter, accs, logger) {
    76  			return nil, fmt.Errorf("at least one input does not have permission to create accounts")
    77  		}
    78  		logger.InfoMsg("Account not found so attempting to create it", "address", outputAddress)
    79  		acc = &acm.Account{
    80  			Address:     outputAddress,
    81  			Sequence:    0,
    82  			Balance:     0,
    83  			Permissions: permission.ZeroAccountPermissions,
    84  		}
    85  	}
    86  
    87  	return acc, nil
    88  }
    89  
    90  func validateOutputs(outs []*payload.TxOutput) (uint64, error) {
    91  	total := uint64(0)
    92  	for _, out := range outs {
    93  		// Good. Add amount to total
    94  		total += out.Amount
    95  	}
    96  	return total, nil
    97  }
    98  
    99  func adjustByInputs(accs map[crypto.Address]*acm.Account, ins []*payload.TxInput) error {
   100  	for _, in := range ins {
   101  		acc := accs[in.Address]
   102  		if acc == nil {
   103  			return fmt.Errorf("adjustByInputs() expects account in accounts, but account %s not found", in.Address)
   104  		}
   105  		if acc.Balance < in.Amount {
   106  			return fmt.Errorf("adjustByInputs() expects sufficient funds but account %s only has balance %v and "+
   107  				"we are deducting %v", in.Address, acc.Balance, in.Amount)
   108  		}
   109  		err := acc.SubtractFromBalance(in.Amount)
   110  		if err != nil {
   111  			return err
   112  		}
   113  	}
   114  	return nil
   115  }
   116  
   117  func adjustByOutputs(accs map[crypto.Address]*acm.Account, outs []*payload.TxOutput) error {
   118  	for _, out := range outs {
   119  		acc := accs[out.Address]
   120  		if acc == nil {
   121  			return fmt.Errorf("adjustByOutputs() expects account in accounts, but account %s not found",
   122  				out.Address)
   123  		}
   124  		err := acc.AddToBalance(out.Amount)
   125  		if err != nil {
   126  			return err
   127  		}
   128  	}
   129  	return nil
   130  }
   131  
   132  //---------------------------------------------------------------
   133  
   134  // Get permission on an account or fall back to global value
   135  func HasPermission(accountGetter acmstate.AccountGetter, acc *acm.Account, perm permission.PermFlag, logger *logging.Logger) bool {
   136  	if perm > permission.AllPermFlags {
   137  		logger.InfoMsg(
   138  			fmt.Sprintf("HasPermission called on invalid permission 0b%b (invalid) > 0b%b (maximum) ",
   139  				perm, permission.AllPermFlags),
   140  			"invalid_permission", perm,
   141  			"maximum_permission", permission.AllPermFlags)
   142  		return false
   143  	}
   144  
   145  	v, err := acc.Permissions.Base.Compose(acmstate.GlobalAccountPermissions(accountGetter).Base).Get(perm)
   146  	if err != nil {
   147  		logger.TraceMsg("Error obtaining permission value (will default to false/deny)",
   148  			"perm_flag", perm.String(),
   149  			structure.ErrorKey, err)
   150  	}
   151  
   152  	if v {
   153  		logger.TraceMsg("Account has permission",
   154  			"account_address", acc.GetAddress,
   155  			"perm_flag", perm.String())
   156  	} else {
   157  		logger.TraceMsg("Account does not have permission",
   158  			"account_address", acc.GetAddress,
   159  			"perm_flag", perm.String())
   160  	}
   161  	return v
   162  }
   163  
   164  func allHavePermission(accountGetter acmstate.AccountGetter, perm permission.PermFlag,
   165  	accs map[crypto.Address]*acm.Account, logger *logging.Logger) error {
   166  	for _, acc := range accs {
   167  		if !HasPermission(accountGetter, acc, perm, logger) {
   168  			return errors.PermissionDenied{
   169  				Address: acc.Address,
   170  				Perm:    perm,
   171  			}
   172  		}
   173  	}
   174  	return nil
   175  }
   176  
   177  func hasProposalPermission(accountGetter acmstate.AccountGetter, acc *acm.Account,
   178  	logger *logging.Logger) bool {
   179  	return HasPermission(accountGetter, acc, permission.Proposal, logger)
   180  }
   181  
   182  func hasInputPermission(accountGetter acmstate.AccountGetter, acc *acm.Account,
   183  	logger *logging.Logger) bool {
   184  	return HasPermission(accountGetter, acc, permission.Input, logger)
   185  }
   186  
   187  func hasBatchPermission(accountGetter acmstate.AccountGetter, acc *acm.Account,
   188  	logger *logging.Logger) bool {
   189  	return HasPermission(accountGetter, acc, permission.Batch, logger)
   190  }
   191  
   192  func hasNamePermission(accountGetter acmstate.AccountGetter, acc *acm.Account,
   193  	logger *logging.Logger) bool {
   194  	return HasPermission(accountGetter, acc, permission.Name, logger)
   195  }
   196  
   197  func hasCallPermission(accountGetter acmstate.AccountGetter, acc *acm.Account,
   198  	logger *logging.Logger) bool {
   199  	return HasPermission(accountGetter, acc, permission.Call, logger)
   200  }
   201  
   202  func hasCreateContractPermission(accountGetter acmstate.AccountGetter, acc *acm.Account,
   203  	logger *logging.Logger) bool {
   204  	return HasPermission(accountGetter, acc, permission.CreateContract, logger)
   205  }
   206  
   207  func hasCreateAccountPermission(accountGetter acmstate.AccountGetter, accs map[crypto.Address]*acm.Account,
   208  	logger *logging.Logger) bool {
   209  	for _, acc := range accs {
   210  		if !HasPermission(accountGetter, acc, permission.CreateAccount, logger) {
   211  			return false
   212  		}
   213  	}
   214  	return true
   215  }
   216  
   217  func hasBondPermission(accountGetter acmstate.AccountGetter, acc *acm.Account,
   218  	logger *logging.Logger) bool {
   219  	return HasPermission(accountGetter, acc, permission.Bond, logger)
   220  }
   221  
   222  func hasBondOrSendPermission(accountGetter acmstate.AccountGetter, accs map[crypto.Address]*acm.Account,
   223  	logger *logging.Logger) bool {
   224  	for _, acc := range accs {
   225  		if !HasPermission(accountGetter, acc, permission.Bond, logger) {
   226  			if !HasPermission(accountGetter, acc, permission.Send, logger) {
   227  				return false
   228  			}
   229  		}
   230  	}
   231  	return true
   232  }