SDK for client and server
npm install sp8de-client-sdk
For use NPM-package in applications, you need to add references to cryptographic libraries to the index.html file:
<script src="https://www.mobilefish.com/scripts/ethereumjs/ethereumjs-util.js"></script>
<script src="https://cdn.ethers.io/scripts/ethers-v3.min.js"></script>
Run creating js-bundle
npm run tocdnmin
Then include sp8deSDK.js in the HTML pages.
<script src="sp8deSDK.js"></script>
doc
: Run generate documentation from jsdoc
test
: Run karma test
dev
: Run watching ts-generator and karma tests
tocdnmin
: Create single file with browserify and minification
import {Sp8deClientSDK} from 'sp8de-client-sdk';
// or
const Sp8deClientSDK = require('sp8de-client-sdk');
const sp8deClientSDK = new Sp8deClientSDK.Methods();
const seed = sp8deClientSDK.generateSeed();
// returns seed
const seed = sp8deClientSDK.generateSeed();
// returns seed
Methods for storing wallet in the storage use the choice between wallets
and user
keys in localstorage (for situations where the user-object in the application).
If in localstorage has a user
key, then wallet will be added to him in key wallets
.
The SDK contains functions that allow you to perform signature signing operations, verify signatures of other services. Also contains methods for adding created wallets to localstorage.
public init() {
// Check wallets in storage
if (this.sp8deClientSDK.isWalletsInStorage()) {
// Get wallet and decript him
from(this.sp8deClientSDK.decryptWallet(this.sp8deClientSDK.getActiveWalletFromStorage(), this.password))
.pipe(
map(item => <any>item)
)
.subscribe(wallet => {
// Save private key
this.privateKey = wallet.privateKey;
// Generate public key
this.pubKey = this.sp8deClientSDK.getPubKey(this.privateKey);
});
} else {
// Generate wallet containing private key.
let wallet;
this.privateKey = wallet.privateKey;
// Generate public key from private key
this.pubKey = this.sp8deClientSDK.getPubKey(this.privateKey);
// Encrypting the wallet before uploading it to the storage for data security.
from(this.sp8deClientSDK.encryptWallet(this.sp8deClientSDK.generateWallet(), password))
.pipe(
map(item => <any>item)
)
.subscribe(res => {
// Save encrypted wallet to localstorage
this.sp8deClientSDK.addWalletToStorage(res);
});
}
}
// Getting wallet from storage
let storageWallet = this.sp8deClientSDK.getActiveWalletFromStorage();
// Decrypt wallet for edxecute private key
this.sp8deClientSDK.decryptWallet(storageWallet, password).then(decryptRes => {
// Getting private key
this.privateKey = decryptRes.privateKey
})
// set current date in milliseconds as nonce
const nonce = +(new Date());
// generate seed
const seed = this.sp8deClientSDK.generateSeed();
// signing message
const sign = this.sp8deClientSDK.signMessage({privateKey: this.privateKey, seed: seed, nonce: nonce});
Validating random number from array-seed
public validateWin(array: number[], serverNumber: number[]): boolean {
// generate random number from array
const clientNumber = this.getRandomFromArray({array: array, min: 1, max: 6, length: winNumber.length});
if (!clientNumber) {
console.error('Server value invalid!');
return;
}
return clientNumber[0] === serverNumber[0];
}
- Sp8deClientSDK
- .generatePrivateKey() ⇒
number
- .generateWallet() ⇒
object
- .encryptWallet(wallet, password) ⇒
promise
- .decryptWallet(wallet, password) ⇒
promise
- .getPubKey(privateKey) ⇒
string
- .generateArraySeed(signs) ⇒
Array.<number>
- .getHash(string) ⇒
ArrayBuffer
- .generateArrayFromHash(hash) ⇒
Array.<number>
- .getRandomFromArray(parameters) ⇒
Array.<number>
- .generateSeed() ⇒
number
- .signMessage(parameters) ⇒
string
- .validateSign(parameters) ⇒
boolean
- .addWalletToStorage(value, storageWallets)
- .removeLastWalletFromStorage(storageWallets)
- .clearWalletStorage(storageWallets)
- .getActiveWalletFromStorage(Wallets) ⇒
string
- .getWalletsListFromStorage(storageWallets) ⇒
Array.<string>
- .isWalletsInStorage(storageWallets) ⇒
boolean
- .generatePrivateKey() ⇒
Returns a new private key
Returns: number
- A public key of 66 characters long
Returns a new wallet
Returns: object
- Object contains wallet
Returns a new wallet
Returns: promise
- promise with json
Param | Type | Description |
---|---|---|
wallet | object |
object with wallet |
password | string |
password |
Returns a new wallet
Returns: promise
- promise with json
Param | Type | Description |
---|---|---|
wallet | string |
Encrypted JSON with wallet |
password | string |
password |
Returns the public key obtains from the private key
Returns: string
- A public key of 42 characters long
Param | Type | Description |
---|---|---|
privateKey | string |
private key |
Returns an seed-array (use keccak-384 algorithm)
Returns: Array.<number>
- An array containing random numbers
Param | Type | Description |
---|---|---|
signs | Array.<string> |
array of signs |
Returns an hash from string
Returns: ArrayBuffer
- An ArrayBuffer contains hash
Param | Type |
---|---|
string | string |
Returns an array of Uint32 numbers from hash
Returns: Array.<number>
- array of of Uint32 numbers
Param | Type |
---|---|
hash | ArrayBuffer |
Returns an array of random numbers from seed-array (use mt19937 algorithm)
Returns: Array.<number>
- An array of length given by a "count" containing random numbers
Param | Type | Description |
---|---|---|
parameters | Object |
{array: [], min: number, max: number, count: number} |
Returns a random number to use as a seed
Returns: number
- Random seed number. Length 9-10
Signs a message from privateKey, seed, nonce. Returns message signed with private key.
Returns: string
- Message signed with private key
Param | Type | Description |
---|---|---|
parameters | Object |
{privateKey: string, seed: number, nonce: number} |
Validates the message. Use sign, nonce, public key and seed. Returns true if the validation was successful.
Returns: boolean
- True if successful, false if unsuccessful
Param | Type | Description |
---|---|---|
parameters | Object |
{sign: string, pubKey: string, seed: number, nonce: number} |
Add to localstorage to key Wallets in key "User" or root. If user without field "Wallets" add it.
Param | Type | Description |
---|---|---|
value | string |
Private key |
storageWallets | object | array |
optional. Object wallet contained in storage |
Removing last private key from array in localstorage
Param | Type | Description |
---|---|---|
storageWallets | object | array |
optional. Object wallet contained in storage |
Clear array of private keys (delete key from localstorage
Param | Type | Description |
---|---|---|
storageWallets | object | array |
Object wallet contained in storage) |
Returns active private key in localstorage
Returns: string
- Active private key or null if no array
Param | Type | Description |
---|---|---|
Wallets | array |
optional. Array wallets contained in storage |
Returns array of string contains all private keys from localstorage
Returns: Array.<string>
- Array of private keys or null if no array
Param | Type | Description |
---|---|---|
storageWallets | object | array |
optional. Object wallet contained in storage |
Check if there are keys in vault
Returns: boolean
- True if there is, false is not
Param | Type | Description |
---|---|---|
storageWallets | object |
optional. User in storage, if it there is |
Next, we show how you can create a minimal-functional game "Dices" on Angular2,
in which the library sp8de-client-sdk
will be used to ensure the reliability of
gaming operations.
For use sp8de-client-sdk
functions it is necessary install him from NPM:
npm install sp8de-client-sdk
And for Angular2 + applications, you need to add references to cryptographic libraries to the index.html file:
<script src="https://www.mobilefish.com/scripts/ethereumjs/ethereumjs-util.js"></script>
<script src="https://cdn.ethers.io/scripts/ethers-v3.min.js"></script>
In module.ts
need to import module sp8de-client-sdk
and add to the array providers
:
import {Sp8deClientSDK} from 'sp8de-client-sdk';
@NgModule({
...
providers: [Sp8deClientSDK]
...
})
Create a service containing a cryptographic functions.
ng g s crypto
In the service import sp8de-client-sdk
and declare it in the constructor.
import {Sp8deClientSDK} from 'sp8de-client-sdk';
...
constructor(public sp8deClientSDK: Sp8deClientSDK) {
}
Implements methods of the game.
Method creates a private key, if it is not in storage and generates a public key for further work:
public init() {
// Check wallets in storage
if (this.sp8deClientSDK.isWalletsInStorage()) {
// Get wallet and decript him
from(this.sp8deClientSDK.decryptWallet(this.sp8deClientSDK.getActiveWalletFromStorage(), this.password))
.pipe(
map(item => <any>item)
)
.subscribe(wallet => {
// Save private key
this.privateKey = wallet.privateKey;
// Generate public key
this.pubKey = this.sp8deClientSDK.getPubKey(this.privateKey);
});
} else {
// Generate wallet containing private key.
let wallet;
this.privateKey = wallet.privateKey;
// Generate public key from private key
this.pubKey = this.sp8deClientSDK.getPubKey(this.privateKey);
// Encrypting the wallet before uploading it to the storage for data security.
from(this.sp8deClientSDK.encryptWallet(this.sp8deClientSDK.generateWallet(), password))
.pipe(
map(item => <any>item)
)
.subscribe(res => {
// Save encrypted wallet to localstorage
this.sp8deClientSDK.addWalletToStorage(res);
});
}
}
Return values, required for the request to start game:
public generateCryptoParameters(): any {
// Create object
// Wtite current date to nonce, current public key to pubKey
// and generate seed
const resultParameters = {
nonce: +(new Date()),
seed: this.sp8deClientSDK.generateSeed(),
sign: '',
pubKey: this.pubKey
},
signParameters = {
privateKey: this.privateKey,
seed: resultParameters.seed,
nonce: resultParameters.nonce
};
// Signing message
resultParameters.sign = this.sp8deClientSDK.signMessage(signParameters);
return resultParameters;
}
Check the random value returned from server at the end of game. Accept servers array and parameters of game:
public getRandomFromArray(parameters: { array: any[]; min: number, max: number, length: number }): number[] {
return this.sp8deClientSDK.getRandomFromArray(parameters);
}
Uses the previous method to validate server values. Accepted server array and random number.
public validateWin(array: number[], serverNumber: number[]): boolean {
const clientNumber = this.getRandomFromArray({array: array, min: 1, max: 6, length: serverNumber.length});
if (!clientNumber) {
console.error('Server value invalid!');
return;
}
return clientNumber[0] === serverNumber[0];
}
Validate correctness of the information transmitted by the server about other players.
public validatePlayers(response: GameFinishResponse): boolean[] {
const result: boolean[] = [];
for (const item of response.items) {
result.push(this.sp8deClientSDK.validateSign({
sign: item.sign,
pubKey: item.pubKey,
seed: item.seed,
nonce: item.nonce
}
));
}
return result;
}
We will create a service containing functions that work with the api and control the beginning and end of the game.
ng g s game
Implements methods for work the game.
Through api sends a request with parameters of start the game.
public startGame(parameters: GameStartRequest): Observable<GameStartResponse> {
return this.api.apiDemoGameStartPost(parameters)
.pipe(
map(
item => {
return <GameFinishResponse>item;
}));
}
To request parameters, use the following interface:
export interface GameStartRequest {
type?: GameStartRequest.TypeEnum;
bet?: Array<number>;
betAmount?: number;
pubKey?: string;
sign?: string;
nonce?: number;
}
Through api sends a request with parameters of start the game.
public endGame(parameters: GameFinishRequest): Observable<GameFinishResponse> {
return this.api.apiDemoGameEndPost(parameters).pipe(
map(
item => {
return <GameFinishResponse>item;
}));
}
To request parameters, use the following interface:
export interface GameFinishRequest {
gameId?: string;
pubKey?: string;
sign?: string;
seed?: number;
nonce?: number;
}
Let's create the logic of the component. Set user parameters on initialize the component and write logic of game.
On initialisation create public and private key for the user.
ngOnInit() {
this.cryptoService.init();
}
The logic of the game is as follows:
- In the event parameter, values relating to the game conditions are passed. This is the type of the game, the selected dices values and the bet.
- In the method create parameters, necessary for cryptographic operations. This is a public key, a signed message and a nonce.
- These parameters are collected together and sent to the server as a request to start games.
The server returns an object in which the following fields are required for the game:
- gameId — game identification
- items — array with the data of other players
- Send to the server request to the end of game with the previously obtained cryptographic parameters and gameId.
- Server should send a response, that contains the following information:
- isWinner — did the player win
- winNumbers — dropped dices
- winAmount — amount of winnings
- items — array with data from other players
- sharedSeedArray — seed-array from which a random value of the dropped dice is generated. The last two values can be validated on the client.
- We'll find out whether the data is correct by running the
sp8de-client-sdk
library to validate data about other players and the dropped value
Below is the code of the method that implements this logic, using previously created services:
public start(event): void {
const cryptoParameters = this.cryptoService.generateCryptoParameters();
this.gameService.startGame({
type: event.type,
bet: event.currentBet,
betAmount: event.currentBetAmount,
pubKey: cryptoParameters.pubKey,
sign: cryptoParameters.sign,
nonce: cryptoParameters.nonce
}).pipe(
switchMap(startResponse => {
return this.gameService.endGame({
gameId: startResponse.gameId,
sign: cryptoParameters.sign,
seed: cryptoParameters.seed,
nonce: cryptoParameters.nonce,
pubKey: cryptoParameters.pubKey
});
})).subscribe(res => {
const validWin = this.cryptoService.validateWin(res.sharedSeedArray, res.winNumbers),
validItems = this.cryptoService.validatePlayers(res),
win = this.checkWin(res.winNumbers, event.currentBet);
console.log('Validate items: ', validItems);
console.log('Validate win: ', validWin);
console.log(win ? `You win ${res.winAmount}` : 'You lose');
},
error => {
console.error(error.message);
});
}
Now you can call method start(event) with game parameters and get to the console result:
js:
start({
type: 'Dice',
bet: [1, 2],
betAmount: 100
})
console:
Validate items: true
Validate win: [true, true, true]
You win 50