Skip to content

Commit

Permalink
Merge pull request #1982 from iotaledger/wasmclient
Browse files Browse the repository at this point in the history
Wasmclient
  • Loading branch information
BugFreeSoftware authored Feb 21, 2023
2 parents d28da34 + f7e23ce commit 7f26334
Show file tree
Hide file tree
Showing 19 changed files with 207 additions and 210 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,6 @@ jobs:
- name: Run global scope golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.51.1
version: v1.51.2
args: --timeout 10m0s
skip-pkg-cache: true
18 changes: 9 additions & 9 deletions contracts/wasm/testwasmlib/test/testwasmlib_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ func (proc *EventProcessor) waitClientEventsParam(t *testing.T, ctx *wasmclient.
}

func setupClient(t *testing.T) *wasmclient.WasmClientContext {
wasmclient.HrpForClient = ""
if useCluster {
return setupClientCluster(t)
}
Expand Down Expand Up @@ -103,7 +102,7 @@ func setupClientCluster(t *testing.T) *wasmclient.WasmClientContext {

// we're testing against wasp-cluster, so defaults will do
chainID := e.Chain.ChainID.String()
return newClient(t, wasmclient.NewWasmClientService("http://localhost:19090"), chainID, wallet)
return newClient(t, wasmclient.NewWasmClientService("http://localhost:19090", chainID), wallet)
}

func setupClientDisposable(t solo.TestContext) *wasmclient.WasmClientContext {
Expand All @@ -116,7 +115,7 @@ func setupClientDisposable(t solo.TestContext) *wasmclient.WasmClientContext {

cfgChain := config["chain"].(string)
cfgChains := config["chains"].(map[string]interface{})
chain := cfgChains[cfgChain].(string)
chainID := cfgChains[cfgChain].(string)

cfgWallet := config["wallet"].(map[string]interface{})
cfgSeed := cfgWallet["seed"].(string)
Expand All @@ -130,20 +129,20 @@ func setupClientDisposable(t solo.TestContext) *wasmclient.WasmClientContext {
seed := cryptolib.NewSeedFromBytes(seedBytes)
wallet := cryptolib.NewKeyPairFromSeed(seed.SubSeed(0))

return newClient(t, wasmclient.NewWasmClientService(cfgWaspAPI), chain, wallet)
return newClient(t, wasmclient.NewWasmClientService(cfgWaspAPI, chainID), wallet)
}

func setupClientSolo(t solo.TestContext) *wasmclient.WasmClientContext {
ctx := wasmsolo.NewSoloContext(t, testwasmlib.ScName, testwasmlibimpl.OnDispatch)
chain := ctx.Chain.ChainID.String()
chainID := ctx.Chain.ChainID.String()
wallet := ctx.Chain.OriginatorPrivateKey

// use Solo as fake Wasp cluster
return newClient(t, wasmsolo.NewSoloClientService(ctx), chain, wallet)
return newClient(t, wasmsolo.NewSoloClientService(ctx, chainID), wallet)
}

func newClient(t solo.TestContext, svcClient wasmclient.IClientService, chain string, wallet *cryptolib.KeyPair) *wasmclient.WasmClientContext {
ctx := wasmclient.NewWasmClientContext(svcClient, chain, testwasmlib.ScName)
func newClient(t solo.TestContext, svcClient wasmclient.IClientService, wallet *cryptolib.KeyPair) *wasmclient.WasmClientContext {
ctx := wasmclient.NewWasmClientContext(svcClient, testwasmlib.ScName)
require.NoError(t, ctx.Err)
ctx.SignRequests(wallet)
return ctx
Expand All @@ -154,7 +153,7 @@ func TestClientAccountBalance(t *testing.T) {
wallet := ctx.CurrentKeyPair()

// note: this calls core accounts contract instead of testwasmlib
ctx = wasmclient.NewWasmClientContext(ctx.CurrentSvcClient(), ctx.CurrentChainID().String(), coreaccounts.ScName)
ctx = wasmclient.NewWasmClientContext(ctx.CurrentSvcClient(), coreaccounts.ScName)
ctx.SignRequests(wallet)

addr := isc.NewAgentID(wallet.Address())
Expand Down Expand Up @@ -246,6 +245,7 @@ func TestClientEvents(t *testing.T) {
proc.name = e.Name
})
ctx.Register(events)
require.NoError(t, ctx.Err)

for _, param := range params {
proc.sendClientEventsParam(t, ctx, param)
Expand Down
2 changes: 0 additions & 2 deletions contracts/wasm/testwasmlib/test/testwasmlib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (

"github.com/iotaledger/wasp/contracts/wasm/erc721/go/erc721"
"github.com/iotaledger/wasp/contracts/wasm/erc721/go/erc721impl"
"github.com/iotaledger/wasp/packages/wasmvm/wasmclient/go/wasmclient"
"github.com/stretchr/testify/require"

iotago "github.com/iotaledger/iota.go/v3"
Expand Down Expand Up @@ -93,7 +92,6 @@ var (
)

func setupTest(t *testing.T) *wasmsolo.SoloContext {
wasmclient.HrpForClient = ""
return wasmsolo.NewSoloContext(t, testwasmlib.ScName, testwasmlibimpl.OnDispatch)
}

Expand Down
2 changes: 1 addition & 1 deletion packages/chain/mempool/typed_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (olp *typedPool[V]) Get(reqRef *isc.RequestRef) V {
if entry, ok := olp.requests[reqRef.AsKey()]; ok {
return entry.req
}
return *new(V) //nolint:gocritic
return *new(V)
}

func (olp *typedPool[V]) Add(request V) {
Expand Down
47 changes: 12 additions & 35 deletions packages/wasmvm/wasmclient/go/wasmclient/wasmclientcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"strings"
"time"

iotago "github.com/iotaledger/iota.go/v3"
"github.com/iotaledger/wasp/packages/cryptolib"
"github.com/iotaledger/wasp/packages/isc"
"github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib"
Expand All @@ -17,7 +16,6 @@ import (
)

type WasmClientContext struct {
chainID wasmtypes.ScChainID
Err error
eventHandlers []wasmlib.IEventHandlers
keyPair *cryptolib.KeyPair
Expand All @@ -33,39 +31,17 @@ var (
_ wasmlib.ScViewCallContext = new(WasmClientContext)
)

func NewWasmClientContext(svcClient IClientService, chain string, scName string) *WasmClientContext {
if HrpForClient == "" {
// local client implementations for some sandbox functions
wasmtypes.Bech32Decode = ClientBech32Decode
wasmtypes.Bech32Encode = ClientBech32Encode
wasmtypes.HashName = ClientHashName
func NewWasmClientContext(svcClient IClientService, scName string) *WasmClientContext {
s := &WasmClientContext{
svcClient: svcClient,
scName: scName,
}

s := &WasmClientContext{}
s.svcClient = svcClient
s.scName = scName
s.ServiceContractName(scName)

if HrpForClient == "" {
// set the network prefix for the current network
hrp, _, err := iotago.ParseBech32(chain)
if err != nil {
s.Err = err
return s
}
if HrpForClient != hrp && HrpForClient != "" {
panic("WasmClient can only connect to one Tangle network per app")
}
HrpForClient = hrp
}

// note that HrpForClient needs to be set
s.chainID = wasmtypes.ChainIDFromString(chain)
return s
}

func (s *WasmClientContext) CurrentChainID() wasmtypes.ScChainID {
return s.chainID
return s.svcClient.CurrentChainID()
}

func (s *WasmClientContext) CurrentKeyPair() *cryptolib.KeyPair {
Expand All @@ -84,17 +60,18 @@ func (s *WasmClientContext) InitViewCallContext(hContract wasmtypes.ScHname) was
return s.scHname
}

func (s *WasmClientContext) Register(handler wasmlib.IEventHandlers) error {
func (s *WasmClientContext) Register(handler wasmlib.IEventHandlers) {
s.Err = nil
for _, h := range s.eventHandlers {
if h == handler {
return nil
return
}
}
s.eventHandlers = append(s.eventHandlers, handler)
if len(s.eventHandlers) > 1 {
return nil
return
}
return s.svcClient.SubscribeEvents(s.processEvent)
s.Err = s.svcClient.SubscribeEvents(s.processEvent)
}

func (s *WasmClientContext) ServiceContractName(contractName string) {
Expand All @@ -108,7 +85,7 @@ func (s *WasmClientContext) SignRequests(keyPair *cryptolib.KeyPair) {
// get last used nonce from accounts core contract
iscAgent := isc.NewAgentID(keyPair.Address())
agent := wasmtypes.AgentIDFromBytes(iscAgent.Bytes())
ctx := NewWasmClientContext(s.svcClient, s.chainID.String(), coreaccounts.ScName)
ctx := NewWasmClientContext(s.svcClient, coreaccounts.ScName)
n := coreaccounts.ScFuncs.GetAccountNonce(ctx)
n.Params.AgentID().SetValue(agent)
n.Func.Call()
Expand All @@ -135,7 +112,7 @@ func (s *WasmClientContext) WaitRequest(reqID ...wasmtypes.ScRequestID) {
if len(reqID) == 1 {
requestID = reqID[0]
}
s.Err = s.svcClient.WaitUntilRequestProcessed(s.chainID, requestID, 60*time.Second)
s.Err = s.svcClient.WaitUntilRequestProcessed(requestID, 60*time.Second)
}

func (s *WasmClientContext) processEvent(msg *ContractEvent) {
Expand Down
34 changes: 28 additions & 6 deletions packages/wasmvm/wasmclient/go/wasmclient/wasmclientsandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (s *WasmClientContext) FnCall(req *wasmrequests.CallRequest) []byte {
return nil
}

res, err := s.svcClient.CallViewByHname(s.chainID, req.Contract, req.Function, req.Params)
res, err := s.svcClient.CallViewByHname(req.Contract, req.Function, req.Params)
if err != nil {
s.Err = err
return nil
Expand All @@ -35,7 +35,7 @@ func (s *WasmClientContext) FnCall(req *wasmrequests.CallRequest) []byte {
}

func (s *WasmClientContext) FnChainID() wasmtypes.ScChainID {
return s.chainID
return s.CurrentChainID()
}

func (s *WasmClientContext) FnPost(req *wasmrequests.PostRequest) []byte {
Expand All @@ -44,7 +44,7 @@ func (s *WasmClientContext) FnPost(req *wasmrequests.PostRequest) []byte {
return nil
}

if req.ChainID != s.chainID {
if req.ChainID != s.CurrentChainID() {
s.Err = fmt.Errorf("unknown chain id: %s", req.ChainID.String())
return nil
}
Expand All @@ -60,7 +60,7 @@ func (s *WasmClientContext) FnPost(req *wasmrequests.PostRequest) []byte {
return nil
}

func ClientBech32Decode(bech32 string) wasmtypes.ScAddress {
func clientBech32Decode(bech32 string) wasmtypes.ScAddress {
hrp, addr, err := iotago.ParseBech32(bech32)
if err != nil {
panic(err)
Expand All @@ -71,12 +71,34 @@ func ClientBech32Decode(bech32 string) wasmtypes.ScAddress {
return cvt.ScAddress(addr)
}

func ClientBech32Encode(scAddress wasmtypes.ScAddress) string {
func clientBech32Encode(scAddress wasmtypes.ScAddress) string {
addr := cvt.IscAddress(&scAddress)
return addr.Bech32(HrpForClient)
}

func ClientHashName(name string) wasmtypes.ScHname {
func clientHashName(name string) wasmtypes.ScHname {
hName := isc.Hn(name)
return cvt.ScHname(hName)
}

func SetSandboxWrappers(chainID string) error {
if HrpForClient != "" {
return nil
}

// local client implementations for some sandbox functions
wasmtypes.Bech32Decode = clientBech32Decode
wasmtypes.Bech32Encode = clientBech32Encode
wasmtypes.HashName = clientHashName

// set the network prefix for the current network
hrp, _, err := iotago.ParseBech32(chainID)
if err != nil {
return err
}
if HrpForClient != hrp && HrpForClient != "" {
panic("WasmClient can only connect to one Tangle network per app")
}
HrpForClient = hrp
return nil
}
27 changes: 19 additions & 8 deletions packages/wasmvm/wasmclient/go/wasmclient/wasmclientservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,42 +34,49 @@ type ContractEvent struct {
type EventProcessor func(event *ContractEvent)

type IClientService interface {
CallViewByHname(chainID wasmtypes.ScChainID, hContract, hFunction wasmtypes.ScHname, args []byte) ([]byte, error)
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)
SubscribeEvents(callback EventProcessor) error
UnsubscribeEvents()
WaitUntilRequestProcessed(chainID wasmtypes.ScChainID, reqID wasmtypes.ScRequestID, timeout time.Duration) error
WaitUntilRequestProcessed(reqID wasmtypes.ScRequestID, timeout time.Duration) error
}

// WasmClientService TODO should be linked to a chain and holds the nonces for signers for that chain
type WasmClientService struct {
callback EventProcessor
chainID wasmtypes.ScChainID
eventDone chan bool
waspClient *apiclient.APIClient
webSocket string
}

var _ IClientService = new(WasmClientService)

func NewWasmClientService(waspAPI string) *WasmClientService {
func NewWasmClientService(waspAPI string, chainID string) *WasmClientService {
err := SetSandboxWrappers(chainID)
if err != nil {
panic(err)
}
client, err := apiextensions.WaspAPIClientByHostName(waspAPI)
if err != nil {
panic(err.Error())
panic(err)
}
return &WasmClientService{
chainID: wasmtypes.ChainIDFromString(chainID),
waspClient: client,
webSocket: strings.Replace(waspAPI, "http:", "ws:", 1) + "/ws",
}
}

func (sc *WasmClientService) CallViewByHname(chainID wasmtypes.ScChainID, hContract, hFunction wasmtypes.ScHname, args []byte) ([]byte, error) {
func (sc *WasmClientService) CallViewByHname(hContract, hFunction wasmtypes.ScHname, args []byte) ([]byte, error) {
params, err := dict.FromBytes(args)
if err != nil {
return nil, err
}

res, _, err := sc.waspClient.RequestsApi.CallView(context.Background()).ContractCallViewRequest(apiclient.ContractCallViewRequest{
ChainId: cvt.IscChainID(&chainID).String(),
ChainId: cvt.IscChainID(&sc.chainID).String(),
ContractHName: cvt.IscHname(hContract).String(),
FunctionHName: cvt.IscHname(hFunction).String(),
Arguments: apiextensions.JSONDictToAPIJSONDict(params.JSONDict()),
Expand All @@ -86,6 +93,10 @@ func (sc *WasmClientService) CallViewByHname(chainID wasmtypes.ScChainID, hContr
return decodedParams.Bytes(), nil
}

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) {
iscChainID := cvt.IscChainID(&chainID)
iscContract := cvt.IscHname(hContract)
Expand Down Expand Up @@ -141,8 +152,8 @@ func (sc *WasmClientService) UnsubscribeEvents() {
sc.eventDone <- true
}

func (sc *WasmClientService) WaitUntilRequestProcessed(chainID wasmtypes.ScChainID, reqID wasmtypes.ScRequestID, timeout time.Duration) error {
iscChainID := cvt.IscChainID(&chainID)
func (sc *WasmClientService) WaitUntilRequestProcessed(reqID wasmtypes.ScRequestID, timeout time.Duration) error {
iscChainID := cvt.IscChainID(&sc.chainID)
iscReqID := cvt.IscRequestID(&reqID)

_, _, err := sc.waspClient.RequestsApi.
Expand Down
25 changes: 22 additions & 3 deletions packages/wasmvm/wasmclient/src/isc/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ pub struct JsonError {
pub(crate) message: String,
}

const BECH32_PREFIX: &'static str = "smr";

pub fn bech32_decode(input: &str) -> Result<(String, ScAddress)> {
let (hrp, data, _v) = match bech32::decode(&input) {
Ok(v) => v,
Expand Down Expand Up @@ -121,7 +119,7 @@ pub fn json_encode(buf: &[u8]) -> JsonDict {
return dict;
}

pub(crate) static mut HRP_FOR_CLIENT: String = String::new();
static mut HRP_FOR_CLIENT: String = String::new();

pub(crate) fn client_bech32_decode(bech32: &str) -> ScAddress {
match bech32_decode(&bech32) {
Expand Down Expand Up @@ -154,3 +152,24 @@ pub(crate) fn client_bech32_encode(addr: &ScAddress) -> String {
pub(crate) fn client_hash_name(name: &str) -> ScHname {
hname_from_bytes(&hname_bytes(name))
}

pub(crate) fn set_sandbox_wrappers(chain_id: &str) -> Result<()> {
unsafe {
// local client implementations for some sandbox functions
BECH32_DECODE = client_bech32_decode;
BECH32_ENCODE = client_bech32_encode;
HASH_NAME = client_hash_name;
}

// set the network prefix for the current network
match bech32_decode(chain_id) {
Ok((hrp, _)) => unsafe {
if HRP_FOR_CLIENT.len() != 0 && HRP_FOR_CLIENT != hrp {
panic!("WasmClient can only connect to one Tangle network per app");
}
HRP_FOR_CLIENT = hrp;
},
Err(e) => return Err(e),
};
Ok(())
}
Loading

0 comments on commit 7f26334

Please sign in to comment.