Skip to content
This repository has been archived by the owner on May 13, 2022. It is now read-only.

Commit

Permalink
Use sha3 has for snative addresses for future-proofing
Browse files Browse the repository at this point in the history
  • Loading branch information
Silas Davis committed Feb 23, 2017
1 parent 50e0a5e commit ec741ce
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 70 deletions.
34 changes: 23 additions & 11 deletions manager/eris-mint/evm/abi/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
4 changes: 0 additions & 4 deletions manager/eris-mint/evm/native.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
107 changes: 60 additions & 47 deletions manager/eris-mint/evm/snative.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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.
Expand All @@ -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},

Expand All @@ -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},

Expand All @@ -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},

Expand All @@ -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},

Expand All @@ -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},

Expand All @@ -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},

Expand All @@ -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},
),
Expand All @@ -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]
Expand All @@ -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))
}
Expand All @@ -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) {
Expand All @@ -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,
Expand Down Expand Up @@ -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())))
}

Expand All @@ -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,
}
}

Expand Down
12 changes: 10 additions & 2 deletions manager/eris-mint/evm/snative_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
//
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions manager/eris-mint/state/permissions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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...)
Expand Down
1 change: 1 addition & 0 deletions util/snatives/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
9 changes: 5 additions & 4 deletions util/snatives/templates/solidity_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ 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]]. Use:
* @dev address constant [[.Name]]ContractAddress = [[.Address]]
*/
contract [[.Name]] {[[range .Functions]]
[[.SolidityIndent 1]]
Expand All @@ -34,7 +35,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)
Expand Down Expand Up @@ -74,7 +75,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
Expand Down Expand Up @@ -104,7 +105,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, ", ")
}
Expand Down

0 comments on commit ec741ce

Please sign in to comment.