Функционал учетной записи WAVES позволяет только убедиться, что выпущенная из нее транзакция действительно была отправлена с этой учетной записи
Смарт-аккаунт запрещает некоторые виды транзакций
Каждый скрипт Ride должен начинаться с директив Эти директивы сообщают компилятору, что:
{-# STDLIB_VERSION 6 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ASSET #-}
После директив вы можете определить вспомогательные переменные и функции. Выражение проверяет транзакции с активом на соответствие заданным условиям. Если условия не выполняются, транзакция отклоняется
let LIMIT = 1500
let START_TRANSFER = 0
# "height" is built-in variable. Blockchain height at the script execution time
func checkBlockHeight() = height >= START_TRANSFER
match tx {
case t: SetScriptTransaction => false
case ttx: TransferTransaction => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) && ttx.amount <= LIMIT && checkBlockHeight()
case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}
Это намного проще, чем вы себе представляете. Скажите «Hello World» в сети WAVES Вставьте этот скрипт в свой проект
{-# STDLIB_VERSION 6 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ASSET #-}
let LIMIT = 1500
let START_TRANSFER = 0
# "height" is built-in variable. Blockchain height at the script execution time
func checkBlockHeight() = height >= START_TRANSFER
match tx {
case t: SetScriptTransaction => false
case ttx: TransferTransaction => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) && ttx.amount <= LIMIT && checkBlockHeight()
case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}
Развертывание скрипта в сети Waves
Во время развертывания вам нужно будет создать свою учетную запись, если у вас ее еще нет
// Запустить тест с помощью Surfboard
// В предыдущем разделе мы выполняем все действия, которые нам нужны для тестирования смарт-аккаунта, и генерируется определенное количество аккаунтов с указанным балансом
describe('Account-script test suite', async function () {
const wvs = 10 ** 8;
let accontScript;
this.timeout(100000);
before(async function () {
// Генерируется какое-то количество аккаунтов с указанным балансом
await setupAccounts({
user: 1 * wvs,
secondUser: 1 * wvs
});
//Развертывание осуществляется транзакцией установки скрипта на аккаунт
accontScript = compile(file('account-script.ride'));
const ssTx = setScript({ script: accontScript }, accounts.user);
await broadcast(ssTx);
await waitForTx(ssTx.id);
const ssTx2 = setScript({ script: accontScript }, accounts.secondUser);
await broadcast(ssTx2);
await waitForTx(ssTx2.id);
console.log('Script has been set')
});
//Каждый раздел it() служит для проверки некоторой функциональности
// Трансфер Waves
it('Should transfer less than 1500', async function () {
const transferTx = transfer({ amount: 1000, recipient: address(accounts.secondUser), fee: 1000000 }, accounts.user);
// Get account effective balance
const userBalanceBefore = await balance(address(accounts.user));
const secondUserBalanceBefore = await balance(address(accounts.secondUser));
await broadcast(transferTx);
await waitForTx(transferTx.id);
const userBalanceAfter = await balance(address(accounts.user));
const secondUserBalanceAfter = await balance(address(accounts.secondUser));
expect(userBalanceBefore).to.be.gt(userBalanceAfter);
expect(secondUserBalanceBefore).to.be.lt(secondUserBalanceAfter);
})
// Попытка осуществить перевод на сумму, превышающую лимит
it('Should not transfer more than 1000', async function () {
const transferTx = transfer({ amount: 2000, recipient: address(accounts.secondUser), fee: 1000000 }, accounts.user);
expect(broadcast(transferTx)).to.be.rejectedWith("");
})
it('Should not set script again', async function () {
const ssTx = setScript({ script: accontScript, fee: 1500000 }, accounts.user);
expect(broadcast(ssTx)).to.be.rejectedWith("");
})
// Вносите записи в список как транзакцию данных, чтобы проверить стандартное поведение аккаунта для других типов транзакций
it('Should store data', async function () {
const records = [
{ key: 'integerVal', value: 1 },
{ key: 'booleanVal', value: true },
{ key: 'stringVal', value: 'Lorem ipsum dolor sit amet' }
]
const dataTx = data({ data: records, fee: 1000000 }, accounts.user);
await broadcast(dataTx);
await waitForTx(dataTx.id);
const store = await accountData(address(accounts.user));
expect(store["integerVal"].value).to.be.equal(1);
expect(store["booleanVal"].value).to.be.equal(true);
expect(store["stringVal"].value).to.be.equal('Lorem ipsum dolor sit amet');
})
})
//Пример вызова функции из dApp
import com.wavesplatform.crypto.Crypto;
import com.wavesplatform.transactions.DataTransaction;
import com.wavesplatform.transactions.SetScriptTransaction;
import com.wavesplatform.transactions.TransferTransaction;
import com.wavesplatform.transactions.account.PrivateKey;
import com.wavesplatform.transactions.common.Amount;
import com.wavesplatform.transactions.common.Base64String;
import com.wavesplatform.transactions.data.BooleanEntry;
import com.wavesplatform.transactions.data.DataEntry;
import com.wavesplatform.transactions.data.IntegerEntry;
import com.wavesplatform.transactions.data.StringEntry;
import com.wavesplatform.wavesj.Node;
import com.wavesplatform.wavesj.Profile;
import com.wavesplatform.wavesj.exceptions.NodeException;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.web3j.utils.Files;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class AccountScriptTest {
static PrivateKey user;
static PrivateKey secondUser;
static Node node;
static Base64String accountScript;
static int errorCode = 307;
// additional info can be found - https://hub.docker.com/r/wavesplatform/waves-private-node
static PrivateKey faucet = PrivateKey.fromSeed("waves private node seed with waves tokens");
@BeforeClass
public static void before() throws NodeException, IOException {
node = new Node(Profile.LOCAL);
// A certain number of accounts with the specified balance is generated
user = PrivateKey.fromSeed(Crypto.getRandomSeedBytes());
TransferTransaction transfer1 = TransferTransaction.builder(
user.address(), Amount.of(1_00_000_000)
).getSignedWith(faucet);
node.broadcast(transfer1);
node.waitForTransaction(transfer1.id());
secondUser = PrivateKey.fromSeed(Crypto.getRandomSeedBytes());
TransferTransaction transfer2 = TransferTransaction.builder(
secondUser.address(), Amount.of(1_00_000_000)
).getSignedWith(faucet);
node.broadcast(transfer2);
node.waitForTransaction(transfer2.id());
// Deployment is carried out by the transaction of installing the script on the account
String source = Files.readString(new File("src/test/resources/account-script.ride"));
accountScript = node.compileScript(source).script();
SetScriptTransaction ssTx = SetScriptTransaction.builder(accountScript).getSignedWith(user);
node.broadcast(ssTx);
node.waitForTransaction(ssTx.id());
SetScriptTransaction ssTx2 = SetScriptTransaction.builder(accountScript).getSignedWith(secondUser);
node.broadcast(ssTx2);
node.waitForTransaction(ssTx2.id());
System.out.println("Script has been set");
}
@Test
public void should_transfer_less_than_1500() throws IOException, NodeException {
// Transfer WAVES
TransferTransaction transferTx = TransferTransaction.builder(
secondUser.address(), Amount.of(1000)
).getSignedWith(user);
// Get account effective balance
long userBalanceBefore = node.getBalance(user.address());
long secondUserBalanceBefore = node.getBalance(secondUser.address());
node.broadcast(transferTx);
node.waitForTransaction(transferTx.id());
long userBalanceAfter = node.getBalance(user.address());
long secondUserBalanceAfter = node.getBalance(secondUser.address());
// Compare user balances
Assert.assertTrue(userBalanceBefore > userBalanceAfter);
Assert.assertTrue(secondUserBalanceBefore < secondUserBalanceAfter);
Assert.assertTrue(secondUserBalanceBefore < secondUserBalanceAfter);
}
@Test
public void should_not_transfer_more_than_1500() throws NodeException, IOException {
// Trying to make a transfer with an amount that exceeds the limit
TransferTransaction transferTx = TransferTransaction.builder(
secondUser.address(), Amount.of(2000)
).getSignedWith(user);
try {
node.broadcast(transferTx);
} catch (NodeException nodeException) {
Assert.assertEquals(errorCode, nodeException.getErrorCode());
}
}
@Test
public void should_not_set_script_again() throws NodeException, IOException {
// Trying to change the script to a new one
SetScriptTransaction ssTx = SetScriptTransaction.builder(accountScript).getSignedWith(user);
try {
node.broadcast(ssTx);
} catch (NodeException nodeException) {
Assert.assertEquals(errorCode, nodeException.getErrorCode());
}
}
@Test
public void should_store_data() throws NodeException, IOException {
// Make records to the state as a data transaction to check the standard behavior of the account for other types of transactions
DataTransaction dataTx = DataTransaction.builder(
IntegerEntry.as("integerVal", 1),
BooleanEntry.as("booleanVal", true),
StringEntry.as("stringVal", "Lorem ipsum dolor sit amet")
).getSignedWith(user);
node.broadcast(dataTx);
node.waitForTransaction(dataTx.id());
List<DataEntry> store = node.getData(user.address());
Assert.assertEquals(3, store.size());
Map<String, DataEntry> dataEntryMap = store.stream().collect(Collectors.toMap(DataEntry::key, item -> item));
Assert.assertEquals(1L, dataEntryMap.get("integerVal").valueAsObject());
Assert.assertEquals(true, dataEntryMap.get("booleanVal").valueAsObject());
Assert.assertEquals("Lorem ipsum dolor sit amet", dataEntryMap.get("stringVal").valueAsObject());
}
}
Вы успешно создали свой первый dApp. Он был опубликован в сети Waves.