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 }