Skip to content

WavesChat script example #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ride4dapps/waveschat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# WavesChat
WavesChat is a decentralized messenger with an end-to-end encryption
Basically works like a chat-roulette with an
1. Optional channel
2. Optional payment, which forces to pay the same amount to talk with you

Try now at https://chat.waves.today (Requires Waves Keeper **with InvokeScript support in TESTNET mode**)

# How it works
1. First user (Bob) invokes script function `init(bobPublicKey)`
2. Second user (Alice) invokes `init(alicePublicKey)`
3. Script matches Bob's and Alice's public keys, so that they can get the recipients public key from data entry `Base58(Blake2b256(publicKey))`
4. Bob gets Alice's public key, encrypts message with it, and then invokes `sendMessage(bobPublicKey, cipherText)`
5. Bob to Alice conversation id is `Base58(Blake2b256(bobPublicKey + alicePublicKey))`, so Alice can get message count from `conversationId + "_n"` and messages from `conversationId + "_" + 0...msgCount` data entries accordingly, and decrypt it with her private key
83 changes: 83 additions & 0 deletions ride4dapps/waveschat/waveschat.ride
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{-# STDLIB_VERSION 3 #-}
{-# CONTENT_TYPE DAPP #-}
{-# SCRIPT_TYPE ACCOUNT #-}

let defaultAmount = 0
let maxAmount = 5_0000_0000

@Callable(invoke)
func init(publicKey: ByteVector, channel: String) = {
let publicKeyHash = toBase58String(blake2b256(publicKey))
let channelId = toBase58String(blake2b256(toBytes(channel)))
let currentKey = "current_" + channelId
let currentAmountKey = "current_amount_" + channelId
let isPublicChannel = size(channel) < 6

let pmtAmount = match invoke.payment {
case pmt:AttachedPayment =>
if (isDefined(pmt.assetId)) then 0
else pmt.amount
case _ =>
0
}

let requiredAmount = match getInteger(this, currentAmountKey) {
case currentAmount: Int => currentAmount
case _ => 0
}

let newRequiredAmount = if (isPublicChannel && pmtAmount >= maxAmount) then maxAmount else pmtAmount

if (pmtAmount < requiredAmount)
then throw("Init requires " + toString(requiredAmount / 100000000) + " waves")
else {
match getBinary(this, publicKeyHash + "_owner") {
case b: ByteVector =>
if (b != invoke.caller.bytes) then throw("Public key is not yours")
else {
match getBinary(this, currentKey) {
case left:ByteVector =>
let leftHash = toBase58String(blake2b256(left))
WriteSet([DataEntry(publicKeyHash + "_owner", invoke.caller.bytes), DataEntry(leftHash, publicKey), DataEntry(publicKeyHash, left), DataEntry(currentKey, 0), DataEntry(currentAmountKey, defaultAmount)])

case _ =>
WriteSet([DataEntry(publicKeyHash + "_owner", invoke.caller.bytes), DataEntry(currentKey, publicKey), DataEntry(currentAmountKey, newRequiredAmount)])
}
}

case _ =>
match getBinary(this, currentKey) {
case left:ByteVector =>
let leftHash = toBase58String(blake2b256(left))
WriteSet([DataEntry(publicKeyHash + "_owner", invoke.caller.bytes), DataEntry(leftHash, publicKey), DataEntry(publicKeyHash, left), DataEntry(currentKey, 0), DataEntry(currentAmountKey, defaultAmount)])

case _ =>
WriteSet([DataEntry(publicKeyHash + "_owner", invoke.caller.bytes), DataEntry(currentKey, publicKey), DataEntry(currentAmountKey, newRequiredAmount)])
}
}
}
}

@Callable(invoke)
func sendMessage(publicKey: ByteVector, message: ByteVector) = {
let publicKeyHash = toBase58String(blake2b256(publicKey))

match getBinary(this, publicKeyHash + "_owner") {
case assignedCaller: ByteVector =>
if (assignedCaller != invoke.caller.bytes) then throw("Public keys not match")
else match getBinary(this, publicKeyHash) {
case right: ByteVector =>
let conversationId = toBase58String(blake2b256(publicKey + right))
let nonceKey = conversationId + "_n"
let nonce = match getInteger(this, nonceKey) {
case i: Int => i + 1
case _ => 1
}
WriteSet([DataEntry(conversationId + "_" + toString(nonce), message), DataEntry(nonceKey, nonce)])

case _ => throw("Not initialized")
}

case _ => throw("Owner not defined")
}
}
27 changes: 27 additions & 0 deletions ride4dapps/waveschat/waveschat_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
describe('Messenger test', () => {
const dappAddress = "3N3st6Cp9ZLz8kmT33EY41AVjwKqBVebvyq"

const firstPK = "FxKjemCJ9s9yrG5RuDXAtGXZNsTsAr1FMhzNmUKG4GyE"
const secondPK = "5nRF8WDnjWrGhZeYEV8zh7MG1kgpYbnJaEt5G3vsR4e2"

const firstPKHash = "4CfQDi9nwsps25XA9yrMAw4XWFaG5NwLf6gFVjrTSGL7"

it('Register for a chat', async function(){
const tx = invokeScript({ fee: 900000, dApp: dappAddress, call:{function:"init",args:[{"type": "binary", "value": firstPK}, {"type": "string", "value": "test"}]},
payment: [{amount: 1000000, assetId: null}]})
const tx1 = invokeScript({ fee: 900000, dApp: dappAddress, call:{function:"init",args:[{"type": "binary", "value": secondPK}, {"type": "string", "value": "test"}]},
payment: [{amount: 1000000, assetId: null}]})

await broadcast(tx)
await broadcast(tx1)
await waitForTx(tx1.id)
})

it('Send message', async function(){
const tx = invokeScript({ fee: 900000, dApp: dappAddress, call:{function:"sendMessage",args:[{"type": "binary", "value": firstPK}, {"type": "binary", "value": "4CfQDi9nwsps25XA9yrMAw4XWFaG5NwLf6gFVjrTSGL7"}]},
payment: [{amount: 1000000, assetId: null}]})

await broadcast(tx)
await waitForTx(tx.id)
})
})