Is a public key truncated in the order (algorithm) specified in the ERACHAIN software. User-owned accounting units are stored on the accounts. Transactions in the blockchain are linked to the accounts.
Is the setting of time limits for connecting to a peer whose actions conclude a deviation from the protocol.
Is a set of validated transactions, provided with header attributes, among which there must be a link to the predecessor block.
Is the sequence number of a block in the blockchain.
Is the signature of the previous block.
Is a set of valid blocks.
Is the transmission of messages to all network participants.
Is the last block of the chain below which chain changes cannot occur.
Is a User who has at least 100 (one hundred) ERA Units of Account, who can collect sets of transactions and form a Block from them.
Is the process of forming a new block. When winning, the network accepts a new block, a commission is charged to the account that formed the block, in the amount of commissions from all transactions in the block, but not less than 0.00032768 COMPU.
Is the creation of a temporary copy of a working database.
Is a block that comes with the Erachain software, having an electronic digital signature in the Base58 format: V5RkNcBrDi88Tbkm71DN1Po7M7yEop9kpu95gxHjPvR7MDa3uMkYotvGJ8awRTmz2abBEGXShZptrVdzmwuMsag and is the first block of the chain.
Is a function that performs an irreversible transformation of an arbitrary data structure into a single number of fixed length.
Is any computer that uses the Erachain software and follows the rules of the protocol.
Is the node on which the Erachain platform is running, with which a connection is established and data is exchanged.
Is sending a request for node availability.
Is a transaction in the form of a byte array in Base58 encoding. The structure of the formation of the byte array of the transaction depends on the type of transaction. The transaction format is presented in Appendices A.
Is the place where the source files of the application are stored and maintained.
Is an interface for accessing private data.
Is a software interface for providing data exchange between nodes.
Is the value of the ERA asset on the forger account, adjusted by reduction coefficients for the amounts recently credited to the account.
Is separate digitally signed data included in blocks.
Are transactions received from peers waiting to be included in the block.
Is the balance of the asset on the account and the incoming turnover.
Is the same, but for the balance of loans (rental of real estate).
Is the same, but for the balance of storage (usually for movable property).
Is the same, but for consumption balance.
Is the same, but for consumption balance.
Is a secret character set used to calculate a key pair based on it (public and private)
Is a separate chain of blocks processed by Erachain and having its own genesis block. The network of such a chain operates on 905X ports.
Is an internal accounting unit of the Erachain blockchain platform, which gives the user the right to create blocks, receiving COMPU for this.
Is an internal accounting unit of the Erachain blockchain platform used for transactions.
The Erachain platform is a distributed registry for storing transaction data and verifying them via consensus algorithm. The registry is formed on transaction data grouped into blocks. Blocks are formed into a chain to prevent changes.
Erachain has the structure and algorithm of the classical blockchain ecosystem. It has a genesis, and this block sets the initial state of the system, a transaction logging database and a current state database, where current account balances and other information are stored.
A distinctive feature of the platform is the convenient verification for users. The need to disclose personal data haunts us constantly. The classical approach to the storage and processing of personal data is experiencing a real crisis, especially in recent years, when, against the background of the incessant growth in the volume of digital information, numerous cases of hacker attacks have appeared, the purpose of which is to substitute and distort information. Blockchain technology can be a solution to this problem.
But the current reality is that the full use of blockchain projects by banks and other traditional institutions is impossible due to the lack of a mechanism for compliance with AML/KYC rules.
The technology of the Erachain platform is based on the mechanism of identification. The data of new users is entered and verified by other identified ecosystem participants who act as guarantors of the authenticity of this data. Thus, the platform allows you to use it as a KYC tool for interaction in business processes.
The graphical interface of the platform provides ample opportunities for automation of work processes. Ready-made forms with an intuitive interface do not require additional programming.
The work of the Erachain ecosystem is based on two accounting units - ERA and COMPU. The ERA unit gives the right to collect blocks and confirm user data on the network, and the COMPU unit is used to pay for transactions and as a reward for assembling blocks. This implementation of the protocol made it possible to introduce rules for issuing tokens independent of each other and increase the economic efficiency of the platform.
Unlike other blockchain platforms, in which the block generation rate is unstable and can range from a few seconds to several hours, the Erachain platform has a stable block generation rate, which is only 30 seconds.
The platform uses the following hashing algorithms:
During the initial installation of the node software, the wallet seed is generated. Any number of account seeds can be generated based on this seed. In the web version of the wallet, it is possible to generate a mnemonic phrase in accordance with BIP39 and generate a wallet seed based on it.
The invoice is an alphanumeric string in Base58 encoding, obtained from the user’s public key using a hash function, supplemented with control values (used for error detection). The account address is used for sending and receiving digital assets as an identifier of the recipient and sender, as well as in other transactions.
An array of 32 elements is filled with random values To generate random values, the standard Java SecretRandom library is used.
For different variants of chains we calculate the signature value using the Ed25519 algorithm.
A transaction is an array of bytes serialized according to its type. The received result is supplemented with the transaction signature details.
Transactions ensure that all transactions in the environment are reflected in the registry, for example, such as the issue and transfer of assets between the parties. An analog of such an operation can be an entry on the client’s account in the bank.
The short transaction reference is 8 bytes, where the first four bytes are the block number and the next four bytes are the transaction number in it. Such a link is hereinafter referred to as SeqNo.
A block is a special structure for recording a group of transactions formed according to the algorithms described in this document. A mandatory parameter of the block header is a link to the previous block.
A forger with a generating balance exceeding 100 ERA can form a candidate block for inclusion in the chain and send it to its peers.
The reference attribute is the signature of the predecessor block (the connection with the previous block is a guarantee against possible changes made by past periods, and is the basis of the blockchain principle), which has a previous height number.
The transactions attribute is an array consisting of raw transactions;
The transactions hash attribute is the value of the SHA256 hash function from the concatenation of byte arrays of transaction signatures;
The block signature attribute is a block data set signed with a forger key according to the SHA512 algorithm;
A detailed description of the block structure and the signature structure is given in Appendix N.
Each type of transaction has its own individual validation procedure, for example, transactions for the transfer of an asset are checked for the sufficiency of the balance of the asset being sent on the transaction creator’s account.
The transaction signature is verified when this transaction is received from the network.
Checks by the base class of the transaction:
All nodes simultaneously compete to get the right to write the next block. Each user acts based on the desire to get the greatest personal financial benefit, so the question arises, why should a user distribute a block created by another user?
The second important question is, who resolves conflicts when several nodes accept a valid block at about the same time? To do this work, the blockchain uses a consensus model that allows a group of mutually distrustful users to work together.
When a new user joins the block system, he agrees with the initial state of the system. This state is determined by the genesis block and the entire chain of blocks sequentially attached to it. Each block is formed from a set of transactions that are recognized as valid. The blockchain protocol has a hard-coded genesis block, which is the first in the chain and each subsequent block must be added to the blockchain after it on the basis of consensus. Each block must be valid and, therefore, can be independently verified by each network user for validity.
To reach a consensus, the following rules must be followed:
As new transactions are created, they are sent to the peers. Each node maintains a list of such unconfirmed transactions. Nodes that forge new blocks form blocks with such transactions and send them to their peers. The node, upon receiving a new block, performs its validation (clause 1.4.2.) and evaluates the admissibility of including this block in the chain. Forging is possible if there is an amount of at least 100 ERA on the account. A block included in the blockchain charges commissions from transactions in the amount of at least 0.00032768 COMPU contained in it to the account of the block creator.
The Proof of Stake consensus is based on the idea that the more assets a user has in the system, the more likely that he is interested in the success and development of the system, and less likely that he will take actions that will harm the system. The Proof-of-Stake method uses the size of the share that the user has in the system as one of the determining factors for creating a new block. Such users are more likely to be able to create new blocks.
The Erachain platform uses the Delegated Proof-of-Stake modification, which allows to transfer the right to issue a new block to another network user by providing an ERA asset for a loan. Consensus on the candidate block is achieved on the basis of the voting principle.
In the Controller class, the onMessage method, the transaction flow and new blocks coming from peers are processed. Upon receipt of a new block, the block verification procedure is started.
If a block is received with a reference corresponding to the signature of the last block in the working block chain, the validation procedure is started.
If the received block and the last block in the chain have the same reference, that is, they are competing, the winner function is calculated. If the value of this function for the received block is strictly greater than the value of the function standing in the chain, the block in the chain is replaced. The frequency of block generation is rigidly set by the protocol and is 30 seconds before block XXX and 30 seconds after it.
Determining the value of the asset balance on the account.
forgingValue = creator.getBalanceUSE(Transaction.RIGHTS_KEY, dcSet).intValue()
Determining the winning value (Winning, Strength of account).
winValue = BlockChain.calcWinValue(dcSet, this.creator, this.heightBlock, this.forgingValue)
To check the validity of a block by its strength, it is necessary to calculate:
CurrentTarget
The value of the currentTarget is the value of the target from the last collected block (this is the average value of the winnings for the last 1024 blocks)
currentTarget = this.parentBlockHead.target
The given winning value for the currentTarget
targetedWinValue = BlockChain.calcWinValueTargetedBase(dcSet, this.heightBlock, this.winValue, currentTarget)
If the value is negative (as a result of overflow) or more than 10 base targets BASE_TARGET then the resulting value of the reduced win targetedWinValue is equated to 10 base targets.
New target for the current block
For the current block, its new target is calculated as the average value of the winning values for the last 1024 blocks
target = BlockChain.calcTarget(this.heightBlock, currentTarget, this.winValue)
If the height is less than the TARGET_COUNT value, the target is calculated using a simplified formula:
target = currentTarget - currentTarget/heightBlock + winValue/currentTarget
The target value is limited from above by the value of 1,5 * currentTarget.
The new target is determined by the following formula:
target = (1023*currentTarget + winValue)*1024
Block synchronization is controlled by the main thread of the application, described in the BlockGenerator class. The thread is an infinite loop that starts when the application starts and ends when the application exits. It monitors the state of the block chain, when it lags behind the current values, in addition, requests the necessary blocks from peers, processes events of blocks’ forced rollback for a specified number of blocks and a complete reset of the chain to the genesis block. The detailed implementation of this mechanism is reflected in the Synchronize class.
For synchronization, the peer with the maximum chain height is selected. If the height of the chains matches, then the chain with the highest weight is selected. The weight of the chain is the sum of the winning values (WinValue) of the blocks.
Next, a request is made to the peer to issue all the block numbers with a height exceeding the current local value. Upon receipt of the response, the process of requesting blocks based on the received signatures is started. The thread operates in sequential mode.
Each received block, as part of the synchronization process, is checked for validity, and is added to a copy of the database through the transaction mechanism. A set of blocks is formed to add to the main database. When the verification of the new chain received from this feast is completed and the protocol rules are observed, that is, all the blocks in it are recognized as valid, and the height and weight of the chain declared by the feast are confirmed, the generated set of new blocks is added to the main database.
If a message about the absence of a new block is received in response to a request for a new block, then a search for a common block occurs and the request for blocks begins with the last common block. In this case, a queue is formed for processing blocks that are validated in a separate thread and grouped into a chain.
To prevent a deep rollback and malicious impact aimed at changing the transaction history, there is a concept called checkpoint, below which the chain cannot be rolled back. If a checkpoint is reached when searching for the last block, it means that the chain is invalid and synchronization with it is impossible, and the peer is banned.
The Erachain platform is built on a Peer-to-Peer network or a decentralized network with no critical or guiding points. All nodes that participate in the network are equal.
Interaction between nodes is carried out via a socket. A socket is a single connection between two network applications. The node opens a socket for each peer. Such a connection is bidirectional, —. both sides of the connection are able to send and receive data. Each message is sent through the socket to all connected peers.
The platform can operate in two modes: “developer” and “working".
In the “developer” mode, the nodes use port 9065 for messaging, in the “working” mode, port 9045 is used.
The following 14 types of internetwork messages are presented in the platform:
Nodes interact with each other by transmitting serialized messages according to the corresponding message type in the scheme, on a principle similar to transaction serialization (paragraph 1.3): primitive data types are converted into an array of bytes and sent through the network controller.
Initially, the program knows only the peers that are in the distribution package (the peers.json file). To receive new peers, a request is made to a working peer from known peers. The received new peers are checked (via ping), information about them is stored in the database, then a connection is established with them.
BigDecimal numbers are transmitted compressed to 8 bytes, which does not allow transmitting numbers larger than 23 digits. Therefore, the number of digits after the decimal point is usually transmitted separately as an integer from 0 to 32 (the method of calculating is given in appendix O).
Since version 5.0, Erachain software allows you to run separate block chains that are not connected to the main chain (Main Net).
In such “chains on the side”, the signature is created not by adding a port, but by adding a genesis block signature, since it turns out to be unique for each chain.
In addition, the first 4 bytes of each message (magic mask) the value of the first 4 bytes of the block signature is also obtained, and in case of a mismatch, the external node is blocked by filtering out nodes with other chains.
MainNet is the main network, the main data chain. Ports 904X. The signature data is formed by adding a port to the byte array of data.
TestNet is test networks and chains. The network port is 906X. The signature data is generated as for the MainNet. It is used for quick verification and testing.
DemoNet is a demonstration network and chain. The network port is 906X. The signature data is generated as for the MainNet. The chain is set by the developers and reset periodically to a new chain. Serves for a quick demonstration.
SideNet are third-party networks and chains (sidechains). The network port is 905X. The signature data is formed by adding the data and the genesis block signature.
To integrate external interfaces, an API command has been introduced that issues the signature of the genesis block of the sidechain, which must be added to the transaction signature: api/info. The response contains the side parameter, which specifies the signature of the genesis block, its creation time and the name of the chain.
The balance is a combination of two values — the total received (No. 1) and the remains (No. 2). The difference between them is how much is issued in total.
There are 5 main balances in the Erachain blockchain (there is still an opportunity to add others):
Own balance — №1. Keeps records of ownership. The creator of the transaction debits funds from his account and charges funds to the recipient’s account - transfers funds (direct transaction). This balance usually allows only direct transactions - that is, a forced refund (withdrawal from the recipient - a return transaction) is prohibited. It is possible to go into the negative if the asset is released without a limit on the amount. Then the total minus on the creator’s account reflects how much he has credited this asset to other accounts.
Debt balance — №2. Keeps records of debts. The creator can either transfer it to the recipient's account or take it from the recipient's account (refund). However, these actions may be prohibited for different types of assets. A minus in the balance means how many loans were issued, plus — how much was borrowed. Additionally, a separate table keeps separate records of the balance amounts for pairs of accounts "creditor-debtor", so you can immediately understand the amount of debt between the two participants. This amount is checked each transaction: it is impossible to return more funds than the remains of the given "debtor-creditor" pair. Usually, you can't lend an amount greater than you have in the property and loans have already been issued.
Hold balance — №3. Keeps records of who has how much funds on their hands. In that case transactions are always reverse (refund) — that is, the transaction creator always charges to his account and debits from the recipient's account. The essence of the transaction is that the creator assumes the obligations that he has accepted something in some volume for safekeeping from someone. It is usually used for accounting for storage, delivery, security, leasing, etc. — without transferring ownership. Only the creator of an unlimited asset can have a negative balance.
Spend balance — №4. Keeps records of how much the asset was consumed or produced. While the transaction isn’t implemented in the protocol, but the idea is as follows:
Pledge balance — №5. Keeps records of the number of pledged assets. For example, when submitting an entry to the exchange, the amount of the asset is transferred from the property balance to the pledge balance. Accordingly, it’s always possible to understand how much of the asset on this account is included in the orders on the exchange. It’s not used in transactions yet
The sides of the balance::
No. 1. Total received
No. 2. Balance
No. 3. Everything is gone (calculated as: 1st side minus 2nd side.The exception is for the COMPU asset - it has the 4th position, minus the 2nd - how many it spent on commissions. And in 5th position minus the 2nd - how many have you forged).
MAINNET is the main Erachain chain.
Ports of operation:
The transaction signature is formed by adding the transaction byte code and 4 bytes from the port value, as shown below in the appendices.
TESTNET is a chain for tests. Ports 906X. It is started by specifying the key at startup -tesnet=[TIMESTAMP], where TIMESTAMP is the time of the genesis block creation, in seconds. At the same time, you can run multiple nodes in one test chain if you specify the same TIMESTAMP start time. You can also start the chain on just one node if you specify a simple start parameter -tesnet - in this case, the node does not start the network module and always in sync, and the speed of the blocks is 5 seconds.
In the test chain, each new account contains 100 COMPU and some (randomly generated) ERA number sufficient to generate blocks. In addition, an unverified account can create and verify Persons.
The transaction signature is formed by adding the transaction byte code and 4 bytes from the port value.
DEMONET is a chain for demonstrating capabilities. It is started by specifying the key at startup -tesnet=demo - this is the same test chain (the same ports), but with a preset genesis block time. In this case, the node will connect to other nodes that support this chain. This mode is useful if you need to show previously saved data in the DEMO chain.
SIDECHAIN is a custom chain, free of charge, with a user-generated genesis block and set protocol parameters. sideGENESIS.json, sidePROTOCOL.json, sidePEERS.json files are used for this. For more information about launching the sidechain, see the z_SIDECHAIN_EXAMPLES folder of the application. Ports 905X.
The transaction signature is formed by adding the transaction byte code and the signature bytes of the gensis block, see the info API command.
CLONECHAIN is a branded user chain (Whitelabel), with a genesis block generated by the user and protocol parameters set. The chainGENESIS.json, chainPROTOCOL.json, chainPEERS.json files are used for this. Any ports except 904X-906X.
The transaction signature is formed by adding the transaction byte code and the signature bytes of the gensis block, see the info API command.
Conclusions:
Thus, we can always say that if ports 904X is a mainnet, if ports 905X is a free sidechain, if 906X is a test (or demo) and all the others are branded blockchain (clonchain).
Type conversion is performed to Java types:
A.0. Transaction Header TX_HEAD
№ | Title | Format | The final form | Default value |
---|---|---|---|---|
1 | HEAD (+) | byte[4] | [TYPE, VERSION, FLAGS1, FLAGS2] | |
2 | TIMESTAMP | byte[8] | long | |
3 | extFLAGS (+) | byte[8] | long | 0 (extended flags temporarily unused) |
4 | CREATOR PUBLIC KEY (+) | byte[32] | byte[32] | |
5 | EX_LINK (+) | byte[] | ExLink | if FLAGS1 & 32 |
6 | FEE POW | byte[1] | byte[1] | [0] |
7 | SIGNATURE (+) | byte[64] | byte[64] |
Creating a bytecode for signing
If the bytecode is assembled to pack transactions to a block, only the fields marked (+) are used.
To create a bytecode for signing, the SIGNATURE field is not used.
To create a unique signature between MainNet, Test Net (Demo Net), Sidechain and Clonechain, an addition to the bytecode is used before creating the signature:
Network type | Add | Format | Comment (note) |
---|---|---|---|
MainNet, DemoNet | PORT | byte[4] | |
Sidechain, Clonechain | GENESIS_SIGNATURE | byte[64] | see sidechaines |
Entity — is the Asset, Person, Status, etc. — all that has index number and is created via transaction.
Title | Numbers |
---|---|
ASSET | 1 |
IMPRINT | 2 |
TEMPLATE | 3 |
PERSON | 4 |
STATUS | 5 |
UNION | 6 |
STATEMENT | 7 |
POLL | 8 |
All entities have a common set of fields — the header
№ | Title | Format | The final form | Default value |
---|---|---|---|---|
1 | TYPE | byte[2] | int | [SUB_CLASS,0] |
2 | MAKER | byte[32] | byte[32] | |
3 | NAME LENGTH | byte[1] | uint | |
4 | NAME (+) | byte[NAME LENGTH] | String UTF-8 | max 256B |
5 | ICON LENGTH | byte[2] | uint | |
6 | ICON | byte[ICON LENGTH] | variable | max 64kB |
7 | IMAGE LENGTH | byte[4] | uint | 7th and sign bits are used as flags |
8 | IMAGE (+) | byte[IMAGE LENGTH] | variable | max 1GB |
9 | APP_DATA | byte[] | variable | |
10 | DESCRIPTION LENGTH | byte[4] | uint | |
11 | DESCRIPTION (+) | byte[DESCRIPTION LENGTH] | String | max 2GB |
12 | REFERENCE | byte[64] | byte[64] | not used when creating an asset |
13 | DB_REF | byte[8] | long | SeqNo |
REFERENCE and DB_REF are not used when creating an asset, but are filled in after, when a transaction is entered into the database, because the transaction number becomes known.
(+) means that this field is used to create a signature under the Entity data (see A.24.0)
If the IMAGE_LENGTH flag in the sign bit is checked, it means that the data also contains expanding data — APP_DATA. It is necessary to remove this flag to not distort the value of the image length.
№ | Title | Format | The final form | Default value |
---|---|---|---|---|
1 | LENGTH | byte[4] | int | |
2 | DATA: | byte[] | ||
2.1 | FLAGS1 | byte[2] | 0 | |
2.2 | FLAGS2 | byte[8] | long | see A.A.3 |
START_DATE | byte[8] | long | if set START_FLAG | |
STOP_DATE | byte[8] | long | if set STOP_FLAG | |
TAGS_LEN | byte | byte | if set TAGS_FLAG | |
TAGS_STR | byte[] | String | if set TAGS_FLAG | |
2.3 | ICON_TYPE = DATA[10] | byte | The sign bit is checked, so the Icon is on the link (URL_FLAG). For the type of media, see A.A.3 | |
2.4 | IMAGE_TYPE = DATA[11] | byte | The sign bit is checked, so the Image is on the link (URL_FLAG). For the type of media, see A.A.3 |
URL_FLAG link flags: if the icon/picture is not loaded, but an external link is used instead of the data, then the URL string bytecode in UTF-8 is written to the data.
Title | Value | Comments (notes) |
---|---|---|
START_FLAGS | [0] = 64 | the start time is set |
STOP_FLAGS | [0] = 32 | the stop time is set |
TAGS_FLAGS | 1L << 63 | flag of tags |
If the start or end flag is checked, it means that the next 8 bytes are the timestamp for this value in milliseconds.
If TAGS_FLAG is checked, it means that the user has set a line of tags (words or phrases separated by commas, to do a quick search in the blockchain). The label string is converted to bytes no longer than 255 bytes and TAGS_STR is entered, and the byte length is set in TAGS_LEN (see A.A.2)
MEDIA_TYPE | Value | Notes |
---|---|---|
IMAGE | 0 | .gif, .jpg, .png |
VIDEO | 1 | .mp4 |
SOUND | 2 | .mp3 |
FRAME | 10 (reserved) |
Example of APP_DATA assembly::
Let's say the user uploaded the icon as a video file, and chose an external link to display the image. Then we get:
DATA[0] = -128
DATA[10] = ICON_TYPE = 1
DATA[11] = -128 | IMAGE_TYPE = -128.
Also, if special values are not used, and the icon and the picture are ordinary files, then there is no point in collecting APP_DATA and inserting entities into the bytecode. Accordingly, everything works the same way.
public static byte[] makeAppData(long flags, boolean iconAsURL, int iconType, boolean imageAsURL, int imageType) {
if (flags != 0 || iconAsURL || imageAsURL || iconType != 0 || imageType != 0) {
.
№ | Title | Format | The final form | Default value |
---|---|---|---|---|
1 | TX_HEAD | see A.0 | head=[21,0,0,0] | |
7 | ASSET ITEM | byte[] | byte[] | collected from the Asset fields, see below |
№ | Title | Format | The final form | Default value |
---|---|---|---|---|
1 | HEAD_DATA | byte[] | see A.A.0 | |
12 | QUANITY | byte[8] | long | only if subclass is VENTURE |
13 | SCALE | byte[1] | int | only if subclass is VENTURE |
14 | ASSET_TYPE | byte[1] | int | see below |
№ | Title | Description |
---|---|---|
1 | UNIQUE | without Quanity and Accuracy |
2 | VENTURE | there are Quanity and Accuracy |
**Also, for convenience, a unique class should be used if the user has set the quantity for the issue = 1. Then we assign 1 to the asset and don’t use Quantity and Scale.
Full list of Asset types with a description can be obtained via the API call:
Title | № | Asset subclass | Description | |
---|---|---|---|---|
1 | Chattel | 0 | All things that can be moved | |
2 | Ordinary asset (token) | 1 | 2 | As a rule, what exists in the real world |
3 | Realty | 2 | 2 | Houses, land, etc. |
4 | External currency | 11 | 2 | Currency out of ecosystem |
5 | External service | 12 | 2 | Services |
6 | External shares | 13 | 2 | Shares of enterprises |
7 | External digital bill | 14 | 1 | External digital bill |
8 | Bill of Exchange Ex | 15 | 1 | Bill of exchange |
9 | My loan | 26 | 2 | My duty to another person is some kind of obligation |
10 | Man-hour | 34 | 2 | An hour of a person's working time |
11 | Man-minute | 35 | 2 | A minute of a person's working time |
12 | External rights of claims | 49 | 2 | External rights of claims for anything |
13 | Digital money | 51 | 2 | Domestic assets in the form of money |
14 | Digital service | 52 | 2 | Services inside the blockchain |
15 | Didital shares | 53 | 2 | Only internal accounting |
16 | Digital bonuses | 54 | 2 | Bonuses can be transferred from verified to verified accounts |
17 | Internal access rights | 55 | 2 | To manage the rights to internal services |
18 | Digital voice | 56 | 2 | Unit of voice |
19 | Bank guarantee | 60 | 1 | One guarantee |
20 | Amount of bank guarantees | 61 | 2 | The total amount of money for all issued bank guarantees |
21 | NFT (non-fungible token) | 65 | 1 | NFT |
22 | Digital index | 100 | 2 | Index on external assets, there is no restriction on transfer between anonymous accounts |
23 | Digital right of claim | 119 | 2 | Other claims rights to internal assets |
24 | Counting units | 123 | 2 | They have no cost or requirements, and are exchanged on the exchange only for other accounting units. Serves for internal accounting and reporting. They don't cost anything, you can do any actions on your own behalf |
25 | Own accounting asset | 124 | 2 | Only I can manage it |
26 | My accounting debt | 125 | 2 | What I owe other people. Assigned by the user himself for personal accounting |
27 | Mutual dependence | 126 | 2 | Mutual Assistance Fund for accounting |
28 | Own cash register money | 127 | 2 | For example, accounting for the HOA cash register. with payment requirements and with automatic withdrawal of the claim upon repayment |
29 | Own property accounting | 128 | 2 | Property with direct accrual to OWN balance. For example, accounting of property in the HOA. 4 balances are available, and Refund is available everywhere |
30 | Own accounting of shares | 129 | 2 | Shares with direct accrual to OWN balance. For example, accounting of shares in the HOA. 4 balances are available, and Refund is available everywhere |
If an asset has a list of payments (royalties) for transactions on the exchange with this asset, check the flag in the FLAGS2 low bit. At the same time, the list itself contains the first byte — the number in the list is 1 (thus a maximum of 256 values are placed), and then the values of objects of the type "Account" ExLink (see A.35.3.2)
Additional Asset Flags in FLAGS2
№ | Title | Value | Description |
---|---|---|---|
1 | List of Royalties | 1 (0th bit) | There is a list of royalties in the data |
2 | Impossible to transfer | 2 (1st bit) | So the asset cannot be transferred |
3 | Anonymous ownership is prohibited | 4 (2nd bit) | Cannot be transferred or sold to an anonymous person |
№ | Title | Format | The final form | Default value |
---|---|---|---|---|
1 | ТХ_НЕАD | see A.0 | head=[21,0,0,0] | |
6 | IMPRINT ITEM | byte[] | byte[] | see below |
№ | Title | Format | The final form | Default value |
---|---|---|---|---|
1 | НЕАD_DATA | byte[] | see A.A.0 |
№ | Title | Format | The final form | Default value |
---|---|---|---|---|
1 | ТХ_НЕАD | see A.0 | head=[23,0,0,0] | |
7 | TEMPLATE ITEM | byte[] | byte[] | see below |
№ | Title | Format | The final form | Default value |
---|---|---|---|---|
1 | НЕАD_DATA | byte[] | see A.A.0 |
№ | Title | Format | The final form | Default value |
---|---|---|---|---|
1 | ТХ_НЕАD | see A.0 | head=[24,0,0,FLAGS2] | |
7 | PERSON ITEM | All data from person info | see below |
"FLAGS2 & 1" means "verifying person's public key"
№ | Title | Format | The final form | Description | To sign data |
---|---|---|---|---|---|
1 | HEAD_DATA | byte[] | see A.A.0 | ||
2 | birthdayBytes | byte[8] | long | + | |
3 | deathdayBytes | byte[8] | long | + | |
4 | Gender | byte[1] | int | + | |
5 | raceLength | byte[1] | int | + | |
6 | raceBytes | byte[raceLength] | String | + | |
7 | birthLatitude | byte[4] | float | + | |
8 | birthlongitude | byte[4] | float | + | |
9 | skinColorLength | byte[1] | int | + | |
10 | skinColor | byte[skinColorLength] | String | + | |
11 | eyeColorLength | byte[1] | int | + | |
12 | eyeColorBytes | byte[eyeColorLength] | String | + | |
13 | hairColorLength | byte[1] | int | + | |
14 | hairColorBytes | byte[hairColorLength] | String | + | |
15 | Height | byte[1] | int | + | |
16 | ownerSignature | byte[64] | byte[] |
Fields with the description “to sign data” are collected in a byte array to sign the person's data themselves and create an ownerSignature
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | ТХ_НЕАD | see A.0 | head=[25,0,0,0] | |
2 | STATUS_ITEM | byte[] | see below |
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | НЕАD_DATA | byte[] | see A.A.0 |
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | TX_HEAD | see A.0 | head=[31, VERS,0,0] | |
2 | RECIPIENT | byte[25] | byte[25] | |
3 | ASSET_KEY | byte[8] | long | not used for emails |
4 | AMOUNT | byte[8] | BigDecimal | not used for emails |
5 | HEAD LENGTH | byte[1] | byte[1] | |
6 | HEAD | byte[HEAD LENGTH] | String | |
7 | MESSAGE_LENGTH | byte[4] | long | |
8 | MESSAGE | byte[MESSAGE LENGTH] | String | |
9 | ENCRYPTED | byte[1] | int | |
10 | IS_TEXT | byte[1] | int |
TRANSACTION_TYPE[1] is VERS (transaction version number). "2" is the actual value.
TRANSACTION_TYPE[2] means that the sign bit contains the "no payment" flag. If it is checked, it means that it's an email. In this case, the ASSET_KEY and AMOUNT fields are not involved in the transaction code, including for creating a signature (table below). You can check this flag with the mask - (byte)128 or -(byte) -128
TRANSACTION_TYPE[2], the 6th bit is the BACKWARD flag. If it checked, it means that it's an asset returning transaction (mask 64).
TRANSACTION_TYPE[3] means that the sign bit contains the "no message" flag. If it is checked, it means that the MESSAGE_LENGTH, MESSAGE, ENCRYPTED and IS_TEXT fields are not involved in the transaction code, including for creating a signature.
The difference in scale from the standard (8 decimal places) is entered in TRANSACTION_TYPE[3], see appendix O.
Balance positions and possible directions
Also, the signs of ASSET_KEY, AMOUNT and BACKWARD fields set the position of the balance with which the transaction works.
Table A.31.B. Types of balances and possible directions (flag BACKWARD)
№ | Balance type | ASSET_KEY sign | AMOUNT sign | BACKWARD | Directions |
---|---|---|---|---|---|
1 | OWN | (+) | (+) | false | direct only |
2 | DEBT | (-) | (+) | false, true | both (give and get) |
3 | HOLD | (+) | (-) | true | reverse only |
4 | SPEND | (-) | (-) | false | direct only |
5 | PLEDGE | (+) | (+) | true | reverse only |
6 | RESERVED | (+) | (-) | false | direct only |
Addition: if the pledge is transferred to the same account from which it is taken, it means that the owner's OWN decreases, and the transferor's increases - so the pledge becomes the property of the pledgee.
For example, if you need to collect a debt (confiscate it from someone you previously issued it to), then the ASSET_KEY is negative, the AMOUNT is positive and the BACKWARD is true.
Or, if you need to make a "hold" transaction (ACTION_HOLD), then you need to make the AMOUNT a negative value and check the BACWARD flag = True (this balance type can be only the reverse direction).
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | TX_HEAD | see A.0 | head=[35, VERS, 0,0] | |
2 | EX_DATA_LENGTH | int[4] | int[4] | Size of bytes of ExData |
3 | EX_DATA | byte[] | byte[] | Collected from ExData fields (below) |
4 | SIGNER (not used) |
The current version (VERS) of the transaction is 3, that is:
TRANSACTION_TYPE[1]=3
TRANSACTION_TYPE[3] = -128 (it means that there is EX_DATA. In Java (byte)-128 = 0x10000000).
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | FLAGS | byte[4] | int | [VERS,0,0,0](VERS is 3 now) |
2 | TITLE LENGTH | byte[1] | int | |
3 | TITLE | byte[NAME LENGTH] | String UTF-8 | |
4 | If FLAGS [1] = (byte)-128 has an external link (ExLink), we need compile the ExLink from the following bytes: | |||
R | If FLAGS [1] & 64 — it has recipients and we compile bytes by them | |||
R.1 | RECIPIENTS FLAGS | byte[1] | byte | |
R.2 | RECIPIENTS LENGTH | byte[3] | int | |
R.3 | RECIPIENTs[][20] | byte[][20] | byte[][] | list of “short address” (20 bytes) |
A | If FLAGS [1] &16 — there are authors and we compile them (see ExLink object) | |||
A.1 | AUTHORS FLAGS | byte[1] | byte | |
A.2 | AUTHORS LENGTH | byte[3] | int | |
A.3 | AUTHORS_DATA | byte[] | byte[] | compile by Authors (see A.35.1.1) |
S | If FLAGS [1] &8 — there are sources and we compile them (see ExLink object) | |||
S.1 | SOURCES FLAGS | byte[1] | byte | |
S.2 | SOURCES LENGTH | byte[3] | int | |
S.3 | SOURCES_DATA | byte[] | byte[] | see A.35.1.1 (Sources) |
T | If FLAGS [1] &4 — there are tags and we compile them | |||
T.1 | TAGS LENGTH | byte | byte | length |
T.2 | TAGS | byte[] | byte[] | Bytes from UTF-8 string. Tags are separated by commas for quick search. For example, “business. promotion, company” |
C | If FLAGS [1] & 32 — it’s encrypted, and we compile passwords | |||
C.1 | SECRETS FLAGS | byte[1] | byte | reserved |
C.2 | SECRETS[][] (the first byte is the secret length) | byte[][] | byte[][] | Number of secrets = Number of recipients + 1 (the last secret for the sender) |
DD | DATA[] | byte[] | byte[] | Other data. If it’s encrypted, it sends as is |
In the zero byte of FLAGS is the ExData version. The current version is 3.
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | TYPE | byte | byte | ExLink typr, see A.35.1.1 |
2 | FLAGS | byte | byte | flags |
3 | VALUE_1 | byte | byte | value 1 |
4 | VALUE_2 | byte | byte | value 2 |
5 | SEQ_NO | byte[8] | long | link to the parent, as SeqNo where 4 bytes are block number and 4 bytes are number of transaction in block |
6 | *MEMO LENGTH | byte | int | comment length |
7 | *MEMO | byte[] | byte[] | bytes from UTF-8 string |
*Not all ExLink types have MEMO LENGTH and MEMO fields (see below)
LINK contains the transaction number in the blockchain as SeqNo: Block number + The number of transaction (int + int).
Moreover, if the link type is an Appendix or a Reply, then it is necessary to add all Recipients and the Creator from the parent Document to the list of Recipients by removing the Creator of the current Document (Reply or Appendix) from the received list (to not send this Reply to yourself). You can do it in the Appendix on the Client-side.
Otherwise, if the list is empty and the type is the Reply, then in fact this is Comment.
№ | Title | Document types | VALUE_1, VALUE_2 | MEMO_LENGTH, MEMO |
---|---|---|---|---|
0 | no link | ordinary, general | no | |
1 | APPENDIX | appendix | no | |
2 | REPLY_COMMENT | Comment if there are no Recipients, Reply if there are | no | |
5 | SURETY | surety for other information | no | |
6 | SOURCE | source with share/size and comment | 2 bytes | yes |
7 | AUTHOR | author with share/size and comment | 2 bytes | yes |
Authors and Sources are a link in which there are two additional fields MEMO_LENGTH and MEMO. Moreover, you can write a comment in the MEMO.
At the same time, links of the Authors and Sources type, instead of 2 fields of 1 byte each, have 1 field of 2 bytes, in which a value from 0 to 1000 can be entered.
The length in bytes of the Author and Source link can be variable. It depends on the comment in it.
Author Link - the Person's Number is set as a reference.
Source Link - the transaction number is set as a reference as 8 bytes on SeqNo.
If the sign bit in the RECIPIENTS_FLAGS flag is checked, it means that only recipients can sign.
A.35.1 Authors and Sources.
To compile an ExLink object from AUTHORS_DATA and SOURCES_DATA, see Table A.35.1.
Used to set the list of rewards in Assets.
Compilation of the "Address" object.
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | TYPE | byte | byte | = 0 |
2 | FLAGS | byte | byte | flags = 0 |
3 | VALUE_1 | byte[4] | int | value 1 |
4 | VALUE_2 | byte[4] | int | value 2 |
5 | ADDRESS | byte[20] | byte[20] | link to the parent, as SeqNo where 4 bytes are block number and 4 bytes are number of transaction in block |
6 | MEMO LENGTH | byte | int | comment length |
7 | MEMO | byte[] | byte[] | bytes from UTF-8 string |
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | JSON_LENGTH | byte[4] | int | [0,0,0,0] |
2 | JSON[] | byte[JSON_LENGTH] | byte[] | UTF-8 |
3 | FILES[][] | byte[][] | byte[][] | bytes — files compilation based on data from JSON, see below |
After JSON_LENGTH there is data for files. Files are parsed according to data in JSON, see the F parameter below.
Title | Format | The final form | Description |
---|---|---|---|
MS | String | String | a free-form message |
MSU | boolean | if True: a unique message (the hash is created and entered into the blockchain for search) | |
TM | long | long | template key |
TMU | boolean | if True: a unique template with parameters (the hash is created from the template number and parameters values and entered into the blockchain for search) | |
PR | JSON | JSON | list of template parameters |
HS | JSON | JSON | list of hashes, where the key is the hash and the value is the path to the fileThese hashes do not correlate in any way with the files in the "F" parameter. These files have their own hashes, which are calculated on the fly and are not stored in the blockchain. |
HSU | boolean | if True: every hash is unique and it entered into the blockchain for search | |
F (old, not used) | JSON | JSON | The key is the file number. Values: FN means filename, ZP means zipped (boolean), SZ means size in bytes in data |
F | Array | Array | FN means filename (String), ZP means zipped (boolean), SZ means size in bytes in data, see DATA above. Hashes are also created for files, but they are not saved anywhere. They are simply shown to users for information. |
FU | boolean | if True: every hash is unique and it entered into the blockchain for search, including checking for uniqueness |
Signature is like everyone else — transaction data without a signature + Port (or signature for sidechains).
If you check the Encrypted checkbox, the size of the secret list will be equal to the length of the recipient list + 1. In this case, the last element is a secret for the creator himself. Each secret is a set of bytes where the first byte is its length and then the secret itself is in bytes.
DATA (the DD field in Table A.35.0) is encrypted with one 32-byte shared secret, which is created using CryptoRandom().
Next, this Shared secret of the document is once again encrypted for each Sender-Recipient pair (this is the Pair Secret) by the method of messages in letters. Thus, the Shared secret is a random variable of 32 bytes (to create it, we use the cryptographic SecureRandom to ensure high randomness). To create a Pair Secret for the document's creator, we take his private and public keys.
The function for creating hashes is SHA256.
The hash of the message must be converted to a byte code from the UTF-8. Here is a sample code in Java:
digest(message.getBytes(StandardCharsets.UTF_8));.
Hash for the template:
digest(("" + templateKey
+ params.toJSONString()).getBytes(StandardCharsets.UTF_8));
The code for creating a Hash from a file is here: org.erachain.utils.FileHash (large files are cut into 1MB pieces and hashed separately).
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | TX_HEAD | see A.0 | head=[36,0,1,0]1 means that one address is linked (you can link up to 256) | |
2 | PERSON KEY | byte[8] | long | |
3 | Account PUBLIC KEY | byte[32] | byte[32] | |
4 | DAY | byte[4] | int |
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | TX_HEAD | see A.0 | head=[37,0,0, Params] | |
2 | KEY STATUS | byte[8] | long | |
3 | ITEM TYPE | byte[1] | int | |
4 | KEY ITEM | byte[8] | long | |
5 | DATE START | byte[8] | long | |
6 | DATE END | byte[8] | long | |
7 | VALUE1 | byte[8] | long | if value is not 0, raise flag in the 0th bit of TRANSACTION_TYPE[3] |
8 | VALUE2 | byte[8] | long | if value is not 0, raise flag in the 1st bit of TRANSACTION_TYPE[3] |
9 | DATA1 LENGTH | byte[1] | int | |
10 | DATA | byte[DATA1 LENGTH] | String | flag in the 2nd bit of TRANSACTION_TYPE[3] |
11 | DATA2 LENGTH | byte[1] | int | |
12 | DATA | byte[DATA2 LENGTH] | String | flag in the 3rd bit of TRANSACTION_TYPE[3] |
13 | REF TO PARENT | byte[8] | long | flag in the 4th bit of TRANSACTION_TYPE[3] |
14 | DESCRIPTION | byte[] | String | flag in the 5th bit of TRANSACTION_TYPE[3] |
Comment: if any of the additional values are not 0 or not Null, then you need to raise the appropriate flag as described in the Description column. For example, if a DESCRIPTION is set, then you need to do:
TRANSACTION_TYPE[3] | 32.
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | TX_HEAD | see A.0 | head=[40,0,0,0] | |
2 | BLOCK HEIGHT | byte[4] | int | |
3 | TRANSACTION NUMBER IN BLOCK | byte[4] | int |
Comment: together, the bytes of the block number and the transaction number in block create a link to a transaction of the form SeqNo (Long).
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | TX_HEAD | see A.0 | head=[41,0,0,0]The 3rd and the 4th bytes are quantity of hashes in transaction | |
2 | REFERENCE | byte[8] | long | [0,0,0,0,0,0,0,0] |
3 | URL LENGTH | byte[1] | int | |
4 | URL | byte[URL LENGTH] | String | |
5 | DATA_SIZE_LENGTH | byte[4] | int | |
6 | DATA | byte[DATA_SIZE_LENGTH] | String | |
7 | Hashes[i] | byte[32][i] | byte[32] | i means the number of HASH records. The hash length is 32 bytes. |
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | TX_HEAD | see A.0 | head=[50,0, X1, X2] | |
2 | HAVE_ASSET | byte[8] | long | the code of asset we have |
3 | WANT_ASSET | byte[8] | long | the code of asset we want |
4 | HAVE_AMOUNT | byte[8] | BigDecimal | the amount of asset we have |
5 | WANT_AMOUNT | byte[8] | BigDecimal | the amount of asset we want |
To convert BigDecimal to byte[8], a difference in scale from the standard (8 decimal places) for HAVE_AMOUNT and for WANT_AMOUNT is entered in TRANSACTION_TYPE into the 3rd (X1) and 4th (X2) bytes, see appendix O.
№ | Title | Status number | Description |
---|---|---|---|
1 | UNCONFIRMED | 0 | there is a transaction in the waiting queue with the creation of this order |
2 | ACTIVE | 1 | unfulfilled, still in the stock the exchange |
3 | FULFILLED | 2 | partially executed order |
4 | COMPLETED | 3 | completely executed order |
5 | CANCELED | 4 | canceled |
6 | ORPHANED | -1 | absent |
№ | Title | Format | Description |
---|---|---|---|
1 | TX_HEAD | see A.0 | head=[51,0,0,0] |
2 | ORDER_SIGNATURE | byte[64] | Signature of the transaction that issued the canceled order |
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | TX_HEAD | see A.0 | head=[52,0,0, VAR3] | |
2 | ORDER_SIGNATURE | byte[64] | Signature of the transaction that issued the modified order | |
3 | NEW_AMOUNT | byte[8] | BigDecimal | New WANT or HAVE quantity (see flag in VAR3) |
If VAR3 & 32 > 0, it means that the change concerns the HAVE quantity.
VAR3 & 31 is the value for converting BigDecimal to byte[8]. The difference in scale from the standard (8 decimal places) is entered into the TRANSACTION_TYPE, see appendix O.
Block data structure
Block
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | VERSION | byte[4] | int | current version is 1 |
2 | REFERENCE | byte[64] | byte[64] | |
3 | CREATOR PUBLIC KEY (GENERATOR) | byte[32] | byte[32] | |
4 | CREATORE BALANCE | byte[4] | int | |
5 | TRANSACTION HASH | byte[32] | byte[32] | |
6 | SIGNATURE | byte[64] | byte[64] | |
7 | TRANSACTIONS COUNT | byte[4] | int | |
8 | TRANSACTIONS | byte[from last position to end] | byte[from last position |
Signature
№ | Title | Format | The final form | Description |
---|---|---|---|---|
1 | VERSION | byte[4] | int | current version is 1 |
2 | REFERENCE | byte[64] | byte[64] | |
3 | TRANSACTION HASH | byte[32] | byte[32] |
To save the BigDecimal in the database or create a transaction bytecode.
Example for creating an Order, the accuracy is written in bytes like:
BlockChain.AMOUNT_DEDAULT_SCALE = 8;
TransactionAmount.SCALE_MASK = 31;
// WRITE ACCURACY of AMOUNT HAVE
int different_scale = this.amountHave.scale() - BlockChain.AMOUNT_DEDAULT_SCALE;
BigDecimal amountBase;
if (different_scale != 0) {
// RESCALE AMOUNT
amountBase = this.amountHave.scaleByPowerOfTen(different_scale);
if (different_scale < 0)
different_scale += TransactionAmount.SCALE_MASK + 1;
data[2] = (byte) (data[2] | different_scale);
} else {
amountBase = this.amountHave;
}
// WRITE AMOUNT HAVE
byte[] amountHaveBytes = amountBase.unscaledValue().toByteArray();
byte[] fill_H = new byte[AMOUNT_LENGTH - amountHaveBytes.length];
amountHaveBytes = Bytes.concat(fill_H, amountHaveBytes);
data = Bytes.concat(data, amountHaveBytes);
// WRITE ACCURACY of AMOUNT WANT
different_scale = this.amountWant.scale() - BlockChain.AMOUNT_DEDAULT_SCALE;
if (different_scale != 0) {
// RESCALE AMOUNT
amountBase = this.amountWant.scaleByPowerOfTen(different_scale);
if (different_scale < 0)
different_scale += TransactionAmount.SCALE_MASK + 1;
data[3] = (byte) (data[3] | different_scale);
} else {
amountBase = this.amountWant;
}
// WRITE AMOUNT WANT
byte[] amountWantBytes = amountBase.unscaledValue().toByteArray();
byte[] fill_W = new byte[AMOUNT_LENGTH - amountWantBytes.length];
amountWantBytes = Bytes.concat(fill_W, amountWantBytes);
data = Bytes.concat(data, amountWantBytes);
The document contains a description of the formation of a public and secret keys in PHP, JS. The implementation of the formation of keys in PHP, JS.
ERA cryptographic algorithms use elliptic curve cryptographic algorithms and ED25519 to electronically sign transactions.
Curve25529 to generate a shared secret in the Elliptic-curve Diffie-Hellman (ECDH) algorithm.
https://en.wikipedia.org/wiki/Curve25519
https://en.wikipedia.org/wiki/Elliptic-curve_Diffie–Hellman
AES256 is used for encryption.
Seed is 32 bytes of secret information from which a sequence of key pair seeds is generated:
A key pair is a pair of keys, private and public, required to create a signature.
Key pair seed is the data from which the key pair is created
Versioned bytes are an array of bytes of 1 or 2 bytes in size, in which versions of cryptography are recorded, in which:
Versioned seed of pair is a pair seed that exceeds 32 bytes in length, in which the first bytes are versioned bytes.
To transfer versioning, 1 or 2 bytes are used - versioned bytes that are initially present in the master side, then pass into the key pair seed, and then into the public key. At the same time, when calling standard cryptography methods, versioned keys are withdrawn, so that the standard master seed (32 bytes), the key pair seed (64 bytes) and the public key (32 bytes) enter the cryptographic library. After receiving the results, if necessary, versioned bytes are added to the beginning of the resulting byte array. For example, when generating a key pair from a versioned seed of a key pair, versioned bytes are removed from it, the remainder is transferred to processing, and versioned bytes are added to the result at the beginning of the byte array and the aggregate is already issued as a result of the work.
Initially, there is a master seed (byte[32..34]) and indent (int), which starts with 1
org.erachain.core.wallet.Wallet#generateAccountSeed(byte[] seed, byte[] nonce)
Example:
byte[] digestNonce;
switch (seedAndVersion.length) {
case PublicKeyAccount.PUBLIC_KEY_LENGTH + 2: {
digestNonce = Bytes.concat(nonceBytes,
Arrays.copyOfRange(seedAndVersion, 2, seedAndVersion.length),
nonceBytes);
}
case PublicKeyAccount.PUBLIC_KEY_LENGTH + 1: {
digestNonce = Bytes.concat(nonceBytes,
Arrays.copyOfRange(seedAndVersion, 1, seedAndVersion.length),
nonceBytes);
}
default:
digestNonce = Bytes.concat(nonceBytes,
seedAndVersion,
nonceBytes);
}
digestNonce = Crypto.getInstance().doubleDigest(digestNonce);
switch (seedAndVersion.length) {
case PublicKeyAccount.PUBLIC_KEY_LENGTH + 2: {
return Bytes.concat(Arrays.copyOf(seedAndVersion, 2), digestNonce);
}
case PublicKeyAccount.PUBLIC_KEY_LENGTH + 1: {
return Bytes.concat(Arrays.copyOf(seedAndVersion, 1), digestNonce);
}
default:
return Crypto.getInstance().doubleDigest(digestNonce);
}
Initially, there are two pairs (byte[32..34]) with the first bytes of the cryptography version (the versioned seed of the pair). From the versioned seed pairs, the first bytes are extracted if its size exceeds 32 bytes, and a pair seed of 32 bytes is obtained. Next, it generates a key pair from it using the method described below. After receiving the pair, versioned bytes are added to the public key (1 or 2 depending on the versioned seed length at the input).
The generation of a new key pair occurs according to the following algorithm:
Repository
Libraries are used to generate the key:
Algorythm of working
The initial data is the seed from which the key pair is formed.
Listing:
const sha512 = require('sha512')
var sodiumSignatures = require('sodium-signatures')
const libbase64 = require('libbase64');
var seed = "iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=";
console.log("seed: " + seed);
var seed_decode = libbase64.decode(seed);
var sha512_seed = sha512(seed_decode);
var sha512_seed_decode = libbase64.encode(sha512_seed)
console.log("seed after sha512: " + sha512_seed_decode);
var seed_keyPair = sodiumSignatures.keyPair(seed_decode)
var seed_keyPair_public = seed_keyPair.publicKey;
var seed_keyPair_private = seed_keyPair.secretKey;
console.log("public key: " + libbase64.encode(seed_keyPair_public));
console.log("private key: " + libbase64.encode(seed_keyPair_private));
seed_keyPair_private_part1 = (seed_keyPair.secretKey).slice(0, 32);
seed_keyPair_private_part2 = (seed_keyPair.secretKey).slice(32, 64);
console.log("first 32 byte: " + libbase64.encode(seed_keyPair_private_part1))
console.log("last 32 byte: " + libbase64.encode(seed_keyPair_private_part2))
seed_keyPair_private_sha512_part1 = sha512(seed_keyPair_private_part1)
console.log("sha512 first part by private key: " + libbase64.encode(seed_keyPair_private_sha512_part1));
var sc=seed_keyPair_private_sha512_part1
var firstByte = sc[0];
firstByte &= 248;
sc[0] = firstByte;
var lastByte = sc[31]
lastByte &= 127
lastByte |= 64
sc[31]=lastByte;
console.log("secret key after mascing: " + libbase64.encode(sc));
Libraries used and OS on which testing was performed:
Testing was performed on PHP 7.2 on Windows.
It is necessary to connect extensions in PHP (php.ini)
extension=php_sodium.dll
extension=php_openssl.dll
Example 1
Generating keys in Sodium PHP and converting to ERA format
PHP code:
<?php
echo "--- SODIUM CRYPTO SIGN ---\n";
$bob_seed_base64 = "iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=";
echo "seed sodium base64: " . $bob_seed_base64 ."\n";
$bob_seed = base64_decode($bob_seed_base64);
$sha512_bob_seed = hash('sha512',$bob_seed , true);
echo "sha512(seed) base64: " .base64_encode($sha512_bob_seed) . "\n";
$bob_sha = $sha512_bob_seed;
$bob_encrypt_kp = sodium_crypto_sign_seed_keypair($bob_seed);
//echo "keypair sodium crypto sign base64: " . base64_encode($bob_encrypt_kp) . "\n";
$bob_box_publickey = sodium_crypto_sign_publickey($bob_encrypt_kp);
$bob_box_secretkey = sodium_crypto_sign_secretkey($bob_encrypt_kp);
echo "Public key sodium crypto sign base64: " . base64_encode($bob_box_publickey) . "\n";
echo "Private key sodium crypto sign base64: " . base64_encode($bob_box_secretkey) . "\n";
echo "So format of Sodium Private key is seed_32_bytes & public_key_32_Bytes !!!: " ."\n";
// format of ERA Secret Key is: 64 bytes sha512(seed) and masking of 0 and 31 bytes
// Let's try to convert Sodium Secret key to Era Secret key
$bob_box_era_secretkey = substr($bob_box_secretkey , 0,32 );
echo "first 32 bytes of Private key sodium base64: " . base64_encode($bob_box_era_secretkey) . "\n";
$bob_box_era_secretkey2 = substr($bob_box_secretkey , 32,32 );
echo "last 32 bytes of Private key sodium base64: " . base64_encode($bob_box_era_secretkey2) . "\n";
$bob_box_era_secretkey = hash('sha512',$bob_box_era_secretkey , true);
echo "sha512 first 32 bytes of Private key sodium base64: " . base64_encode($bob_box_era_secretkey) . "\n";
// masking first and last bytes
$first_byte = ord($bob_box_era_secretkey[0]);
$first_byte &= 248;
$bob_box_era_secretkey[0] = chr($first_byte);
$last_byte = ord($bob_box_era_secretkey[31]);
$last_byte &= 63;
$last_byte |= 64;
$bob_box_era_secretkey[31] = chr($last_byte);
echo "After masking 0 and 31 byte here is converted Sodium Private key to ERA format base64: \n". base64_encode($bob_box_era_secretkey) . "\n";
echo"--- JAVA ERA to compare ---\n";
// result from java ERA code for control
echo "Seed java base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=" . "\n";
echo "Public key java base64: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=" . "\n";
echo "Private key java base64: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==". "\n";
?>
Result:
--- SODIUM CRYPTO SIGN ---
seed sodium base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=
sha512(seed) base64: nuGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==
Public key sodium crypto sign base64: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=
Private key sodium crypto sign base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt7FTz3TmcdYiqLT3PBuPer6VMEbNKPEOzLyq5bGWEsheg==
So format of Sodium Private key is seed_32_bytes & public_key_32_Bytes !!!:
first 32 bytes of Private key sodium base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=
last 32 bytes of Private key sodium base64: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=
sha512 first 32 bytes of Private key sodium base64: nuGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==
After masking 0 and 31 byte here is converted Sodium Private key to ERA format base64: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==
--- JAVA ERA to compare ---
Seed java base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=
Public key java base64: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=
Private key java base64: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==
Example 2
Getting a shared secret in Sodium PHP and comparing it with ERA:
PHP code:
<?php
echo "--- Shared secret ---\n";
//--- Generate Bob's keypair
$bob_seed_base64 = "iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=";
echo "Bob seed sodium base64: " .$bob_seed_base64 . "\n";
$bob_seed = base64_decode($bob_seed_base64);
$bob_encrypt_kp = sodium_crypto_sign_seed_keypair($bob_seed);
$bob_publickey = sodium_crypto_sign_publickey($bob_encrypt_kp);
$bob_secretkey = sodium_crypto_sign_secretkey($bob_encrypt_kp);
echo "Bob Public key sodium crypto sign base64: " . base64_encode($bob_publickey) . "\n";
echo "Bob Private key sodium crypto sign base64: " . base64_encode($bob_secretkey) . "\n";
//--- Generate Alice's keypair
$alice_seed_base64 = "1QK+er2p+qdTulnaVez1q1fGATd0ta++g48UdnDd9dQ=";
echo "Alice seed sodium base64: " .$alice_seed_base64 . "\n";
$alice_seed = base64_decode($alice_seed_base64);
$alice_encrypt_kp = sodium_crypto_sign_seed_keypair($alice_seed);
$alice_publickey = sodium_crypto_sign_publickey($alice_encrypt_kp);
$alice_secretkey = sodium_crypto_sign_secretkey($alice_encrypt_kp);
echo "Alice Public key sodium crypto sign base64: " . base64_encode($alice_publickey) . "\n";
echo "Alice Private key sodium crypto sign base64: " . base64_encode($alice_secretkey) . "\n";
//--- Convert SODIUM CRYPTO SIGN ED25519 TO CURVE25529 ---
echo "--- Convert SODIUM CRYPTO SIGN keys ED25519 TO CURVE25529 ---\n";
$bob_curve_pk = sodium_crypto_sign_ed25519_pk_to_curve25519 ( $bob_publickey);
echo "Bob Public key sodium curve25519 base64: " . base64_encode($bob_curve_pk) . "\n";
$bob_curve_sk = sodium_crypto_sign_ed25519_sk_to_curve25519 ( $bob_secretkey );
echo "Bob Private key sodium curve25519 base64: " . base64_encode($bob_curve_sk) . "\n";
$alice_curve_pk = sodium_crypto_sign_ed25519_pk_to_curve25519 ( $alice_publickey);
echo "Alice Public key sodium curve25519 base64: " . base64_encode($alice_curve_pk) . "\n";
$alice_curve_sk = sodium_crypto_sign_ed25519_sk_to_curve25519 ( $alice_secretkey);
echo "Alice Private key sodium curve25519 base64: " . base64_encode($alice_curve_sk) . "\n";
echo "--- Generate DH shared secret ---\n";
// sodium_crypto_scalarmult($secretkey1, $publickey2);
// sodium_crypto_scalarmult($secretkey2, $publickey1);
$ss1 = sodium_crypto_scalarmult($bob_curve_sk,$alice_curve_pk);
echo "Shared secret on Bob side (Bob-Alice) base64: ".base64_encode($ss1)."\n";
$ss2 = sodium_crypto_scalarmult($alice_curve_sk,$bob_curve_pk);
echo "Shared secret on Alice side (Alice-Bob) base64: ".base64_encode($ss2)."\n";
echo "--- JAVA ERA to compare ---\n";
// result from java ERA code for control
echo "Account 1 base64 Seed: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=" . "\n";
echo "Account 1 base64 Public key: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=" . "\n";
echo "Account 1 base64 Private key: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==". "\n";
echo "Account 2 base64 Seed: 1QK+er2p+qdTulnaVez1q1fGATd0ta++g48UdnDd9dQ=" . "\n";
echo "Account 2 base64 Public key: OiQUzvZ4kknpZTbCPghnE78jTSzdi6+kBHsa7OTvJAc=" . "\n";
echo "Account 2 base64 Private key: +G6rh4MKI0krAQDzaVMWxPijhBQJq3XmR3tc7HwK6XZS917QL+G1meSpvHFQBSnnasAvAzFGqTwJu/e5Jk0FKA==". "\n";
echo "Shared secret 1-2 base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=" . "\n";
echo "Shared secret 2-1 base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=" . "\n";
Result:
--- Shared secret ---
Bob seed sodium base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=
Bob Public key sodium crypto sign base64: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=
Bob Private key sodium crypto sign base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt7FTz3TmcdYiqLT3PBuPer6VMEbNKPEOzLyq5bGWEsheg==
Alice seed sodium base64: 1QK+er2p+qdTulnaVez1q1fGATd0ta++g48UdnDd9dQ=
Alice Public key sodium crypto sign base64: OiQUzvZ4kknpZTbCPghnE78jTSzdi6+kBHsa7OTvJAc=
Alice Private key sodium crypto sign base64: 1QK+er2p+qdTulnaVez1q1fGATd0ta++g48UdnDd9dQ6JBTO9niSSellNsI+CGcTvyNNLN2Lr6QEexrs5O8kBw==
--- Convert SODIUM CRYPTO SIGN keys ED25519 TO CURVE25529 ---
Bob Public key sodium curve25519 base64: 9oVonTcqGTVWTb17g+Gp8LgEgXCD64NVQAPammJ/8yA=
Bob Private key sodium curve25519 base64: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClw=
Alice Public key sodium curve25519 base64: r7UwQh5XUOB2W8AwYoig0qgXUUxIZb3MaxFrUcswkA0=
Alice Private key sodium curve25519 base64: +G6rh4MKI0krAQDzaVMWxPijhBQJq3XmR3tc7HwK6XY=
--- Generate DH shared secret ---
Shared secret on Bob side (Bob-Alice) base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
Shared secret on Alice side (Alice-Bob) base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
--- JAVA ERA to compare ---
Account 1 base64 Seed: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=
Account 1 base64 Public key: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=
Account 1 base64 Private key: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==
Account 2 base64 Seed: 1QK+er2p+qdTulnaVez1q1fGATd0ta++g48UdnDd9dQ=
Account 2 base64 Public key: OiQUzvZ4kknpZTbCPghnE78jTSzdi6+kBHsa7OTvJAc=
Account 2 base64 Private key: +G6rh4MKI0krAQDzaVMWxPijhBQJq3XmR3tc7HwK6XZS917QL+G1meSpvHFQBSnnasAvAzFGqTwJu/e5Jk0FKA==
Shared secret 1-2 base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
Shared secret 2-1 base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
Example 3
OpenSSL PHP encryption and comparison with ERA:
PHP code:
<?php
//Already converted to Curve25519 format
$pk1 = base64_decode("9oVonTcqGTVWTb17g+Gp8LgEgXCD64NVQAPammJ/8yA=");
$sk1 = base64_decode("mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClw=");
$pk2= base64_decode("r7UwQh5XUOB2W8AwYoig0qgXUUxIZb3MaxFrUcswkA0=");
$sk2 = base64_decode("+G6rh4MKI0krAQDzaVMWxPijhBQJq3XmR3tc7HwK6XY=");
// sodium_crypto_scalarmult($secretkey1, $publickey2);
// sodium_crypto_scalarmult($secretkey2, $publickey1);
$ss1 = sodium_crypto_scalarmult($sk1,$pk2);
echo "pk1-sk2: ".base64_encode($ss1)."\n";
$ss2 = sodium_crypto_scalarmult($sk2,$pk1);
echo "pk2-sk1: ".base64_encode($ss2)."\n";
// try encrypt with openssl aes-256-cbc
$plaintext = '12345';
$method = 'aes-256-cbc';
// password in ERA is sha256(shared secret).
// Must be exact 32 chars (256 bit) sha 256 from sharedsecret like ERA algoritm
$password = substr(hash('sha256',$ss1, true),0, 32);
echo "password base64: ".base64_encode($password)."\n";
// IV constant must be exact 16 chars (128 bit)
// Value of IV constant from ERA code {6, 4, 3, 8, 1, 2, 1, 2, 7, 2, 3, 8, 5, 7, 1, 1}
$iv = chr(0x06) . chr(0x04) . chr(0x03) . chr(0x08) . chr(0x01) . chr(0x02) . chr(0x01) . chr(0x02) . chr(0x07) . chr(0x02) . chr(0x03) . chr(0x08) . chr(0x05) . chr(0x07) . chr(0x01) . chr(0x01);
echo "iv= ".base64_encode($iv) . "\n";
$encrypted = openssl_encrypt($plaintext,$method, $password,OPENSSL_RAW_DATA, $iv);
// My secret message
$decrypted = openssl_decrypt($encrypted,$method, $password,OPENSSL_RAW_DATA, $iv);
echo 'plaintext= ' . $plaintext . "\n";
echo 'plaintext base64 = ' .base64_encode($plaintext) . "\n";
echo 'cipher= ' . $method . "\n";
echo 'encrypted base64: ' . base64_encode($encrypted) . "\n";
echo 'encrypted hex: ' . bin2hex($encrypted) . "\n";
// For ERA first byte should be added to encrypted data. It is encryption method type = 1
$era_encrypted = chr(0x01) . $encrypted;
echo 'era encrypted base64: ' .base64_encode($era_encrypted) . "\n";
echo 'era encrypted hex: ' .bin2hex($era_encrypted) . "\n";
// java result
echo "--- Java results ---"."\n";
echo "Java plaintext= 12345"."\n";
echo "Java password base64: KPOo4BgJwpcG5O6E+UZ+Fr0ADaD3BPZeR6+3p6MmXs0="."\n";
echo "Java Message bytes base64: MTIzNDU=" . "\n";
echo "Java Encrypted result base64: ASqcvML2g7HzjZ6Lbk2Dq58=" . "\n";
echo "Java Encrypted result hex: 012a9cbcc2f683b1f38d9e8b6e4d83ab9f" . "\n";
echo "Java Result decrypt: 12345" ."\n";
// -- Test decrypting message from ERA
echo "--- Decrypt message from ERA ---" . "\n";
$era_encrypted_base64 = "ASCCq4KtYUJyw4Ax8Jj19O0XnqOe25XTOgvh6GYlz8LgHa6VKAjx+XKZdPgWVlnStw==";
echo "Java Encrypted message base64: " . $era_encrypted_base64 ."\n";
$era_encrypted = base64_decode($era_encrypted_base64);
// cut off first byte from ERA encrypted data
$encrypted = substr($era_encrypted,1, strlen($era_encrypted)-1);
// prepare arguments
$password = substr(hash('sha256',$ss1, true),0, 32);
$method = 'aes-256-cbc';
$iv = chr(0x06) . chr(0x04) . chr(0x03) . chr(0x08) . chr(0x01) . chr(0x02) . chr(0x01) . chr(0x02) . chr(0x07) . chr(0x02) . chr(0x03) . chr(0x08) . chr(0x05) . chr(0x07) . chr(0x01) . chr(0x01);
$decrypted = openssl_decrypt($encrypted,$method, $password,OPENSSL_RAW_DATA, $iv);
echo "Decrypted message: " .$decrypted . "\n";
Result:
pk1-sk2: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
pk2-sk1: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
password base64: KPOo4BgJwpcG5O6E+UZ+Fr0ADaD3BPZeR6+3p6MmXs0=
iv= BgQDCAECAQIHAgMIBQcBAQ==
plaintext= 12345
plaintext base64 = MTIzNDU=
cipher= aes-256-cbc
encrypted base64: Kpy8wvaDsfONnotuTYOrnw==
encrypted hex: 2a9cbcc2f683b1f38d9e8b6e4d83ab9f
era encrypted base64: ASqcvML2g7HzjZ6Lbk2Dq58=
era encrypted hex: 012a9cbcc2f683b1f38d9e8b6e4d83ab9f
--- Java results ---
Java plaintext= 12345
Java password base64: KPOo4BgJwpcG5O6E+UZ+Fr0ADaD3BPZeR6+3p6MmXs0=
Java Message bytes base64: MTIzNDU=
Java Encrypted result base64: ASqcvML2g7HzjZ6Lbk2Dq58=
Java Encrypted result hex: 012a9cbcc2f683b1f38d9e8b6e4d83ab9f
Java Result decrypt: 12345
--- Decrypt message from ERA ---
Java Encrypted message base64: ASCCq4KtYUJyw4Ax8Jj19O0XnqOe25XTOgvh6GYlz8LgHa6VKAjx+XKZdPgWVlnStw==
Decrypted message: This is a secret message
Example 4
Signature generation and verification in Sodium PHP and comparison with ERA:
PHP code:
<?php
echo "--- SODIUM SIGNAGE ---"."\n";
// $sign_seed = random_bytes(SODIUM_CRYPTO_SIGN_SEEDBYTES);
// $sign_pair = sodium_crypto_sign_seed_keypair($sign_seed);
$seed_base64 = "iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=";
$sign_pair = sodium_crypto_sign_seed_keypair(base64_decode($seed_base64));
$sign_secret = sodium_crypto_sign_secretkey($sign_pair);
$sign_public = sodium_crypto_sign_publickey($sign_pair);
//--------------------------------------------------
// Signing
$message = 'Hello';
echo "--- Signaning in Sodium ---" ."\n";
$signature = sodium_crypto_sign_detached($message,$sign_secret);
echo "Message: " .$message . "\n";
echo "Sodium signature of message base64: " . base64_encode($signature) . "\n";
//--- Java result to compare ----
echo "Java message to sign: " ."Hello". "\n";
echo "Java Account 1 create signature base64: owVT9t8OOkN3mZXrDuZtmBAKFgSydeYwFy7qaoZ7Gb4TKrggBZ8SroJcXttdgNtoj43XthJQIZvToDjCVsCg==". "\n";
echo "Java Result account 1 validation signature by public key account 1: true" . "\n";
//--------------------------------------------------
// Verifying
echo "--- Verifing in Sodium: " .$message . "\n";
//Java message: Проверь мою подпись
//Java signature: wl7/LHiRc6LSDJqJxCt0pY9rqRcFj7vxGhOGRhaBjPCaWm5K0VeqYiaAE5sNd8gxOA9h9sx79RZhCrRLIdgiAA==
$message = "Проверь мою подпись";
$signature_base64 = "wl7/LHiRc6LSDJqJxCt0pY9rqRcFj7vxGhOGRhaBjPCaWm5K0VeqYiaAE5sNd8gxOA9h9sx79RZhCrRLIdgiAA==";
$signature = base64_decode($signature_base64);
echo "Message signed in Java: " .$message . "\n";
echo "Signature of message in Java base64: " . base64_encode($signature) . "\n";
$message_valid = sodium_crypto_sign_verify_detached($signature, $message,$sign_public);
echo "Sodium signature validation result: " . $message_valid . "\n";
if (!$message_valid) {
exit('Message has been changed.');
}
?>