-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapi.go
364 lines (310 loc) · 9.94 KB
/
api.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
package svm
/*
#cgo CFLAGS: -I.
#cgo linux LDFLAGS: ${SRCDIR}/artifacts/libsvm.a -lm -ldl
#cgo darwin LDFLAGS: ${SRCDIR}/artifacts/libsvm.a -lm -ldl -framework Security -framework Foundation
#cgo windows LDFLAGS: -L ${SRCDIR}/artifacts/ -lsvm -lm -ldl
#include "svm.h"
#include "memory.h"
*/
import "C"
import (
"errors"
"unsafe"
)
type svmParams struct {
envPtr *C.uchar
msgPtr *C.uchar
msgLen C.uint32_t
ctxPtr *C.uchar
}
// TODO: we might want to guard calling `Init` with a Mutex.
var initialized = false
// `Init` must be called at least once before interacting with any other API of SVM.
// Each future call to `NewRuntime` (see later) assumes the settings given the `Init` call.
//
// Please note that this function is idempotent and won't do anything after the
// first call.
//
// # Params
//
// * `inMemory` - whether the data of the `SVM Global State` will be in-memory or persisted.
// * `path` - the path under which `SVM Global State` will store its content.
// This is relevant only when `isMemory=false` (otherwise the `path` value will be ignored).
//
// # Returns
//
// Returns an error in case the initialization has failed.
func Init(inMemory bool, path string) error {
if initialized {
return nil
}
bytes := ([]byte)(path)
rawPath := (*C.uchar)(unsafe.Pointer(&bytes))
pathLen := (C.uint32_t)(uint32(len(path)))
res := C.svm_init((C.bool)(inMemory), rawPath, pathLen)
if _, err := copySvmResult(res); err != nil {
panic("Init has failed!")
}
return nil
}
// Asserts that `Init` has already been called.
//
// # Panics
//
// Panics when `Init` has **NOT** been previously called.
func AssertInitialized() {
if !initialized {
panic("Forgot to call `Init`")
}
}
// `NewRuntime` creates a new `Runtime`.
//
// On success returns it and the `error` is set to `nil`.
// On failure returns `(nil, error).
func NewRuntime() (*Runtime, error) {
rt := &Runtime{}
res := C.svm_runtime_create(&rt.raw)
_, err := copySvmResult(res)
return rt, err
}
func RuntimesCount() int {
count := C.uint64_t(0)
C.svm_runtimes_count(&count)
return int(count)
}
func ReceiptsCount() int {
// TODO
return 0
// count := C.uint32(0)
// result := C.svm_receipts_count(&count)
// return int(count)
}
// Releases the SVM Runtime
func (rt *Runtime) Destroy() {
if rt.raw != nil {
C.svm_runtime_destroy(rt.raw)
}
}
// Validates the `Deploy Message` given in its binary form.
//
// Returns `(true, nil)` when the `msg` is syntactically valid,
// and `(false, error)` otherwise. In that case `error` will have non-`nil` value.
func (rt *Runtime) ValidateDeploy(msg []byte) (bool, error) {
return runValidation(msg, func(rawMsg *C.uchar, msgLen C.uint32_t) C.svm_result_t {
return C.svm_validate_deploy(rt.raw, rawMsg, msgLen)
})
}
// Executes a `Deploy` transaction and returns back a receipt.
//
// # Params
//
// * `env` - the transaction `Envelope`
// * `msg` - the transaction `Message`
// * `ctx` - the executed `Context` (the `current layer` etc).
//
// # Notes
//
// A Receipt is always being returned, even if there was an internal error inside SVM.
func (rt *Runtime) Deploy(env *Envelope, msg []byte, ctx *Context) (*DeployReceipt, error) {
object, err := runAction(env, msg, ctx, func(params *svmParams) C.svm_result_t {
return C.svm_deploy(rt.raw, params.envPtr, params.msgPtr, params.msgLen, params.ctxPtr)
})
if err != nil {
return nil, err
}
return object.(*DeployReceipt), nil
}
// Validates the `Spawn Message` given in its binary form.
//
// Returns `(true, nil)` when the `msg` is syntactically valid,
// and `(false, error)` otherwise. In that case `error` will have non-`nil` value.
func (rt *Runtime) ValidateSpawn(msg []byte) (bool, error) {
return runValidation(msg, func(rawMsg *C.uchar, msgLen C.uint32_t) C.svm_result_t {
return C.svm_validate_spawn(rt.raw, rawMsg, msgLen)
})
}
// Executes a `Spawn` transaction and returns back a receipt.
//
// # Params
//
// * `env` - the transaction `Envelope`
// * `msg` - the transaction `Message`
// * `ctx` - the executed `Context` (the `current layer` etc).
//
//
// # Notes
//
// A Receipt is always being returned, even if there was an internal error inside SVM.
func (rt *Runtime) Spawn(env *Envelope, msg []byte, ctx *Context) (*SpawnReceipt, error) {
object, err := runAction(env, msg, ctx, func(params *svmParams) C.svm_result_t {
return C.svm_spawn(rt.raw, params.envPtr, params.msgPtr, params.msgLen, params.ctxPtr)
})
if err != nil {
return nil, err
}
return object.(*SpawnReceipt), nil
}
// Validates the `Call Message` given in its binary form.
//
// Returns `(true, nil)` when the `msg` is syntactically valid,
// and `(false, error)` otherwise. In that case `error` will have non-`nil` value.
func (rt *Runtime) ValidateCall(msg []byte) (bool, error) {
return runValidation(msg, func(rawMsg *C.uchar, msgLen C.uint32_t) C.svm_result_t {
return C.svm_validate_call(rt.raw, rawMsg, msgLen)
})
}
// Executes a `Call` transaction and returns back a receipt.
//
// # Params
//
// * `env` - the transaction `Envelope`
// * `msg` - the transaction `Message`
// * `ctx` - the executed `Context` (the `current layer` etc).
//
// # Notes
//
// A Receipt is always being returned, even if there was an internal error inside SVM.
func (rt *Runtime) Call(env *Envelope, msg []byte, ctx *Context) (*CallReceipt, error) {
object, err := runAction(env, msg, ctx, func(params *svmParams) C.svm_result_t {
return C.svm_call(rt.raw, params.envPtr, params.msgPtr, params.msgLen, params.ctxPtr)
})
if err != nil {
return nil, err
}
return object.(*CallReceipt), nil
}
// Executes the `Verify` stage and returns back a receipt.
//
// Calling `Verify` is in many ways very similar to the latter `Call` execution.
// For that reason, the `Verify` returns a `CallReceipt` as well.
//
// # Params
//
// * `env` - the transaction `Envelope`
// * `msg` - the transaction `Message`
// * `ctx` - the executed `Context` (the `current layer` etc).
//
// # Notes
//
// A Receipt is always being returned, even if there was an internal error inside SVM.
func (rt *Runtime) Verify(env *Envelope, msg []byte, ctx *Context) (*CallReceipt, error) {
object, err := runAction(env, msg, ctx, func(params *svmParams) C.svm_result_t {
return C.svm_verify(rt.raw, params.envPtr, params.msgPtr, params.msgLen, params.ctxPtr)
})
if err != nil {
return nil, err
}
return object.(*CallReceipt), nil
}
// Signaling `SVM` that we are about to start playing a list of transactions under the input `layer` Layer.
//
// # Notes
//
// * The value of `layer` is expected to equal the last known `committed/rewinded` layer plus one
// Any other `layer` given as input will result in an error returned.
//
// * Calling `Open` twice in a row will result in an `error` returned.
func (rt *Runtime) Open(layer Layer) error {
panic("TODO")
}
// Rewinds the `SVM Global State` back to the input `layer`.
//
// In case there is no such layer to rewind to - returns an `error`.
func (rt *Runtime) Rewind(layer Layer) (State, error) {
panic("TODO")
}
// Commits the dirty changes of `SVM` and signals the termination of the current layer.
// On success returns `(layer, nil)` when `layer` is the value of the previous `current layer`.
// In other words, returns the `layer` associated with the just-committed changes.
//
// In case commits fails (for example, persisting to disk failure) - returns `(0, error)`
func (rt *Runtime) Commit() (Layer, State, error) {
panic("TODO")
}
// Given an `Account Address` - retrieves its most basic information encapuslated within an `Account` struct.
//
// Returns a `(nil, error)` in case the requested `Account` doesn't exist.
func (rt *Runtime) GetAccount(addr Address) (Account, error) {
panic("TODO")
}
func (rt *Runtime) CreateAccount(account Account) error {
panic("TODO")
}
// Increases the balance of an Account (i.e printing coins)
//
// # Params
//
// * `addr` - The `Account Address` we want to increase its balance.
// * `amount` - The `Amount` by which we are going to increase the account's balance.
func (rt *Runtime) IncreaseBalance(addr Address, amount Amount) {
panic("TODO")
}
func NewEnvelope(principal Address, amount Amount, txNonce TxNonce, gasLimit Gas, gasFee GasFee) *Envelope {
return &Envelope{
Principal: principal,
Amount: amount,
TxNonce: txNonce,
GasLimit: gasLimit,
GasFee: gasFee,
}
}
func NewContext(layer Layer, txId TxId) *Context {
return &Context{
Layer: layer,
TxId: txId,
}
}
func toSvmParams(env *Envelope, msg []byte, ctx *Context) *svmParams {
envBytes := encodeEnvelope(env)
envPtr := (*C.uchar)(unsafe.Pointer(&envBytes[0]))
msgPtr := (*C.uchar)(unsafe.Pointer(&msg[0]))
msgLen := (C.uint32_t)(uint32(len(msg)))
ctxBytes := encodeContext(ctx)
ctxPtr := (*C.uchar)(unsafe.Pointer(&ctxBytes[0]))
return &svmParams{
envPtr,
msgPtr,
msgLen,
ctxPtr,
}
}
func copySvmResult(res C.struct_svm_result_t) ([]byte, error) {
defer C.free(unsafe.Pointer(res.receipt))
defer C.free(unsafe.Pointer(res.error))
size := C.int(res.buf_size)
receipt := ([]byte)(nil)
err := (error)(nil)
if res.receipt != nil {
ptr := unsafe.Pointer(res.receipt)
receipt = C.GoBytes(ptr, size)
} else if res.error != nil {
ptr := unsafe.Pointer(res.error)
err = errors.New(string(C.GoBytes(ptr, size)))
}
return receipt, err
}
type svmAction func(params *svmParams) C.svm_result_t
type svmValidation func(rawMsg *C.uchar, msgLen C.uint32_t) C.svm_result_t
func runAction(env *Envelope, msg []byte, ctx *Context, action svmAction) (interface{}, error) {
if len(msg) == 0 {
return false, errors.New("`msg` cannot be empty")
}
params := toSvmParams(env, msg, ctx)
res := action(params)
bytes, err := copySvmResult(res)
if err != nil {
panic(err)
}
return decodeReceipt(bytes)
}
func runValidation(msg []byte, validator svmValidation) (bool, error) {
if len(msg) == 0 {
return false, errors.New("`msg` cannot be empty")
}
rawMsg := (*C.uchar)(unsafe.Pointer(&msg[0]))
msgLen := (C.uint32_t)(len(msg))
res := validator(rawMsg, msgLen)
_, err := copySvmResult(res)
return err == nil, err
}