github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/native/permissions_test.go (about)

     1  // Copyright Monax Industries Limited
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package native
     5  
     6  import (
     7  	"encoding/hex"
     8  	"math/big"
     9  	"strconv"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/hyperledger/burrow/acm/acmstate"
    14  	"github.com/hyperledger/burrow/binary"
    15  	"github.com/hyperledger/burrow/execution/engine"
    16  	"github.com/hyperledger/burrow/execution/exec"
    17  	"github.com/hyperledger/burrow/logging"
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/hyperledger/burrow/acm"
    21  	"github.com/hyperledger/burrow/crypto"
    22  
    23  	"github.com/hyperledger/burrow/execution/errors"
    24  	"github.com/hyperledger/burrow/execution/evm/abi"
    25  	"github.com/hyperledger/burrow/execution/evm/asm/bc"
    26  	"github.com/hyperledger/burrow/permission"
    27  	"github.com/stretchr/testify/assert"
    28  )
    29  
    30  // Compiling the Permissions solidity contract at
    31  // (generated by 'burrow natives' command) and passing to
    32  // https://ethereum.github.io/browser-solidity (toggle details to get list)
    33  // yields:
    34  // Keep this updated to drive TestPermissionsContractSignatures
    35  const compiledSigs = `
    36  7d72aa65 addRole(address,string)
    37  1bfe0308 removeRole(address,string)
    38  217fe6c6 hasRole(address,string)
    39  dbd4a8ea setBase(address,uint64,bool)
    40  b7d4dc0d unsetBase(address,uint64)
    41  225b6574 hasBase(address,uint64)
    42  c4bc7b70 setGlobal(uint64,bool)
    43  `
    44  
    45  var logger = logging.NewNoopLogger()
    46  
    47  func TestPermissionsContractSignatures(t *testing.T) {
    48  	contract := Permissions.GetByName("Permissions").(*Contract)
    49  
    50  	nFuncs := len(contract.functions)
    51  
    52  	sigMap := idToSignatureMap()
    53  
    54  	assert.Len(t, sigMap, nFuncs,
    55  		"Permissions contract defines %s functions so we need %s "+
    56  			"signatures in compiledSigs",
    57  		nFuncs, nFuncs)
    58  
    59  	for funcID, signature := range sigMap {
    60  		assertFunctionIDSignature(t, contract, funcID, signature)
    61  	}
    62  }
    63  
    64  func TestSNativeContractDescription_Dispatch(t *testing.T) {
    65  	contract := Permissions.GetByName("Permissions").(*Contract)
    66  	st := acmstate.NewMemoryState()
    67  	caller := &acm.Account{
    68  		Address: crypto.Address{1, 1, 1},
    69  	}
    70  	grantee := &acm.Account{
    71  		Address: crypto.Address{2, 2, 2},
    72  	}
    73  	require.NoError(t, st.UpdateAccount(caller))
    74  	require.NoError(t, st.UpdateAccount(grantee))
    75  	state := engine.State{
    76  		CallFrame: engine.NewCallFrame(st),
    77  		EventSink: exec.NewNoopEventSink(),
    78  	}
    79  
    80  	function := contract.FunctionByName("setBase")
    81  	require.NotNil(t, function, "Could not get function: %s")
    82  	funcID := function.Abi().FunctionID
    83  	// Should fail since we have no permissions
    84  	input := bc.MustSplice(funcID[:], grantee.Address, permFlagToWord256(permission.CreateAccount))
    85  	params := engine.CallParams{
    86  		Caller: caller.Address,
    87  		Input:  input,
    88  		Gas:    big.NewInt(1000),
    89  	}
    90  	_, err := contract.Call(state, params)
    91  	if !assert.Error(t, err, "Should fail due to lack of permissions") {
    92  		return
    93  	}
    94  	assert.Equal(t, errors.Codes.NativeFunction, errors.GetCode(err))
    95  
    96  	// Grant all permissions and dispatch should success
    97  	err = engine.UpdateAccount(state, caller.Address, func(acc *acm.Account) error {
    98  		return acc.Permissions.Base.Set(permission.SetBase, true)
    99  	})
   100  	require.NoError(t, err)
   101  	bondFlagWord := permFlagToWord256(permission.Bond)
   102  	params.Input = bc.MustSplice(funcID[:], grantee.Address.Word256(), bondFlagWord, binary.One256)
   103  
   104  	retValue, err := contract.Call(state, params)
   105  	require.NoError(t, err)
   106  	assert.Equal(t, bondFlagWord[:], retValue)
   107  }
   108  
   109  func TestSNativeContractDescription_Address(t *testing.T) {
   110  	contract, err := NewContract("CoolButVeryLongNamedContractOfDoom", "A comment", logger)
   111  	require.NoError(t, err)
   112  	assert.Equal(t, crypto.Keccak256(([]byte)(contract.Name))[12:], contract.Address().Bytes())
   113  }
   114  
   115  func TestHasPermission(t *testing.T) {
   116  	cache := acmstate.NewMemoryState()
   117  
   118  	base, err := permission.BasePermissionsFromStringList([]string{"createContract", "createAccount", "bond", "proposal", "setBase", "unsetBase", "setGlobal", "addRole", "removeRole"})
   119  	require.NoError(t, err)
   120  
   121  	acc := &acm.Account{
   122  		Address: engine.AddressFromName("frog"),
   123  		Permissions: permission.AccountPermissions{
   124  			Base: base,
   125  		},
   126  	}
   127  
   128  	require.NoError(t, cache.UpdateAccount(acc))
   129  	// Ensure we are falling through to global permissions on those bits not set
   130  
   131  	flag := permission.Send | permission.Call | permission.Name | permission.HasRole
   132  	hasPermission, err := engine.HasPermission(cache, acc.Address, flag)
   133  	require.NoError(t, err)
   134  	assert.True(t, hasPermission)
   135  }
   136  
   137  //
   138  // Helpers
   139  //
   140  func BasePermissionsFromStrings(t *testing.T, perms, setBit string) permission.BasePermissions {
   141  	return permission.BasePermissions{
   142  		Perms:  PermFlagFromString(t, perms),
   143  		SetBit: PermFlagFromString(t, setBit),
   144  	}
   145  }
   146  
   147  func PermFlagFromString(t *testing.T, binaryString string) permission.PermFlag {
   148  	permFlag, err := strconv.ParseUint(binaryString, 2, 64)
   149  	require.NoError(t, err)
   150  	return permission.PermFlag(permFlag)
   151  }
   152  
   153  func assertFunctionIDSignature(t *testing.T, contract *Contract,
   154  	funcIDHex string, expectedSignature string) {
   155  	fromHex := funcIDFromHex(t, funcIDHex)
   156  	function, err := contract.FunctionByID(fromHex)
   157  	assert.NoError(t, err,
   158  		"Error retrieving Function with ID %s", funcIDHex)
   159  	if err == nil {
   160  		assert.Equal(t, expectedSignature, function.Signature())
   161  	}
   162  }
   163  
   164  func funcIDFromHex(t *testing.T, hexString string) (funcID abi.FunctionID) {
   165  	bs, err := hex.DecodeString(hexString)
   166  	assert.NoError(t, err, "Could not decode hex string '%s'", hexString)
   167  	if len(bs) != 4 {
   168  		t.Fatalf("FunctionSelector must be 4 bytes but '%s' is %v bytes", hexString,
   169  			len(bs))
   170  	}
   171  	copy(funcID[:], bs)
   172  	return
   173  }
   174  
   175  func permFlagToWord256(permFlag permission.PermFlag) binary.Word256 {
   176  	return binary.Uint64ToWord256(uint64(permFlag))
   177  }
   178  
   179  // turns the solidity compiler function summary into a map to drive signature
   180  // test
   181  func idToSignatureMap() map[string]string {
   182  	sigMap := make(map[string]string)
   183  	lines := strings.Split(compiledSigs, "\n")
   184  	for _, line := range lines {
   185  		trimmed := strings.Trim(line, " \t")
   186  		if trimmed != "" {
   187  			idSig := strings.Split(trimmed, " ")
   188  			sigMap[idSig[0]] = idSig[1]
   189  		}
   190  	}
   191  	return sigMap
   192  }