-
Notifications
You must be signed in to change notification settings - Fork 2
/
contract.go
170 lines (151 loc) · 5.2 KB
/
contract.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright Monax Industries Limited
// SPDX-License-Identifier: Apache-2.0
package native
import (
"fmt"
"github.com/klyed/hivesmartchain/acm"
"github.com/klyed/hivesmartchain/acm/acmstate"
"github.com/klyed/hivesmartchain/crypto"
"github.com/klyed/hivesmartchain/execution/engine"
"github.com/klyed/hivesmartchain/execution/errors"
"github.com/klyed/hivesmartchain/execution/evm/abi"
"github.com/klyed/hivesmartchain/logging"
)
//
// Native (go) contracts are dispatched based on account permissions and can access
// and modify an account's permissions
//
// Instructions on adding an native function. First declare a function like so:
//
// func unsetBase(context Context, args unsetBaseArgs) (unsetBaseRets, error) {
// }
//
// The name of the function will be used as the name of the function in solidity. The
// first arguments is Context; this will give you access to state, and the logger
// etc. The second arguments must be a struct type. The members of this struct must be
// exported (start with uppercase letter), and they will be converted into arguments
// for the solidity function, with the same types. The first return value is a struct
// which defines the return values from solidity just like the arguments.
//
// The second return value must be error. If non-nil is returned for error, then
// the current transaction will be aborted and the execution will stop.
//
// For each contract you will need to create a Contract{} struct,
// with the function listed. Only the PermFlag and the function F needs to be filled
// in for each Function. Add this to the SNativeContracts() function.
// Contract is metadata for native contract. Acts as a call target
// from the EVM. Can be used to generate bindings in a smart contract languages.
type Contract struct {
// Comment describing purpose of native contract and reason for assembling
// the particular functions
Comment string
// Name of the native contract
Name string
functionsByID map[abi.FunctionID]*Function
functions []*Function
address crypto.Address
logger *logging.Logger
}
var _ engine.Native = &Contract{}
// Create a new native contract description object by passing a comment, name
// and a list of member functions descriptions
func NewContract(name string, comment string, logger *logging.Logger, fs ...Function) (*Contract, error) {
address := engine.AddressFromName(name)
functionsByID := make(map[abi.FunctionID]*Function, len(fs))
functions := make([]*Function, len(fs))
logger = logger.WithScope("NativeContract")
for i, f := range fs {
function := f
err := function.init(address)
if err != nil {
return nil, err
}
if function.abi == nil {
return nil, fmt.Errorf("could not establish ABI for function - contract functions must have a " +
"struct second argument in order to establish ABI")
}
function.contractName = name
function.logger = logger
fid := function.abi.FunctionID
otherF, ok := functionsByID[fid]
if ok {
return nil, fmt.Errorf("function with ID %x already defined: %s", fid, otherF.Signature())
}
functionsByID[fid] = &function
functions[i] = &function
}
return &Contract{
Comment: comment,
Name: name,
functionsByID: functionsByID,
functions: functions,
address: address,
logger: logger,
}, nil
}
// Dispatch is designed to be called from the EVM once a native contract
// has been selected.
func (c *Contract) Call(state engine.State, params engine.CallParams) (output []byte, err error) {
if len(params.Input) < abi.FunctionIDSize {
return nil, errors.Errorf(errors.Codes.NativeFunction,
"HiveSmartChain Native dispatch requires a 4-byte function identifier but arguments are only %v bytes long",
len(params.Input))
}
var id abi.FunctionID
copy(id[:], params.Input)
function, err := c.FunctionByID(id)
if err != nil {
return nil, err
}
params.Input = params.Input[abi.FunctionIDSize:]
return function.Call(state, params)
}
func (c *Contract) SetExternals(externals engine.Dispatcher) {
for _, f := range c.functions {
f.SetExternals(externals)
}
}
func (c *Contract) FullName() string {
return c.Name
}
// We define the address of an native contact as the last 20 bytes of the sha3
// hash of its name
func (c *Contract) Address() crypto.Address {
return c.address
}
// Get function by calling identifier FunctionSelector
func (c *Contract) FunctionByID(id abi.FunctionID) (*Function, errors.CodedError) {
f, ok := c.functionsByID[id]
if !ok {
return nil,
errors.Errorf(errors.Codes.NativeFunction, "unknown native function with ID %x", id)
}
return f, nil
}
// Get function by name
func (c *Contract) FunctionByName(name string) *Function {
for _, f := range c.functions {
if f.name == name {
return f
}
}
return nil
}
// Get functions in order of declaration
func (c *Contract) Functions() []*Function {
functions := make([]*Function, len(c.functions))
copy(functions, c.functions)
return functions
}
func (c *Contract) ContractMeta() []*acm.ContractMeta {
// FIXME: make this return actual ABI metadata
metadata := "{}"
metadataHash := acmstate.GetMetadataHash(metadata)
return []*acm.ContractMeta{
{
CodeHash: []byte(c.Name),
MetadataHash: metadataHash[:],
Metadata: metadata,
},
}
}