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 }