How to log peer messages in IPFS / libp2p in the browser? - ipfs

I did a test to see what would happen if I try getting a CID that doesn't exist, to see if I could continuously ask every peer in IPFS. It doesn't seem to work. I "connect" to around 10 peers, then it gives up. I'm not sure I'm actually "connecting" though. I'm not really sure what going on. I don't know that I'm actually connecting to any peer and asking them for a CID. I seem to connect to a star server, and see a list a peer, but not sure I ever connect to any of those peers.
Are there other events I should be listening to to debug what's going on? How can I listen to the messages I'm sending to peers?
My assumption from how Bittorrent DHT works is that, if a CID doesn't exist, I should eventually be asking every single peer on the network if they have this CID. Is there only 10 peers in the entire network? Or only 10 peers on that star server? Is there no star server discovery? How do I find a "busy" star server?
<script src="https://cdn.jsdelivr.net/npm/ipfs/dist/index.min.js"></script>
<script>
(async () => {
window.node = await Ipfs.create()
window.node.libp2p.on('peer:discovery', (peer) => console.log('peer:discovery', peer))
window.node.libp2p.on('peer:connect', peerInfo => console.log('peer:connect', peerInfo))
window.node.libp2p.on('peer:disconnect', peerInfo => console.log('peer:disconnect', peerInfo))
window.node.libp2p.peerStore.on('peer', (peerId) => console.log('peer', peerId))
window.node.libp2p.peerStore.on('change:multiaddrs', ({ peerId, multiaddrs}) => {
const addresses = []
for (const multiaddr of multiaddrs) {
addresses.push(multiaddr.buffer.toString())
}
console.log('change:multiaddrs', {peerId, multiaddrs, addresses})
})
window.node.libp2p.peerStore.on('change:protocols', ({ peerId, protocols}) => console.log('change:protocols', {peerId, protocols}))
window.node.libp2p.on('error', (err) => console.log('error', err))
window.node.libp2p.connectionManager.on('peer:connect', (connection) => {
console.log('connectionManager:peer:connect', {connection, remoteAddr: connection.remoteAddr.buffer.toString()})
})
window.node.libp2p.connectionManager.on('peer:disconnect', (connection) => console.log('connectionManager:peer:disconnect', connection))
// fake CID does not exist
results = window.node.get("QmZbj5ruYneZb8FuR9wnLuJCpCXMQudhSdWhdhp5U1oPWJ")
for await (res of results) {
for await (content of res.content) {
window.content = content
console.log(content.toString())
}
}
})()
</script>

js-ipfs does not have the DHT enabled by default at this moment. You need to explicitly enable it through the create options. That would result in the IPFS bitswap protocol to query the network for the content you are looking for. During the DHT query, as you also described, the peer will connect to other peers on the network according to the DHT algorithm, in order to find the content.
So, why do we have the DHT disabled by default? The JS DHT implementation was not scaling well for several reasons, some related to the DHT implementation itself and others like undiable nodes in the network and a not efficient connection manager. GO DHT was really improved a few months ago and the JS implementation will be completely refactored according to the recent changes in the GO implementation. The connection manager improvements are also on the way.
While the DHT is not refactored and stable, it is recommended to leverage delegate nodes. A delegate node is a node that will make content and peer routing queries on behalf of others. js-ipfs#0.48 was released with some public delegated nodes configured by default. The delegate nodes are GO nodes that will do the DHT queries on behalf of the JS nodes, this way, it will be the go node behind the scenes that will establish the connections with other nodes.
Let me know if I could answer your issue

Related

WebBluetooth GATT Characteric Notification Setup too slow - How to improve Setup time?

I try to connect to a BLE device (a LEGO Powered UP device) using WebBluetooth. I did this already using .NET/WinRT on the same notebook (works nicely) and now I try to write an adapter for using it in Blazor. The LEGO PoweredUp device implements a communication protocol using BLE GATT characteristic w/ Notifications and WriteValue.
As soon as the device is connecting, it is instantly sending a series of notifications (as kind of a response to the connect before) exposing information I need. I am able to setup the notification receiver fast enough in .NET using WinRT. However, with Chrome's WebBluetooth I only receive - depending on the timing/iteration - between the last 3 and 9 message (9 messages is expected). I guess this is just a regular race condition.
My Question: Is this expected? Can I do something against it?
Below a minimal viable test (which should return 9 messages when connected to a LEGO Technic Control+ Hub).
function writeToLog(x) {
console.log(x.target.value.buffer);
}
async function connectToLwpDevice() {
const serviceUuid = "00001623-1212-EFDE-1623-785FEABCD123".toLowerCase();
const characteristicUuid = "00001624-1212-EFDE-1623-785FEABCD123".toLowerCase();
const device = await navigator.bluetooth.requestDevice({ filters: [{ services: [serviceUuid] }] });
const connectedDevice = await device.gatt.connect();
const service = await connectedDevice.getPrimaryService(serviceUuid);
const characteristic = await service.getCharacteristic(characteristicUuid);
characteristic.addEventListener('characteristicvaluechanged', writeToLog);
await characteristic.startNotifications();
}
I don't believe there is anything that can be done about this at the moment. I've filed an issue against the Web Bluetooth specification to track the changes I believe are necessary in order to enable the reception of these notifications.

How to get IP address of WebRTC peer in IPFS / libp2p?

I'm using this code to play around with IPFS in the browser. I'm wondering how I can access the ip addresses of the webRTC peers? or even know if the peers are actually webRTC, or http peers?
<script src="https://cdn.jsdelivr.net/npm/ipfs/dist/index.min.js"></script>
<script>
(async () => {
window.node = await Ipfs.create({
config: {
Addresses: {
Swarm: []
},
Bootstrap: []
}
})
window.node.libp2p.on('peer:discovery', (peer) => console.log('peer:discovery', peer))
window.node.libp2p.on('peer:connect', peerInfo => console.log('peer:connect', peerInfo))
window.node.libp2p.on('peer:disconnect', peerInfo => console.log('peer:disconnect', peerInfo))
window.node.libp2p.peerStore.on('peer', (peerId) => console.log('peer', peerId))
window.node.libp2p.peerStore.on('change:multiaddrs', ({ peerId, multiaddrs}) => console.log('change:multiaddrs', {peerId, multiaddrs}))
window.node.libp2p.peerStore.on('change:protocols', ({ peerId, protocols}) => console.log('change:protocols', {peerId, protocols}))
window.node.libp2p.on('error', (err) => console.log('error', err))
window.node.libp2p.connectionManager.on('peer:connect', (connection) => console.log('peer:connect', connection))
window.node.libp2p.connectionManager.on('peer:disconnect', (connection) => console.log('peer:disconnect', connection))
const data = 'Hello'
const results = await window.node.add(data)
console.log({results})
})()
</script>
node.swarm.peers() gives me a list of peers, but it doesn't seem to include the IP address.
Looking at your config, it seems that you did not configure any swarm address. A swarm address must be configured for your peer to be diable from other peers in the network.
Some context, in this specific case, you are dealing with a browser environment. Currently, Browsers do not allow listening for connections. One of the limitations is actually that a browser does not provide an "IP Address" that someone can use to reach to it. They are designed in a client-server model where the server IP Address is known and the client will establish the connection with it.
As one of the ways to solve the issue above, there is libp2p-webrtc-star transport. It basically uses a server that will be responsible for listening for connections on behalf of browser nodes. You can use one of the available servers for experimenting https://github.com/libp2p/js-libp2p-webrtc-star#hosted-rendezvous-server. Basically, you should add to your swarm addresses, a multiaddr such as /dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star. Once your node starts, your browser node will establish a connection with the server and the server will inform all the other peers about the peer who joined. These peers can dial your peer via this star server and the multiaddrs will look something like: /dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star/p2p/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM.
With this setup, you should be able to open multiple browsers and peers discover and connect to each other. If you run the ipfs.swarm.peers you should see the addresses of the other nodes peers via the star server.
I hope this helps you move forward. I also highly recommend you to check the following examples:
https://github.com/ipfs/js-ipfs/tree/master/examples/browser-exchange-files
https://github.com/libp2p/js-libp2p/blob/master/examples/libp2p-in-the-browser
As a complement, there are a few new features being worked on that aim to improve the browser experience in this regard. You can follow the developments on: https://github.com/libp2p/js-libp2p/issues/703

How to query all services (groups) from an iotagent

We have a hard time understanding the meaning of the different hierarchy levels provided by the iotagent. There is the fiware-service, the fiware-servicepath and underneath it is a bunch of services that in turn have a bunch of devices associated.
Now we understood how to query for all devices and all services underneath a given fiware-service and fiware-servicepath. We also understood how to query for all fiware-servicepaths given a certain fiware-service. But how to query for all those "top level" fiware-services?
Our goal is to create a device management user interface which enables an end user to provision and unprovision the devices he is managing. Maybe we have a misconception of the fiware-service here but since one can add such services with a certain POST request our expectation would be that we can somehow query for all those services. What are we missing?
If there really is no way to query the top level services, I'd like to ask for the reasoning of this as I cannot find that in the docs.
Under NGSI-v2, all context brokers are implicitly multitenant. Using a different fiware-service for your provisioned devices should imply that the devices and their data are owned by separate business concern, so there should be no need to retrieve and combine provisioned devices from separated concerns.
When using the mongo-DB option with an IoT Agent, the fiware-service helps to provide a unique database name for each tenanted service.
There should be no need to combine the IoT Agent data (services and devices), however there may be a valid use case for combining Context Data coming from separate Tenants (after securing legal agreement from each party of course) - in this case you could create a simple proxy handler which is capable of handling the /v2/op/query and/or /v2/op/update endpoints and forwarding the request with amended headers.
const express = require('express');
const router = express.Router();
const request = require('request-promise');
const BASE_PATH =
process.env.CONTEXT_BROKER || 'http://localhost:1026/v2';
function forwardRequest(req, res) {
// Add necessary validation
const headers = req.headers;
headers['fiware-service' : 'XXXX'];
headers['fiware-servicepath': 'YYYY'];
headers['Accept': 'application/json'];
const options = {
url: BASE_PATH + req.path,
method: req.method,
headers,
json: true
};
request(options)
.then(async function(cbResponse) {
return res.send(compacted);
})
.catch(function(err) {
return res.send(err);
});
}
router.post(
'/op/query', forwardRequest
);

How can I use socket io for just server client communication?

I would like to build a web page with interactive content.
I would like to use socket IO, but I've got a problem when I send two number from client to the server.
The server adds the two numbers and sends for every user (but I want just send back to the one user). I wouldn't like to store the users. So I would like to ask how can I build this example using NodeJS and Socket IO?
if you look into socket.io docs, you will find out that you can specify the key for each message you send, for instance:
io.on('connection', function (socket) {
// specify a clientId for each client you have, you may define it at the moment the connection starts.
socket.emit(`news:${clientId}`, { hello: 'world' });
});
And, on the client side, you have:
socket.on(`news:${myClientId}`, function (data) {
console.log(data);
});
You may generate this id randomly by using many libraries, for instance Node-Forge.
Hope it helps! Feel free to ask further.

Errors when ICE Candidates are received before answer is sent

I'm establishing WebRTC connection on Chrome 23. To attach local stream you need to allow browser to use the camera and microphone. On the caller side I'm checking if local stream can be obtained and I'm not sending offer until this moment. Then offer is sent and the browser immediately starts to send ICE candidates.
Then if remote browser didn't obtained local media stream yet I get SYNTAX_ERR: DOM Exception 12 on peerConnection.addIceCandidate(candidate) for every ICE candidate received.
I checked documentation on addIceCandidate but there is no info about prerequisites.
I think I can delay sending ICE candidates from offerer by delaying it and waiting for signal that remote client added local stream, but this is additional communication needed and doesn't look right.
Can I somehow add remote ICE candidates to webkitRTCPeerConnection before answer is sent and local media stream is attached?
After I wrote this question an answer came into my mind... There is no need for attaching local stream before receiving ICE candidates, but remoteDescription should be set (which should be done at the moment of receiving offer). In my code I waited with setting remoteDescription and sending answer until browser gets the local stream.
The solution from Episodex helped me.
First setRemoteDescription, then create own stream, then create and send the answer.
// On read message
if (msg.sdp.type === 'offer') {
this.peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp))
.then(() => navigator.mediaDevices.getUserMedia({audio: true, video: true}))
.then(stream => this.peerConnection.addStream(stream));
.then(() => this.peerConnection.createAnswer())
.then(answer => this.peerConnection.setLocalDescription(answer))
.then(() => this.sendMessage({sdp: this.peerConnection.localDescription}))
}