Skip to content

Commit

Permalink
Merge pull request hyperledger-archives#507 from silasdavis/snative-t…
Browse files Browse the repository at this point in the history
…emplates

Use sha3 has for snative addresses for future-proofing
  • Loading branch information
RJ Catalano authored Feb 23, 2017
2 parents 468b67b + 87baace commit 415f96a
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 73 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
114 changes: 67 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,16 +177,22 @@ 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},
),
}

contractMap := make(map[string]*SNativeContractDescription, len(contracts))
for _, contract := range contracts {
if _, ok := contractMap[contract.Name]; ok {
// If this happens we have a pseudo compile time error that will be caught
// on native.go init()
panic(fmt.Errorf("Duplicate contract with name %s defined. " +
"Contract names must be unique.", contract.Name))
}
contractMap[contract.Name] = contract
}
return contractMap
Expand All @@ -197,7 +203,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 +226,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 +236,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 +253,28 @@ 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 last 20 bytes of the sha3
// hash of its name
func (contract *SNativeContractDescription) Address() abi.Address {
var address abi.Address
hash := sha3.Sha3([]byte(contract.Name))
copy(address[:], hash[len(hash)-abi.AddressLength:])
return address
}

// 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 FuncID
func (contract *SNativeContractDescription) FunctionByID(id FuncID) (*SNativeFunctionDescription, error) {
// 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 +306,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 +324,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))[12:], 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
Loading

0 comments on commit 415f96a

Please sign in to comment.