How do I await a .wasm (WebAssembly file) from a Rest API? - rest

I am making a Next.js app that has a page that connects to a REST API. The REST API simply spits out a raw .wasm (WebAssembly) file. I know that sounds atypical but it's 100% necessary for my app. I have this function which is supposed to fetch the .wasm file from the API:
const fetchedWasmWrapped = useCallback( async () => {
const endpoint = "/.netlify/functions" + "/decrypt?authSig="+JSON.stringify(props.props.authSig);
const options = {
method: 'GET',
headers: {
'Content-Type': 'application/wasm',
},
};
const response = await fetch(endpoint, options);
const result = await response.text();
console.log(result);
setDecryptedWasm(result);
}, [props.props.authSig])
The function works. It DOES output the .wasm file but console.log(result) writes a bizarre string with a lot of symbols and empty characters. I believe the reason why result is all jumbled up is because I don't think WebAssembly is meant to be printed as a string. It's actually a blob. result is then read as a file later in my app but that fails and I'm assuming it's because the WebAssembly code was not imported as a blob but rather a string.
So how do I tell typescript that I want the ".wasm" code from response? I don't believe there a response.wasm() function so how do I do this?

The key is that you have to change const result = await response.text(); to const result = await response.blob();
This threw more errors in my vscode terminal because setDecryptedWasm was expecting a string. So change const [decryptedWasm, setDecryptedWasm] = useState(''); to const [decryptedWasm, setDecryptedWasm] = useState(new File([], ''));
And rather than setting decryptedWasm with setDecryptedWasm(result), do setDecryptedWasm(new File([result], 'myunityapp.wasm'));

Related

Flutter S3 download using presigned URL issue

I am attempting to use presigned urls to download files on s3 to my flutter app. I have a lambda function that generates and passes the url. The presigned url works fine by itself but once it gets into flutter the data is somehow changed and AWS comes back with 404 error. It seems that somehow the token is corrupted.
If have tried parsing the data returned as XML, JSON and with no parsing what so ever. I have also changed the output from the lambda to be JSON or just send the url directly, neither solved the issue. None of these approaches have worked. Do I have to extract XML or something?
Here's the code that gets the url from a lambda call:
http.Response res1 = await http.get(url2);
dynamic data1 = cnv.jsonDecode(res1.body); //XmlDocument.parse(res1.body);
if (data1['theStatus'] == "error") {
String theStatus2 = "error";
return theStatus2;
} else {
(data1['theUrl']);
writeData(77, data1['theUrl']); //save in Hive
return data1;
}
Here's the code that uses the presigned url:
tFuture<File?> downloadFile(String url, String name) async {
final appStorage = await getApplicationDocumentsDirectory();
final file = File('${appStorage.path}/$name');
final response = await Dio().get(
url,
options: Options(
responseType: ResponseType.bytes,
followRedirects: false,
receiveTimeout: 0,
),
);
final raf = file.openSync(mode: FileMode.write);
raf.writeFromSync(response.data);
print('file saved');
await raf.close();
return file;
}
http.Response res1 = await http.get(url2);
final data1 = cnv.jsonDecode(res1.body); //XmlDocument.parse(res1.body);
if (data1['theStatus'] == "error") {
String theStatus2 = "error";
return theStatus2;
} else {
String theUrl = data1['theUrl'];
writeData(77, data1['theUrl']); //save in Hive
return theUrl;
}
If I hard code theUrl above with a presigned url from the browser accessing the lambda everything works fine...
I believe the issue is something to do with XML but when I use XML parse it throws an error no root... Any help appreciated.
Found the issue... my lambda function did not have the correct credentials. I found the solution using this:
How I can generated pre-signed url for different file versions of AWS S3 objects in NodeJS?

I can't send headers with feathersJS rest api /axios. What do I do wrong?

I have feathers API with REST/axios. Configured like this:
const axios = require("axios");
const restClient = rest(serverPaths.serverPath.slice(0, -1));
restApi.configure(restClient.axios(axios.create({
headers: {accessToken: '--change---me'} // this works all the time
})));
The request:
const sr = await this.$restApi.service('users').find({}, {
headers: {
accesstoken: '-this is a new token-' // this does not work
}
});
However simply changing to get and it works:
const sr = await this.$restApi.service('users').get(1, {
headers: {
accesstoken: '-this is a new token-' // now it works
}
});
So for get I receive: accesstoken: --change---me, -this is a new token- so both of them, which is fine, I'll just delete the first one from config file.
But that's mean I do something wrong. Something simple and stupid but I just can't figure it out what.
Note: all regular API request with axios, not using feathers services, works without problem, I can send any header I want and it just work.

Unable to verify message signed by sol-wallet-adapter

Having created a signed message I'm unsure how to use the resulting signature to verify the message using the publicKey.
My use case is, I'm wanting to use a Solana Wallet to login to an API server with a pattern like:
GET message: String (from API server)
sign message with privateKey
POST signature (to API server)
verify signature with stored publicKey
I've attempted to use nodeJS crypto.verify to decode the signed message on the API side but am a bit out of my depth digging into Buffers and elliptic curves:
// Front-end code
const toHexString = (buffer: Buffer) =>
buffer.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
const data = new TextEncoder().encode('message to verify');
const signed = await wallet.sign(data, "hex");
await setLogin({ // sends API post call to backend
variables: {
publicAddress: walletPublicKey,
signature: toHexString(signed.signature),
},
});
// Current WIP for backend code
const ALGORITHM = "ed25519";
const fromHexString = (hexString) =>
new Uint8Array(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
const signature = fromHexString(args.signature);
const nonceUint8 = new TextEncoder().encode('message to verify');
const verified = crypto.verify(
ALGORITHM,
nonceUint8,
`-----BEGIN PUBLIC KEY-----\n${user.publicAddress}\n-----END PUBLIC KEY-----`,
signature
);
console.log("isVerified: ", verified);
I'm pretty sure I'm going about this the wrong way and there must be an obvious method I'm missing.
As the space matures I expect a verify function or lib will appear to to consume the output of const signed = await wallet.sign(data, "hex");
Something like:
import { VerifyMessage } from '#solana/web3.js';
const verified = VerifyMessage(message, publicKey, signature, 'hex');
But after 3 days of pushing hard I'm starting to hit my limits and my brain is failing. Any help or direction where to look much appreciated 🙏
Solved with input from the fantastic Project Serum discord devs. High level solution is to use libs that are also used in the sol-wallet-adapter repo, namely tweetnacl and bs58:
const signatureUint8 = base58.decode(args.signature);
const nonceUint8 = new TextEncoder().encode(user?.nonce);
const pubKeyUint8 = base58.decode(user?.publicAddress);
nacl.sign.detached.verify(nonceUint8, signatureUint8, pubKeyUint8)
// true
I recommend staying in solana-labs trail and use tweetnacl
spl-token-wallet (sollet.io) signs an arbitrary message with
nacl.sign.detached(message, this.account.secretKey)
https://github.com/project-serum/spl-token-wallet/blob/9c9f1d48a589218ffe0f54b7d2f3fb29d84f7b78/src/utils/walletProvider/localStorage.js#L65-L67
on the other end, verify is done with
nacl.sign.detached.verify
in #solana/web3.js
https://github.com/solana-labs/solana/blob/master/web3.js/src/transaction.ts#L560
Use nacl.sign.detached.verify in your backend and you should be good. I also recommend avoiding any data format manipulation, I am not sure what you were trying to do but if you do verify that each step is correct.
For iOS, solana.request will cause error. Use solana.signMessage and base58 encode the signature.
var _signature = '';
try {
signedMessage = await window.solana.request({
method: "signMessage",
params: {
message: encodedMessage
},
});
_signature = signedMessage.signature;
} catch (e) {
try {
signedMessage = await window.solana.signMessage(encodedMessage);
_signature = base58.encode(signedMessage.signature);
} catch (e1) {
alert(e1.message);
}
}
//
try {
signIn('credentials',
{
publicKey: signedMessage.publicKey,
signature: _signature,
callbackUrl: `${window.location.origin}/`
}
)
} catch (e) {
alert(e.message);
}
I needed to convert Uint8Array to string and convert it back to Uint8Array for HTTP communication. I found the toLocaleString method of Uint8Array helpful in this case. It outputs comma-separated integers as a string.
const signedMessage = await window.solana.signMessage(encodedMessage, "utf8");
const signature = signedMessage.signature.toLocaleString();
And then you can convert it back to Uint8Array with the following code.
const signatureUint8 = new Uint8Array(signature.split(",").map(Number));
Edit
The solution above was working on the desktop but when I tried my code inside the Phantom wallet iOS browser it gave an error. I guess the toLocaleString method is not available in that browser. I found a more solid solution to convert Uint8Array to a comma-separated string
Array.apply([], signedMessage.signature).join(",")
Signing and base64 encode:
const data = new TextEncoder().encode(message);
const signature = await wallet.signMessage(data); // Uint8Array
const signatureBase64 = Buffer.from(signature).toString('base64')
Base64 decode and verifying:
const signatureUint8 = new Uint8Array(atob(signature).split('').map(c => c.charCodeAt(0)))
const messageUint8 = new TextEncoder().encode(message)
const pubKeyUint8 = wallet.publicKey.toBytes() // base58.decode(publicKeyAsString)
const result = nacl.sign.detached.verify(messageUint8, signatureUint8, pubKeyUint8) // true or false
Full code example: https://github.com/enginer/solana-message-sign-verify-example

Url as req.params in Express

I'd like to use express and mongo DB to find a document based off of a URL.
My Schema includes
bandUrl: {
type: String
}
This is my Rout inside of the Express server.
// Get Single Band By bandUrl
router.get('/bandUrl/:url', (req, res) => {
quoteGenerator.find({bandUrl: req.params.url}).then(gen => res.json(gen))
})
I set one of the documents to have bandUrl as 'http://localhost:3000/'.
I tested the route using something other than a URL - just using a string works fine... I'd really like to use the URL though. Is there a way to do this?
Here is my fake/test route form the application..
const getFakeInfo = async () => {
try {
const response = await fetch(`/api/autoquotegenerators/bandUrl/http://localhost:3000/"`, {
method: 'GET',
})
const responseData = await response.json()
console.log(responseData)
} catch (error) {
console.log(error)
}
}
I am thinking the extra slashes in the URL are whats causing the issue.
Thanks for your help!
What you want to you use is encodeURIComponent(). This function escapes all URI specific characters, so they get interpreted properly. Your request code should look something like this:
const response = await fetch(`/api/autoquotegenerators/bandUrl/${encodeURIComponent("http://localhost:3000/")}`, {
method: 'GET',
})
If you want to learn more about this function, you can have a look over here.

Google Cloud Storage unexpected identifier "storage"

I found this documentation and this documentation about uploading files to Google Cloud Storage. I wrote this code:
const {Storage} = require('#google-cloud/storage')();
const projectId = 'myapp-cd94d';
const storage = new Storage({
projectId: projectId,
});
const bucketName = "myapp-cd94d.appspot.com";
const filename = { 'test': 'file'};
var serviceAccount = require("./serviceAccountKey.json")
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
storageBucket: "myapp-cd94d.appspot.com"
});
var bucket = admin.storage().bucket();
await storage.bucket(bucketName).upload(filename, {
gzip: true,
metadata: {
cacheControl: 'no-cache',
},
});
console.log(`${filename} uploaded to ${bucketName}.`);
I tried to deploy the code and got this error message:
await storage.bucket(bucketName).upload(filename, {
^^^^^^^
SyntaxError: Unexpected identifier
Is there something wrong with my first line
const {Storage} = require('#google-cloud/storage')();
Why are there curly brackets around Storage? Am I supposed to replace {Storage} with something else?
Why are there curly brackets around Storage? Am I supposed to replace {Storage} with something else?
The curly braces around Storage indicate a deconstructing assignment. It allows you to extract multiple values from data stored in objects and Arrays. The example import from the documentation you shared (const {Storage} = require('#google-cloud/storage');) is correct.
The error message SyntaxError: Unexpected identifier is not saying that it didn't identify storage, it's saying that it didn't expect storage to be there. The error is due to await.
await can only be used in an async function. For example:
async function myAsyncFunction() {
let result = await lengthyFunction();
console.log(result);
}
myAsyncFunction();
Try wrapping the bucket upload call inside an async function to avoid the error when using await.