What are the use of Transaction ID respond in invoke-transaction.js of Balance Transfer sample? - node.js

I am currently learning how to develop client app using Hyperledger Fabric Node SDK. To do so, I'm now trying to understand the Node JS code of balance-transfer sample in fabric-samples.
In the balance-transfer/app/invoke-transaction.js file, invokeChaincode function returns Transaction ID. And that returned value is the res parameter of app.js invoke post function.
My question is, what can be done with Transaction ID alone? What if I, say, want to know the block # of my invocation? Is it just a response with no further use?
Please bear with me if this is a stupid question, I'm new to this.
Thanks!
Here's the code snippet of stated app.js
// Invoke transaction on chaincode on target peers
app.post('/channels/:channelName/chaincodes/:chaincodeName', function(req, res) {
logger.debug('==================== INVOKE ON CHAINCODE ==================');
var peers = req.body.peers;
var chaincodeName = req.params.chaincodeName;
var channelName = req.params.channelName;
var fcn = req.body.fcn;
var args = req.body.args;
logger.debug('channelName : ' + channelName);
logger.debug('chaincodeName : ' + chaincodeName);
logger.debug('fcn : ' + fcn);
logger.debug('args : ' + args);
if (!chaincodeName) {
res.json(getErrorMessage('\'chaincodeName\''));
return;
}
if (!channelName) {
res.json(getErrorMessage('\'channelName\''));
return;
}
if (!fcn) {
res.json(getErrorMessage('\'fcn\''));
return;
}
if (!args) {
res.json(getErrorMessage('\'args\''));
return;
}
invoke.invokeChaincode(peers, channelName, chaincodeName, fcn, args, req.username, req.orgname)
.then(function(message) {
res.send(message);
});
});

You can use the transaction ID to listen for any events or query the ledger for the transaction later.
Listen for events: https://github.com/hyperledger/fabric-samples/blob/release/balance-transfer/app/invoke-transaction.js#L101
Query Transactions: https://github.com/hyperledger/fabric-samples/blob/release/balance-transfer/app/query.js#L100
It depends on how you can put it to use.
Refer to the SDK docs for more information: https://fabric-sdk-node.github.io/index.html

Related

Recording Bot using Skype.bot.media

We are creating a bot that can join the team meeting and it can start the recording as it joins the team meeting. But we are getting this error(Expected not null
Parameter name: client). I am attaching the code below:
when debugger goes to CreateLocalMediaSession() session method then at that method it gives the error.(Expected not null
Parameter name: client)
public async Task<ICall> JoinCallAsync()
{
// A tracking id for logging purposes. Helps identify this call in logs.
var scenarioId = Guid.NewGuid();
var (chatInfo, meetingInfo) = JoinInfo.ParseJoinURL("https://teams.microsoft.com/l/meetup-join/19:meeting_YTI5NDQ2ODQtMmNlNy00YTBhLTg2NTMtYmZmOGIyMzdhMTgw#thread.v2/0?context=%7B%22Tid%22:%22204d6395-ea6c-4e64-abea-e04cd30845e2%22,%22Oid%22:%225a95f69b-70e2-40d3-8b9a-5810ffcc6ec9%22%7D");
var tenantId = (meetingInfo as OrganizerMeetingInfo).Organizer.GetPrimaryIdentity().GetTenantId();
var mediaSession = this.CreateLocalMediaSession(scenarioId);
var joinParams = new JoinMeetingParameters(chatInfo, meetingInfo, mediaSession)
{
TenantId = tenantId,
};
if (!string.IsNullOrWhiteSpace("bot"))
{
// Teams client does not allow changing of one's display name.
// If the display name is specified, we join as an anonymous (guest) user
// with the specified display name. This will put the bot in lobby
// unless lobby bypass is disabled.
joinParams.GuestIdentity = new Identity
{
Id = Guid.NewGuid().ToString(),
DisplayName = "bot",
};
}
var statefulCall = await this.Client.Calls().AddAsync(joinParams, scenarioId).ConfigureAwait(false);
statefulCall.GraphLogger.Info($"Call creation complete: {statefulCall.Id}");
return statefulCall;
}
Code for creating local media session:
private ILocalMediaSession CreateLocalMediaSession(Guid mediaSessionId = default)
{
try
{
// create media session object, this is needed to establish call connections
return this.Client.CreateMediaSession(
new AudioSocketSettings
{
StreamDirections = StreamDirection.Recvonly,
// Note! Currently, the only audio format supported when receiving unmixed audio is Pcm16K
SupportedAudioFormat = AudioFormat.Pcm16K,
ReceiveUnmixedMeetingAudio = true //get the extra buffers for the speakers
},
new VideoSocketSettings
{
StreamDirections = StreamDirection.Inactive
},
mediaSessionId: mediaSessionId);
}
catch (Exception e)
{
_logger.Log(System.Diagnostics.TraceLevel.Error, e.Message);
throw;
}
}
We are creating a bot that can join the team meeting and it can start the recording as it joins the team meeting. But we are getting this error(Expected not null
Parameter name: client). I am attaching the code below:
error facing:
enter image description here

fetch customerId for myApiClient

I am trying to fetch the customerId for myAPIClient using swift firebase and node.js. When I run my application it crashes and tells me that the customerId is nil. The reason that I am needing this customerId is in order to create the ephemeralKey.
The code that I use in node.js is,
exports.createEphemeralKey = functions.https.onRequest((req, res) => {
const stripe_version = req.body.api_version;
const customerId = req.body.customerId
if (!stripe_version) {
console.log('I did not see any api version')
res.status(400).end()
return;
}
stripe.ephemeralKeys.create(
{customer: customerId},
{stripe_version: apiVersion}
).then((key) => {
console.log("Ephemeral key: " + key)
res.status(200).json(key)
}).catch((err) => {
console.log('stripe version is ' + stripe_version + " and customer id is " + customerId + " for key: " + stripe_key + " and err is " + err.message )
res.status(500).json(err)
});
});
and the code that I am using inside of myApiClient is.
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
let url = self.baseURL.appendingPathComponent("ephemeral_keys")
let defaults = UserDefaults.standard
let customerId = defaults.string(forKey: "customerId")
AF.request(url, method: .post, parameters: [
"api_version": apiVersion, "customer_id": customerId!
])
.validate(statusCode: 200..<300)
.responseJSON { responseJSON in
switch responseJSON.result {
case .success(let json):
completion(json as? [String: AnyObject], nil)
case .failure(let error):
completion(nil, error)
}
}
}
I have been stuck on creating this ephemeralKey for a while now and have asked other questions but have not gotten a real answer. Is there anything that I am missing? How can I actually access the customersId from stripe inside of my ios project.
You should first check that let customerId = defaults.string(forKey: "customerId") provides a valid customerId. You can do so by debugging your app and inspecting the value of customerId. Assuming that it is valid, I see a couple of things that may be causing issues for you:
The Alamofire library that you are using has 2 Parameter Encoders with different properties and options. You can see them here.
My hypothesis is that you are nor passing the customerId correclty to the body of the request. I suggest you start by trying the JSONParameterEncoder. Your request would be somehting like this:
AF.request(url, method: .post, parameters: [
"api_version": apiVersion, "customer_id": customerId!
],
encoder: JSONParameterEncoder.default)
It is possible that you also have to tweak the way your retrieve the customerID from the Cloud Function according to how you pass the info from the swift app. This docs may provide some insights on how to parse HTTP request info

Call a smart contract function using INFURA

I can not understand how I can prepare the transaction, sign it and then send it using INFURA. I expect to get something wrong with the NodeJS code I posted. Can anyone kindly help me solve this problem?
Note: I am able to send the transaction on my smart contract, but the EVM reverts the execution.
Part of the Smart Contract (Solidity) (the function that I want to call):
function testFunction() public pure returns (string memory) {
return "testSuccess";
}
The function used in NodeJS:
const printRequestTransaction = async (gcodeHash) => {
log(`Preparing a transaction to register that the gcode has been sent`.yellow);
// With every new transaction send using a specific wallet address, it needed to increase a nonce which is tied to the sender wallet
let nonce = await web3.eth.getTransactionCount(web3.eth.defaultAccount);
// Fetch the current transaction gas prices from https://ethgasstation.info/
let gasPrices = await getCurrentGasPrices();
// Build a new transaction object and sign it locally
let details = {
"to": process.env.SMART_CONTRACT_ADDRESS,
//"value": web3.utils.toHex(web3.utils.toWei(amountToSend.toString(), 'ether')),
"gas": 210000,
"gasPrice": gasPrices.low * 1000000000, // converts the gwei price to wei
"nonce": nonce,
//"data": gcodeHash,
"function": "testFunction",
"chainId": 3 // EIP 155 chainId - mainnet: 1, ropsten: 3, rinkeby: 4 (https://ethereum.stackexchange.com/questions/17051/how-to-select-a-network-id-or-is-there-a-list-of-network-ids/17101#17101)
};
const transaction = new EthereumTx(details);
// This is where the transaction is authorized on Local PC behalf. The private key is what unlocks Local PC wallet.
transaction.sign(Buffer.from(process.env.WALLET_PRIVATE_KEY, 'hex'));
// Compress the transaction info down into a transportable object
const serializedTransaction = transaction.serialize();
// The Web3 library is able to automatically determine the "from" address based on Local PC private key
// Submit the raw transaction details to the provider configured above (INFURA)
const transactionHash = await web3.eth.sendSignedTransaction('0x' + serializedTransaction.toString('hex')).then(function (receipt) {
log(`result of the invokation: ${receipt})`.red);
return receipt.transactionHash;
}).catch((err) => {
log(`error occurred: ${err})`.red);
});
//const transactionHash = "exampleHash";
_transactionHash = transactionHash;
// Now it is known the transaction ID, so let's build the public Etherscan url where the transaction details can be viewed.
const url = `https://ropsten.etherscan.io/tx/${transactionHash}`;
log(url.cyan);
log(`Note: please allow for 30 seconds before transaction appears on Etherscan`.yellow)
};
Output:
error occurred: Error: Transaction has been reverted by the EVM:
{
"blockHash": "0x6205c1b8ce2fde3693e85843dce684ff11472e9df01fd850bc99e5771b3262d5",
"blockNumber": 5024524,
"contractAddress": null,
"cumulativeGasUsed": "0x50170f",
"from": "0x8882528C7104e146E0500203C353C09922575385",
"gasUsed": "0x5236",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x0",
"to": "0x1732089F6F6EeFA942871707c5AE1909B60774EB",
"transactionHash": "0xf748c9a6bdb19e776db4d8a9802d2bf5f8b588158da387029529249aca00a499",
"transactionIndex": 1
})
I would like to pay particular attention to the let details = {...}; object for which I am not very sure it is correct, I have a little confusion about this.

WebRTC - How to establish a peer connection after offers and answers

I have a node.js running which the users will connect to. The offer and answer will be generated and sent through node.js.
I'm trying to establish a peer connection and send over a camera stream. I tried my code without using ICE candidates as the computers where in the same subnet. I tried to implement ICE afterwards. I'm not sure if i've done it right though or if it's even needed if the computers are on the same subnet.
var localStream;
//Connect to signaling server
var signalingChannel = io.connect('http://85.134.54.193:8001');
console.log("Connect to signaling server");
var servers = null;
var video1;
var video2;
var audio1;
var audio2;
var cfg = {"iceServers":[{"url":"stun:stun.l.google.com:19302"}]};//{ "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] };
var con = { 'optional': [{'DtlsSrtpKeyAgreement': true}, {'RtpDataChannels': true }] };
var peerConnection;
//Runs after the page has been loaded
window.onload=function(){
//Gets ID for the video element which will display the local stream
video1 = document.getElementById("audio1");
//Gets ID for the video element which will display the remote stream
video2 = document.getElementById("audio2");
audio1 = document.getElementById("audio1");
audio2 = document.getElementById("audio2");
}
//Start button function
function caller(){
peerConnection = new webkitRTCPeerConnection(cfg);
navigator.webkitGetUserMedia({'audio':true, video:true}, function (stream) {
console.log("Got local audio", stream);
video1.src = window.webkitURL.createObjectURL(stream)
peerConnection.addStream(stream);
},
function ( err ) {
console.log( 'error: ', err );
});
console.log("Calling");
//Create Offer
peerConnection.createOffer(function (offerDesc) {
console.log("Created local offer", offerDesc.sdp);
peerConnection.setLocalDescription(offerDesc);
}, function () { console.warn("Couldn't create offer"); });
//ICE Candidates Generator
peerConnection.onicecandidate = function(evt) {
//When The Ice Gathering is complete
if (evt.target.iceGatheringState == "complete") {
//Create a new offer with ICE candidates
peerConnection.createOffer(function(offer) {
console.log("Offer with ICE candidates: " + offer.sdp);
signalingChannel.emit('offer', JSON.stringify(offer));
console.log("offer sent");
signalingChannel.on('answer', function(data){
console.log("Receive answer");
//The answer is set as the remote description for the offerer
peerConnection.setRemoteDescription(new RTCSessionDescription(JSON.parse(data)));
console.log("Set remote desc");
peerConnection.onaddstream = gotRemoteStream;
console.log("Add remote stream to peer connection");
});
});
}
}
}
function answerer(){
peerConnection = new webkitRTCPeerConnection(cfg);
navigator.webkitGetUserMedia({'audio':true, video:true}, function (stream) {
console.log("Got local audio", stream);
video1.src = window.webkitURL.createObjectURL(stream)
peerConnection.addStream(stream);
},
function ( err ) {
console.log( 'error: ', err );
});
console.log("Answering");
//Listen for offer
signalingChannel.on('offer', function(data){
console.log("Offer Received");
//Set the remote description from caller's local description
peerConnection.setRemoteDescription(new RTCSessionDescription(JSON.parse(data)));
//Generate answer after getting the remote description
peerConnection.createAnswer(function(sessionDescription) {
//Set local description
peerConnection.setLocalDescription(sessionDescription);
//The local desc will be the answer sent back to offerer
signalingChannel.emit('answer', JSON.stringify(sessionDescription));
console.log("Answer sent");
});
});
}
function gotRemoteStream(event){
video2.src = window.webkitURL.createObjectURL(event.stream);
}
Here is a sequence of events I have working today (Feb 2014) in Chrome. This is for a simplified case where peer 1 will stream video to peer 2.
Set up some way for the peers to exchange messages. (The variance in how people accomplish this is what makes different WebRTC code samples so incommensurable, sadly. But mentally, and in your code organization, try to separate this logic out from the rest.)
On each side, set up message handlers for the important signalling messages. You can set them up and leave them up. There are 3 core messages to handle & send:
an ice candidate sent from the other side ==> call addIceCandidate with it
an offer message ==> SetRemoteDescription with it, then make an answer & send it
an answer message ===> SetRemoteDescription with it
On each side, create a new peerconnection object and attach event handlers to it for important events: onicecandidate, onremovestream, onaddstream, etc.
ice candidate ===> send it to other side
stream added ===> attach it to a video element so you can see it
When both peers are present and all the handlers are in place, peer 1 gets a trigger message of some kind to start video capture (using the getUserMedia call)
Once getUserMedia succeeds, we have a stream. Call addStream on the peer 1's peer connection object.
Then -- and only then -- peer 1 makes an offer
Due to the handlers we set up in step 2, peer 2 gets this and sends an answer
Concurrently with this (and somewhat obscurely), the peer connection object starts producing ice candidates. They get sent back and forth between the two peers and handled (steps 2 & 3 above)
Streaming starts by itself, opaquely, as a result of 2 conditions:
offer/answer exchange
ice candidates received, exchanged, and added
When I want to change the stream, I go back to step 3 and set up a new peer connection object and do the whole offer/answer again.
Why do you wait for ICE to complete before creating an answer? what about doing them simultaneously? That might help, as it is just meant to work simultaneously. If you can post your logs after this when it would still not work we can try debugging it even further. If you want to see an audio-only example of this (it sends both music-audio and microphone-audio) check here, and the github source. Server made with node.js and ws plugin. The audio connection works with webRTC.

Handle XMPP presence with Node

I'm using the node-xmpp module to connect to a XMPP server and join a group chat. Connecting to the server, setting the presence, joining the room and reading out messages works so far. But I want to receive the userlist of the room too.
The XMPP protocol requires to send a presence stanza when the client enters the room (http://xmpp.org/extensions/xep-0045.html#enter-pres). But how can I now parse it in node?
My code currently looks like this:
var xmpp = require('node-xmpp');
// Create the XMPP Client
var cl = new xmpp.Client({
jid: jid,
password: password,
reconnect: true
});
// Do things when online
cl.on('online', function() {
util.log("We're online!");
// Set client's presence
cl.send(new xmpp.Element('presence', { type: 'available' }).c('show').t('chat'));
cl.send(new xmpp.Element('presence', { to: room_jid+'/'+room_nick }).c('x', { xmlns: 'http://jabber.org/protocol/muc' }).c('history', {seconds: 1}));
// Send keepalive
setInterval(function() {
cl.send(' ');
}, 30000);
cl.on('stanza', function(stanza) {
// always log error stanzas
if (stanza.attrs.type == 'error') {
util.log('[error] ' + stanza);
return;
}
// ignore everything that isn't a room message
if (!stanza.is('message') || !stanza.attrs.type == 'chat') {
return;
}
var body = stanza.getChild('body');
// message without body is probably a topic change
if (!body) {
return;
}
// Extract username
var from, room, _ref;
_ref = stanza.attrs.from.split('/'), room = _ref[0], from = _ref[1];
var message = body.getText();
// Log topics and messages to the console
if(!from) {
util.log('Topic: ' + message);
} else {
util.log('[' + from + ']: ' + message);
}
});
});
I already tried triggering presence by using
if(stanza.is('presence')) {}
within the cl.on('stanza') part but it doesn't work.
UPDATE: I'm describing a new method now which doesn't require the client to send requests.
Background: When the client joins a group chat, the server returns presence stanzas which contain information about the connected users to the group chat.
cl.on('stanza', function(stanza) {
// always log error stanzas
if (stanza.attrs.type == 'error') {
util.log('[error] ' + stanza);
return;
}
if(stanza.is('presence')){
// We are only interested in stanzas with <x> in the payload or it will throw some errors
if(stanza.getChild('x') !== undefined) {
// Deciding what to do based on the xmlns attribute
var _presXmlns = stanza.getChild('x').attrs.xmlns;
switch(_presXmlns) {
// If someone is joining or leaving
case 'http://jabber.org/protocol/muc#user':
// Get the role of joiner/leaver
_presRole = stanza.getChild('x').getChild('item').attrs.role;
// Get the JID of joiner/leaver
_presJID = stanza.getChild('x').getChild('item').attrs.jid;
// Get the nick of joiner/leaver
_presNick = stanza.attrs.from.split('/')[1];
// If it's not none, this user must be joining or changing his nick
if(_presRole !== 'none') {
// We are now handling the data of joinging / nick changing users. I recommend to use an in-memory store like 'dirty' [https://github.com/felixge/node-dirty] to store information of the users currentliy in the group chat.
} else {
// We are now handling the data of leaving users
}
break;
}
return;
}
return;
}
OLD METHOD
I previously described a method how to query the server for current users in the group chat. By maintaining a store where all user traffic (joining, leaving, nick changing) is stored, this is no longer required. However you could still use it to make sure the data is consistent by issues like a presence stanza was not delivered to the client correctly. That's the reason it's still described below:
To request a list with users connected to the room, you need to perform the following actions:
First send a request to the server and ask for the user list:
cl.send(new xmpp.Element('iq', {from: jid, to: room_jid, type: 'get' }).c('query', { xmlns: 'http://jabber.org/protocol/disco#items' }));
then listen for iq-stanzas, parse them and populate an array with the data:
// Catching the requested user list
if(stanza.is('iq')){
// Fetching usernames from return data (data structure: http://xmpp.org/extensions/xep-0045.html#example-12)
var _items = stanza.getChild('query').getChildren('item');
var users = new Array();
for(var i = 0; i<_items.length; i++) {
// We are building an object here to add more data later
users[i] = new Object();
users[i]['name'] = _items[i].attrs.name;
}
console.log(util.inspect(users, {depth: null, colors: true}));
return;
}
This will provide you with a user list. To request unique JIDs you have to probe every user. To keep the list up to date, you should remove users when they leave and add + probe when they join.

Resources