Skip to content

Commit

Permalink
Merge pull request #1993 from iotaledger/wasmclient
Browse files Browse the repository at this point in the history
Wasmclient
  • Loading branch information
BugFreeSoftware authored Feb 22, 2023
2 parents fc7c834 + 58eeeec commit 41c7022
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 68 deletions.
15 changes: 0 additions & 15 deletions packages/wasmvm/wasmclient/go/wasmclient/wasmclientcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@ import (
"github.com/iotaledger/wasp/packages/cryptolib"
"github.com/iotaledger/wasp/packages/isc"
"github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib"
"github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/coreaccounts"
"github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/wasmtypes"
)

type WasmClientContext struct {
Err error
eventHandlers []wasmlib.IEventHandlers
keyPair *cryptolib.KeyPair
nonce uint64
ReqID wasmtypes.ScRequestID
scName string
scHname wasmtypes.ScHname
Expand Down Expand Up @@ -80,19 +78,6 @@ func (s *WasmClientContext) ServiceContractName(contractName string) {

func (s *WasmClientContext) SignRequests(keyPair *cryptolib.KeyPair) {
s.keyPair = keyPair

// TODO not here
// get last used nonce from accounts core contract
iscAgent := isc.NewAgentID(keyPair.Address())
agent := wasmtypes.AgentIDFromBytes(iscAgent.Bytes())
ctx := NewWasmClientContext(s.svcClient, coreaccounts.ScName)
n := coreaccounts.ScFuncs.GetAccountNonce(ctx)
n.Params.AgentID().SetValue(agent)
n.Func.Call()
s.Err = ctx.Err
if s.Err == nil {
s.nonce = n.Results.AccountNonce().Value()
}
}

func (s *WasmClientContext) Unregister(handler wasmlib.IEventHandlers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ func (s *WasmClientContext) FnPost(req *wasmrequests.PostRequest) []byte {
}

scAssets := wasmlib.NewScAssets(req.Transfer)
s.nonce++
s.ReqID, s.Err = s.svcClient.PostRequest(req.ChainID, req.Contract, req.Function, req.Params, scAssets, s.keyPair, s.nonce)
s.ReqID, s.Err = s.svcClient.PostRequest(req.ChainID, req.Contract, req.Function, req.Params, scAssets, s.keyPair)
return nil
}

Expand Down
39 changes: 37 additions & 2 deletions packages/wasmvm/wasmclient/go/wasmclient/wasmclientservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package wasmclient
import (
"context"
"strings"
"sync"
"time"

"nhooyr.io/websocket"
Expand All @@ -22,6 +23,7 @@ import (
"github.com/iotaledger/wasp/packages/publisher"
"github.com/iotaledger/wasp/packages/publisher/publisherws"
"github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib"
"github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/coreaccounts"
"github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/wasmtypes"
)

Expand All @@ -36,7 +38,7 @@ type EventProcessor func(event *ContractEvent)
type IClientService interface {
CallViewByHname(hContract, hFunction wasmtypes.ScHname, args []byte) ([]byte, error)
CurrentChainID() wasmtypes.ScChainID
PostRequest(chainID wasmtypes.ScChainID, hContract, hFunction wasmtypes.ScHname, args []byte, allowance *wasmlib.ScAssets, keyPair *cryptolib.KeyPair, nonce uint64) (wasmtypes.ScRequestID, error)
PostRequest(chainID wasmtypes.ScChainID, hContract, hFunction wasmtypes.ScHname, args []byte, allowance *wasmlib.ScAssets, keyPair *cryptolib.KeyPair) (wasmtypes.ScRequestID, error)
SubscribeEvents(callback EventProcessor) error
UnsubscribeEvents()
WaitUntilRequestProcessed(reqID wasmtypes.ScRequestID, timeout time.Duration) error
Expand All @@ -47,6 +49,8 @@ type WasmClientService struct {
callback EventProcessor
chainID wasmtypes.ScChainID
eventDone chan bool
nonceLock sync.Mutex
nonces map[string]uint64
waspClient *apiclient.APIClient
webSocket string
}
Expand All @@ -64,6 +68,7 @@ func NewWasmClientService(waspAPI string, chainID string) *WasmClientService {
}
return &WasmClientService{
chainID: wasmtypes.ChainIDFromString(chainID),
nonces: make(map[string]uint64),
waspClient: client,
webSocket: strings.Replace(waspAPI, "http:", "ws:", 1) + "/ws",
}
Expand Down Expand Up @@ -97,7 +102,7 @@ func (sc *WasmClientService) CurrentChainID() wasmtypes.ScChainID {
return sc.chainID
}

func (sc *WasmClientService) PostRequest(chainID wasmtypes.ScChainID, hContract, hFunction wasmtypes.ScHname, args []byte, allowance *wasmlib.ScAssets, keyPair *cryptolib.KeyPair, nonce uint64) (reqID wasmtypes.ScRequestID, err error) {
func (sc *WasmClientService) PostRequest(chainID wasmtypes.ScChainID, hContract, hFunction wasmtypes.ScHname, args []byte, allowance *wasmlib.ScAssets, keyPair *cryptolib.KeyPair) (reqID wasmtypes.ScRequestID, err error) {
iscChainID := cvt.IscChainID(&chainID)
iscContract := cvt.IscHname(hContract)
iscFunction := cvt.IscHname(hFunction)
Expand All @@ -106,6 +111,11 @@ func (sc *WasmClientService) PostRequest(chainID wasmtypes.ScChainID, hContract,
return reqID, err
}

nonce, err := sc.cachedNonce(keyPair)
if err != nil {
return reqID, err
}

req := isc.NewOffLedgerRequest(iscChainID, iscContract, iscFunction, params, nonce)
iscAllowance := cvt.IscAllowance(allowance)
req.WithAllowance(iscAllowance)
Expand Down Expand Up @@ -164,6 +174,31 @@ func (sc *WasmClientService) WaitUntilRequestProcessed(reqID wasmtypes.ScRequest
return err
}

func (sc *WasmClientService) cachedNonce(keyPair *cryptolib.KeyPair) (uint64, error) {
sc.nonceLock.Lock()
defer sc.nonceLock.Unlock()

key := string(keyPair.GetPublicKey().AsBytes())
nonce, ok := sc.nonces[key]
if !ok {
// note that even while getting the current nonce we keep the lock active
// that way prevent other potential contenders to do the same in parallel
iscAgent := isc.NewAgentID(keyPair.Address())
agent := wasmtypes.AgentIDFromBytes(iscAgent.Bytes())
ctx := NewWasmClientContext(sc, coreaccounts.ScName)
n := coreaccounts.ScFuncs.GetAccountNonce(ctx)
n.Params.AgentID().SetValue(agent)
n.Func.Call()
if ctx.Err != nil {
return 0, ctx.Err
}
nonce = n.Results.AccountNonce().Value()
}
nonce++
sc.nonces[key] = nonce
return nonce, nil
}

func (sc *WasmClientService) eventLoop(ctx context.Context, ws *websocket.Conn) {
for {
evt := publisher.ISCEvent{}
Expand Down
21 changes: 4 additions & 17 deletions packages/wasmvm/wasmclient/src/wasmclientcontext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,23 @@ pub struct WasmClientContext {
pub(crate) event_done: Arc<Mutex<bool>>,
pub(crate) event_handlers: Arc<Mutex<Vec<Box<dyn IEventHandlers>>>>,
pub(crate) key_pair: Option<KeyPair>,
pub(crate) nonce: Arc<Mutex<u64>>,
pub(crate) req_id: Arc<Mutex<ScRequestID>>,
pub(crate) sc_name: String,
pub(crate) sc_hname: ScHname,
pub(crate) svc_client: WasmClientService,
pub(crate) svc_client: Arc<WasmClientService>,
}

impl WasmClientContext {
pub fn new(svc_client: &WasmClientService, sc_name: &str) -> WasmClientContext {
pub fn new(svc_client: Arc<WasmClientService>, sc_name: &str) -> WasmClientContext {
WasmClientContext {
error: Arc::new(Mutex::new(Ok(()))),
event_done: Arc::default(),
event_handlers: Arc::default(),
key_pair: None,
nonce: Arc::default(),
req_id: Arc::new(Mutex::new(request_id_from_bytes(&[]))),
sc_name: String::from(sc_name),
sc_hname: hname_from_bytes(&hname_bytes(&sc_name)),
svc_client: svc_client.clone(),
svc_client: svc_client,
}
}

Expand All @@ -44,7 +42,7 @@ impl WasmClientContext {
self.key_pair.clone()
}

pub fn current_svc_client(&self) -> WasmClientService {
pub fn current_svc_client(&self) -> Arc<WasmClientService> {
self.svc_client.clone()
}

Expand Down Expand Up @@ -75,17 +73,6 @@ impl WasmClientContext {

pub fn sign_requests(&mut self, key_pair: &KeyPair) {
self.key_pair = Some(key_pair.clone());
// get last used nonce from accounts core contract
let isc_agent = ScAgentID::from_address(&key_pair.address());
let ctx = WasmClientContext::new(
&self.svc_client,
coreaccounts::SC_NAME,
);
let n = coreaccounts::ScFuncs::get_account_nonce(&ctx);
n.params.agent_id().set_value(&isc_agent);
n.func.call();
let mut nonce = self.nonce.lock().unwrap();
*nonce = n.results.account_nonce().value();
}

pub fn unregister(&mut self, id: &str) {
Expand Down
3 changes: 0 additions & 3 deletions packages/wasmvm/wasmclient/src/wasmclientsandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,13 @@ impl ScFuncCallContext for WasmClientContext {
}

let sc_assets = ScAssets::new(&req.transfer);
let mut nonce = self.nonce.lock().unwrap();
*nonce += 1;
let res = self.svc_client.post_request(
&req.chain_id,
&req.contract,
&req.function,
&req.params,
&sc_assets,
self.key_pair.as_ref().unwrap(),
*nonce,
);

match res {
Expand Down
44 changes: 39 additions & 5 deletions packages/wasmvm/wasmclient/src/wasmclientservice.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
// // Copyright 2020 IOTA Stiftung
// // SPDX-License-Identifier: Apache-2.0

use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread::spawn;
use std::time::Duration;

use crypto::signatures::ed25519::PublicKey;
use reqwest::{blocking, StatusCode};
use serde::{Deserialize, Serialize};
use wasmlib::*;
use ws::{CloseCode, connect, Message, Sender};

use crate::*;
use crate::codec::*;
use crate::keypair::KeyPair;

pub const ISC_EVENT_KIND_NEW_BLOCK: &str = "new_block";
pub const ISC_EVENT_KIND_RECEIPT: &str = "receipt";
Expand Down Expand Up @@ -46,9 +49,9 @@ pub struct ContractEvent {
pub data: String,
}

#[derive(Clone, PartialEq)]
pub struct WasmClientService {
chain_id: ScChainID,
nonces: Arc<Mutex<HashMap<PublicKey, u64>>>,
wasp_api: String,
}

Expand All @@ -57,6 +60,7 @@ impl WasmClientService {
set_sandbox_wrappers(chain_id).unwrap();
WasmClientService {
chain_id: chain_id_from_string(chain_id),
nonces: Arc::default(),
wasp_api: String::from(wasp_api),
}
}
Expand Down Expand Up @@ -95,7 +99,7 @@ impl WasmClientService {
let status_code = failed_status_code.as_u16();
match v.json::<JsonError>() {
Ok(err_msg) => {
return Err(format!("{status_code}: {}", err_msg.message));
return Err(format!("{}: {}", status_code, err_msg.message));
}
Err(e) => return Err(e.to_string()),
}
Expand All @@ -119,8 +123,12 @@ impl WasmClientService {
args: &[u8],
allowance: &ScAssets,
key_pair: &keypair::KeyPair,
nonce: u64,
) -> Result<ScRequestID> {
let nonce: u64;
match self.cache_nonce(key_pair) {
Ok(n) => nonce = n,
Err(e) => return Err(e),
}
let mut req =
offledgerrequest::OffLedgerRequest::new(
chain_id,
Expand All @@ -147,7 +155,7 @@ impl WasmClientService {
let status_code = failed_status_code.as_u16();
match v.json::<JsonError>() {
Ok(err_msg) => {
return Err(format!("{status_code}: {}", err_msg.message));
return Err(format!("{}: {}", status_code, err_msg.message));
}
Err(e) => return Err(e.to_string()),
}
Expand Down Expand Up @@ -228,7 +236,7 @@ impl WasmClientService {
let status_code = failed_status_code.as_u16();
match v.text() {
Ok(err_msg) => {
return Err(format!("{status_code}: {err_msg}"));
return Err(format!("{}: {}", status_code, err_msg));
}
Err(e) => return Err(e.to_string()),
}
Expand All @@ -239,4 +247,30 @@ impl WasmClientService {
}
}
}

fn cache_nonce(&self, key_pair: &KeyPair) -> Result<u64> {
let key = key_pair.public_key;
let mut nonces = self.nonces.lock().unwrap();
let mut nonce: u64;
match nonces.get(&key) {
None => {
// get last used nonce from accounts core contract
let isc_agent = ScAgentID::from_address(&key_pair.address());
let chain_id = self.chain_id.to_string();
let wcs = Arc::new(WasmClientService::new(&self.wasp_api, &chain_id));
let ctx = WasmClientContext::new(wcs, coreaccounts::SC_NAME);
let n = coreaccounts::ScFuncs::get_account_nonce(&ctx);
n.params.agent_id().set_value(&isc_agent);
n.func.call();
match ctx.err() {
Ok(_) => nonce = n.results.account_nonce().value(),
Err(e) => return Err(e),
}
}
Some(n) => nonce = *n,
}
nonce += 1;
nonces.insert(key, nonce);
Ok(nonce)
}
}
6 changes: 3 additions & 3 deletions packages/wasmvm/wasmclient/tests/wasmclient_verified.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::{Arc, Mutex};

use wasmclient::{self, isc::keypair, wasmclientcontext::*, wasmclientservice::*};

const MYCHAIN: &str = "atoi1pz269enxjz07faf63z8ec876tzlqvjk9jvttfuejkku9mjd8828a6ezenjx";
const MYCHAIN: &str = "atoi1pq3v5c0mvgtzrjaegx3n6lf2dv9qn23rvtcj8vatn6m94laezrz8z5rtwnr";
const MYSEED: &str = "0xa580555e5b84a4b72bbca829b4085a4725941f3b3702525f36862762d76c21f3";

const PARAMS: &[&str] = &[
Expand Down Expand Up @@ -59,8 +59,8 @@ fn check_error(ctx: &WasmClientContext) {
}

fn setup_client() -> WasmClientContext {
let svc = WasmClientService::new("http://localhost:19090", MYCHAIN);
let mut ctx = WasmClientContext::new(&svc, "testwasmlib");
let svc = Arc::new(WasmClientService::new("http://localhost:19090", MYCHAIN));
let mut ctx = WasmClientContext::new(svc.clone(), "testwasmlib");
ctx.sign_requests(&keypair::KeyPair::from_sub_seed(
&wasmlib::bytes_from_string(MYSEED),
0,
Expand Down
13 changes: 0 additions & 13 deletions packages/wasmvm/wasmclient/ts/wasmclient/lib/wasmclientcontext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import * as isc from './isc';
import * as wasmlib from 'wasmlib';
import {panic} from 'wasmlib';
import * as coreaccounts from 'wasmlib/coreaccounts';
import {WasmClientSandbox} from './wasmclientsandbox';
import {ContractEvent, WasmClientService} from './wasmclientservice';

Expand Down Expand Up @@ -51,18 +50,6 @@ export class WasmClientContext extends WasmClientSandbox implements wasmlib.ScFu

public signRequests(keyPair: isc.KeyPair) {
this.keyPair = keyPair;

// TODO not here
// get last used nonce from accounts core contract
const agent = wasmlib.ScAgentID.fromAddress(keyPair.address());
const ctx = new WasmClientContext(this.svcClient, coreaccounts.ScName);
const n = coreaccounts.ScFuncs.getAccountNonce(ctx);
n.params.agentID().setValue(agent);
n.func.call();
this.Err = ctx.Err;
if (this.Err == null) {
this.nonce = n.results.accountNonce().value();
}
}

public unregister(handler: wasmlib.IEventHandlers): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ export class WasmClientSandbox {
}

const scAssets = new wasmlib.ScAssets(req.transfer);
this.nonce++;
[this.ReqID, this.Err] = this.svcClient.postRequest(req.chainID, req.contract, req.function, req.params, scAssets, this.keyPair, this.nonce);
[this.ReqID, this.Err] = this.svcClient.postRequest(req.chainID, req.contract, req.function, req.params, scAssets, this.keyPair);
return new Uint8Array(0);
}
}
Loading

0 comments on commit 41c7022

Please sign in to comment.