Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement CallCallablePoint to VM #97

Closed
wants to merge 12 commits into from
17 changes: 17 additions & 0 deletions api/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,23 @@ struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache,
struct UnmanagedVector *attributes,
struct UnmanagedVector *error_msg);

struct UnmanagedVector call_callable_point(struct ByteSliceView name,
struct cache_t *cache,
struct ByteSliceView checksum,
bool is_readonly,
struct ByteSliceView callstack,
struct ByteSliceView env,
struct ByteSliceView args,
struct Db db,
struct GoApi api,
struct GoQuerier querier,
uint64_t gas_limit,
bool print_debug,
uint64_t *gas_used,
struct UnmanagedVector *events,
struct UnmanagedVector *attributes,
struct UnmanagedVector *error_msg);

struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length);

void destroy_unmanaged_vector(struct UnmanagedVector v);
Expand Down
50 changes: 50 additions & 0 deletions api/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,56 @@ func IBCPacketTimeout(
return copyAndDestroyUnmanagedVector(res), copyAndDestroyUnmanagedVector(events), copyAndDestroyUnmanagedVector(attributes), uint64(gasUsed), nil
}

// name: Serialized string
// args: Serialized [][]byte
// callstack: Serialized []string
// returned used_gas: without instantiation cost
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does used_gas refer to the name of the variable? If so, isn't it gasUsed?

func CallCallablePoint(
name []byte,
cache Cache,
checksum []byte,
isReadonly bool,
callstack []byte,
env []byte,
args []byte,
gasMeter *GasMeter,
store KVStore,
api *GoAPI,
querier *Querier,
gasLimit uint64,
printDebug bool,
) ([]byte, []byte, []byte, uint64, error) {
n := makeView(name)
defer runtime.KeepAlive(name)
cs := makeView(checksum)
defer runtime.KeepAlive(checksum)
e := makeView(env)
defer runtime.KeepAlive(env)
s := makeView(callstack)
defer runtime.KeepAlive(callstack)
as := makeView(args)
defer runtime.KeepAlive(args)

callID := startCall()
defer endCall(callID)

dbState := buildDBState(store, callID)
db := buildDB(&dbState, gasMeter)
a := buildAPI(api)
q := buildQuerier(querier)
var gasUsed cu64
errmsg := newUnmanagedVector(nil)
events := newUnmanagedVector(nil)
attributes := newUnmanagedVector(nil)

res, err := C.call_callable_point(n, cache.ptr, cs, cbool(isReadonly), s, e, as, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &events, &attributes, &errmsg)
if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success {
// Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0.
return nil, nil, nil, uint64(gasUsed), errorWithMessage(err, errmsg)
}
return copyAndDestroyUnmanagedVector(res), copyAndDestroyUnmanagedVector(events), copyAndDestroyUnmanagedVector(attributes), uint64(gasUsed), nil
}

/**** To error module ***/

func errorWithMessage(err error, b C.UnmanagedVector) error {
Expand Down
144 changes: 141 additions & 3 deletions api/lib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1143,7 +1143,7 @@ func TestEventManager(t *testing.T) {
diff := time.Now().Sub(start)
require.NoError(t, err)
requireOkResponse(t, res, 0)
assert.Equal(t, uint64(0xc586dd30), cost)
assert.Equal(t, uint64(0xc5c6f370), cost)
t.Logf("Time (%d gas): %s\n", cost, diff)

// make sure it does not uses EventManager
Expand All @@ -1170,7 +1170,7 @@ func TestEventManager(t *testing.T) {
diff = time.Now().Sub(start)
require.NoError(t, err)
requireOkResponse(t, res, 0)
assert.Equal(t, uint64(0x1d133cc00), cost)
assert.Equal(t, uint64(0x1d0d83e80), cost)
t.Logf("Time (%d gas): %s\n", cost, diff)

// check events and attributes
Expand Down Expand Up @@ -1200,7 +1200,7 @@ func TestEventManager(t *testing.T) {
diff = time.Now().Sub(start)
require.NoError(t, err)
requireOkResponse(t, res, 0)
assert.Equal(t, uint64(0x13ba22790), cost)
assert.Equal(t, uint64(0x13d2bd4d0), cost)
t.Logf("Time (%d gas): %s\n", cost, diff)

// check events and attributes
Expand Down Expand Up @@ -1376,5 +1376,143 @@ func TestDynamicReadWritePermission(t *testing.T) {
requireOkResponse(t, res, 0)
assert.Equal(t, uint64(0x535da85b0), cost)
t.Logf("Time (%d gas): %s\n", cost, diff)
}

func TestCallCallablePoint(t *testing.T) {
cache, cleanup := withCache(t)
defer cleanup()
checksum := createEventsContract(t, cache)

gasMeter1 := NewMockGasMeter(TESTING_GAS_LIMIT)
igasMeter1 := GasMeter(gasMeter1)
// instantiate it with this store
store := NewLookup(gasMeter1)
api := NewMockAPI()
balance := types.Coins{}
querier := DefaultQuerier(MOCK_CONTRACT_ADDR, balance)
env := MockEnvBin(t)
info := MockInfoBin(t, "creator")
msg := []byte(`{}`)

start := time.Now()
res, eventsData, attributesData, cost, err := Instantiate(cache, checksum, env, info, msg, &igasMeter1, store, api, &querier, TESTING_GAS_LIMIT, TESTING_PRINT_DEBUG)
diff := time.Now().Sub(start)
require.NoError(t, err)
requireOkResponse(t, res, 0)
assert.Equal(t, uint64(0xc5c6f370), cost)
t.Logf("Time (%d gas): %s\n", cost, diff)

// make sure it does not uses EventManager
var events types.Events
err = events.UnmarshalJSON(eventsData)
require.NoError(t, err)
require.Equal(t, 0, len(events))

var attributes types.EventAttributes
err = attributes.UnmarshalJSON(attributesData)
require.NoError(t, err)
require.Equal(t, 0, len(attributes))

// issue events with EventManager
gasMeter2 := NewMockGasMeter(TESTING_GAS_LIMIT)
igasMeter2 := GasMeter(gasMeter2)
store.SetGasMeter(gasMeter2)
name := "add_events_dyn"
nameBin, err := json.Marshal(name)
require.NoError(t, err)
eventsIn := types.Events{
types.Event{
Type: "ty1",
Attributes: types.EventAttributes{
types.EventAttribute{
Key: "alice",
Value: "101010",
},
types.EventAttribute{
Key: "bob",
Value: "42",
},
},
},
types.Event{
Type: "ty2",
Attributes: types.EventAttributes{
types.EventAttribute{
Key: "ALICE",
Value: "42",
},
types.EventAttribute{
Key: "BOB",
Value: "101010",
},
},
},
}
eventsInBin, err := eventsIn.MarshalJSON()
require.NoError(t, err)
argsEv := [][]byte{eventsInBin}
argsEvBin, err := json.Marshal(argsEv)
require.NoError(t, err)
empty := []types.HumanAddress{}
emptyBin, err := json.Marshal(empty)
require.NoError(t, err)

start = time.Now()
res, eventsData, attributesData, cost, err = CallCallablePoint(nameBin, cache, checksum, false, emptyBin, env, argsEvBin, &igasMeter2, store, api, &querier, TESTING_GAS_LIMIT, TESTING_PRINT_DEBUG)
Kynea0b marked this conversation as resolved.
Show resolved Hide resolved
diff = time.Now().Sub(start)
require.NoError(t, err)
assert.Equal(t, uint64(0x1766fb680), cost)
t.Logf("Time (%d gas): %s\n", cost, diff)
require.Equal(t, []byte(`null`), res)

// check events and attributes
err = events.UnmarshalJSON(eventsData)
require.NoError(t, err)

require.Equal(t, eventsIn, events)

err = attributes.UnmarshalJSON(attributesData)
require.NoError(t, err)
require.Equal(t, 0, len(attributes))

// issue attributes with EventManager
gasMeter3 := NewMockGasMeter(TESTING_GAS_LIMIT)
igasMeter3 := GasMeter(gasMeter3)
store.SetGasMeter(gasMeter3)
name = "add_attributes_dyn"
nameBin, err = json.Marshal(name)
require.NoError(t, err)
attrsIn := types.EventAttributes{
types.EventAttribute{
Key: "alice",
Value: "42",
},
types.EventAttribute{
Key: "bob",
Value: "101010",
},
}
attrsInBin, err := attrsIn.MarshalJSON()
require.NoError(t, err)
argsAt := [][]byte{attrsInBin}
argsAtBin, err := json.Marshal(argsAt)
require.NoError(t, err)

start = time.Now()
res, eventsData, attributesData, cost, err = CallCallablePoint(nameBin, cache, checksum, false, emptyBin, env, argsAtBin, &igasMeter3, store, api, &querier, TESTING_GAS_LIMIT, TESTING_PRINT_DEBUG)
diff = time.Now().Sub(start)
require.NoError(t, err)
assert.Equal(t, uint64(0xd753e6c0), cost)
t.Logf("Time (%d gas): %s\n", cost, diff)
require.Equal(t, []byte(`null`), res)

// check events and attributes
err = events.UnmarshalJSON(eventsData)
require.NoError(t, err)
require.Equal(t, 0, len(events))

err = attributes.UnmarshalJSON(attributesData)
require.NoError(t, err)

require.Equal(t, attrsIn, attributes)
}
Binary file modified api/libwasmvm.aarch64.so
Binary file not shown.
Binary file modified api/libwasmvm.dylib
Binary file not shown.
Binary file modified api/libwasmvm.x86_64.so
Binary file not shown.
Binary file modified api/testdata/events.wasm
Binary file not shown.
49 changes: 49 additions & 0 deletions lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,55 @@ func (vm *VM) IBCPacketTimeout(
return resp.Ok, gasUsed, nil
}

func (vm *VM) CallCallablePoint(
name []byte,
checksum Checksum,
isReadonly bool,
callstack []byte,
env types.Env,
args []byte,
store KVStore,
goapi GoAPI,
querier Querier,
gasMeter GasMeter,
gasLimit uint64,
deserCost types.UFraction,
) ([]byte, types.Events, types.EventAttributes, uint64, error) {
envBin, err := json.Marshal(env)
if err != nil {
return nil, nil, nil, 0, err
}

data, eventsData, attributesData, gasUsed, err := api.CallCallablePoint(name, vm.cache, checksum, isReadonly, callstack, envBin, args, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug)
if err != nil {
return nil, nil, nil, gasUsed, err
}

gasForDeserializingEvents := deserCost.Mul(uint64(len(eventsData))).Floor()
if gasLimit < gasForDeserializingEvents+gasUsed {
return nil, nil, nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize events (%d bytes)", len(eventsData))
}
gasUsed += gasForDeserializingEvents
var events types.Events
err = events.UnmarshalJSON(eventsData)
if err != nil {
return nil, nil, nil, gasUsed, err
}
gasForDeserializingAttrs := deserCost.Mul(uint64(len(attributesData))).Floor()

if gasLimit < gasForDeserializingAttrs+gasUsed {
return nil, nil, nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize attributes (%d bytes)", len(attributesData))
}
gasUsed += gasForDeserializingAttrs
var attributes types.EventAttributes
err = attributes.UnmarshalJSON(attributesData)
if err != nil {
return nil, nil, nil, gasUsed, err
}

return data, events, attributes, gasUsed, nil
}

func (vm *VM) GetCache() *Cache {
return &vm.cache
}
Expand Down
Loading