Sirius Chain Developer Center 0.2.6

Sirius Chain Developer Center 0.2.6

  • Getting Started
  • Built-in Features
  • REST API Endpoints
  • Guides
  • Cheat Sheet

›Aggregate Transaction

Account

  • Creating and opening an account
  • Getting account information
  • Getting the amount of XPX sent to an account
  • Reading transactions from an account

Account Restriction

  • Preventing spam attacks with account restrictions

Aggregate Transaction

  • Sending payouts with aggregate-complete transaction
  • Creating an escrow with aggregate bonded transaction
  • Asking for mosaics with aggregate-bonded transaction
  • Signing announced aggregate-bonded transactions

Block

  • Listening New Blocks
  • Getting block by height

Cross Chain Swaps

  • Atomic cross-chain swap between Sirius public and private chains

Metadata

  • Account Metadata
  • Mosaic Metadata
  • Namespace Metadata

Monitoring

  • Monitor transaction

Mosaic

  • Creating a mosaic
  • Getting the mosaic information
  • Getting the asset identifier behind a namespace with receipts
  • Modifying Mosaic Supply

Multisig Account

  • Converting an account to multisig
  • Modifying a multisig account
  • Creating a multi-level multisig-account
  • Sending a multisig transaction

Namespace

  • Registering a namespace
  • Registering a subnamespace
  • Getting the Namespace information
  • Linking a namespace to a mosaic
  • Linking namespace to account

Transfer Transaction

  • Transfer transaction
  • Sending an encrypted message

Creating an escrow with aggregate bonded transaction

Learn about aggregate bonded transactions, by creating an escrow contract.

Background

An escrow is a contractual arrangement in which a third party receives and disburses money or documents for the primary transacting parties. Transacting parties will agree to the conditions in which the disbursement depend on. For example, it can be an account established by a broker for holding funds on behalf of the broker’s principal or some other person until the consummation or termination of a transaction. An escrow can also be a trust account held in the borrower’s name to pay obligations such as property taxes and insurance premiums.

Reference:

  • Wikipedia Contributors. "Escrow." Wikipedia, Wikipedia Foundation, 25 June 2019. en.wikipedia.org/wiki/Escrow.

For this example, imagine the two parties agree on a virtual service, implying that the escrow can be executed immediately:

  1. Buyer and seller agree on terms.
  2. Buyer submits payment to escrow
  3. Seller delivers goods or service to buyer.
  4. Buyer approves goods or service.
  5. Escrow releases payment to the seller.

How is it applied to Proximax Sirius Chain?

Normalizing the language into Sirius Chain related concepts:

  • contractual arrangement: A new type of transaction called Aggregate Transaction.
  • third party receives and disburses money: There is no third party, we are going to use blockchain technology.
  • primary transacting parties: Sirius Chain accounts will represent the participants.
  • conditions agreed to by the transacting parties: Whe nevery participant signs the aggregate transaction.
  • account established by a broker for holding funds: There will not be an intermediate account, the exchange will happen atomically using an aggregate transaction.
  • until the consummation or termination of a transaction: The transaction gets included in a block or expires.

Prerequisites

  • XPX-Chain-SDK
  • A text editor or IDE
  • Finish creating a mosaic guide
  • Finish sending payouts with aggregate complete transactions
  • An account with xpx

Getting into some code

Aggregate Escrow

Multi-Asset Escrowed Transactions

Setting up the required accounts and mosaics

Alice and a ticket distributor want to swap the following mosaics.

OwnerAmountMosaicIdDescription
Alice100xpxNative currency mosaic
Ticket distributor17cdf3b117a3c40ccRepresents a museum ticket.

Before continuing, create the two accounts loaded with xpx.

Then, create a mosaic with the ticket distributor account. This new mosaic will represent the ticket.

Creating the escrow contract

Alice will send a transaction to the ticket distributor exchanging 100 xpx for 1 7cdf3b117a3c40cc (museum ticket).

  1. Create two transfer transaction:

A. From Alice to the ticket distributor sending 100 xpx B. From the ticket distributor to Alice sending 1 7cdf3b117a3c40cc.

**Note**

The museum ticket does not have the id 7cdf3b117a3c40cc in your network. Replace the MosaicId for the one you have created in the previous step.

Golang
TypeScript
JavaScript
Java
conf, err := sdk.NewConfig(context.Background(), []string{"http://localhost:3000"})
if err != nil {
panic(err)
}

// Use the default http client
client := sdk.NewClient(nil, conf)

aliceAccount, err := client.NewAccountFromPrivateKey(os.Getenv("ALICE_PRIVATE_KEY"))
if err != nil {
panic(err)
}

ticketDistributorPublicAccount, err := client.NewAccountFromPublicKey(os.Getenv("TICKET_DISTRIBUTOR_PUBLIC_KEY"))
if err != nil {
panic(err)
}

aliceTransferTransaction, err := client.NewTransferTransaction(sdk.NewDeadline(time.Hour), ticketDistributorPublicAccount.Address, []*sdk.Mosaic{sdk.XpxRelative(100)}, sdk.NewPlainMessage("send 100 xpx to distributor"))
if err != nil {
panic(err)
}

id, err := strconv.ParseInt("7cdf3b117a3c40cc", 16, 64)
if err != nil {
panic(err)
}
mosaicId, err := sdk.NewMosaicId(uint64(id))
if err != nil {
panic(err)
}
mosaic, err := sdk.NewMosaic(mosaicId, 1)
if err != nil {
panic(err)
}

ticketDistributorTransferTransaction, err := client.NewTransferTransaction(sdk.NewDeadline(time.Hour), aliceAccount.PublicAccount.Address, []*sdk.Mosaic{mosaic}, sdk.NewPlainMessage("send 1 museum ticket to alice"))
if err != nil {
panic(err)
}
const nodeUrl = 'http://localhost:3000';
const transactionHttp = new TransactionHttp(nodeUrl);
const listener = new Listener(nodeUrl);

const alicePrivateKey = '<privateKey>';
const aliceAccount = Account.createFromPrivateKey(alicePrivateKey, NetworkType.TEST_NET);

const ticketDistributorPublicKey = '<publicKey>';
const ticketDistributorPublicAccount = PublicAccount.createFromPublicKey(ticketDistributorPublicKey, NetworkType.TEST_NET);

const aliceToTicketDistributorTx = TransferTransaction.create(
Deadline.create(),
ticketDistributorPublicAccount.address,
[NetworkCurrencyMosaic.createRelative(100)],
PlainMessage.create('send 100 xpx to distributor'),
NetworkType.TEST_NET);

const ticketDistributorToAliceTx = TransferTransaction.create(
Deadline.create(),
aliceAccount.address,
[new Mosaic(new MosaicId('7cdf3b117a3c40cc'), UInt64.fromUint(1))],
PlainMessage.create('send 1 museum ticket to alice'),
NetworkType.TEST_NET);
const nodeUrl = 'http://localhost:3000';
const transactionHttp = new TransactionHttp(nodeUrl);
const listener = new Listener(nodeUrl);

const alicePrivateKey = '<privateKey>';
const aliceAccount = Account.createFromPrivateKey(alicePrivateKey, NetworkType.TEST_NET);

const ticketDistributorPublicKey = '<publicKey>';
const ticketDistributorPublicAccount = PublicAccount.createFromPublicKey( ticketDistributorPublicKey, NetworkType.TEST_NET);

const aliceToTicketDistributorTx = TransferTransaction.create(
Deadline.create(),
ticketDistributorPublicAccount.address,
[NetworkCurrencyMosaic.createRelative(100)],
PlainMessage.create('send 100 xpx to distributor'),
NetworkType.TEST_NET);

const ticketDistributorToAliceTx = TransferTransaction.create(
Deadline.create(),
aliceAccount.address,
[new Mosaic( new MosaicId('7cdf3b117a3c40cc'), UInt64.fromUint(1))],
PlainMessage.create('send 1 museum ticket to alice'),
NetworkType.TEST_NET);
    // Replace with private key
final String alicePrivateKey = "<privateKey>";

// Replace with public key
final String ticketDistributorPublicKey = "<publicKey>";

final Account aliceAccount = Account.createFromPrivateKey(alicePrivateKey, NetworkType.TEST_NET);
final PublicAccount ticketDistributorPublicAccount = PublicAccount.createFromPublicKey(ticketDistributorPublicKey, NetworkType.TEST_NET);

final TransferTransaction aliceToTicketDistributorTx = TransferTransaction.create(
Deadline.create(2, HOURS),
ticketDistributorPublicAccount.getAddress(),
Collections.singletonList(NetworkCurrencyMosaic.createRelative(BigInteger.valueOf(100))),
PlainMessage.create("send 100 xpx to distributor"),
NetworkType.TEST_NET
);

final TransferTransaction ticketDistributorToAliceTx = TransferTransaction.create(
Deadline.create(2, HOURS),
aliceAccount.getAddress(),
Collections.singletonList(new Mosaic(new MosaicId("7cdf3b117a3c40cc"), BigInteger.valueOf(1))),
PlainMessage.create("send 1 museum ticket to alice"),
NetworkType.TEST_NET
);

  1. Wrap the defined transactions in an aggregate transaction sign it.

An aggregate transaction is complete if before announcing it to the network, all required cosigners have signed it. If valid, it will be included in a block.

In case that signatures are required from other participants and the transaction is announced to the network, it is considered an aggregate bonded.

Golang
TypeScript
JavaScript
Java
aliceTransferTransaction.ToAggregate(aliceAccount.PublicAccount)
ticketDistributorTransferTransaction.ToAggregate(ticketDistributorPublicAccount)

aggregateTransaction, err := client.NewBondedAggregateTransaction(sdk.NewDeadline(time.Hour), []sdk.Transaction{aliceTransferTransaction, ticketDistributorTransferTransaction})
if err != nil {
panic(err)
}

signedAggregateBoundedTransaction, err := aliceAccount.Sign(aggregateTransaction)
if err != nil {
panic(err)
}
const aggregateTransaction = AggregateTransaction.createBonded(Deadline.create(),
[aliceToTicketDistributorTx.toAggregate(aliceAccount.publicAccount),
ticketDistributorToAliceTx.toAggregate(ticketDistributorPublicAccount)],
NetworkType.TEST_NET);

const signedTransaction = aliceAccount.sign(aggregateTransaction, generationHash);

const aggregateTransaction = AggregateTransaction.createBonded(Deadline.create(),
[aliceToTicketDistributorTx.toAggregate(aliceAccount.publicAccount),
ticketDistributorToAliceTx.toAggregate(ticketDistributorPublicAccount)],
NetworkType.TEST_NET);

const signedTransaction = aliceAccount.sign(aggregateTransaction, generationHash);


final AggregateTransaction aggregateTransaction = new TransactionBuilderFactory().aggregateBonded()
.innerTransactions(Arrays.asList(
aliceToTicketDistributorTx.toAggregate(aliceAccount.getPublicAccount()),
ticketDistributorToAliceTx.toAggregate(ticketDistributorPublicAccount)
)).deadline(new Deadline(2, ChronoUnit.HOURS)).networkType(NetworkType.TEST_NET);

final SignedTransaction aggregateSignedTransaction = aliceAccount.sign(aggregateTransaction, generationHash);
  1. When an aggregate transaction is bonded, Alice will need to lock at least 10 xpx. Once the ticket distributor signs the aggregate transaction, the amount of locked xpx becomes available again on Alice’s account, and the exchange will get through.
Golang
TypeScript
JavaScript
Java
lockFundsTransaction, err := client.NewLockFundsTransaction(
sdk.NewDeadline(time.Hour),
sdk.XpxRelative(10),
sdk.Duration(1000),
signedAggregateBoundedTransaction,
)
if err != nil {
panic(err)
}

signedLockFundsTransaction, err := aliceAccount.Sign(lockFundsTransaction)
if err != nil {
panic(err)
}
// Announce transaction
_, err = client.Transaction.Announce(context.Background(), signedLockFundsTransaction)
if err != nil {
panic(err)
}
const lockFundsTransaction = LockFundsTransaction.create(
Deadline.create(),
NetworkCurrencyMosaic.createRelative(10),
UInt64.fromUint(1000),
signedTransaction,
NetworkType.TEST_NET);

const lockFundsTransactionSigned = aliceAccount.sign(lockFundsTransaction, generationHash);

listener.open().then(() => {

transactionHttp
.announce(lockFundsTransactionSigned)
.subscribe(x => console.log(x), err => console.error(err));

listener
.confirmed(aliceAccount.address)
.pipe(
filter((transaction) => transaction.transactionInfo !== undefined
&& transaction.transactionInfo.hash === lockFundsTransactionSigned.hash),
mergeMap(ignored => transactionHttp.announceAggregateBonded(signedTransaction))
)
.subscribe(announcedAggregateBonded => console.log(announcedAggregateBonded),
err => console.error(err));
});
const lockFundsTransaction = LockFundsTransaction.create(
Deadline.create(),
NetworkCurrencyMosaic.createRelative(10),
UInt64.fromUint(480),
signedTransaction,
NetworkType.TEST_NET);

const lockFundsTransactionSigned = aliceAccount.sign(lockFundsTransaction, generationHash);

listener.open().then(() => {

transactionHttp
.announce(lockFundsTransactionSigned)
.subscribe(x => console.log(x), err => console.error(err));

listener
.confirmed(aliceAccount.address)
.pipe(
filter((transaction) => transaction.transactionInfo !== undefined
&& transaction.transactionInfo.hash === lockFundsTransactionSigned.hash),
mergeMap(ignored => transactionHttp.announceAggregateBonded(signedTransaction))
)
.subscribe(announcedAggregateBonded => console.log(announcedAggregateBonded),
err => console.error(err));
});
 // Creating the lock funds transaction and announce it

final LockFundsTransaction lockFundsTransaction = LockFundsTransaction.create(
Deadline.create(2, HOURS),
NetworkCurrencyMosaic.createRelative(BigInteger.valueOf(10)),
BigInteger.valueOf(480),
aggregateSignedTransaction,
NetworkType.TEST_NET
);

final SignedTransaction lockFundsTransactionSigned = aliceAccount.sign(lockFundsTransaction, generationHash);

final TransactionHttp transactionHttp = new TransactionHttp("http://localhost:3000");

transactionHttp.announce(lockFundsTransactionSigned).toFuture().get();

System.out.println(lockFundsTransactionSigned.getHash());

final Listener listener = new Listener("http://localhost:3000");

listener.open().get();

final Transaction transaction = listener.confirmed(aliceAccount.getAddress()).take(1).toFuture().get();

transactionHttp.announceAggregateBonded(aggregateSignedTransaction).toFuture().get();

The distributor has not signed the aggregate bonded transaction yet, so the exchange has not been completed.

Copy the aggregate transaction hash, and check how to cosign the aggregate transaction in the following guide.

Is it possible without aggregate transactions?

It is not secure, since:

  • Alice could decide not to pay the distributor after receiving the ticket.
  • The distributor could choose not to send the ticket after receiving the payment.

Using the aggregate transaction feature we ensure that multiple transactions are executed at the same time when all the participants agree. The seller does not send the virtual goods.

What’s next?

Afterwards, try to swap mosaics between multiple participants.

Aggregate Escrow

Multi-Asset Escrowed Transactions

← Sending payouts with aggregate-complete transactionAsking for mosaics with aggregate-bonded transaction →
  • Background
  • Prerequisites
  • Getting into some code
    • Setting up the required accounts and mosaics
    • Creating the escrow contract
  • Is it possible without aggregate transactions?
  • What’s next?
  • Join #general discussion
  • Ask development questions
  • Follow the dev updates
  • Explore Github
Protocol
BlockConsensus AlgorithmsCryptographyInflationNodeReceiptTransactionValidating
Built-in Features
AccountAccount FilterAggregate TransactionCross-Chain SwapsExchange MarketMetadataMosaicMultisig AccountNamespaceSuper contractTransfer Transaction
References
REST APISDKsXPX-Chain-CLICheat Sheet
Documentation Forked From NEM
Copyright © 2020 ProximaX