An open API service indexing awesome lists of open source software.

https://github.com/diva-exchange/i2p-sam

I2P SAM: peer-to-peer communication between applications over I2P
https://github.com/diva-exchange/i2p-sam

crypto diva i2p p2p peer-to-peer sam

Last synced: 5 months ago
JSON representation

I2P SAM: peer-to-peer communication between applications over I2P

Awesome Lists containing this project

README

          

# I2P SAM
An I2P SAM library: enabling applications to communicate through the I2P network.

Long story short: I2P is an anonymous network layer allowing censorship-resistant and end-to-end-encrypted communication. I2P is a fully distributed, "privacy-by-design" peer-to-peer network.

To get I2P up and running, take a look at the project: https://github.com/diva-exchange/i2p

## Use Cases
I2P is an instantly available peer-to-peer network which can be used for things like:
* chat, social media and alike - all private and secure
* distributed databases, aka blockchains (see https://testnet.diva.exchange as an example)
* gaming, file sharing and ... whatever else you come up with

I2P is fully distributed, well researched and gets further developed by a competent community.

This I2P SAM library helps developers to create an I2P application quickly and hassle-free.

## Get Started

`npm i @diva.exchange/i2p-sam`

or, lighter, without developer dependencies:

`npm i --omit dev @diva.exchange/i2p-sam`

## Quick Start - Examples

### How to Use Streams

Send an HTTP GET request to diva.i2p and output the response:

```
import { createStream } from '@diva.exchange/i2p-sam';

(async () => {

const s = await createStream({
stream: {
destination: 'diva.i2p'
},
sam: {
// your local I2P SAM host,
// like 172.19.74.11 if you use the given test
// docker container (see "Unit Tests" below)
host: '127.0.0.1',
// your local I2P SAM port, this is the default
portTCP: 7656
},
});
s.on('data', (data: Buffer) => {
console.log('Incoming Data: ' + data.toString());
});

s.stream(Buffer.from('GET /hosts.txt HTTP/1.1\r\nHost: diva.i2p\r\n\r\n'));

})();
```

Forward incoming streaming data to a local socket server:

```
import { createStream, createForward, I2pSamStream } from '@diva.exchange/i2p-sam';
import net from 'net';

(async () => {
const serverForward = net.createServer((c) => {
console.debug('client connected');
c.on('end', () => {
console.debug('client disconnected');
});
c.on('data', (data: Buffer) => {
console.debug(data.toString());
c.write(`Hello Client!\n`);
});
});
serverForward.listen(20222, '127.0.0.2');

const samForward: I2pSamStream = await createForward({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
},
forward: {
host: '127.0.0.2', // your local listener, see above
port: 20222, // your local listener, see above
},
});

const samClient: I2pSamStream = await createStream({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
},
stream: {
destination: samForward.getPublicKey()
},
});
// event handler
samClient.on('data', (data: Buffer) => {
console.debug(data.toString());
});
// send some data to destination
samClient.stream(Buffer.from(`Hi Server!\n`));
})();
```

### How to Use Reply-able Datagrams

NOTE: reply-able datagrams contain the origin of the data. An "origin" is defined as the public key of a node in the I2P network.

Send reply-able UDP messages from peer **A** to peer **B** through the I2P network:

```
import { createDatagram, toB32 } from '@diva.exchange/i2p-sam';

(async () => {
// instantiate Peer A
const peerA = await createDatagram({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
}
});

// instantiate Peer B
const peerB = await createDatagram({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
},
listen: {
address: '127.0.0.1', // udp listener
port: 20202 // udp listener
}
}).on('data', (data: Buffer, from) => {
console.debug(`Incoming Data from ${toB32(from)}: ${data.toString()}`);
});


// send 100 messages via UDP, every 500ms a message
// IMPORTANT: UDP is not reliable. Some messages might get lost.
const msg: string = 'Hello World';
await new Promise((resolve) => {
let t = 0;
const i = setInterval(() => {
peerA.send(peerB.getPublicKey(), Buffer.from(`${t} ${msg}`));
if (t++ >= 100) {
clearInterval(i);
resolve(true);
}
}, 500);
});
})();
```

### How to Use Raw Datagrams

NOTE: raw datagrams do not contain the "origin" of the data. A typical use case for raw datagrams: broadcasting of data. Raw datagrams are lean.

Send raw UDP messages from peer **A** to peer **B** through the I2P network:

```
import { createRaw } from '@diva.exchange/i2p-sam';

(async () => {
// instantiate Peer A
const peerA = await createRaw({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
}
});

// instantiate Peer B
const peerB = await createRaw({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
},
listen: {
address: '127.0.0.1', // udp listener
port: 20202 // udp listener
}
}).on('data', (data: Buffer) => {
console.log('Incoming Data: ' + data.toString());
});

// send 100 messages via UDP, every 500ms a message
// IMPORTANT: UDP is not reliable. Some messages might get lost.
const msg: string = 'Hello Peer B - I am Peer A';
await new Promise((resolve) => {
let t = 0;
const i = setInterval(() => {
peerA.send(peerB.getPublicKey(), Buffer.from(`${t} ${msg}`));
if (t++ >= 100) {
clearInterval(i);
resolve(true);
}
}, 500);
});
})();
```

## API

### getPublicKey(): string

Get the public key of the local destination.

Example:

```
import { createDatagram } from '@diva.exchange/i2p-sam';

createDatagram({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
}
}).then((sam) => console.log(sam.getPublicKey()));
```

### getPrivateKey(): string

Get the private key of the local destination.

Example:

```
import { createDatagram } from '@diva.exchange/i2p-sam';

createDatagram({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
}
}).then((sam) => console.log(sam.getPrivateKey()));
```

### getKeyPair(): { public: string, private: string }

Get the public and private key of the local destination.

Example:

```
import { createStream } from '@diva.exchange/i2p-sam';

createStream({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
},
stream: {
destination: 'diva.i2p'
},
}).then((sam) => console.log(sam.getKeyPair()));
```

### close()

Close a SAM connection.

Example:

```
import { createRaw } from '@diva.exchange/i2p-sam';

(async () => {
const sam = await createRaw({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
}
});

sam.close();
})();
```

### toB32(destination: string): string

Convert a destination to a b32 address (without any extensions - just a Base32 string).

Example:

```
import { toB32 } from '@diva.exchange/i2p-sam';

console.log(toB32('[some base64-encoded destination]'));
```

### createLocalDestination(c: Configuration): Promise\<{ address: string, public: string, private: string }\>

Create a new local destination and return its properties.

Example:

```
import { createLocalDestination } from '@diva.exchange/i2p-sam';

createLocalDestination({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
}
}).then((obj) => console.log(obj));
```

### lookup(c: Configuration, name: string): Promise\

Lookup (aka resolve) an I2P address (like diva.i2p or also a .b32.i2p address) to a destination. The destination, which is the public key, is a base64 encoded string.

Example:

```
import { lookup } from '@diva.exchange/i2p-sam';

lookup({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
}
}, 'diva.i2p').then((dest) => console.log(dest));
```

### stream(msg: Buffer)

Example: see the _Get Started: How to Use Streams_ above.

### send(destination: string, msg: Buffer)

Example: see _Get Started: How to Use Datagrams_ above.

### Configuration and its Defaults
```
type tSession = {
id?: string;
options?: string;
};

type tStream = {
destination: string;
};

type tForward = {
host: string;
port: number;
silent?: boolean;
};

type tListen = {
address: string;
port: number;
hostForward?: string;
portForward?: number;
};

type tSam = {
host: string;
portTCP: number;
portUDP?: number;
versionMin?: string;
versionMax?: string;
publicKey?: string;
privateKey?: string;
timeout?: number;
};

export type Configuration = {
session?: tSession;
stream?: tStream;
forward?: tForward;
listen?: tListen;
sam?: tSam;
};

type ConfigurationDefault = {
session: tSession;
stream: tStream;
forward: tForward;
listen: tListen;
sam: tSam;
};

const DEFAULT_CONFIGURATION: ConfigurationDefault = {
session: {
id: '',
options: '',
},
stream: {
destination: '',
},
forward: {
host: '',
port: 0,
silent: false,
},
listen: {
address: '127.0.0.1',
port: 0,
hostForward: '',
portForward: 0,
},
sam: {
host: '127.0.0.1',
portTCP: 7656,
portUDP: 7655,
versionMin: '',
versionMax: '',
publicKey: '',
privateKey: '',
timeout: 300,
},
};
```

### Events

#### data
Incoming data.

#### error
Generic Error event - emitted if sockets report errors.

```
import { createRaw } from '@diva.exchange/i2p-sam';

(async () => {
const sam = await createRaw({
sam: {
host: '127.0.0.1', // your local I2P SAM host
portTCP: 7656 // your local I2P SAM port
}
});
sam.on('error', (error) => console.debug(error));
})();
```

#### close
Emitted if one of the involved sockets got closed.

## How to Run Unit Tests

Assumptions:
1. git, node and npm is available.
2. docker and docker-compose is available.

Clone the source code from git `git clone https://github.com/diva-exchange/i2p-sam.git` and enter the folder `i2p-sam`.

Prepare the test environment by creating the docker container:

```
docker compose -f test/sam.diva.i2p.yml up -d
```

Check whether the I2P test node is properly running by accessing the local console on: http://172.19.74.11:7070.

To modify the IP address of the local console, adapt the file `test/sam.diva.i2p.yml`.

After the docker container is _running for about five minutes_ (reason: the I2P network needs some minutes to integrate), execute the unit tests:

```
npm run test
```
Executing the unit tests will take around 5 minutes. Reason: the communication via I2P gets tested - which is the purpose of this library.

Stop the container (and purge all data within):
```
docker compose -f test/sam.diva.i2p.yml down --volumes
```

## Linting

To lint the code, use
```
npm run lint
```

## Contributions
Contributions are very welcome. This is the general workflow:

1. Fork from https://github.com/diva-exchange/divachain/
2. Pull the forked project to your local developer environment
3. Make your changes, test, commit and push them
4. Create a new pull request on github.com

It is strongly recommended to sign your commits: https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key

If you have questions, please just contact us (see below).

## Donations

Your donation goes entirely to the project. Your donation makes the development of DIVA.EXCHANGE faster. Thanks a lot.

### XMR

42QLvHvkc9bahHadQfEzuJJx4ZHnGhQzBXa8C9H3c472diEvVRzevwpN7VAUpCPePCiDhehH4BAWh8kYicoSxpusMmhfwgx

![XMR](https://www.diva.exchange/wp-content/uploads/2020/06/diva-exchange-monero-qr-code-1.jpg)

or via https://www.diva.exchange/en/join-in/

### BTC

3Ebuzhsbs6DrUQuwvMu722LhD8cNfhG1gs

![BTC](https://www.diva.exchange/wp-content/uploads/2020/06/diva-exchange-bitcoin-qr-code-1.jpg)

## Contact the Developers

On [DIVA.EXCHANGE](https://www.diva.exchange) you'll find various options to get in touch with the team.

Talk to us via [Telegram](https://t.me/diva_exchange_chat_de) (English or German).

## References

SAM docs: https://geti2p.net/en/docs/api/samv3

I2Pd: https://i2pd.readthedocs.io/

## License

[APACHE 2.0](https://github.com/diva-exchange/i2p-sam/blob/main/LICENSE)