Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/contract-verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: ContractVerify

on:
push:
branches: [ "main", "develop" ]
paths:
- 'src/contracts/*.h'
- '.github/workflows/contract-verify.yml'
pull_request:
branches: [ "main", "develop" ]
paths:
- 'src/contracts/*.h'
- '.github/workflows/contract-verify.yml'

jobs:
contract_verify_job:
runs-on: ubuntu-latest
name: Verify smart contract files
steps:
# Checkout repo to use files of the repo as input for container action
- name: Checkout
uses: actions/checkout@v4
- name: Find all contract files to verify
id: filepaths
run: |
files=$(find src/contracts/ -maxdepth 1 -type f -name "*.h" ! -name "*TestExample*" ! -name "*math_lib*" ! -name "*qpi*" -printf "%p\n" | paste -sd, -)
echo "contract-filepaths=$files" >> "$GITHUB_OUTPUT"
- name: Contract verify action step
id: verify
uses: Franziska-Mueller/qubic-contract-verify@v0.3.3-beta
with:
filepaths: '${{ steps.filepaths.outputs.contract-filepaths }}'
26 changes: 17 additions & 9 deletions doc/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ In order to develop a contract, follow these steps:
- Design and implement the interfaces of your contract (the user procedures and user functions along with its inputs and outputs).
The QPI available for implementing the contract is defined in `src/contracts/qpi.h`.
- Implement the system procedures needed and remove all the system procedures that are not needed by your contract.
- Add the short form contract name as a prefix to all global constants (if any).
- Follow the general [qubic style guidelines](https://github.com/qubic/core/blob/main/doc/contributing.md#style-guidelines) when writing your code.
- Add the short form contract name as a prefix to all global constants, structs and classes (if any).
- Make sure your code is efficient. Execution time will cost fees in the future.
Think about the data structures you use, for example if you can use a hash map instead of an array with linear search.
Check if you can optimize code in loops and especially in nested loops.
Expand All @@ -92,8 +93,9 @@ In order to develop a contract, follow these steps:
## Review and tests

Each contract must be validated with the following steps:
1. The contract is verified with a special software tool, ensuring that it complies with the formal requirements mentioned above, such as no use of forbidden C++ features.
(Currently, this tool has not been implemented yet. Thus, this check needs to be done during the review in point 3.)
1. The contract is verified with the [Qubic Contract Verification Tool](https://github.com/Franziska-Mueller/qubic-contract-verify), ensuring that it complies with the formal requirements mentioned above, such as no use of [forbidden C++ features](#restrictions-of-c-language-features).
In the `qubic/core` repository, the tool is run automatically as GitHub workflow for PRs to the `develop` and `main` branches (as well as for commits in these branches).
However, since workflow runs on PRs require maintainer approval, we highly recommend to either build the tool from source or use the GitHub action provided in the tool's repository to analyze your contract header file before opening your PR.
2. The features of the contract have to be extensively tested with automated tests implemented within the Qubic Core's GoogleTest framework.
3. The contract and testing code must be reviewed by at least one of the Qubic Core devs, ensuring it meets high quality standards.
4. Before integrating the contract in the official Qubic Core release, the features of the contract must be tested in a test network with multiple nodes, showing that the contract works well in practice and that the nodes run stable with the contract.
Expand Down Expand Up @@ -547,8 +549,8 @@ In consequence, the procedure is executed but with `qpi.invocationReward() == 0`

## Restrictions of C++ Language Features

It is prohibited to locally instantiating objects or variables on the function call stack.
Instead, use the function and procedure definition macros with the postfix `_WITH_LOCALS` (see above).
It is prohibited to locally instantiate objects or variables on the function call stack. This includes loop index variables `for (int i = 0; ...)`.
Instead, use the function and procedure definition macros with the postfix `_WITH_LOCALS` (see above).
In procedures you alternatively may store temporary variables permanently as members of the state.

Defining, casting, and dereferencing pointers is forbidden.
Expand All @@ -566,22 +568,25 @@ The division operator `/` and the modulo operator `%` are prohibited to prevent
Use `div()` and `mod()` instead, which return zero in case of division by zero.

Strings `"` and chars `'` are forbidden, because they can be used to jump to random memory fragments.
If you want to use `static_assert` you can do so via the `STATIC_ASSERT` macro defined in `qpi.h` which does not require a string literal.

Variadic arguments are prohibited (character combination `...`).
Variadic arguments, template parameter packs, and function parameter packs are prohibited (character combination `...`).

Double underscores `__` must not be used in a contract, because these are reserved for internal functions and compiler macros that are prohibited to be used directly.
For similar reasons, `QpiContext` and `const_cast` are prohibited too.

The scope resolution operator `::` is also prohibited, except for structs, enums, and namespaces defined in contracts and `qpi.h`.

The keywords `typedef` and `union` are prohibited to make the code easier to read and prevent tricking code audits.
The keyword `union` is prohibited to make the code easier to read and prevent tricking code audits.
Similarly, the keywords `typedef` and `using` are only allowed in local scope, e.g. inside structs or functions.
The only exception is `using namespace QPI` which can be used at global scope.

Global variables are not permitted.
Global constants must begin with the name of the contract state struct.
Global constants, structs and classes must begin with the name of the contract state struct.

There is a limit for recursion and depth of nested contract function / procedure calls (the limit is 10 at the moment).

The input and output structs of contract user procedures and functions may only use integer and boolean types (such as `uint64`, `sint8`, `bit`) as well as `id`, `Array`, and `BitArray`.
The input and output structs of contract user procedures and functions may only use integer and boolean types (such as `uint64`, `sint8`, `bit`) as well as `id`, `Array`, and `BitArray`, and struct types containing only allowed types.
Complex types that may have an inconsistent internal state, such as `Collection`, are forbidden in the public interface of a contract.


Expand Down Expand Up @@ -627,3 +632,6 @@ The file `proposal.cpp` has a lot of examples showing how to use both functions.
For example, `getProposalIndices()` shows how to call a contract function requiring input and providing output with `runContractFunction()`.
An example use case of `makeContractTransaction()` can be found in `gqmpropSetProposal()`.
The function `castVote()` is a more complex example combining both, calling a contract function and invoking a contract procedure.



18 changes: 18 additions & 0 deletions doc/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ The code formatting rules are enforced using `clang-format`, ideally setup as a
They are based on the "Microsoft" style with some custom modifications.
Currently, the style guidelines are designed to improve consistency while minimizing the number of changes needed in the existing codebase.

#### Naming

The following naming rules are not strictly enforced, but should be followed at least in new code:

- **Preprocessor symbols** must be defined `ALL_CAPS`.
Expand Down Expand Up @@ -221,6 +223,21 @@ The following naming rules are not strictly enforced, but should be followed at

- **Functions** are named following the same pattern as variables. They usually start with a verb. Examples: `getComputerDigest()`, `processExchangePublicPeers()`, `initContractExec()`.

#### Curly Braces Style

Every opening curly brace should be on a new line. This applies to conditional blocks, loops, functions, classes, structs, etc. For example:

```
if (cond)
{
// do something
}
else
{
// do something else
}
```



## Version naming scheme
Expand Down Expand Up @@ -373,3 +390,4 @@ Even when bound by serializing instructions, the system environment at the time
[AMD64 Architecture Programmer's Manual, Volumes 1-5, 13.2.4 Time-Stamp Counter](https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/40332.pdf)

Another rich source: [Intel® 64 and IA-32 Architectures Software Developer's Manual Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4](https://cdrdv2.intel.com/v1/dl/getContent/671200)

5 changes: 5 additions & 0 deletions src/contract_core/qpi_mining_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ m256i QPI::QpiContextFunctionCall::computeMiningFunction(const m256i miningSeed,
(*score_qpi)(0, publicKey, miningSeed, nonce);
return score_qpi->getLastOutput(0);
}

void QPI::QpiContextFunctionCall::initMiningSeed(const m256i miningSeed) const
{
score_qpi->initMiningData(miningSeed);
}
6 changes: 4 additions & 2 deletions src/contracts/ComputorControlledFund.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ struct CCF : public ContractBase
struct GetProposal_output
{
bit okay;
uint8 _padding0[7];
Array<uint8, 4> _padding0;
Array<uint8, 2> _padding1;
Array<uint8, 1> _padding2;
id proposerPubicKey;
ProposalDataT proposal;
};
Expand Down Expand Up @@ -285,7 +287,7 @@ struct CCF : public ContractBase
continue;

// Option for transfer has been accepted?
if (locals.results.optionVoteCount.get(1) > QUORUM / 2)
if (locals.results.optionVoteCount.get(1) > div<uint32>(QUORUM, 2U))
{
// Prepare log entry and do transfer
locals.transfer.destination = locals.proposal.transfer.destination;
Expand Down
6 changes: 4 additions & 2 deletions src/contracts/GeneralQuorumProposal.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,9 @@ struct GQMPROP : public ContractBase
struct GetProposal_output
{
bit okay;
uint8 _padding0[7];
Array<uint8, 4> _padding0;
Array<uint8, 2> _padding1;
Array<uint8, 1> _padding2;
id proposerPubicKey;
ProposalDataT proposal;
};
Expand Down Expand Up @@ -410,7 +412,7 @@ struct GQMPROP : public ContractBase
}

// Option for changing status quo has been accepted? (option 0 is "no change")
if (locals.mostVotedOptionIndex > 0 && locals.mostVotedOptionVotes > QUORUM / 2)
if (locals.mostVotedOptionIndex > 0 && locals.mostVotedOptionVotes > div<uint32>(QUORUM, 2U))
{
// Set in revenueDonation table (cannot be done in END_EPOCH, because this may overwrite entries that
// are still needed unchanged for this epoch for the revenue donation which is run after END_EPOCH)
Expand Down
1 change: 1 addition & 0 deletions src/contracts/MsVault.h
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ struct MSVAULT : public ContractBase
// [TODO]: Uncomment this to enable live fee update
PUBLIC_PROCEDURE_WITH_LOCALS(voteFeeChange)
{
return;
// locals.ish_in.candidate = qpi.invocator();
// isShareHolder(qpi, state, locals.ish_in, locals.ish_out, locals.ish_locals);
// if (!locals.ish_out.result)
Expand Down
Loading
Loading