Skip to content
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

docs: guide for oracles and PayingForTx #219

Merged
merged 2 commits into from
Jan 7, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 3 additions & 2 deletions docs/usage-guides/mdw.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ If you need convenient access to any other specific API endpoint feel free to [o
### Get a specific auction

```java
// TODO
String aensName = "xyz.chain";
NameAuctionResult nameAuctionResult = aeternityService.mdw.blockingGetNameAuction(aensName);
```

### Get all auctions

```java
// TODO
NameAuctionsResult nameAuctionsResult = aeternityService.mdw.blockingGetNameAuctions();
```
110 changes: 103 additions & 7 deletions docs/usage-guides/oracles.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,35 @@ This guide shows you how to perform all the operations that you need within the

## 1. Oracle: register
You register an oracle that responds with the temperature of the city that is included in the query.
This guide uses an example response of https://api.openweathermap.org.

```java
// TODO
KeyPair oracleKeyPair = ...
BigInteger nonce = aeternityService
.accounts
.blockingGetNextNonce(oracleKeyPair.getAddress());

OracleRegisterTransactionModel oracleRegisterTx =
OracleRegisterTransactionModel.builder()
.accountId(oracleKeyPair.getAddress())
.nonce(nonce)
// lives for 5000 keyblocks if it isn't extended
.oracleTtl(BigInteger.valueOf(5000))
// using delta instead of fixed block height
.oracleTtlType(OracleTTLType.DELTA)
// fee in aettos a caller needs to pay
.queryFee(BigInteger.valueOf(100))
.queryFormat("string")
.responseFormat("string")
.build();

aeternityService.transactions.blockingPostTransaction(
oracleRegisterTx, oracleKeyPair.getEncodedPrivateKey());
```

Note:

- By default the oracle will exist for the next 500 KeyBlocks.
- By default the oracle will exist for the next 5000 KeyBlocks.
- If you intend to keep your oracle running longer you should increase the `oracleTtl` and/or set up a service that automatically extends the TTL before it expires.
- The `oracleId` will be similar to the address of the account that registered the Oracle.
- The only difference is the prefix that will be `ok_` instead of `ak_`
Expand All @@ -24,7 +45,31 @@ Note:
After the oracle has been registered and as long as it isn't expired, everybody that knows the `oracleId` can query it.

```java
// TODO
KeyPair callerKeyPair = ...
BigInteger nonce = aeternityService
.accounts
.blockingGetNextNonce(callerKeyPair.getAddress());
String oracleId = "ok_...";
String queryString = "{\"lat\":48.78,\"lon\":9.18}";

OracleQueryTransactionModel oracleQueryTx =
OracleQueryTransactionModel.builder()
.senderId(callerKeyPair.getAddress())
.oracleId(oracleId)
.nonce(nonce)
.query(queryString)
// fee needs to fit the fee defined by the oracle
.queryFee(BigInteger.valueOf(100))
// oracle can respond within the next 50 keyblocks
.queryTtl(BigInteger.valueOf(50))
.queryTtlType(OracleTTLType.DELTA)
// response will be available 100 keyblocks
// before being garbage collected together with the query
.responseTtl(BigInteger.valueOf(100))
.build();

aeternityService.transactions.blockingPostTransaction(
oracleQueryTx, callerKeyPair.getEncodedPrivateKey());
```

Note:
Expand All @@ -33,19 +78,57 @@ Note:
- If you don't provide a sufficient fee the transaction is invalid.

### Poll for response
Now you have access to the query object and can poll for the response to that specific query:
After broadcasting the OracleQueryTx you can poll for the response to that specific query like this:

```java
// TODO
// using the same nonce and oracleId as like in the OracleQueryTx
String queryId = EncodingUtils.queryId(callerKeyPair.getAddress(), nonce, oracleId);
String response = null;
// wait for the response
while(response == null || response.isEmpty()) {
OracleQueryResult oracleQueryResult =
this.aeternityService.oracles.blockingGetOracleQuery(oracleId, queryId);
response = oracleQueryResult.getResponse();
}
// do something with the response
```

Note:

- The `OracleQueryResult` will only contain the response property if the Oracle responded.

## 3. Oracle: poll for queries and respond

### Poll for queries & respond
Typically the oracle itself polls for its own queries and responds as soon as possible:

```java
// TODO
// fetch oracle queries
OracleQueriesResult oracleQueriesResult =
this.aeternityService.oracles.blockingGetOracleQueries(
oracleKeyPair.getOracleAddress());

// typically the oracle would respond to all queries it didn't already respond to
// in this case it only responds to the first query in the list
OracleQueryResult oracleQueryResult =
oracleQueriesResult.getQueryResults().get(0);

String responseString = "{\"coord\":{\"lon\":9.18,\"lat\":48.78},\"weather\":[{\"id\":310,\"main\":\"Drizzle\",\"description\":\"light intensity drizzle rain\",\"icon\":\"09n\"}],\"base\":\"stations\",\"main\":{\"temp\":282.56,\"pressure\":1021,\"humidity\":93,\"temp_min\":279.82,\"temp_max\":285.37},\"visibility\":7000,\"wind\":{\"speed\":4.1,\"deg\":330},\"clouds\":{\"all\":90},\"dt\":1572217099,\"sys\":{\"type\":1,\"id\":1274,\"country\":\"DE\",\"sunrise\":1572156074,\"sunset\":1572192774},\"timezone\":3600,\"id\":2825297,\"name\":\"Stuttgart\",\"cod\":200}";

BigInteger nonce = aeternityService
.accounts
.blockingGetNextNonce(oracleKeyPair.getAddress());
OracleRespondTransactionModel oracleRespondTx =
OracleRespondTransactionModel.builder()
.oracleId(oracleKeyPair.getOracleAddress())
.queryId(oracleQueryResult.getId())
.nonce(nonce)
.response(responseString)
.responseTtl(BigInteger.valueOf(100))
.build();

aeternityService.transactions.blockingPostTransaction(
oracleRespondTx, oracleKeyPair.getEncodedPrivateKey());
```

Note:
Expand All @@ -57,7 +140,20 @@ Note:
As mentioned above an Oracle has a certain TTL that can be specified when registering it. You might want to extend the TTL of the oracle before it expires. You can do that as follows:

```java
// TODO
BigInteger nonce = aeternityService
.accounts
.blockingGetNextNonce(oracleKeyPair.getAddress());

OracleExtendTransactionModel oracleExtendTx =
OracleExtendTransactionModel.builder()
.nonce(nonce)
.oracleId(oracleKeyPair.getOracleAddress())
// extend the oracle ttl for another 100 keyblocks
.relativeTtl(BigInteger.valueOf(100))
.build();

aeternityService.transactions.blockingPostTransaction(
oracleExtendTx, oracleKeyPair.getEncodedPrivateKey());
```

## Delegate signature to contract (Oracle interface)
Expand Down
49 changes: 43 additions & 6 deletions docs/usage-guides/payingfortx.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,54 @@
# PayingForTx

## Introduction
This guide explains you how to perform a `PayingForTx` (also known as meta-transaction) using the SDK.
This guide explains you how to perform a `PayingForTx` (also known as meta-transaction) using this SDK.

It is a very powerful and efficient solution that is crucial for onboarding new users into you ecosystem. By making use of the `PayingForTx` you will be able to cover the fees of your users.
It is a very powerful and efficient solution that is for example crucial for onboarding new users into the æternity ecosystem. By making use of the `PayingForTx` you will be able to cover the fees of your users.

## How it works
Typically somebody that you want to pay the transaction for (e.g. a new user of your decentralized aepp) signs the inner transaction (e.g. of type `ContractCallTx`) with a specific signature that is used for inner transactions.

You can then collect the signed inner transaction, wrap it into a `PayingForTx` and broadcast it to the network.

## Contract call in PayingForTx
This example can be used for onboarding users that don't have any funds by paying the fee for their contract calls.
This way your aepp can hide some complexity of using the underlying blockchain and your users don't have to buy AE in order to use your aepp!
## SpendTx in PayingForTx
This is an example how one can cover the transaction fee for another account that
simply wants to send some AE:

```java
// TODO
KeyPair senderWhoDelegates = ...
KeyPair recipient = ...
KeyPair payerDelegate = ...

BigInteger senderNonce = aeternityService
.accounts
.blockingGetNextNonce(senderWhoDelegates.getAddress());

SpendTransactionModel spendTx =
SpendTransactionModel.builder()
.sender(senderWhoDelegates.getAddress())
.recipient(anotherKeypair.getAddress())
.amount(amount)
.payload("yeah, somebody else payed the tx fee for this transaction =)")
.nonce(senderNonce)
.build();

// the inner tx of a PayingForTx needs to be signed with a specific prefix
// the sdk provides the respective method to do this
String signedInnerTx = aeternityService.transactions.signPayingForInnerTransaction(
spendTx, senderWhoDelegates.getEncodedPrivateKey());

BigInteger payerNonce = aeternityService
.accounts
.blockingGetNextNonce(payerDelegate.getAddress());

PayingForTransactionModel payingForTx =
PayingForTransactionModel.builder()
.payerId(payerDelegate)
.nonce(payerNonce)
.innerTx(signedInnerTx)
.build();

aeternityService.transactions.blockingPostTransaction(payingForTx);
```

Note:
Expand All @@ -25,6 +58,10 @@ Note:
- The aepp sends the signed transaction to the Java backend which wraps it into `PayingForTx` and pays the required fee.
- You can even [combine the usage of a Generalized Account with the PayingForTx](https://aeternity.com/protocol/generalized_accounts/ga_explained.html#payingfor-example) which provides lots of possiblities!

## Contract calls in PayingForTx
If you need to onboard users that don't have any funds you can pay the fee of their contract calls using the `PayingForTx`.
This way your aepp can hide some complexity of using the underlying blockchain and your users don't have to buy AE in order to use your aepp!

## Use cases

- Game developers that want to quickly onboard new users.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ public void bOracleRegisterTest(TestContext context) {
OracleRegisterTransactionModel oracleRegisterTx =
OracleRegisterTransactionModel.builder()
.accountId(oracleKeyPair.getAddress())
.abiVersion(ZERO)
.nonce(nonce)
.oracleTtl(initialOracleTtl)
.oracleTtlType(OracleTTLType.BLOCK)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class TransactionPayingForTest extends BaseTest {
static UnitConversionService unitConversionService = new DefaultUnitConversionServiceImpl();

@Test
public void testPayingForTx(TestContext testContext) {
public void testPayingForTxWithSpendTx(TestContext testContext) {
this.executeTest(
testContext,
t -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
public class OracleRegisterTransactionModel extends AbstractTransactionModel<OracleRegisterTx> {

@Mandatory private String accountId;
@Mandatory private BigInteger abiVersion;
@Default private BigInteger abiVersion = BigInteger.ZERO;
@Mandatory private BigInteger nonce;
@Mandatory private BigInteger oracleTtl;
@Mandatory private OracleTTLType oracleTtlType;
Expand Down