github.com/0chain/gosdk@v1.17.11/wasmsdk/proxy.go (about)

     1  //go:build js && wasm
     2  // +build js,wasm
     3  
     4  package main
     5  
     6  import (
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"os"
    11  	"runtime/debug"
    12  	"strconv"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/0chain/gosdk/core/sys"
    17  	"github.com/0chain/gosdk/core/version"
    18  	"github.com/0chain/gosdk/core/zcncrypto"
    19  	"github.com/0chain/gosdk/wasmsdk/jsbridge"
    20  	"github.com/0chain/gosdk/zboxcore/client"
    21  	"github.com/0chain/gosdk/zboxcore/sdk"
    22  	"github.com/0chain/gosdk/zcncore"
    23  
    24  	"github.com/hack-pad/safejs"
    25  
    26  	"syscall/js"
    27  )
    28  
    29  //-----------------------------------------------------------------------------
    30  
    31  var (
    32  	signMutex sync.Mutex
    33  	signCache = make(map[string]string)
    34  )
    35  
    36  func main() {
    37  	fmt.Printf("0CHAIN - GOSDK (version=%v)\n", version.VERSIONSTR)
    38  	sys.Files = sys.NewMemFS()
    39  	sdkLogger = sdk.GetLogger()
    40  	zcnLogger = zcncore.GetLogger()
    41  
    42  	window := js.Global()
    43  
    44  	mode := os.Getenv("MODE")
    45  	fmt.Println("initializing: ", mode)
    46  
    47  	zcn := window.Get("__zcn_wasm__")
    48  	if !(zcn.IsNull() || zcn.IsUndefined()) {
    49  		fmt.Println("zcn is null, set it")
    50  
    51  		jsProxy := zcn.Get("jsProxy")
    52  		// import functions from js object
    53  		if !(jsProxy.IsNull() || jsProxy.IsUndefined()) {
    54  			jsSign := jsProxy.Get("sign")
    55  
    56  			if !(jsSign.IsNull() || jsSign.IsUndefined()) {
    57  				signFunc := func(hash string) (string, error) {
    58  					c := client.GetClient()
    59  					if c == nil || len(c.Keys) == 0 {
    60  						return "", errors.New("no keys found")
    61  					}
    62  					pk := c.Keys[0].PrivateKey
    63  					result, err := jsbridge.Await(jsSign.Invoke(hash, pk))
    64  
    65  					if len(err) > 0 && !err[0].IsNull() {
    66  						return "", errors.New("sign: " + err[0].String())
    67  					}
    68  					return result[0].String(), nil
    69  				}
    70  
    71  				//update sign with js sign
    72  				zcncrypto.Sign = signFunc
    73  				zcncore.SignFn = signFunc
    74  				sys.Sign = func(hash, signatureScheme string, keys []sys.KeyPair) (string, error) {
    75  					// js already has signatureScheme and keys
    76  					return signFunc(hash)
    77  				}
    78  
    79  				sys.SignWithAuth = func(hash, signatureScheme string, keys []sys.KeyPair) (string, error) {
    80  					sig, err := sys.Sign(hash, signatureScheme, keys)
    81  					if err != nil {
    82  						return "", fmt.Errorf("failed to sign with split key: %v", err)
    83  					}
    84  
    85  					data, err := json.Marshal(struct {
    86  						Hash      string `json:"hash"`
    87  						Signature string `json:"signature"`
    88  						ClientID  string `json:"client_id"`
    89  					}{
    90  						Hash:      hash,
    91  						Signature: sig,
    92  						ClientID:  client.GetClient().ClientID,
    93  					})
    94  					if err != nil {
    95  						return "", err
    96  					}
    97  
    98  					if sys.AuthCommon == nil {
    99  						return "", errors.New("authCommon is not set")
   100  					}
   101  
   102  					rsp, err := sys.AuthCommon(string(data))
   103  					if err != nil {
   104  						return "", err
   105  					}
   106  
   107  					var sigpk struct {
   108  						Sig string `json:"sig"`
   109  					}
   110  
   111  					err = json.Unmarshal([]byte(rsp), &sigpk)
   112  					if err != nil {
   113  						return "", err
   114  					}
   115  
   116  					return sigpk.Sig, nil
   117  				}
   118  			} else {
   119  				PrintError("__zcn_wasm__.jsProxy.sign is not installed yet")
   120  			}
   121  
   122  			jsVerify := jsProxy.Get("verify")
   123  
   124  			if !(jsVerify.IsNull() || jsVerify.IsUndefined()) {
   125  				verifyFunc := func(signature, hash string) (bool, error) {
   126  					result, err := jsbridge.Await(jsVerify.Invoke(signature, hash))
   127  
   128  					if len(err) > 0 && !err[0].IsNull() {
   129  						return false, errors.New("verify: " + err[0].String())
   130  					}
   131  					return result[0].Bool(), nil
   132  				}
   133  
   134  				//update Verify with js sign
   135  				sys.Verify = verifyFunc
   136  			} else {
   137  				PrintError("__zcn_wasm__.jsProxy.verify is not installed yet")
   138  			}
   139  
   140  			jsVerifyWith := jsProxy.Get("verifyWith")
   141  			if !(jsVerifyWith.IsNull() || jsVerifyWith.IsUndefined()) {
   142  				verifyFuncWith := func(pk, signature, hash string) (bool, error) {
   143  					result, err := jsbridge.Await(jsVerifyWith.Invoke(pk, signature, hash))
   144  
   145  					if len(err) > 0 && !err[0].IsNull() {
   146  						return false, errors.New("verify: " + err[0].String())
   147  					}
   148  					return result[0].Bool(), nil
   149  				}
   150  
   151  				//update Verify with js sign
   152  				sys.VerifyWith = verifyFuncWith
   153  			} else {
   154  				PrintError("__zcn_wasm__.jsProxy.verifyWith is not installed yet")
   155  			}
   156  
   157  			jsAddSignature := jsProxy.Get("addSignature")
   158  			if !(jsAddSignature.IsNull() || jsAddSignature.IsUndefined()) {
   159  				zcncore.AddSignature = func(privateKey, signature, hash string) (string, error) {
   160  					result, err := jsbridge.Await(jsAddSignature.Invoke(privateKey, signature, hash))
   161  					if len(err) > 0 && !err[0].IsNull() {
   162  						return "", errors.New("add signature: " + err[0].String())
   163  					}
   164  
   165  					return result[0].String(), nil
   166  				}
   167  			} else {
   168  				PrintError("__zcn_wasm__.jsProxy.addSignature is not installed yet")
   169  			}
   170  
   171  			jsCreateObjectURL := jsProxy.Get("createObjectURL")
   172  			if !(jsCreateObjectURL.IsNull() || jsCreateObjectURL.IsUndefined()) {
   173  
   174  				CreateObjectURL = func(buf []byte, mimeType string) string {
   175  
   176  					arrayBuffer := js.Global().Get("ArrayBuffer").New(len(buf))
   177  
   178  					uint8Array := js.Global().Get("Uint8Array").New(arrayBuffer)
   179  
   180  					js.CopyBytesToJS(uint8Array, buf)
   181  
   182  					result, err := jsbridge.Await(jsCreateObjectURL.Invoke(uint8Array, mimeType))
   183  
   184  					if len(err) > 0 && !err[0].IsNull() {
   185  						PrintError(err[0].String())
   186  						return ""
   187  					}
   188  
   189  					return result[0].String()
   190  				}
   191  			} else {
   192  				PrintError("__zcn_wasm__.jsProxy.createObjectURL is not installed yet")
   193  			}
   194  
   195  			sys.Sleep = func(d time.Duration) {
   196  				<-time.After(d)
   197  			}
   198  		} else {
   199  			PrintError("__zcn_wasm__.jsProxy is not installed yet")
   200  		}
   201  
   202  		// tiny wasm sdk with new methods
   203  		sdk := zcn.Get("sdk")
   204  		// register go functions on wasm.sdk
   205  		if !(sdk.IsNull() || sdk.IsUndefined()) {
   206  			jsbridge.BindAsyncFuncs(sdk, map[string]interface{}{
   207  				//sdk
   208  				"init":                   initSDKs,
   209  				"setWallet":              setWallet,
   210  				"getPublicEncryptionKey": zcncore.GetPublicEncryptionKey,
   211  				"hideLogs":               hideLogs,
   212  				"showLogs":               showLogs,
   213  				"getUSDRate":             getUSDRate,
   214  				"isWalletID":             isWalletID,
   215  				"getVersion":             getVersion,
   216  				"getLookupHash":          getLookupHash,
   217  				"createThumbnail":        createThumbnail,
   218  				"makeSCRestAPICall":      makeSCRestAPICall,
   219  
   220  				//blobber
   221  				"delete":                    Delete,
   222  				"share":                     Share,
   223  				"multiDownload":             multiDownload,
   224  				"upload":                    upload,
   225  				"setUploadMode":             setUploadMode,
   226  				"multiUpload":               multiUpload,
   227  				"multiOperation":            MultiOperation,
   228  				"listObjects":               listObjects,
   229  				"listObjectsFromAuthTicket": listObjectsFromAuthTicket,
   230  				"createDir":                 createDir,
   231  				"downloadBlocks":            downloadBlocks,
   232  				"getFileStats":              getFileStats,
   233  				"updateBlobberSettings":     updateBlobberSettings,
   234  				"getRemoteFileMap":          getRemoteFileMap,
   235  				"getBlobbers":               getBlobbers,
   236  				"getcontainers":             GetContainers,
   237  				"updatecontainer":           UpdateContainer,
   238  				"searchcontainer":           SearchContainer,
   239  				"updateForbidAllocation":    UpdateForbidAllocation,
   240  				"send":                      send,
   241  				"cancelUpload":              cancelUpload,
   242  				"pauseUpload":               pauseUpload,
   243  				"repairAllocation":          repairAllocation,
   244  				"checkAllocStatus":          checkAllocStatus,
   245  				"skipStatusCheck":           skipStatusCheck,
   246  				"terminateWorkers":          terminateWorkers,
   247  				"createWorkers":             createWorkers,
   248  				"getFileMetaByName":         getFileMetaByName,
   249  				"downloadDirectory":         downloadDirectory,
   250  				"cancelDownloadDirectory":   cancelDownloadDirectory,
   251  
   252  				// player
   253  				"play":           play,
   254  				"stop":           stop,
   255  				"getNextSegment": getNextSegment,
   256  
   257  				//allocation
   258  				"createAllocation":           createAllocation,
   259  				"getAllocationBlobbers":      getAllocationBlobbers,
   260  				"getBlobberIds":              getBlobberIds,
   261  				"listAllocations":            listAllocations,
   262  				"getAllocation":              getAllocation,
   263  				"reloadAllocation":           reloadAllocation,
   264  				"transferAllocation":         transferAllocation,
   265  				"freezeAllocation":           freezeAllocation,
   266  				"cancelAllocation":           cancelAllocation,
   267  				"updateAllocation":           updateAllocation,
   268  				"updateAllocationWithRepair": updateAllocationWithRepair,
   269  				"getAllocationMinLock":       getAllocationMinLock,
   270  				"getUpdateAllocationMinLock": getUpdateAllocationMinLock,
   271  				"getAllocationWith":          getAllocationWith,
   272  				"createfreeallocation":       createfreeallocation,
   273  
   274  				// readpool
   275  				"getReadPoolInfo": getReadPoolInfo,
   276  				"lockReadPool":    lockReadPool,
   277  				"unLockReadPool":  unLockReadPool,
   278  				"createReadPool":  createReadPool,
   279  
   280  				// claim rewards
   281  				"collectRewards": collectRewards,
   282  
   283  				// stakepool
   284  				"getSkatePoolInfo": getSkatePoolInfo,
   285  				"lockStakePool":    lockStakePool,
   286  				"unlockStakePool":  unlockStakePool,
   287  
   288  				// writepool
   289  				"lockWritePool": lockWritePool,
   290  
   291  				"decodeAuthTicket": decodeAuthTicket,
   292  				"allocationRepair": allocationRepair,
   293  				"repairSize":       repairSize,
   294  
   295  				//smartcontract
   296  				"executeSmartContract": executeSmartContract,
   297  				"faucet":               faucet,
   298  
   299  				// bridge
   300  				"initBridge":                    initBridge,
   301  				"burnZCN":                       burnZCN,
   302  				"mintZCN":                       mintZCN,
   303  				"getMintWZCNPayload":            getMintWZCNPayload,
   304  				"getNotProcessedWZCNBurnEvents": getNotProcessedWZCNBurnEvents,
   305  				"getNotProcessedZCNBurnTickets": getNotProcessedZCNBurnTickets,
   306  				"estimateBurnWZCNGasAmount":     estimateBurnWZCNGasAmount,
   307  				"estimateMintWZCNGasAmount":     estimateMintWZCNGasAmount,
   308  				"estimateGasPrice":              estimateGasPrice,
   309  
   310  				//zcn
   311  				"getWalletBalance": getWalletBalance,
   312  
   313  				//0box api
   314  				"getCsrfToken":    getCsrfToken,
   315  				"createJwtToken":  createJwtToken,
   316  				"refreshJwtToken": refreshJwtToken,
   317  
   318  				//split key
   319  				"splitKeys":     splitKeys,
   320  				"setWalletInfo": setWalletInfo,
   321  				"setAuthUrl":    setAuthUrl,
   322  
   323  				"registerAuthorizer": js.FuncOf(registerAuthorizer),
   324  				"registerAuthCommon": js.FuncOf(registerAuthCommon),
   325  				"callAuth":           js.FuncOf(callAuth),
   326  				"authResponse":       authResponse,
   327  
   328  				// zauth
   329  				"registerZauthServer": registerZauthServer,
   330  				// zvault
   331  				"zvaultNewWallet":             zvaultNewWallet,
   332  				"zvaultNewSplit":              zvaultNewSplit,
   333  				"zvaultStoreKey":              zvaultStoreKey,
   334  				"zvaultRetrieveKeys":          zvaultRetrieveKeys,
   335  				"zvaultRevokeKey":             zvaultRevokeKey,
   336  				"zvaultDeletePrimaryKey":      zvaultDeletePrimaryKey,
   337  				"zvaultRetrieveWallets":       zvaultRetrieveWallets,
   338  				"zvaultRetrieveSharedWallets": zvaultRetrieveSharedWallets,
   339  			})
   340  
   341  			fmt.Println("__wasm_initialized__ = true;")
   342  			zcn.Set("__wasm_initialized__", true)
   343  		} else {
   344  			PrintError("__zcn_wasm__.sdk is not installed yet")
   345  		}
   346  
   347  	} else {
   348  		fmt.Println("zcn is not null")
   349  		fmt.Println("zcn is not null - signWithAuth:", sys.SignWithAuth)
   350  	}
   351  
   352  	if mode != "" {
   353  		respChan := make(chan string, 1)
   354  		jsProxy := window.Get("__zcn_worker_wasm__")
   355  		if !(jsProxy.IsNull() || jsProxy.IsUndefined()) {
   356  			jsSign := jsProxy.Get("sign")
   357  			if !(jsSign.IsNull() || jsSign.IsUndefined()) {
   358  				signFunc := func(hash string) (string, error) {
   359  					c := client.GetClient()
   360  					if c == nil || len(c.Keys) == 0 {
   361  						return "", errors.New("no keys found")
   362  					}
   363  					pk := c.Keys[0].PrivateKey
   364  					result, err := jsbridge.Await(jsSign.Invoke(hash, pk))
   365  
   366  					if len(err) > 0 && !err[0].IsNull() {
   367  						return "", errors.New("sign: " + err[0].String())
   368  					}
   369  					return result[0].String(), nil
   370  				}
   371  				//update sign with js sign
   372  				zcncrypto.Sign = signFunc
   373  				zcncore.SignFn = signFunc
   374  				sys.Sign = func(hash, signatureScheme string, keys []sys.KeyPair) (string, error) {
   375  					// js already has signatureScheme and keys
   376  					return signFunc(hash)
   377  				}
   378  
   379  				sys.SignWithAuth = func(hash, signatureScheme string, keys []sys.KeyPair) (string, error) {
   380  					fmt.Println("[worker] SignWithAuth pubkey:", keys[0])
   381  					sig, err := sys.Sign(hash, signatureScheme, keys)
   382  					if err != nil {
   383  						return "", fmt.Errorf("failed to sign with split key: %v", err)
   384  					}
   385  
   386  					data, err := json.Marshal(struct {
   387  						Hash      string `json:"hash"`
   388  						Signature string `json:"signature"`
   389  						ClientID  string `json:"client_id"`
   390  					}{
   391  						Hash:      hash,
   392  						Signature: sig,
   393  						ClientID:  client.GetClient().ClientID,
   394  					})
   395  					if err != nil {
   396  						return "", err
   397  					}
   398  
   399  					if sys.AuthCommon == nil {
   400  						return "", errors.New("authCommon is not set")
   401  					}
   402  
   403  					rsp, err := sys.AuthCommon(string(data))
   404  					if err != nil {
   405  						return "", err
   406  					}
   407  
   408  					var sigpk struct {
   409  						Sig string `json:"sig"`
   410  					}
   411  
   412  					err = json.Unmarshal([]byte(rsp), &sigpk)
   413  					if err != nil {
   414  						return "", err
   415  					}
   416  
   417  					return sigpk.Sig, nil
   418  				}
   419  
   420  				fmt.Println("Init SignWithAuth:", sys.SignWithAuth)
   421  
   422  			} else {
   423  				PrintError("__zcn_worker_wasm__.jsProxy.sign is not installed yet")
   424  			}
   425  
   426  			initProxyKeys := jsProxy.Get("initProxyKeys")
   427  			if !(initProxyKeys.IsNull() || initProxyKeys.IsUndefined()) {
   428  				gInitProxyKeys = func(publicKey, privateKey string) {
   429  					// jsProxy.Set("publicKey", bls.DeserializeHexStrToPublicKey(publicKey))
   430  					// jsProxy.Set("secretKey", bls.DeserializeHexStrToSecretKey(privateKey))
   431  					_, err := jsbridge.Await(initProxyKeys.Invoke(publicKey, privateKey))
   432  					if len(err) > 0 && !err[0].IsNull() {
   433  						PrintError("initProxyKeys: ", err[0].String())
   434  						return
   435  					}
   436  
   437  					// return result[0].String(), nil
   438  					return
   439  				}
   440  			}
   441  
   442  			fmt.Println("Init SignWithAuth:", sys.SignWithAuth)
   443  		} else {
   444  			PrintError("__zcn_worker_wasm__ is not installed yet")
   445  		}
   446  
   447  		fmt.Println("CLIENT_ID:", os.Getenv("CLIENT_ID"))
   448  		isSplitEnv := os.Getenv("IS_SPLIT")
   449  		// convert to bool
   450  		isSplit, err := strconv.ParseBool(isSplitEnv)
   451  		if err != nil {
   452  			fmt.Println("convert isSplitEnv failed:", err)
   453  			return
   454  		}
   455  
   456  		clientID := os.Getenv("CLIENT_ID")
   457  		clientKey := os.Getenv("CLIENT_KEY")
   458  		publicKey := os.Getenv("PUBLIC_KEY")
   459  		peerPublicKey := os.Getenv("PEER_PUBLIC_KEY")
   460  		mnemonic := os.Getenv("MNEMONIC")
   461  		privateKey := os.Getenv("PRIVATE_KEY")
   462  		zauthServer := os.Getenv("ZAUTH_SERVER")
   463  
   464  		gInitProxyKeys(publicKey, privateKey)
   465  
   466  		if isSplit {
   467  			sys.AuthCommon = func(msg string) (string, error) {
   468  				// send message to main thread
   469  				sendMessageToMainThread(msg)
   470  				// wait for response from main thread
   471  				rsp := <-respChan
   472  				return rsp, nil
   473  			}
   474  
   475  			// TODO: differe the registerAuthorizer
   476  			// registerZauthServer("http://18.191.13.66:8080", publicKey)
   477  			// registerZauthServer("http://127.0.0.1:8080", publicKey)
   478  			registerZauthServer(zauthServer)
   479  		}
   480  
   481  		setWallet(clientID, clientKey, peerPublicKey, publicKey, privateKey, mnemonic, isSplit)
   482  		hideLogs()
   483  		debug.SetGCPercent(75)
   484  		debug.SetMemoryLimit(1 * 1024 * 1024 * 1024) //1GB
   485  		err = startListener(respChan)
   486  		if err != nil {
   487  			fmt.Println("Error starting listener", err)
   488  			return
   489  		}
   490  	}
   491  
   492  	hideLogs()
   493  	debug.SetGCPercent(75)
   494  	debug.SetMemoryLimit(3.5 * 1024 * 1024 * 1024) //3.5 GB
   495  
   496  	<-make(chan bool)
   497  
   498  	jsbridge.Close()
   499  }
   500  
   501  var gInitProxyKeys func(publicKey, privateKey string)
   502  
   503  func sendMessageToMainThread(msg string) {
   504  	PrintInfo("[send to main thread]:", msg)
   505  	jsbridge.PostMessage(jsbridge.GetSelfWorker(), jsbridge.MsgTypeAuth, map[string]string{"msg": msg})
   506  }
   507  
   508  func UpdateWalletWithEventData(data *safejs.Value) error {
   509  	clientID, err := jsbridge.ParseEventDataField(data, "client_id")
   510  	if err != nil {
   511  		return err
   512  	}
   513  	clientKey, err := jsbridge.ParseEventDataField(data, "client_key")
   514  	if err != nil {
   515  		return err
   516  	}
   517  	peerPublicKey, err := jsbridge.ParseEventDataField(data, "peer_public_key")
   518  	if err != nil {
   519  		return err
   520  	}
   521  
   522  	publicKey, err := jsbridge.ParseEventDataField(data, "public_key")
   523  	if err != nil {
   524  		return err
   525  	}
   526  	privateKey, err := jsbridge.ParseEventDataField(data, "private_key")
   527  	if err != nil {
   528  		return err
   529  	}
   530  	mnemonic, err := jsbridge.ParseEventDataField(data, "mnemonic")
   531  	if err != nil {
   532  		return err
   533  	}
   534  	isSplitStr, err := jsbridge.ParseEventDataField(data, "is_split")
   535  	if err != nil {
   536  		return err
   537  	}
   538  
   539  	isSplit, err := strconv.ParseBool(isSplitStr)
   540  	if err != nil {
   541  		isSplit = false
   542  	}
   543  
   544  	fmt.Println("update wallet with event data")
   545  	setWallet(clientID, clientKey, peerPublicKey, publicKey, privateKey, mnemonic, isSplit)
   546  	return nil
   547  }