From 4ecc033f3de343e60cdbc82df11e6723931fc502 Mon Sep 17 00:00:00 2001
From: Silas Davis <silas@erisindustries.com>
Date: Thu, 23 Feb 2017 16:10:18 +0000
Subject: [PATCH] Use sha3 has for snative addresses for future-proofing

---
 manager/eris-mint/evm/abi/types.go            |  34 ++++--
 manager/eris-mint/evm/native.go               |   4 -
 manager/eris-mint/evm/snative.go              | 107 ++++++++++--------
 manager/eris-mint/evm/snative_test.go         |  12 +-
 manager/eris-mint/state/permissions_test.go   |   4 +-
 util/snatives/cmd/main.go                     |   1 +
 util/snatives/templates/solidity_templates.go |  10 +-
 7 files changed, 102 insertions(+), 70 deletions(-)

diff --git a/manager/eris-mint/evm/abi/types.go b/manager/eris-mint/evm/abi/types.go
index 40c12f54..09ac0e4c 100644
--- a/manager/eris-mint/evm/abi/types.go
+++ b/manager/eris-mint/evm/abi/types.go
@@ -18,24 +18,36 @@ package abi
 // (application binary interface) here: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
 // We make a start of representing them here
 
-type Type string
+// We use TypeName rather than Type to reserve 'Type' for a possible future
+// ABI type the can hold an ABI-native type mapping
+type TypeName string
 
 type Arg struct {
-	Name string
-	Type Type
+	Name     string
+	TypeName TypeName
 }
 
 type Return struct {
-	Name string
-	Type Type
+	Name     string
+	TypeName TypeName
 }
 
 const (
 	// We don't need to be exhaustive here, just make what we used strongly typed
-	Address Type = "address"
-	Int     Type = "int"
-	Uint64  Type = "uint64"
-	Bytes32 Type = "bytes32"
-	String  Type = "string"
-	Bool    Type = "bool"
+	AddressTypeName TypeName = "address"
+	IntTypeName     TypeName = "int"
+	Uint64TypeName  TypeName = "uint64"
+	Bytes32TypeName TypeName = "bytes32"
+	StringTypeName  TypeName = "string"
+	BoolTypeName    TypeName = "bool"
+)
+
+const (
+	FunctionSelectorLength = 4
+	AddressLength          = 20
+)
+
+type (
+	Address          [AddressLength]byte
+	FunctionSelector [FunctionSelectorLength]byte
 )
diff --git a/manager/eris-mint/evm/native.go b/manager/eris-mint/evm/native.go
index a5740c6c..e5694745 100644
--- a/manager/eris-mint/evm/native.go
+++ b/manager/eris-mint/evm/native.go
@@ -54,10 +54,6 @@ func registerNativeContracts() {
 
 type NativeContract func(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error)
 
-const FuncIDLength = 4
-
-type FuncID [FuncIDLength]byte
-
 /* Removed due to C dependency
 func ecrecoverFunc(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) {
 	// Deduct gas
diff --git a/manager/eris-mint/evm/snative.go b/manager/eris-mint/evm/snative.go
index b15d51de..73275c7b 100644
--- a/manager/eris-mint/evm/snative.go
+++ b/manager/eris-mint/evm/snative.go
@@ -40,7 +40,7 @@ type SNativeContractDescription struct {
 	Comment string
 	// Name of the SNative contract
 	Name          string
-	functionsByID map[FuncID]*SNativeFunctionDescription
+	functionsByID map[abi.FunctionSelector]*SNativeFunctionDescription
 	functions     []*SNativeFunctionDescription
 }
 
@@ -59,20 +59,20 @@ type SNativeFunctionDescription struct {
 	// Permissions required to call function
 	PermFlag ptypes.PermFlag
 	// Native function to which calls will be dispatched when a containing
-	// contract is called with a FuncID matching this NativeContract
+	// contract is called with a FunctionSelector matching this NativeContract
 	F NativeContract
 }
 
 func registerSNativeContracts() {
 	for _, contract := range SNativeContracts() {
-		registeredNativeContracts[contract.Address()] = contract.Dispatch
+		registeredNativeContracts[contract.AddressWord256()] = contract.Dispatch
 	}
 }
 
 // Returns a map of all SNative contracts defined indexed by name
 func SNativeContracts() map[string]*SNativeContractDescription {
-	permFlagType := abi.Uint64
-	roleType := abi.Bytes32
+	permFlagTypeName := abi.Uint64TypeName
+	roleTypeName := abi.Bytes32TypeName
 	contracts := []*SNativeContractDescription{
 		NewSNativeContract(`
 		* Interface for managing Secure Native authorizations.
@@ -87,10 +87,10 @@ func SNativeContracts() map[string]*SNativeContractDescription {
 			`,
 				"addRole",
 				[]abi.Arg{
-					arg("_account", abi.Address),
-					arg("_role", roleType),
+					arg("_account", abi.AddressTypeName),
+					arg("_role", roleTypeName),
 				},
-				ret("result", abi.Bool),
+				ret("result", abi.BoolTypeName),
 				ptypes.AddRole,
 				addRole},
 
@@ -102,10 +102,10 @@ func SNativeContracts() map[string]*SNativeContractDescription {
 			`,
 				"removeRole",
 				[]abi.Arg{
-					arg("_account", abi.Address),
-					arg("_role", roleType),
+					arg("_account", abi.AddressTypeName),
+					arg("_role", roleTypeName),
 				},
-				ret("result", abi.Bool),
+				ret("result", abi.BoolTypeName),
 				ptypes.RmRole,
 				removeRole},
 
@@ -117,10 +117,10 @@ func SNativeContracts() map[string]*SNativeContractDescription {
 			`,
 				"hasRole",
 				[]abi.Arg{
-					arg("_account", abi.Address),
-					arg("_role", roleType),
+					arg("_account", abi.AddressTypeName),
+					arg("_role", roleTypeName),
 				},
-				ret("result", abi.Bool),
+				ret("result", abi.BoolTypeName),
 				ptypes.HasRole,
 				hasRole},
 
@@ -133,11 +133,11 @@ func SNativeContracts() map[string]*SNativeContractDescription {
 			`,
 				"setBase",
 				[]abi.Arg{
-					arg("_account", abi.Address),
-					arg("_permission", permFlagType),
-					arg("_set", abi.Bool),
+					arg("_account", abi.AddressTypeName),
+					arg("_permission", permFlagTypeName),
+					arg("_set", abi.BoolTypeName),
 				},
-				ret("result", permFlagType),
+				ret("result", permFlagTypeName),
 				ptypes.SetBase,
 				setBase},
 
@@ -149,9 +149,9 @@ func SNativeContracts() map[string]*SNativeContractDescription {
       `,
 				"unsetBase",
 				[]abi.Arg{
-					arg("_account", abi.Address),
-					arg("_permission", permFlagType)},
-				ret("result", permFlagType),
+					arg("_account", abi.AddressTypeName),
+					arg("_permission", permFlagTypeName)},
+				ret("result", permFlagTypeName),
 				ptypes.UnsetBase,
 				unsetBase},
 
@@ -163,9 +163,9 @@ func SNativeContracts() map[string]*SNativeContractDescription {
 			`,
 				"hasBase",
 				[]abi.Arg{
-					arg("_account", abi.Address),
-					arg("_permission", permFlagType)},
-				ret("result", permFlagType),
+					arg("_account", abi.AddressTypeName),
+					arg("_permission", permFlagTypeName)},
+				ret("result", permFlagTypeName),
 				ptypes.HasBase,
 				hasBase},
 
@@ -177,9 +177,9 @@ func SNativeContracts() map[string]*SNativeContractDescription {
 			`,
 				"setGlobal",
 				[]abi.Arg{
-					arg("_permission", permFlagType),
-					arg("_set", abi.Bool)},
-				ret("result", permFlagType),
+					arg("_permission", permFlagTypeName),
+					arg("_set", abi.BoolTypeName)},
+				ret("result", permFlagTypeName),
 				ptypes.SetGlobal,
 				setGlobal},
 		),
@@ -197,7 +197,7 @@ func SNativeContracts() map[string]*SNativeContractDescription {
 func NewSNativeContract(comment, name string,
 	functions ...*SNativeFunctionDescription) *SNativeContractDescription {
 
-	functionsByID := make(map[FuncID]*SNativeFunctionDescription, len(functions))
+	functionsByID := make(map[abi.FunctionSelector]*SNativeFunctionDescription, len(functions))
 	for _, f := range functions {
 		fid := f.ID()
 		otherF, ok := functionsByID[fid]
@@ -220,7 +220,7 @@ func NewSNativeContract(comment, name string,
 // So it can be looked up by SNative address
 func (contract *SNativeContractDescription) Dispatch(appState AppState,
 	caller *Account, args []byte, gas *int64) (output []byte, err error) {
-	if len(args) < FuncIDLength {
+	if len(args) < abi.FunctionSelectorLength {
 		return nil, fmt.Errorf("SNatives dispatch requires a 4-byte function "+
 			"identifier but arguments are only %s bytes long", len(args))
 	}
@@ -230,7 +230,7 @@ func (contract *SNativeContractDescription) Dispatch(appState AppState,
 		return nil, err
 	}
 
-	remainingArgs := args[FuncIDLength:]
+	remainingArgs := args[abi.FunctionSelectorLength:]
 
 	// check if we have permission to call this function
 	if !HasPermission(appState, caller, function.PermFlag) {
@@ -247,14 +247,27 @@ func (contract *SNativeContractDescription) Dispatch(appState AppState,
 	return function.F(appState, caller, remainingArgs, gas)
 }
 
-// We define the address of an SNative contact as the simplest possible hash of
-// its canonical name
-func (contract *SNativeContractDescription) Address() Word256 {
-	return LeftPadWord256([]byte(contract.Name))
+// We define the address of an SNative contact as the first 20 bytes of the sha3
+// hash of its name
+func (contract *SNativeContractDescription) Address() abi.Address {
+	var address abi.Address
+	copy(address[:], sha3.Sha3([]byte(contract.Name))[:abi.AddressLength])
+	return address
 }
 
-// Get function by calling identifier FuncID
-func (contract *SNativeContractDescription) FunctionByID(id FuncID) (*SNativeFunctionDescription, error) {
+// Get address as a byte slice
+func (contract *SNativeContractDescription) AddressBytes() []byte {
+	address := contract.Address()
+	return address[:]
+}
+
+// Get address as a left-padded Word256
+func (contract *SNativeContractDescription) AddressWord256() Word256 {
+	return LeftPadWord256(contract.AddressBytes())
+}
+
+// Get function by calling identifier FunctionSelector
+func (contract *SNativeContractDescription) FunctionByID(id abi.FunctionSelector) (*SNativeFunctionDescription, error) {
 	f, ok := contract.functionsByID[id]
 	if !ok {
 		return nil,
@@ -286,16 +299,16 @@ func (contract *SNativeContractDescription) Functions() []*SNativeFunctionDescri
 
 // Get function signature
 func (function *SNativeFunctionDescription) Signature() string {
-	argTypes := make([]string, len(function.Args))
+	argTypeNames := make([]string, len(function.Args))
 	for i, arg := range function.Args {
-		argTypes[i] = string(arg.Type)
+		argTypeNames[i] = string(arg.TypeName)
 	}
 	return fmt.Sprintf("%s(%s)", function.Name,
-		strings.Join(argTypes, ","))
+		strings.Join(argTypeNames, ","))
 }
 
-// Get function calling identifier FuncID
-func (function *SNativeFunctionDescription) ID() FuncID {
+// Get function calling identifier FunctionSelector
+func (function *SNativeFunctionDescription) ID() abi.FunctionSelector {
 	return firstFourBytes(sha3.Sha3([]byte(function.Signature())))
 }
 
@@ -304,17 +317,17 @@ func (function *SNativeFunctionDescription) NArgs() int {
 	return len(function.Args)
 }
 
-func arg(name string, abiType abi.Type) abi.Arg {
+func arg(name string, abiTypeName abi.TypeName) abi.Arg {
 	return abi.Arg{
-		Name: name,
-		Type: abiType,
+		Name:     name,
+		TypeName: abiTypeName,
 	}
 }
 
-func ret(name string, abiType abi.Type) abi.Return {
+func ret(name string, abiTypeName abi.TypeName) abi.Return {
 	return abi.Return{
-		Name: name,
-		Type: abiType,
+		Name:     name,
+		TypeName: abiTypeName,
 	}
 }
 
diff --git a/manager/eris-mint/evm/snative_test.go b/manager/eris-mint/evm/snative_test.go
index 200f37bb..a0722251 100644
--- a/manager/eris-mint/evm/snative_test.go
+++ b/manager/eris-mint/evm/snative_test.go
@@ -21,9 +21,11 @@ import (
 	"strings"
 
 	. "github.com/eris-ltd/eris-db/manager/eris-mint/evm/opcodes"
+	"github.com/eris-ltd/eris-db/manager/eris-mint/evm/sha3"
 	ptypes "github.com/eris-ltd/eris-db/permission/types"
 	. "github.com/eris-ltd/eris-db/word256"
 	"github.com/stretchr/testify/assert"
+	"github.com/eris-ltd/eris-db/manager/eris-mint/evm/abi"
 )
 
 // Compiling the Permissions solidity contract at
@@ -92,6 +94,12 @@ func TestSNativeContractDescription_Dispatch(t *testing.T) {
 	assert.Equal(t, retValue, LeftPadBytes([]byte{1}, 32))
 }
 
+func TestSNativeContractDescription_Address(t *testing.T) {
+	contract := NewSNativeContract("A comment",
+		"CoolButVeryLongNamedContractOfDoom")
+	assert.Equal(t, sha3.Sha3(([]byte)(contract.Name))[:20], contract.AddressBytes())
+}
+
 //
 // Helpers
 //
@@ -105,11 +113,11 @@ func assertFunctionIDSignature(t *testing.T, contract *SNativeContractDescriptio
 	}
 }
 
-func funcIDFromHex(t *testing.T, hexString string) FuncID {
+func funcIDFromHex(t *testing.T, hexString string) abi.FunctionSelector {
 	bs, err := hex.DecodeString(hexString)
 	assert.NoError(t, err, "Could not decode hex string '%s'", hexString)
 	if len(bs) != 4 {
-		t.Fatalf("FuncID must be 4 bytes but '%s' is %v bytes", hexString,
+		t.Fatalf("FunctionSelector must be 4 bytes but '%s' is %v bytes", hexString,
 			len(bs))
 	}
 	return firstFourBytes(bs)
diff --git a/manager/eris-mint/state/permissions_test.go b/manager/eris-mint/state/permissions_test.go
index 28da4926..c8497f13 100644
--- a/manager/eris-mint/state/permissions_test.go
+++ b/manager/eris-mint/state/permissions_test.go
@@ -1206,7 +1206,7 @@ func permNameToFuncID(name string) []byte {
 }
 
 func snativePermTestInputCALL(name string, user *acm.PrivAccount, perm ptypes.PermFlag, val bool) (addr []byte, pF ptypes.PermFlag, data []byte) {
-	addr = permissionsContract.Address().Postfix(20)
+	addr = permissionsContract.AddressBytes()
 	switch name {
 	case "hasBase", "unsetBase":
 		data = LeftPadBytes(user.Address, 32)
@@ -1242,7 +1242,7 @@ func snativePermTestInputTx(name string, user *acm.PrivAccount, perm ptypes.Perm
 }
 
 func snativeRoleTestInputCALL(name string, user *acm.PrivAccount, role string) (addr []byte, pF ptypes.PermFlag, data []byte) {
-	addr = permissionsContract.Address().Postfix(20)
+	addr = permissionsContract.AddressBytes()
 	data = LeftPadBytes(user.Address, 32)
 	data = append(data, RightPadBytes([]byte(role), 32)...)
 	data = append(permNameToFuncID(name), data...)
diff --git a/util/snatives/cmd/main.go b/util/snatives/cmd/main.go
index ddf456c2..0b12ba33 100644
--- a/util/snatives/cmd/main.go
+++ b/util/snatives/cmd/main.go
@@ -26,6 +26,7 @@ func main() {
 	contracts := vm.SNativeContracts()
 	// Index of next contract
 	i := 1
+	fmt.Print("pragma solidity >=0.0.0;\n\n")
 	for _, contract := range contracts {
 		solidity, err := templates.NewSolidityContract(contract).Solidity()
 		if err != nil {
diff --git a/util/snatives/templates/solidity_templates.go b/util/snatives/templates/solidity_templates.go
index a0c75d3d..2f7d45fe 100644
--- a/util/snatives/templates/solidity_templates.go
+++ b/util/snatives/templates/solidity_templates.go
@@ -25,7 +25,9 @@ import (
 
 const contractTemplateText = `/**
 [[.Comment]]
-* @dev These functions can be accessed as if this contract were deployed at the address [[.Address]]
+* @dev These functions can be accessed as if this contract were deployed at the address [[.Address]].
+* @dev For readability you may wish to define the following constant to refer to the SNative contract:
+* @dev address constant [[.Name]]ContractAddress = [[.Address]]
 */
 contract [[.Name]] {[[range .Functions]]
 [[.SolidityIndent 1]]
@@ -34,7 +36,7 @@ contract [[.Name]] {[[range .Functions]]
 const functionTemplateText = `/**
 [[.Comment]]
 */
-function [[.Name]]([[.ArgList]]) constant returns ([[.Return.Type]] [[.Return.Name]]);`
+function [[.Name]]([[.ArgList]]) constant returns ([[.Return.TypeName]] [[.Return.Name]]);`
 
 // Solidity style guide recommends 4 spaces per indentation level
 // (see: http://solidity.readthedocs.io/en/develop/style-guide.html)
@@ -74,7 +76,7 @@ func NewSolidityContract(contract *vm.SNativeContractDescription) *solidityContr
 
 func (contract *solidityContract) Address() string {
 	return fmt.Sprintf("0x%x",
-		contract.SNativeContractDescription.Address().Postfix(20))
+		contract.SNativeContractDescription.Address())
 }
 
 // Generate Solidity code for this SNative contract
@@ -104,7 +106,7 @@ func NewSolidityFunction(function *vm.SNativeFunctionDescription) *solidityFunct
 func (function *solidityFunction) ArgList() string {
 	argList := make([]string, len(function.Args))
 	for i, arg := range function.Args {
-		argList[i] = fmt.Sprintf("%s %s", arg.Type, arg.Name)
+		argList[i] = fmt.Sprintf("%s %s", arg.TypeName, arg.Name)
 	}
 	return strings.Join(argList, ", ")
 }
-- 
GitLab