diff --git a/internal/api/bindings.h b/internal/api/bindings.h index 60f301408..5b38b7736 100644 --- a/internal/api/bindings.h +++ b/internal/api/bindings.h @@ -342,6 +342,26 @@ typedef struct GoQuerier { struct Querier_vtable vtable; } GoQuerier; +typedef struct GasReport { + /** + * The original limit the instance was created with + */ + uint64_t limit; + /** + * The remaining gas that can be spend + */ + uint64_t remaining; + /** + * The amount of gas that was spend and metered externally in operations triggered by this instance + */ + uint64_t used_externally; + /** + * The amount of gas that was spend and metered internally (i.e. by executing Wasm and calling + * API methods which are not metered externally) + */ + uint64_t used_internally; +} GasReport; + struct cache_t *init_cache(struct ByteSliceView data_dir, struct ByteSliceView available_capabilities, uint32_t cache_size, @@ -391,7 +411,7 @@ struct UnmanagedVector instantiate(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector execute(struct cache_t *cache, @@ -404,7 +424,7 @@ struct UnmanagedVector execute(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector migrate(struct cache_t *cache, @@ -416,7 +436,7 @@ struct UnmanagedVector migrate(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector sudo(struct cache_t *cache, @@ -428,7 +448,7 @@ struct UnmanagedVector sudo(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector reply(struct cache_t *cache, @@ -440,7 +460,7 @@ struct UnmanagedVector reply(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector query(struct cache_t *cache, @@ -452,7 +472,7 @@ struct UnmanagedVector query(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_channel_open(struct cache_t *cache, @@ -464,7 +484,7 @@ struct UnmanagedVector ibc_channel_open(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_channel_connect(struct cache_t *cache, @@ -476,7 +496,7 @@ struct UnmanagedVector ibc_channel_connect(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_channel_close(struct cache_t *cache, @@ -488,7 +508,7 @@ struct UnmanagedVector ibc_channel_close(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_packet_receive(struct cache_t *cache, @@ -500,7 +520,7 @@ struct UnmanagedVector ibc_packet_receive(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_packet_ack(struct cache_t *cache, @@ -512,7 +532,7 @@ struct UnmanagedVector ibc_packet_ack(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache, @@ -524,7 +544,7 @@ struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length); diff --git a/internal/api/lib.go b/internal/api/lib.go index 4f4f07236..b73874307 100644 --- a/internal/api/lib.go +++ b/internal/api/lib.go @@ -172,7 +172,7 @@ func Instantiate( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -189,15 +189,15 @@ func Instantiate( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.instantiate(cache.ptr, cs, e, i, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.instantiate(cache.ptr, cs, e, i, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func Execute( @@ -212,7 +212,7 @@ func Execute( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -229,15 +229,15 @@ func Execute( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.execute(cache.ptr, cs, e, i, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.execute(cache.ptr, cs, e, i, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func Migrate( @@ -251,7 +251,7 @@ func Migrate( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -266,15 +266,15 @@ func Migrate( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.migrate(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.migrate(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func Sudo( @@ -288,7 +288,7 @@ func Sudo( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -303,15 +303,15 @@ func Sudo( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.sudo(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.sudo(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func Reply( @@ -325,7 +325,7 @@ func Reply( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -340,15 +340,15 @@ func Reply( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.reply(cache.ptr, cs, e, r, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.reply(cache.ptr, cs, e, r, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func Query( @@ -362,7 +362,7 @@ func Query( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -377,15 +377,15 @@ func Query( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.query(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.query(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func IBCChannelOpen( @@ -399,7 +399,7 @@ func IBCChannelOpen( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -414,15 +414,15 @@ func IBCChannelOpen( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.ibc_channel_open(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.ibc_channel_open(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func IBCChannelConnect( @@ -436,7 +436,7 @@ func IBCChannelConnect( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -451,15 +451,15 @@ func IBCChannelConnect( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.ibc_channel_connect(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.ibc_channel_connect(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func IBCChannelClose( @@ -473,7 +473,7 @@ func IBCChannelClose( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -488,15 +488,15 @@ func IBCChannelClose( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.ibc_channel_close(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.ibc_channel_close(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func IBCPacketReceive( @@ -510,7 +510,7 @@ func IBCPacketReceive( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -525,15 +525,15 @@ func IBCPacketReceive( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.ibc_packet_receive(cache.ptr, cs, e, pa, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.ibc_packet_receive(cache.ptr, cs, e, pa, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func IBCPacketAck( @@ -547,7 +547,7 @@ func IBCPacketAck( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -562,15 +562,15 @@ func IBCPacketAck( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.ibc_packet_ack(cache.ptr, cs, e, ac, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.ibc_packet_ack(cache.ptr, cs, e, ac, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } func IBCPacketTimeout( @@ -584,7 +584,7 @@ func IBCPacketTimeout( querier *Querier, gasLimit uint64, printDebug bool, -) ([]byte, uint64, error) { +) ([]byte, types.GasReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) e := makeView(env) @@ -599,15 +599,24 @@ func IBCPacketTimeout( db := buildDB(&dbState, gasMeter) a := buildAPI(api) q := buildQuerier(querier) - var gasUsed cu64 + var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.ibc_packet_timeout(cache.ptr, cs, e, pa, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) + res, err := C.ibc_packet_timeout(cache.ptr, cs, e, pa, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &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, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) + } + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil +} + +func convertGasReport(report C.GasReport) types.GasReport { + return types.GasReport{ + Limit: uint64(report.limit), + Remaining: uint64(report.remaining), + UsedExternally: uint64(report.used_externally), + UsedInternally: uint64(report.used_internally), } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil } /**** To error module ***/ diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index 9fd43e72b..e4d926f40 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -360,7 +360,7 @@ func TestInstantiate(t *testing.T) { res, cost, err := Instantiate(cache, checksum, env, info, msg, &igasMeter, store, api, &querier, TESTING_GAS_LIMIT, TESTING_PRINT_DEBUG) require.NoError(t, err) requireOkResponse(t, res, 0) - assert.Equal(t, uint64(0x13a78a36c), cost) + assert.Equal(t, uint64(0x13a78a36c), cost.UsedInternally) var result types.ContractResult err = json.Unmarshal(res, &result) @@ -391,8 +391,8 @@ func TestExecute(t *testing.T) { diff := time.Since(start) require.NoError(t, err) requireOkResponse(t, res, 0) - assert.Equal(t, uint64(0x13a78a36c), cost) - t.Logf("Time (%d gas): %s\n", cost, diff) + assert.Equal(t, uint64(0x13a78a36c), cost.UsedInternally) + t.Logf("Time (%d gas): %s\n", cost.UsedInternally, diff) // execute with the same store gasMeter2 := NewMockGasMeter(TESTING_GAS_LIMIT) @@ -404,8 +404,8 @@ func TestExecute(t *testing.T) { res, cost, err = Execute(cache, checksum, env, info, []byte(`{"release":{}}`), &igasMeter2, store, api, &querier, TESTING_GAS_LIMIT, TESTING_PRINT_DEBUG) diff = time.Since(start) require.NoError(t, err) - assert.Equal(t, uint64(0x222892d70), cost) - t.Logf("Time (%d gas): %s\n", cost, diff) + assert.Equal(t, uint64(0x222892d70), cost.UsedInternally) + t.Logf("Time (%d gas): %s\n", cost.UsedInternally, diff) // make sure it read the balance properly and we got 250 atoms var result types.ContractResult @@ -512,8 +512,8 @@ func TestExecuteCpuLoop(t *testing.T) { diff := time.Since(start) require.NoError(t, err) requireOkResponse(t, res, 0) - assert.Equal(t, uint64(0xd45091d0), cost) - t.Logf("Time (%d gas): %s\n", cost, diff) + assert.Equal(t, uint64(0xd45091d0), cost.UsedInternally) + t.Logf("Time (%d gas): %s\n", cost.UsedInternally, diff) // execute a cpu loop maxGas := uint64(40_000_000) @@ -525,8 +525,8 @@ func TestExecuteCpuLoop(t *testing.T) { _, cost, err = Execute(cache, checksum, env, info, []byte(`{"cpu_loop":{}}`), &igasMeter2, store, api, &querier, maxGas, TESTING_PRINT_DEBUG) diff = time.Since(start) require.Error(t, err) - assert.Equal(t, cost, maxGas) - t.Logf("CPULoop Time (%d gas): %s\n", cost, diff) + assert.Equal(t, cost.UsedInternally, maxGas) + t.Logf("CPULoop Time (%d gas): %s\n", cost.UsedInternally, diff) } func TestExecuteStorageLoop(t *testing.T) { @@ -557,15 +557,15 @@ func TestExecuteStorageLoop(t *testing.T) { store.SetGasMeter(gasMeter2) info = MockInfoBin(t, "fred") start := time.Now() - _, cost, err := Execute(cache, checksum, env, info, []byte(`{"storage_loop":{}}`), &igasMeter2, store, api, &querier, maxGas, TESTING_PRINT_DEBUG) + _, gasReport, err := Execute(cache, checksum, env, info, []byte(`{"storage_loop":{}}`), &igasMeter2, store, api, &querier, maxGas, TESTING_PRINT_DEBUG) diff := time.Since(start) require.Error(t, err) - t.Logf("StorageLoop Time (%d gas): %s\n", cost, diff) + t.Logf("StorageLoop Time (%d gas): %s\n", gasReport.UsedInternally, diff) t.Logf("Gas used: %d\n", gasMeter2.GasConsumed()) - t.Logf("Wasm gas: %d\n", cost) + t.Logf("Wasm gas: %d\n", gasReport.UsedInternally) // the "sdk gas" * GasMultiplier + the wasm cost should equal the maxGas (or be very close) - totalCost := cost + gasMeter2.GasConsumed() + totalCost := gasReport.UsedInternally + gasMeter2.GasConsumed() require.Equal(t, int64(maxGas), int64(totalCost)) } @@ -663,7 +663,7 @@ func TestMultipleInstances(t *testing.T) { require.NoError(t, err) requireOkResponse(t, res, 0) // we now count wasm gas charges and db writes - assert.Equal(t, uint64(0x138559c5c), cost) + assert.Equal(t, uint64(0x138559c5c), cost.UsedInternally) // instance2 controlled by mary gasMeter2 := NewMockGasMeter(TESTING_GAS_LIMIT) @@ -674,7 +674,7 @@ func TestMultipleInstances(t *testing.T) { res, cost, err = Instantiate(cache, checksum, env, info, msg, &igasMeter2, store2, api, &querier, TESTING_GAS_LIMIT, TESTING_PRINT_DEBUG) require.NoError(t, err) requireOkResponse(t, res, 0) - assert.Equal(t, uint64(0x1399177bc), cost) + assert.Equal(t, uint64(0x1399177bc), cost.UsedInternally) // fail to execute store1 with mary resp := exec(t, cache, checksum, "mary", store1, api, querier, 0x1218ff5d0) @@ -922,7 +922,7 @@ func exec(t *testing.T, cache Cache, checksum []byte, signer types.HumanAddress, info := MockInfoBin(t, signer) res, cost, err := Execute(cache, checksum, env, info, []byte(`{"release":{}}`), &igasMeter, store, api, &querier, TESTING_GAS_LIMIT, TESTING_PRINT_DEBUG) require.NoError(t, err) - assert.Equal(t, gasExpected, cost) + assert.Equal(t, gasExpected, cost.UsedInternally) var result types.ContractResult err = json.Unmarshal(res, &result) diff --git a/lib.go b/lib.go index 99f842fd8..99bd7c97b 100644 --- a/lib.go +++ b/lib.go @@ -136,26 +136,20 @@ func (vm *VM) Instantiate( if err != nil { return nil, 0, err } - data, gasUsed, err := api.Instantiate(vm.cache, checksum, envBin, infoBin, initMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.Instantiate(vm.cache, checksum, envBin, infoBin, initMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) - } - gasUsed += gasForDeserialization - var result types.ContractResult - err = json.Unmarshal(data, &result) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &result) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if result.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", result.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", result.Err) } - return result.Ok, gasUsed, nil + return result.Ok, gasReport.UsedInternally, nil } // Execute calls a given contract. Since the only difference between contracts with the same Checksum is the @@ -184,26 +178,20 @@ func (vm *VM) Execute( if err != nil { return nil, 0, err } - data, gasUsed, err := api.Execute(vm.cache, checksum, envBin, infoBin, executeMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.Execute(vm.cache, checksum, envBin, infoBin, executeMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err - } - - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) + return nil, gasReport.UsedInternally, err } - gasUsed += gasForDeserialization var result types.ContractResult - err = json.Unmarshal(data, &result) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &result) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if result.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", result.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", result.Err) } - return result.Ok, gasUsed, nil + return result.Ok, gasReport.UsedInternally, nil } // Query allows a client to execute a contract-specific query. If the result is not empty, it should be @@ -224,26 +212,20 @@ func (vm *VM) Query( if err != nil { return nil, 0, err } - data, gasUsed, err := api.Query(vm.cache, checksum, envBin, queryMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.Query(vm.cache, checksum, envBin, queryMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) - } - gasUsed += gasForDeserialization - var resp types.QueryResponse - err = json.Unmarshal(data, &resp) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &resp) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if resp.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", resp.Err) } - return resp.Ok, gasUsed, nil + return resp.Ok, gasReport.UsedInternally, nil } // Migrate will migrate an existing contract to a new code binary. @@ -267,26 +249,20 @@ func (vm *VM) Migrate( if err != nil { return nil, 0, err } - data, gasUsed, err := api.Migrate(vm.cache, checksum, envBin, migrateMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.Migrate(vm.cache, checksum, envBin, migrateMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err - } - - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) + return nil, gasReport.UsedInternally, err } - gasUsed += gasForDeserialization var resp types.ContractResult - err = json.Unmarshal(data, &resp) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &resp) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if resp.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", resp.Err) } - return resp.Ok, gasUsed, nil + return resp.Ok, gasReport.UsedInternally, nil } // Sudo allows native Go modules to make priviledged (sudo) calls on the contract. @@ -310,26 +286,20 @@ func (vm *VM) Sudo( if err != nil { return nil, 0, err } - data, gasUsed, err := api.Sudo(vm.cache, checksum, envBin, sudoMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.Sudo(vm.cache, checksum, envBin, sudoMsg, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err - } - - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) + return nil, gasReport.UsedInternally, err } - gasUsed += gasForDeserialization var resp types.ContractResult - err = json.Unmarshal(data, &resp) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &resp) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if resp.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", resp.Err) } - return resp.Ok, gasUsed, nil + return resp.Ok, gasReport.UsedInternally, nil } // Reply allows the native Go wasm modules to make a priviledged call to return the result @@ -355,26 +325,20 @@ func (vm *VM) Reply( if err != nil { return nil, 0, err } - data, gasUsed, err := api.Reply(vm.cache, checksum, envBin, replyBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.Reply(vm.cache, checksum, envBin, replyBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) - } - gasUsed += gasForDeserialization - var resp types.ContractResult - err = json.Unmarshal(data, &resp) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &resp) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if resp.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", resp.Err) } - return resp.Ok, gasUsed, nil + return resp.Ok, gasReport.UsedInternally, nil } // IBCChannelOpen is available on IBC-enabled contracts and is a hook to call into @@ -398,26 +362,20 @@ func (vm *VM) IBCChannelOpen( if err != nil { return nil, 0, err } - data, gasUsed, err := api.IBCChannelOpen(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.IBCChannelOpen(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) - } - gasUsed += gasForDeserialization - var resp types.IBCChannelOpenResult - err = json.Unmarshal(data, &resp) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &resp) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if resp.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", resp.Err) } - return resp.Ok, gasUsed, nil + return resp.Ok, gasReport.UsedInternally, nil } // IBCChannelConnect is available on IBC-enabled contracts and is a hook to call into @@ -441,26 +399,20 @@ func (vm *VM) IBCChannelConnect( if err != nil { return nil, 0, err } - data, gasUsed, err := api.IBCChannelConnect(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.IBCChannelConnect(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err - } - - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) + return nil, gasReport.UsedInternally, err } - gasUsed += gasForDeserialization var resp types.IBCBasicResult - err = json.Unmarshal(data, &resp) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &resp) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if resp.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", resp.Err) } - return resp.Ok, gasUsed, nil + return resp.Ok, gasReport.UsedInternally, nil } // IBCChannelClose is available on IBC-enabled contracts and is a hook to call into @@ -484,26 +436,20 @@ func (vm *VM) IBCChannelClose( if err != nil { return nil, 0, err } - data, gasUsed, err := api.IBCChannelClose(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.IBCChannelClose(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err - } - - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) + return nil, gasReport.UsedInternally, err } - gasUsed += gasForDeserialization var resp types.IBCBasicResult - err = json.Unmarshal(data, &resp) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &resp) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if resp.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", resp.Err) } - return resp.Ok, gasUsed, nil + return resp.Ok, gasReport.UsedInternally, nil } // IBCPacketReceive is available on IBC-enabled contracts and is called when an incoming @@ -527,23 +473,17 @@ func (vm *VM) IBCPacketReceive( if err != nil { return nil, 0, err } - data, gasUsed, err := api.IBCPacketReceive(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.IBCPacketReceive(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) - } - gasUsed += gasForDeserialization - var resp types.IBCReceiveResult - err = json.Unmarshal(data, &resp) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &resp) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } - return &resp, gasUsed, nil + return &resp, gasReport.UsedInternally, nil } // IBCPacketAck is available on IBC-enabled contracts and is called when an @@ -568,26 +508,20 @@ func (vm *VM) IBCPacketAck( if err != nil { return nil, 0, err } - data, gasUsed, err := api.IBCPacketAck(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.IBCPacketAck(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err - } - - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) + return nil, gasReport.UsedInternally, err } - gasUsed += gasForDeserialization var resp types.IBCBasicResult - err = json.Unmarshal(data, &resp) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &resp) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if resp.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", resp.Err) } - return resp.Ok, gasUsed, nil + return resp.Ok, gasReport.UsedInternally, nil } // IBCPacketTimeout is available on IBC-enabled contracts and is called when an @@ -612,24 +546,34 @@ func (vm *VM) IBCPacketTimeout( if err != nil { return nil, 0, err } - data, gasUsed, err := api.IBCPacketTimeout(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.IBCPacketTimeout(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } - gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() - if gasLimit < gasForDeserialization+gasUsed { - return nil, gasUsed, fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) - } - gasUsed += gasForDeserialization - var resp types.IBCBasicResult - err = json.Unmarshal(data, &resp) + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &resp) if err != nil { - return nil, gasUsed, err + return nil, gasReport.UsedInternally, err } if resp.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.Err) + return nil, gasReport.UsedInternally, fmt.Errorf("%s", resp.Err) } - return resp.Ok, gasUsed, nil + return resp.Ok, gasReport.UsedInternally, nil +} + +func DeserializeResponse(gasLimit uint64, deserCost types.UFraction, gasReport *types.GasReport, data []byte, response any) error { + gasForDeserialization := deserCost.Mul(uint64(len(data))).Floor() + if gasLimit < gasForDeserialization+gasReport.UsedInternally { + return fmt.Errorf("Insufficient gas left to deserialize contract execution result (%d bytes)", len(data)) + } + gasReport.UsedInternally += gasForDeserialization + gasReport.Remaining -= gasForDeserialization + + err := json.Unmarshal(data, response) + if err != nil { + return err + } + + return nil } diff --git a/libwasmvm/Cargo.lock b/libwasmvm/Cargo.lock index 6b60b4c1c..772fb4364 100644 --- a/libwasmvm/Cargo.lock +++ b/libwasmvm/Cargo.lock @@ -1994,7 +1994,7 @@ checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" [[package]] name = "wasmvm" -version = "1.3.0" +version = "1.3.1" dependencies = [ "cbindgen", "cosmwasm-std", diff --git a/libwasmvm/Cargo.toml b/libwasmvm/Cargo.toml index 83c1f4c7b..92f0a0eea 100644 --- a/libwasmvm/Cargo.toml +++ b/libwasmvm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmvm" -version = "1.3.0" +version = "1.3.1" publish = false authors = ["Ethan Frey "] edition = "2021" diff --git a/libwasmvm/bindings.h b/libwasmvm/bindings.h index 60f301408..5b38b7736 100644 --- a/libwasmvm/bindings.h +++ b/libwasmvm/bindings.h @@ -342,6 +342,26 @@ typedef struct GoQuerier { struct Querier_vtable vtable; } GoQuerier; +typedef struct GasReport { + /** + * The original limit the instance was created with + */ + uint64_t limit; + /** + * The remaining gas that can be spend + */ + uint64_t remaining; + /** + * The amount of gas that was spend and metered externally in operations triggered by this instance + */ + uint64_t used_externally; + /** + * The amount of gas that was spend and metered internally (i.e. by executing Wasm and calling + * API methods which are not metered externally) + */ + uint64_t used_internally; +} GasReport; + struct cache_t *init_cache(struct ByteSliceView data_dir, struct ByteSliceView available_capabilities, uint32_t cache_size, @@ -391,7 +411,7 @@ struct UnmanagedVector instantiate(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector execute(struct cache_t *cache, @@ -404,7 +424,7 @@ struct UnmanagedVector execute(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector migrate(struct cache_t *cache, @@ -416,7 +436,7 @@ struct UnmanagedVector migrate(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector sudo(struct cache_t *cache, @@ -428,7 +448,7 @@ struct UnmanagedVector sudo(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector reply(struct cache_t *cache, @@ -440,7 +460,7 @@ struct UnmanagedVector reply(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector query(struct cache_t *cache, @@ -452,7 +472,7 @@ struct UnmanagedVector query(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_channel_open(struct cache_t *cache, @@ -464,7 +484,7 @@ struct UnmanagedVector ibc_channel_open(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_channel_connect(struct cache_t *cache, @@ -476,7 +496,7 @@ struct UnmanagedVector ibc_channel_connect(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_channel_close(struct cache_t *cache, @@ -488,7 +508,7 @@ struct UnmanagedVector ibc_channel_close(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_packet_receive(struct cache_t *cache, @@ -500,7 +520,7 @@ struct UnmanagedVector ibc_packet_receive(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_packet_ack(struct cache_t *cache, @@ -512,7 +532,7 @@ struct UnmanagedVector ibc_packet_ack(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache, @@ -524,7 +544,7 @@ struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache, struct GoQuerier querier, uint64_t gas_limit, bool print_debug, - uint64_t *gas_used, + struct GasReport *gas_report, struct UnmanagedVector *error_msg); struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length); diff --git a/libwasmvm/src/args.rs b/libwasmvm/src/args.rs index 69ab5fe72..cef0114f0 100644 --- a/libwasmvm/src/args.rs +++ b/libwasmvm/src/args.rs @@ -4,7 +4,7 @@ pub const AVAILABLE_CAPABILITIES_ARG: &str = "available_capabilities"; pub const CACHE_ARG: &str = "cache"; pub const WASM_ARG: &str = "wasm"; pub const CHECKSUM_ARG: &str = "checksum"; -pub const GAS_USED_ARG: &str = "gas_used"; +pub const GAS_REPORT_ARG: &str = "gas_report"; pub const ARG1: &str = "arg1"; pub const ARG2: &str = "arg2"; pub const ARG3: &str = "arg3"; diff --git a/libwasmvm/src/calls.rs b/libwasmvm/src/calls.rs index 520d2e1e5..8d89a8788 100644 --- a/libwasmvm/src/calls.rs +++ b/libwasmvm/src/calls.rs @@ -11,13 +11,14 @@ use cosmwasm_vm::{ }; use crate::api::GoApi; -use crate::args::{ARG1, ARG2, ARG3, CACHE_ARG, CHECKSUM_ARG, GAS_USED_ARG}; +use crate::args::{ARG1, ARG2, ARG3, CACHE_ARG, CHECKSUM_ARG, GAS_REPORT_ARG}; use crate::cache::{cache_t, to_cache}; use crate::db::Db; use crate::error::{handle_c_error_binary, Error}; use crate::memory::{ByteSliceView, UnmanagedVector}; use crate::querier::GoQuerier; use crate::storage::GoStorage; +use crate::GasReport; fn into_backend(db: Db, api: GoApi, querier: GoQuerier) -> Backend { Backend { @@ -39,7 +40,7 @@ pub extern "C" fn instantiate( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_3_args( @@ -54,7 +55,7 @@ pub extern "C" fn instantiate( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -71,7 +72,7 @@ pub extern "C" fn execute( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_3_args( @@ -86,7 +87,7 @@ pub extern "C" fn execute( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -102,7 +103,7 @@ pub extern "C" fn migrate( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( @@ -116,7 +117,7 @@ pub extern "C" fn migrate( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -132,7 +133,7 @@ pub extern "C" fn sudo( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( @@ -146,7 +147,7 @@ pub extern "C" fn sudo( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -162,7 +163,7 @@ pub extern "C" fn reply( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( @@ -176,7 +177,7 @@ pub extern "C" fn reply( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -192,7 +193,7 @@ pub extern "C" fn query( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( @@ -206,7 +207,7 @@ pub extern "C" fn query( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -222,7 +223,7 @@ pub extern "C" fn ibc_channel_open( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( @@ -236,7 +237,7 @@ pub extern "C" fn ibc_channel_open( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -252,7 +253,7 @@ pub extern "C" fn ibc_channel_connect( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( @@ -266,7 +267,7 @@ pub extern "C" fn ibc_channel_connect( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -282,7 +283,7 @@ pub extern "C" fn ibc_channel_close( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( @@ -296,7 +297,7 @@ pub extern "C" fn ibc_channel_close( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -312,7 +313,7 @@ pub extern "C" fn ibc_packet_receive( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( @@ -326,7 +327,7 @@ pub extern "C" fn ibc_packet_receive( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -342,7 +343,7 @@ pub extern "C" fn ibc_packet_ack( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( @@ -356,7 +357,7 @@ pub extern "C" fn ibc_packet_ack( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -372,7 +373,7 @@ pub extern "C" fn ibc_packet_timeout( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( @@ -386,7 +387,7 @@ pub extern "C" fn ibc_packet_timeout( querier, gas_limit, print_debug, - gas_used, + gas_report, error_msg, ) } @@ -411,7 +412,7 @@ fn call_2_args( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { let r = match to_cache(cache) { @@ -427,7 +428,7 @@ fn call_2_args( querier, gas_limit, print_debug, - gas_used, + gas_report, ) })) .unwrap_or_else(|err| { @@ -452,9 +453,9 @@ fn do_call_2_args( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, ) -> Result, Error> { - let gas_used = gas_used.ok_or_else(|| Error::empty_arg(GAS_USED_ARG))?; + let gas_report = gas_report.ok_or_else(|| Error::empty_arg(GAS_REPORT_ARG))?; let checksum: Checksum = checksum .read() .ok_or_else(|| Error::unset_arg(CHECKSUM_ARG))? @@ -470,7 +471,7 @@ fn do_call_2_args( let mut instance = cache.get_instance(&checksum, backend, options)?; // We only check this result after reporting gas usage and returning the instance into the cache. let res = vm_fn(&mut instance, arg1, arg2); - *gas_used = instance.create_gas_report().used_internally; + *gas_report = instance.create_gas_report().into(); instance.recycle(); Ok(res?) } @@ -497,7 +498,7 @@ fn call_3_args( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { let r = match to_cache(cache) { @@ -514,7 +515,7 @@ fn call_3_args( querier, gas_limit, print_debug, - gas_used, + gas_report, ) })) .unwrap_or_else(|err| { @@ -539,9 +540,9 @@ fn do_call_3_args( querier: GoQuerier, gas_limit: u64, print_debug: bool, - gas_used: Option<&mut u64>, + gas_report: Option<&mut GasReport>, ) -> Result, Error> { - let gas_used = gas_used.ok_or_else(|| Error::empty_arg(GAS_USED_ARG))?; + let gas_report = gas_report.ok_or_else(|| Error::empty_arg(GAS_REPORT_ARG))?; let checksum: Checksum = checksum .read() .ok_or_else(|| Error::unset_arg(CHECKSUM_ARG))? @@ -558,7 +559,7 @@ fn do_call_3_args( let mut instance = cache.get_instance(&checksum, backend, options)?; // We only check this result after reporting gas usage and returning the instance into the cache. let res = vm_fn(&mut instance, arg1, arg2, arg3); - *gas_used = instance.create_gas_report().used_internally; + *gas_report = instance.create_gas_report().into(); instance.recycle(); Ok(res?) } diff --git a/libwasmvm/src/gas_report.rs b/libwasmvm/src/gas_report.rs new file mode 100644 index 000000000..1f9e80ad6 --- /dev/null +++ b/libwasmvm/src/gas_report.rs @@ -0,0 +1,31 @@ +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct GasReport { + /// The original limit the instance was created with + pub limit: u64, + /// The remaining gas that can be spend + pub remaining: u64, + /// The amount of gas that was spend and metered externally in operations triggered by this instance + pub used_externally: u64, + /// The amount of gas that was spend and metered internally (i.e. by executing Wasm and calling + /// API methods which are not metered externally) + pub used_internally: u64, +} + +impl From for GasReport { + fn from( + cosmwasm_vm::GasReport { + limit, + remaining, + used_externally, + used_internally, + }: cosmwasm_vm::GasReport, + ) -> Self { + Self { + limit, + remaining, + used_externally, + used_internally, + } + } +} diff --git a/libwasmvm/src/lib.rs b/libwasmvm/src/lib.rs index 8cf1a0a17..2e431fe30 100644 --- a/libwasmvm/src/lib.rs +++ b/libwasmvm/src/lib.rs @@ -8,6 +8,7 @@ mod calls; mod db; mod error; mod gas_meter; +mod gas_report; mod iterator; mod memory; mod querier; @@ -23,6 +24,7 @@ pub use api::GoApi; pub use cache::{cache_t, load_wasm}; pub use db::{db_t, Db}; pub use error::GoError; +pub use gas_report::GasReport; pub use memory::{ destroy_unmanaged_vector, new_unmanaged_vector, ByteSliceView, U8SliceView, UnmanagedVector, }; diff --git a/types/types.go b/types/types.go index aa2cecdac..cfc3ed857 100644 --- a/types/types.go +++ b/types/types.go @@ -115,6 +115,22 @@ func (o OutOfGasError) Error() string { return "Out of gas" } +type GasReport struct { + Limit uint64 + Remaining uint64 + UsedExternally uint64 + UsedInternally uint64 +} + +func EmptyGasReport(limit uint64) GasReport { + return GasReport{ + Limit: limit, + Remaining: limit, + UsedExternally: 0, + UsedInternally: 0, + } +} + // Contains static analysis info of the contract (the Wasm code to be precise). // This type is returned by VM.AnalyzeCode(). type AnalysisReport struct {