Initial, based on PBC Example Contracts
This commit is contained in:
commit
6aaabff522
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Rust / Cargo
|
||||||
|
Cargo.lock
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Expanded rust
|
||||||
|
expanded.rs
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Maven with auto-import, exclude module files
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
|
19
contract-java-test/.coverage-allowlist.txt
Normal file
19
contract-java-test/.coverage-allowlist.txt
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
mia-game/src/lib.rs
|
||||||
|
mia-game/src/zk_compute.rs
|
||||||
|
upgradable-v1/src/lib.rs # junit does not support coverage for upgrade invocations
|
||||||
|
upgradable-v2/src/lib.rs # junit does not support coverage for upgrade invocations
|
||||||
|
upgradable-v2/src/upgrade_from.rs # junit does not support coverage for upgrade invocations
|
||||||
|
upgradable-v3/src/lib.rs # junit does not support coverage for upgrade invocations
|
||||||
|
upgradable-v3/src/upgrade_from.rs # junit does not support coverage for upgrade invocations
|
||||||
|
zk-average-salary/src/contract.rs
|
||||||
|
zk-average-salary/src/zk_compute.rs
|
||||||
|
zk-multi-functional/src/zk_compute.rs
|
||||||
|
zk-second-price-auction/src/contract.rs
|
||||||
|
zk-second-price-auction/src/zk_compute.rs
|
||||||
|
zk-statistics/src/zk_compute.rs
|
||||||
|
zk-struct-open/src/lib.rs
|
||||||
|
zk-struct-open/src/zk_compute.rs
|
||||||
|
zk-voting-simple/src/contract.rs
|
||||||
|
zk-voting-simple/src/zk_compute.rs
|
||||||
|
zk-classification/src/lib.rs
|
||||||
|
zk-classification/src/zk_compute.rs
|
206
contract-java-test/pom.xml
Normal file
206
contract-java-test/pom.xml
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.partisiablockchain.language</groupId>
|
||||||
|
<artifactId>example-contracts-java-test</artifactId>
|
||||||
|
<version>4.18.0</version>
|
||||||
|
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>AGPL-3.0-or-later</name>
|
||||||
|
<url>https://www.gnu.org/licenses/agpl.txt</url>
|
||||||
|
<distribution>repo</distribution>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>gitlab-partisiablockchain</id>
|
||||||
|
<url>https://gitlab.com/api/v4/groups/12499775/-/packages/maven/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
<pluginRepositories>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>gitlab-partisiablockchain</id>
|
||||||
|
<url>https://gitlab.com/api/v4/groups/12499775/-/packages/maven/</url>
|
||||||
|
</pluginRepository>
|
||||||
|
</pluginRepositories>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.partisiablockchain</groupId>
|
||||||
|
<artifactId>pom</artifactId>
|
||||||
|
<version>4.116.0</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<governance.SYS_BINDER>com.partisiablockchain.governance:sys-binder</governance.SYS_BINDER>
|
||||||
|
<governance.REAL_BINDER>com.partisiablockchain.language:real-wasm-binder</governance.REAL_BINDER>
|
||||||
|
<governance.REAL_DEPLOY>com.partisiablockchain.real:deploy</governance.REAL_DEPLOY>
|
||||||
|
<governance.PUB_BINDER>com.partisiablockchain.language:pub-wasm-binder</governance.PUB_BINDER>
|
||||||
|
<governance.PUB_DEPLOY>com.partisiablockchain.governance:pubdeploy</governance.PUB_DEPLOY>
|
||||||
|
<governance.ROUTING>com.partisiablockchain.governance:routing</governance.ROUTING>
|
||||||
|
<governance.CONSENSUS>com.partisiablockchain.consensus.fasttrack:plugin</governance.CONSENSUS>
|
||||||
|
<governance.ACCOUNT_PLUGIN>com.partisiablockchain.governance:fee</governance.ACCOUNT_PLUGIN>
|
||||||
|
<governance.SHARED_OBJECT_STORE>com.partisiablockchain.governance:shared-object-store</governance.SHARED_OBJECT_STORE>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.language</groupId>
|
||||||
|
<artifactId>codegen-lib</artifactId>
|
||||||
|
<version>5.147.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.governance</groupId>
|
||||||
|
<artifactId>sys-binder</artifactId>
|
||||||
|
<version>5.44.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.governance</groupId>
|
||||||
|
<artifactId>fee</artifactId>
|
||||||
|
<version>5.64.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.governance</groupId>
|
||||||
|
<artifactId>pubdeploy</artifactId>
|
||||||
|
<version>5.183.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.governance</groupId>
|
||||||
|
<artifactId>routing</artifactId>
|
||||||
|
<version>5.52.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.language</groupId>
|
||||||
|
<artifactId>pub-wasm-binder</artifactId>
|
||||||
|
<version>5.55.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.language</groupId>
|
||||||
|
<artifactId>real-wasm-binder</artifactId>
|
||||||
|
<version>5.64.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.real</groupId>
|
||||||
|
<artifactId>deploy</artifactId>
|
||||||
|
<version>5.189.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.language</groupId>
|
||||||
|
<artifactId>junit-contract-test</artifactId>
|
||||||
|
<version>5.264.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.consensus.fasttrack</groupId>
|
||||||
|
<artifactId>plugin</artifactId>
|
||||||
|
<version>5.53.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.partisiablockchain.governance</groupId>
|
||||||
|
<artifactId>shared-object-store</artifactId>
|
||||||
|
<version>5.55.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>build-helper-maven-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>generate-test-sources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>add-test-source</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<sources>
|
||||||
|
<source>${project.basedir}/target/generated-test-sources/generated-abi</source>
|
||||||
|
</sources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.partisiablockchain.language</groupId>
|
||||||
|
<artifactId>abi-generation-maven-plugin</artifactId>
|
||||||
|
<version>5.147.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>generate-test-sources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>abi-code-gen</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<generateTestSources>true</generateTestSources>
|
||||||
|
<pbcPath>${project.basedir}/../rust/target/wasm32-unknown-unknown/release/</pbcPath>
|
||||||
|
<deserializeRpc>true</deserializeRpc>
|
||||||
|
<namedTypes>
|
||||||
|
<namedType>Model</namedType>
|
||||||
|
<namedType>InternalVertex</namedType>
|
||||||
|
<namedType>LeafVertex</namedType>
|
||||||
|
<namedType>Sample</namedType>
|
||||||
|
</namedTypes>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>properties-maven-plugin</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>generate-test-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>write-project-properties</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputFile>${project.build.testOutputDirectory}/maven.properties</outputFile>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<version>3.5.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy</id>
|
||||||
|
<phase>generate-test-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>list</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputFile>${project.build.testOutputDirectory}/maven.dependencies</outputFile>
|
||||||
|
<outputAbsoluteArtifactFilename>true</outputAbsoluteArtifactFilename>
|
||||||
|
<excludeTransitive>true</excludeTransitive>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
76
run-java-tests.sh
Executable file
76
run-java-tests.sh
Executable file
|
@ -0,0 +1,76 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
compile_contracts() {
|
||||||
|
pushd rust 1> /dev/null || exit
|
||||||
|
if [ "$coverage" = true ]; then
|
||||||
|
cargo partisia-contract build --release --coverage
|
||||||
|
else
|
||||||
|
cargo partisia-contract build --release
|
||||||
|
fi
|
||||||
|
popd 1> /dev/null || exit
|
||||||
|
}
|
||||||
|
|
||||||
|
run_java_tests() {
|
||||||
|
if [ "$coverage" = true ]; then
|
||||||
|
test_with_coverage
|
||||||
|
else
|
||||||
|
test_without_coverage
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
merge_and_report() {
|
||||||
|
pushd contract-java-test 1> /dev/null || exit
|
||||||
|
|
||||||
|
# Determine profraw files
|
||||||
|
find ./target/coverage/profraw/ -type f -name '*.profraw' > ./target/coverage/all-profraw-files
|
||||||
|
|
||||||
|
# Merge profraw
|
||||||
|
rust-profdata merge -sparse --input-files=./target/coverage/all-profraw-files --output=target/coverage/java_test.profdata
|
||||||
|
|
||||||
|
# Generate report
|
||||||
|
find ../rust/target/wasm32-unknown-unknown/release/ -type f -executable -print |
|
||||||
|
sed "s/^/--object /" |
|
||||||
|
xargs rust-cov show --ignore-filename-regex=".cargo\.*" --ignore-filename-regex="target\.*" \
|
||||||
|
--instr-profile=target/coverage/java_test.profdata --Xdemangler=rustfilt --format="html" \
|
||||||
|
--output-dir=target/coverage/html
|
||||||
|
popd 1> /dev/null || exit
|
||||||
|
}
|
||||||
|
|
||||||
|
test_with_coverage() {
|
||||||
|
# Run contract tests
|
||||||
|
pushd contract-java-test 1> /dev/null || exit
|
||||||
|
mvn test -Dcoverage
|
||||||
|
popd 1> /dev/null || exit
|
||||||
|
|
||||||
|
merge_and_report
|
||||||
|
}
|
||||||
|
|
||||||
|
test_without_coverage() {
|
||||||
|
# Run contract tests
|
||||||
|
pushd contract-java-test 1> /dev/null || exit
|
||||||
|
mvn test
|
||||||
|
popd 1> /dev/null || exit
|
||||||
|
}
|
||||||
|
|
||||||
|
help() {
|
||||||
|
echo "usage: ./run-java-tests.sh [-b][-c][-h]"
|
||||||
|
echo "-b Build the contracts before running tests (if coverage is enabled also generates the instrumented executables)"
|
||||||
|
echo "-c Test with coverage enabled"
|
||||||
|
echo "-h Print this help message"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
while getopts :bch flag; do
|
||||||
|
case "${flag}" in
|
||||||
|
b) build=true ;;
|
||||||
|
c) coverage=true ;;
|
||||||
|
h) help ;;
|
||||||
|
*) echo "Invalid option: -$flag." && help ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$build" = true ]; then
|
||||||
|
compile_contracts
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_java_tests
|
54
rust/Cargo.toml
Normal file
54
rust/Cargo.toml
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
|
members = [
|
||||||
|
"voting",
|
||||||
|
"multi-voting",
|
||||||
|
"petition",
|
||||||
|
"ping",
|
||||||
|
"mia-game",
|
||||||
|
"nickname",
|
||||||
|
"access-control",
|
||||||
|
"dns",
|
||||||
|
"dns-voting-client",
|
||||||
|
"zk-second-price-auction",
|
||||||
|
"zk-struct-open",
|
||||||
|
"zk-immediate-open",
|
||||||
|
"zk-voting-simple",
|
||||||
|
"zk-average-salary",
|
||||||
|
"zk-statistics",
|
||||||
|
"zk-multi-functional",
|
||||||
|
"zk-file-share",
|
||||||
|
"zk-classification",
|
||||||
|
"upgradable-v1",
|
||||||
|
"upgradable-v2",
|
||||||
|
"upgradable-v3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
version = "5.203.0"
|
||||||
|
description = "Example contract for the Partisia Blockchain."
|
||||||
|
homepage = "https://gitlab.com/partisiablockchain/language/example-contracts"
|
||||||
|
repository = "https://gitlab.com/partisiablockchain/language/example-contracts"
|
||||||
|
documentation = "https://gitlab.com/partisiablockchain/language/example-contracts"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[workspace.metadata.partisiablockchain]
|
||||||
|
cargo-partisia = "5.155.0"
|
||||||
|
|
||||||
|
[workspace.metadata.zkcompiler]
|
||||||
|
url = "https://gitlab.com/api/v4/groups/12499775/-/packages/maven/com/partisiablockchain/language/zkcompiler/5.61.0/zkcompiler-5.61.0-jar-with-dependencies.jar"
|
||||||
|
|
||||||
|
[workspace.metadata.abi-cli]
|
||||||
|
url = "https://gitlab.com/api/v4/groups/12499775/-/packages/maven/com/partisiablockchain/language/abi-cli/5.147.0/abi-cli-5.147.0-jar-with-dependencies.jar"
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
pbc_contract_common = { git = "https://git@gitlab.com/partisiablockchain/language/contract-sdk.git", tag = "v.16.79.0" }
|
||||||
|
pbc_contract_codegen = { git = "https://git@gitlab.com/partisiablockchain/language/contract-sdk.git", tag = "v.16.79.0" }
|
||||||
|
pbc_traits = { git = "https://git@gitlab.com/partisiablockchain/language/contract-sdk.git", tag = "v.16.79.0" }
|
||||||
|
pbc_lib = { git = "https://git@gitlab.com/partisiablockchain/language/contract-sdk.git", tag = "v.16.79.0" }
|
||||||
|
read_write_rpc_derive = { git = "https://git@gitlab.com/partisiablockchain/language/contract-sdk.git", tag = "v.16.79.0" }
|
||||||
|
read_write_state_derive = { git = "https://git@gitlab.com/partisiablockchain/language/contract-sdk.git", tag = "v.16.79.0" }
|
||||||
|
create_type_spec_derive = { git = "https://git@gitlab.com/partisiablockchain/language/contract-sdk.git", tag = "v.16.79.0" }
|
||||||
|
pbc_zk = { git = "https://git@gitlab.com/partisiablockchain/language/contract-sdk.git", tag = "v.16.79.0" }
|
31
rust/mia-game/Cargo.toml
Normal file
31
rust/mia-game/Cargo.toml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[package]
|
||||||
|
name = "mia-game"
|
||||||
|
readme = "README.md"
|
||||||
|
version.workspace = true
|
||||||
|
description.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
abi = ["pbc_contract_common/abi", "pbc_contract_codegen/abi", "pbc_traits/abi", "create_type_spec_derive/abi", "pbc_lib/abi", "pbc_zk/abi"]
|
||||||
|
plus_metadata = []
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/lib.rs"
|
||||||
|
crate-type = ['rlib', 'cdylib']
|
||||||
|
|
||||||
|
[package.metadata.zk]
|
||||||
|
zk-compute-path = "src/zk_compute.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pbc_contract_common.workspace = true
|
||||||
|
pbc_traits.workspace = true
|
||||||
|
pbc_lib.workspace = true
|
||||||
|
read_write_rpc_derive.workspace = true
|
||||||
|
read_write_state_derive.workspace = true
|
||||||
|
create_type_spec_derive.workspace = true
|
||||||
|
pbc_contract_codegen.workspace = true
|
||||||
|
pbc_zk.workspace = true
|
43
rust/mia-game/README.md
Normal file
43
rust/mia-game/README.md
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# Mia Gaming Contract
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
3 or more players, all start with 6 lives.
|
||||||
|
|
||||||
|
The complete order of rolls (from highest to lowest):
|
||||||
|
|
||||||
|
21 (Mia), 31 (Little Mia), 66, 55, 44, 33, 22, 11, 65, 64, 63, 62, 61, 54, 53, 52, 51, 43, 42, 41, 32
|
||||||
|
|
||||||
|
### Order of actions
|
||||||
|
|
||||||
|
The first player rolls the dice and keeps their value concealed from the other players.
|
||||||
|
|
||||||
|
The player then has three choices:
|
||||||
|
|
||||||
|
- Tell the truth and announce what has been rolled.
|
||||||
|
- Lie and announce a greater value than that rolled.
|
||||||
|
- Lie and announce a lesser value.
|
||||||
|
|
||||||
|
The concealed dice are then passed to the next player in a clockwise fashion.
|
||||||
|
|
||||||
|
The receiving player now has two options:
|
||||||
|
|
||||||
|
- Believe the passer, roll the dice and pass it on, announcing a higher value—with or without looking at them.
|
||||||
|
- Call the passer a liar and look at the dice.
|
||||||
|
If the dice show a lesser value than that announced, the passer loses a life and the receiving player starts a new round.
|
||||||
|
However, if the dice show a greater or equal value, the current player loses a life and the next player starts a new round.
|
||||||
|
|
||||||
|
**Note:**
|
||||||
|
Each player must always announce a value greater than or equal to the previous value announced.
|
||||||
|
|
||||||
|
##### Mia announced
|
||||||
|
|
||||||
|
If Mia is announced, the next player has two choices:
|
||||||
|
|
||||||
|
- They may give up without looking at the dice and lose one life.
|
||||||
|
- They may look at the dice. If it was a Mia, they lose two lives.
|
||||||
|
If it wasn't, the previous player loses two lives.
|
||||||
|
|
||||||
|
### Winning the game
|
||||||
|
|
||||||
|
Last remaining player is the winner.
|
572
rust/mia-game/src/lib.rs
Normal file
572
rust/mia-game/src/lib.rs
Normal file
|
@ -0,0 +1,572 @@
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate pbc_contract_codegen;
|
||||||
|
extern crate pbc_contract_common;
|
||||||
|
extern crate pbc_lib;
|
||||||
|
|
||||||
|
mod zk_compute;
|
||||||
|
|
||||||
|
use create_type_spec_derive::CreateTypeSpec;
|
||||||
|
use pbc_contract_common::address::Address;
|
||||||
|
use pbc_contract_common::context::ContractContext;
|
||||||
|
use pbc_contract_common::events::EventGroup;
|
||||||
|
use pbc_contract_common::sorted_vec_map::{SortedVecMap, SortedVecSet};
|
||||||
|
use pbc_contract_common::zk::{SecretVarId, ZkInputDef, ZkState, ZkStateChange};
|
||||||
|
use pbc_traits::ReadWriteState;
|
||||||
|
use pbc_zk::{Sbi8, SecretBinary};
|
||||||
|
use read_write_rpc_derive::ReadWriteRPC;
|
||||||
|
use read_write_state_derive::ReadWriteState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata information associated with each individual variable.
|
||||||
|
*/
|
||||||
|
#[derive(ReadWriteState, ReadWriteRPC, Debug)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum SecretVarType {
|
||||||
|
#[discriminant(0)]
|
||||||
|
/// Randomness used for dice throws.
|
||||||
|
Randomness {},
|
||||||
|
#[discriminant(1)]
|
||||||
|
/// Result of dice throws.
|
||||||
|
ThrowResult {},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The state of the Mia game, which is persisted on-chain.
|
||||||
|
#[state]
|
||||||
|
pub struct MiaState {
|
||||||
|
// The current amount of randomness contributions received for a dice throw.
|
||||||
|
nr_of_randomness_contributions: u32,
|
||||||
|
// The number of players at the start of the game.
|
||||||
|
nr_of_players_at_the_start: u32,
|
||||||
|
// The player currently throwing the dice and declaring a value.
|
||||||
|
player_throwing: u32,
|
||||||
|
// The current phase the game is in, to determine allowed actions.
|
||||||
|
game_phase: GamePhase,
|
||||||
|
// The players at the start of the game.
|
||||||
|
starting_players: Vec<Address>,
|
||||||
|
// The current players active in the game.
|
||||||
|
players: Vec<Address>,
|
||||||
|
// The remaining lives of the players in the game.
|
||||||
|
player_lives: SortedVecMap<Address, u8>,
|
||||||
|
// The last throw's secret variable id.
|
||||||
|
throw_result_id: Option<SecretVarId>,
|
||||||
|
// The stated value of the current throw.
|
||||||
|
stated_throw: Option<DiceThrow>,
|
||||||
|
// The revealed value of a throw.
|
||||||
|
throw_result: Option<DiceThrow>,
|
||||||
|
// The announced throw value, where the next announced throw must be higher than, to be eligible.
|
||||||
|
throw_to_beat: DiceThrow,
|
||||||
|
// The winner of the game.
|
||||||
|
winner: Option<Address>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MiaState {
|
||||||
|
/// Get the current player in turn.
|
||||||
|
fn current_player(&self) -> &Address {
|
||||||
|
&self.players[self.player_throwing as usize]
|
||||||
|
}
|
||||||
|
/// Get the next player in turn.
|
||||||
|
fn next_player(&self) -> &Address {
|
||||||
|
&self.players[(self.player_throwing + 1) as usize % self.players.len()]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace the current player in turn with the next player.
|
||||||
|
fn go_to_next_player(&mut self) {
|
||||||
|
self.player_throwing = (self.player_throwing + 1) % self.players.len() as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether a player is dead i.e. have no lives left.
|
||||||
|
fn is_player_dead(&self, player: Address) -> bool {
|
||||||
|
self.player_lives[&player] == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a dead player from the list of players.
|
||||||
|
fn remove_dead_player(&mut self, player: Address) {
|
||||||
|
self.players.retain(|p| player != *p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reduce a players lives by a given integer.
|
||||||
|
fn reduce_players_life_by(&mut self, player: Address, lives_lost: u8) {
|
||||||
|
if self.player_lives[&player] >= lives_lost {
|
||||||
|
self.player_lives
|
||||||
|
.insert(player, self.player_lives[&player] - lives_lost);
|
||||||
|
} else {
|
||||||
|
self.player_lives.insert(player, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether the game if finished, i.e. whether only one player remains.
|
||||||
|
fn is_the_game_finished(&self) -> bool {
|
||||||
|
self.players.len() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the last remaining player, the winner.
|
||||||
|
fn get_winner(&self) -> Address {
|
||||||
|
*self.players.first().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A throw of two dice.
|
||||||
|
#[derive(ReadWriteState, ReadWriteRPC, CreateTypeSpec, Debug, Copy, Clone)]
|
||||||
|
pub struct DiceThrow {
|
||||||
|
d1: u8,
|
||||||
|
d2: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiceThrow {
|
||||||
|
/// The value of each die is reduced to be between 0 and 5.
|
||||||
|
fn reduce(&self) -> DiceThrow {
|
||||||
|
DiceThrow {
|
||||||
|
d1: self.d1 % 6,
|
||||||
|
d2: self.d2 % 6,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether a throw is better than the current dice throw to beat.
|
||||||
|
/// The dice throws are compared based on their associated values.
|
||||||
|
fn better_than_or_equal(self, actual: DiceThrow) -> bool {
|
||||||
|
self.get_throw_score() >= actual.get_throw_score()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether a dice throw is Mia, i.e. is (0,1) or (1,0).
|
||||||
|
fn is_mia(self) -> bool {
|
||||||
|
(self.d1 == 0 && self.d2 == 1) || (self.d2 == 0 && self.d1 == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether a dice throw is Little Mia, i.e. is (0,2) or (2,0).
|
||||||
|
fn is_little_mia(self) -> bool {
|
||||||
|
(self.d1 == 0 && self.d2 == 2) || (self.d2 == 0 && self.d1 == 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether both dices in the dice throw have the same value.
|
||||||
|
fn is_pair(self) -> bool {
|
||||||
|
self.d1 == self.d2
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the score of a dice throw.
|
||||||
|
/// The throw values are determined such that the highest roll is Mia, then Little Mia,
|
||||||
|
/// followed by the doubles from (5,5) to (0,0), and then all other rolls from (5,4)
|
||||||
|
/// down to (2,1).
|
||||||
|
fn get_throw_score(self) -> u8 {
|
||||||
|
let mut value = 0;
|
||||||
|
if self.is_mia() {
|
||||||
|
value += 128;
|
||||||
|
};
|
||||||
|
if self.is_little_mia() {
|
||||||
|
value += 64;
|
||||||
|
};
|
||||||
|
if self.is_pair() {
|
||||||
|
value += 32;
|
||||||
|
};
|
||||||
|
if (self.d1 == 5) || (self.d2 == 5) {
|
||||||
|
value += 16;
|
||||||
|
}
|
||||||
|
if (self.d1 == 4) || (self.d2 == 4) {
|
||||||
|
value += 8;
|
||||||
|
}
|
||||||
|
if (self.d1 == 3) || (self.d2 == 3) {
|
||||||
|
value += 4;
|
||||||
|
}
|
||||||
|
if (self.d1 == 2) || (self.d2 == 2) {
|
||||||
|
value += 2;
|
||||||
|
}
|
||||||
|
if (self.d1 == 1) || (self.d2 == 1) {
|
||||||
|
value += 1;
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The contribution each player must send to make a dice throw. The contributions should be in the
|
||||||
|
/// interval \[ 0, 5 \] inclusive. If the contributions are outside this interval,
|
||||||
|
/// they are normalized to the interval.
|
||||||
|
#[derive(CreateTypeSpec, SecretBinary)]
|
||||||
|
pub struct RandomContribution {
|
||||||
|
d1: Sbi8,
|
||||||
|
d2: Sbi8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The different phases the contract can be in before, during and after a game of Mia.
|
||||||
|
#[derive(ReadWriteRPC, ReadWriteState, CreateTypeSpec, Debug, PartialEq, Copy, Clone)]
|
||||||
|
pub enum GamePhase {
|
||||||
|
#[discriminant(0)]
|
||||||
|
/// The game has been initialized.
|
||||||
|
Start {},
|
||||||
|
#[discriminant(1)]
|
||||||
|
/// Players can add randomness as secret inputs.
|
||||||
|
AddRandomness {},
|
||||||
|
#[discriminant(2)]
|
||||||
|
/// The player in turn can throw the dice.
|
||||||
|
Throw {},
|
||||||
|
#[discriminant(3)]
|
||||||
|
/// The player in turn can announce their throw.
|
||||||
|
Announce {},
|
||||||
|
#[discriminant(4)]
|
||||||
|
/// The next player in turn can believe or call out the player's announced throw.
|
||||||
|
Decide {},
|
||||||
|
#[discriminant(5)]
|
||||||
|
/// If a player was called out, they can reveal their actual throw.
|
||||||
|
Reveal {},
|
||||||
|
#[discriminant(6)]
|
||||||
|
/// The game is finished.
|
||||||
|
Done {},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a new mia game.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `_ctx` - the contract context containing information about the sender and the blockchain.
|
||||||
|
/// *
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The initial state of the petition, with no signers.
|
||||||
|
///
|
||||||
|
#[init(zk = true)]
|
||||||
|
pub fn initialize(
|
||||||
|
context: ContractContext,
|
||||||
|
zk_state: ZkState<SecretVarType>,
|
||||||
|
addresses_to_play: Vec<Address>,
|
||||||
|
) -> (MiaState, Vec<EventGroup>) {
|
||||||
|
assert!(
|
||||||
|
addresses_to_play.len() >= 3,
|
||||||
|
"There must be at least 3 players to play Mia."
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
SortedVecSet::from(addresses_to_play.clone()).len(),
|
||||||
|
addresses_to_play.len(),
|
||||||
|
"No duplicates in players."
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut state = MiaState {
|
||||||
|
starting_players: addresses_to_play.clone(),
|
||||||
|
players: addresses_to_play.clone(),
|
||||||
|
nr_of_players_at_the_start: addresses_to_play.len() as u32,
|
||||||
|
player_lives: SortedVecMap::new(),
|
||||||
|
game_phase: GamePhase::Start {},
|
||||||
|
player_throwing: 0,
|
||||||
|
nr_of_randomness_contributions: 0,
|
||||||
|
throw_result_id: None,
|
||||||
|
stated_throw: None,
|
||||||
|
throw_result: None,
|
||||||
|
winner: None,
|
||||||
|
throw_to_beat: DiceThrow { d1: 1, d2: 2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
for address in addresses_to_play {
|
||||||
|
state.player_lives.insert(address, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
(state, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start the game.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ctx` - the contract context containing information about the sender and the blockchain.
|
||||||
|
/// * `state` - the current state of the game.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The updated vote state reflecting the new signing.
|
||||||
|
///
|
||||||
|
#[action(shortname = 0x01, zk = true)]
|
||||||
|
pub fn start_round(
|
||||||
|
context: ContractContext,
|
||||||
|
mut state: MiaState,
|
||||||
|
zk_state: ZkState<SecretVarType>,
|
||||||
|
) -> (MiaState, Vec<EventGroup>, Vec<ZkStateChange>) {
|
||||||
|
assert_eq!(
|
||||||
|
state.players[state.player_throwing as usize], context.sender,
|
||||||
|
"Only the player whose turn it is can start the round."
|
||||||
|
);
|
||||||
|
state.game_phase = GamePhase::AddRandomness {};
|
||||||
|
|
||||||
|
(state, vec![], vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add randomness for the next dice throw.
|
||||||
|
/// The sender must be a player in the game to add randomness.
|
||||||
|
#[zk_on_secret_input(shortname = 0x40, secret_type = "RandomContribution")]
|
||||||
|
pub fn add_randomness_to_throw(
|
||||||
|
context: ContractContext,
|
||||||
|
state: MiaState,
|
||||||
|
zk_state: ZkState<SecretVarType>,
|
||||||
|
) -> (
|
||||||
|
MiaState,
|
||||||
|
Vec<EventGroup>,
|
||||||
|
ZkInputDef<SecretVarType, RandomContribution>,
|
||||||
|
) {
|
||||||
|
assert_eq!(
|
||||||
|
state.game_phase,
|
||||||
|
GamePhase::AddRandomness {},
|
||||||
|
"Must be in the AddRandomness phase to input secret randomness."
|
||||||
|
);
|
||||||
|
assert!(state.starting_players.contains(&context.sender));
|
||||||
|
assert!(
|
||||||
|
zk_state
|
||||||
|
.secret_variables
|
||||||
|
.iter()
|
||||||
|
.chain(zk_state.pending_inputs.iter())
|
||||||
|
.all(|(_, secret_variable)| secret_variable.owner != context.sender),
|
||||||
|
"Each Player is only allowed to send one contribution to the randomness of the dice throw. Sender: {:?}",
|
||||||
|
context.sender
|
||||||
|
);
|
||||||
|
|
||||||
|
let input_def = ZkInputDef::with_metadata(
|
||||||
|
Some(SHORTNAME_INPUTTED_VARIABLE),
|
||||||
|
SecretVarType::Randomness {},
|
||||||
|
);
|
||||||
|
|
||||||
|
(state, vec![], input_def)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Automatically called when a variable is confirmed on chain.
|
||||||
|
///
|
||||||
|
/// Initializes opening.
|
||||||
|
#[zk_on_variable_inputted(shortname = 0x01)]
|
||||||
|
fn inputted_variable(
|
||||||
|
context: ContractContext,
|
||||||
|
mut state: MiaState,
|
||||||
|
zk_state: ZkState<SecretVarType>,
|
||||||
|
variable_id: SecretVarId,
|
||||||
|
) -> MiaState {
|
||||||
|
if state.nr_of_randomness_contributions == state.nr_of_players_at_the_start - 1 {
|
||||||
|
state.nr_of_randomness_contributions = 0;
|
||||||
|
state.game_phase = GamePhase::Throw {};
|
||||||
|
} else {
|
||||||
|
state.nr_of_randomness_contributions += 1;
|
||||||
|
}
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start the computation to compute the dice throw.
|
||||||
|
#[action(shortname = 0x02, zk = true)]
|
||||||
|
pub fn throw_dice(
|
||||||
|
context: ContractContext,
|
||||||
|
state: MiaState,
|
||||||
|
zk_state: ZkState<SecretVarType>,
|
||||||
|
) -> (MiaState, Vec<EventGroup>, Vec<ZkStateChange>) {
|
||||||
|
assert_eq!(
|
||||||
|
state.game_phase,
|
||||||
|
GamePhase::Throw {},
|
||||||
|
"The dice can only be thrown in the Throw phase"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
*state.current_player(),
|
||||||
|
context.sender,
|
||||||
|
"Only the player in turn can throw the dice. It is currently {:?}s turn",
|
||||||
|
state.current_player()
|
||||||
|
);
|
||||||
|
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
vec![],
|
||||||
|
vec![zk_compute::compute_dice_throw_start(
|
||||||
|
Some(SHORTNAME_SUM_COMPUTE_COMPLETE),
|
||||||
|
&SecretVarType::ThrowResult {},
|
||||||
|
)],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Automatically called when the sum of the random contributions are done.
|
||||||
|
/// Transfers the resulting throw to the player throwing the dice.
|
||||||
|
#[zk_on_compute_complete(shortname = 0x01)]
|
||||||
|
fn sum_compute_complete(
|
||||||
|
_context: ContractContext,
|
||||||
|
mut state: MiaState,
|
||||||
|
zk_state: ZkState<SecretVarType>,
|
||||||
|
output_variables: Vec<SecretVarId>,
|
||||||
|
) -> (MiaState, Vec<EventGroup>, Vec<ZkStateChange>) {
|
||||||
|
let Some(result_id) = output_variables.first() else {
|
||||||
|
panic!("No result")
|
||||||
|
};
|
||||||
|
|
||||||
|
state.throw_result_id = Some(*result_id);
|
||||||
|
state.game_phase = GamePhase::Announce {};
|
||||||
|
let player_to_transfer_to = *state.current_player();
|
||||||
|
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
vec![],
|
||||||
|
vec![
|
||||||
|
ZkStateChange::TransferVariable {
|
||||||
|
variable: *result_id,
|
||||||
|
new_owner: player_to_transfer_to,
|
||||||
|
},
|
||||||
|
ZkStateChange::DeleteVariables {
|
||||||
|
variables_to_delete: zk_state
|
||||||
|
.secret_variables
|
||||||
|
.iter()
|
||||||
|
.map(|(variable_id, _)| variable_id)
|
||||||
|
.filter(|id| id != result_id)
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Announce a value such that the next player can decide if they believe it or not.
|
||||||
|
/// The value must be higher than or equal to the throw to beat.
|
||||||
|
#[action(shortname = 0x03, zk = true)]
|
||||||
|
fn announce_throw(
|
||||||
|
context: ContractContext,
|
||||||
|
mut state: MiaState,
|
||||||
|
zk_state: ZkState<SecretVarType>,
|
||||||
|
dice_value: DiceThrow,
|
||||||
|
) -> (MiaState, Vec<EventGroup>, Vec<ZkStateChange>) {
|
||||||
|
assert_eq!(
|
||||||
|
*state.current_player(),
|
||||||
|
context.sender,
|
||||||
|
"Only the current player can state the value of the dice throw."
|
||||||
|
);
|
||||||
|
|
||||||
|
let reduced_dice_value = dice_value.reduce();
|
||||||
|
|
||||||
|
if !reduced_dice_value.better_than_or_equal(state.throw_to_beat) {
|
||||||
|
panic!("Stated throw must be better than the last stated throw.")
|
||||||
|
}
|
||||||
|
|
||||||
|
state.stated_throw = Some(dice_value);
|
||||||
|
state.game_phase = GamePhase::Decide {};
|
||||||
|
|
||||||
|
(state, vec![], vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The next player believes the stated throw, and continues the round, where the throw to beat is
|
||||||
|
/// the stated throw.
|
||||||
|
#[action(shortname = 0x04, zk = true)]
|
||||||
|
fn believe(
|
||||||
|
context: ContractContext,
|
||||||
|
mut state: MiaState,
|
||||||
|
zk_state: ZkState<SecretVarType>,
|
||||||
|
) -> (MiaState, Vec<EventGroup>, Vec<ZkStateChange>) {
|
||||||
|
assert_eq!(
|
||||||
|
context.sender,
|
||||||
|
*state.next_player(),
|
||||||
|
"Only the next player can say if they believe the stated throw."
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
state.game_phase,
|
||||||
|
GamePhase::Decide {},
|
||||||
|
"Must be in the deciding phase to say believe."
|
||||||
|
);
|
||||||
|
|
||||||
|
state.game_phase = GamePhase::AddRandomness {};
|
||||||
|
state.throw_to_beat = state.stated_throw.unwrap();
|
||||||
|
state.stated_throw = None;
|
||||||
|
state.go_to_next_player();
|
||||||
|
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
vec![],
|
||||||
|
vec![ZkStateChange::DeleteVariables {
|
||||||
|
variables_to_delete: zk_state
|
||||||
|
.secret_variables
|
||||||
|
.iter()
|
||||||
|
.map(|(variable_id, _)| variable_id)
|
||||||
|
.collect(),
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The next player does not believe the stated throw, and starts a reveal of the dice.
|
||||||
|
#[action(shortname = 0x05, zk = true)]
|
||||||
|
fn call_out(
|
||||||
|
context: ContractContext,
|
||||||
|
mut state: MiaState,
|
||||||
|
zk_state: ZkState<SecretVarType>,
|
||||||
|
) -> (MiaState, Vec<EventGroup>, Vec<ZkStateChange>) {
|
||||||
|
assert_eq!(
|
||||||
|
context.sender,
|
||||||
|
*state.next_player(),
|
||||||
|
"Only the next player can say if the throwing player is lying."
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
state.game_phase,
|
||||||
|
GamePhase::Decide {},
|
||||||
|
"Must be in the deciding phase say if the throwing player is lying."
|
||||||
|
);
|
||||||
|
let variable_to_open = state.throw_result_id.unwrap();
|
||||||
|
state.game_phase = GamePhase::Reveal {};
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
vec![],
|
||||||
|
vec![ZkStateChange::OpenVariables {
|
||||||
|
variables: vec![variable_to_open],
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saves the opened variable in state and readies another computation.
|
||||||
|
#[zk_on_variables_opened]
|
||||||
|
fn save_opened_variable(
|
||||||
|
context: ContractContext,
|
||||||
|
mut state: MiaState,
|
||||||
|
zk_state: ZkState<SecretVarType>,
|
||||||
|
opened_variables: Vec<SecretVarId>,
|
||||||
|
) -> (MiaState, Vec<EventGroup>, Vec<ZkStateChange>) {
|
||||||
|
assert_eq!(
|
||||||
|
opened_variables.len(),
|
||||||
|
1,
|
||||||
|
"Can only show one set of dice at a time."
|
||||||
|
);
|
||||||
|
|
||||||
|
let variable_id = opened_variables.first().unwrap();
|
||||||
|
let result: DiceThrow = read_opened_variable_data(&zk_state, variable_id).unwrap();
|
||||||
|
|
||||||
|
let result_reduced = result.reduce();
|
||||||
|
|
||||||
|
let Some(stated_throw) = state.stated_throw else {
|
||||||
|
panic!("Could not find a stated throw in state.")
|
||||||
|
};
|
||||||
|
|
||||||
|
let stated_throw_reduced = stated_throw.reduce();
|
||||||
|
|
||||||
|
let loser_of_round = if result.better_than_or_equal(stated_throw_reduced) {
|
||||||
|
*state.next_player()
|
||||||
|
} else {
|
||||||
|
*state.current_player()
|
||||||
|
};
|
||||||
|
|
||||||
|
if stated_throw.is_mia() {
|
||||||
|
state.reduce_players_life_by(loser_of_round, 2);
|
||||||
|
} else {
|
||||||
|
state.reduce_players_life_by(loser_of_round, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.is_player_dead(loser_of_round) {
|
||||||
|
state.remove_dead_player(loser_of_round);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.throw_result = Some(result_reduced);
|
||||||
|
|
||||||
|
if state.is_the_game_finished() {
|
||||||
|
state.game_phase = GamePhase::Done {};
|
||||||
|
state.winner = Some(state.get_winner());
|
||||||
|
} else {
|
||||||
|
state.go_to_next_player();
|
||||||
|
state.game_phase = GamePhase::AddRandomness {};
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
vec![],
|
||||||
|
vec![ZkStateChange::DeleteVariables {
|
||||||
|
variables_to_delete: opened_variables,
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the data from a revealed secret variable
|
||||||
|
fn read_opened_variable_data<T: ReadWriteState>(
|
||||||
|
zk_state: &ZkState<SecretVarType>,
|
||||||
|
variable_id: &SecretVarId,
|
||||||
|
) -> Option<T> {
|
||||||
|
let variable = zk_state.get_variable(*variable_id)?;
|
||||||
|
variable.open_value()
|
||||||
|
}
|
44
rust/mia-game/src/zk_compute.rs
Normal file
44
rust/mia-game/src/zk_compute.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
use pbc_zk::*;
|
||||||
|
|
||||||
|
/// Output variable type
|
||||||
|
#[derive(pbc_zk::SecretBinary, Clone)]
|
||||||
|
struct RandomnessInput {
|
||||||
|
/// Token amount.
|
||||||
|
d1: Sbi8,
|
||||||
|
d2: Sbi8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a zk computation on secret-shared randomness added to make a random dice throw.
|
||||||
|
///
|
||||||
|
/// ### Returns:
|
||||||
|
///
|
||||||
|
/// The sum of the randomness contributions variables.
|
||||||
|
#[zk_compute(shortname = 0x61)]
|
||||||
|
pub fn compute_dice_throw() -> RandomnessInput {
|
||||||
|
let mut throw = RandomnessInput {
|
||||||
|
d1: Sbi8::from(0),
|
||||||
|
d2: Sbi8::from(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
for variable_id in secret_variable_ids() {
|
||||||
|
let mut raw_contribution: RandomnessInput = load_sbi::<RandomnessInput>(variable_id);
|
||||||
|
|
||||||
|
let d1_reduced = reduce_contribution(raw_contribution.d1);
|
||||||
|
let d2_reduced = reduce_contribution(raw_contribution.d2);
|
||||||
|
|
||||||
|
throw.d1 = throw.d1 + d1_reduced;
|
||||||
|
throw.d2 = throw.d2 + d2_reduced;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reduce the contribution if it is not between 0 and 5.
|
||||||
|
fn reduce_contribution(value: Sbi8) -> Sbi8 {
|
||||||
|
let reduce = value & Sbi8::from(0b111);
|
||||||
|
if reduce >= Sbi8::from(6) {
|
||||||
|
reduce - Sbi8::from(6)
|
||||||
|
} else {
|
||||||
|
reduce
|
||||||
|
}
|
||||||
|
}
|
29
rust/notamon-asset-contract/Cargo.toml
Normal file
29
rust/notamon-asset-contract/Cargo.toml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
[package]
|
||||||
|
name = "zk-file-share"
|
||||||
|
readme = "README.md"
|
||||||
|
version.workspace = true
|
||||||
|
description.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
abi = ["pbc_contract_common/abi", "pbc_contract_codegen/abi", "pbc_traits/abi", "create_type_spec_derive/abi", "pbc_lib/abi"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ['rlib', 'cdylib']
|
||||||
|
|
||||||
|
[package.metadata.zk]
|
||||||
|
zk-compute-path = "src/zk_compute.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pbc_contract_common.workspace = true
|
||||||
|
pbc_traits.workspace = true
|
||||||
|
pbc_lib.workspace = true
|
||||||
|
read_write_rpc_derive.workspace = true
|
||||||
|
read_write_state_derive.workspace = true
|
||||||
|
create_type_spec_derive.workspace = true
|
||||||
|
pbc_contract_codegen.workspace = true
|
||||||
|
pbc_zk.workspace = true
|
0
rust/notamon-asset-contract/README.md
Normal file
0
rust/notamon-asset-contract/README.md
Normal file
106
rust/notamon-asset-contract/src/lib.rs
Normal file
106
rust/notamon-asset-contract/src/lib.rs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate pbc_contract_codegen;
|
||||||
|
extern crate pbc_contract_common;
|
||||||
|
extern crate pbc_lib;
|
||||||
|
|
||||||
|
use pbc_contract_common::address::Address;
|
||||||
|
use pbc_contract_common::context::ContractContext;
|
||||||
|
use pbc_contract_common::events::EventGroup;
|
||||||
|
use pbc_contract_common::zk::{SecretVarId, ZkInputDef, ZkState, ZkStateChange};
|
||||||
|
use pbc_zk::Sbi8;
|
||||||
|
use read_write_rpc_derive::ReadWriteRPC;
|
||||||
|
use read_write_state_derive::ReadWriteState;
|
||||||
|
|
||||||
|
mod zk_compute;
|
||||||
|
|
||||||
|
/// Metadata for secret-shared files.
|
||||||
|
#[derive(ReadWriteState, ReadWriteRPC, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SecretVarMetadata {}
|
||||||
|
|
||||||
|
/// Empty contract state, as all stored files are secret-shared.
|
||||||
|
#[state]
|
||||||
|
pub struct CollectionState {}
|
||||||
|
|
||||||
|
/// Initializes contract with empty state.
|
||||||
|
#[init(zk = true)]
|
||||||
|
pub fn initialize(ctx: ContractContext, zk_state: ZkState<SecretVarMetadata>) -> CollectionState {
|
||||||
|
CollectionState {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Upload a new file with a specific size of `file_length`.
|
||||||
|
///
|
||||||
|
/// `file_length` is the size of the file in *bytes*.
|
||||||
|
/// Fails if the uploaded file has a different size than `file_length`.
|
||||||
|
#[zk_on_secret_input(shortname = 0x42)]
|
||||||
|
pub fn add_file(
|
||||||
|
context: ContractContext,
|
||||||
|
state: CollectionState,
|
||||||
|
zk_state: ZkState<SecretVarMetadata>,
|
||||||
|
file_length: u32,
|
||||||
|
) -> (
|
||||||
|
CollectionState,
|
||||||
|
Vec<EventGroup>,
|
||||||
|
ZkInputDef<SecretVarMetadata, Vec<Sbi8>>,
|
||||||
|
) {
|
||||||
|
let input_def = ZkInputDef::with_metadata_and_size(None, SecretVarMetadata {}, file_length * 8);
|
||||||
|
(state, vec![], input_def)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Changes ownership of the secret-shared file with id `file_id`
|
||||||
|
/// from the sender to `new_owner`.
|
||||||
|
///
|
||||||
|
/// Fails if the sender is not the current owner of the referenced file.
|
||||||
|
#[action(shortname = 0x03, zk = true)]
|
||||||
|
pub fn change_file_owner(
|
||||||
|
ctx: ContractContext,
|
||||||
|
state: CollectionState,
|
||||||
|
zk_state: ZkState<SecretVarMetadata>,
|
||||||
|
file_id: u32,
|
||||||
|
new_owner: Address,
|
||||||
|
) -> (CollectionState, Vec<EventGroup>, Vec<ZkStateChange>) {
|
||||||
|
let file_id = SecretVarId::new(file_id);
|
||||||
|
let file_owner = zk_state.get_variable(file_id).unwrap().owner;
|
||||||
|
assert_eq!(
|
||||||
|
file_owner, ctx.sender,
|
||||||
|
"Only the owner of the secret file is allowed to change ownership."
|
||||||
|
);
|
||||||
|
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
vec![],
|
||||||
|
vec![ZkStateChange::TransferVariable {
|
||||||
|
variable: file_id,
|
||||||
|
new_owner,
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes the secret-shared file with id `file_id`.
|
||||||
|
///
|
||||||
|
/// Fails if the sender is not the current owner of the secret file.
|
||||||
|
#[action(shortname = 0x05, zk = true)]
|
||||||
|
pub fn delete_file(
|
||||||
|
ctx: ContractContext,
|
||||||
|
state: CollectionState,
|
||||||
|
zk_state: ZkState<SecretVarMetadata>,
|
||||||
|
file_id: u32,
|
||||||
|
) -> (CollectionState, Vec<EventGroup>, Vec<ZkStateChange>) {
|
||||||
|
let file_id = SecretVarId::new(file_id);
|
||||||
|
let file_owner = zk_state.get_variable(file_id).unwrap().owner;
|
||||||
|
assert_eq!(
|
||||||
|
file_owner, ctx.sender,
|
||||||
|
"Only the owner of the secret file is allowed to delete it."
|
||||||
|
);
|
||||||
|
|
||||||
|
(
|
||||||
|
state,
|
||||||
|
vec![],
|
||||||
|
vec![ZkStateChange::DeleteVariables {
|
||||||
|
variables_to_delete: vec![file_id],
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
}
|
1
rust/notamon-asset-contract/src/zk_compute.rs
Normal file
1
rust/notamon-asset-contract/src/zk_compute.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
25
rust/upgradable-v1/Cargo.toml
Normal file
25
rust/upgradable-v1/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "upgradable-v1"
|
||||||
|
readme = "README.md"
|
||||||
|
version.workspace = true
|
||||||
|
description.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ['rlib', 'cdylib']
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pbc_contract_common.workspace = true
|
||||||
|
pbc_traits.workspace = true
|
||||||
|
pbc_lib.workspace = true
|
||||||
|
read_write_rpc_derive.workspace = true
|
||||||
|
read_write_state_derive.workspace = true
|
||||||
|
create_type_spec_derive.workspace = true
|
||||||
|
pbc_contract_codegen.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
abi = ["pbc_contract_common/abi", "pbc_contract_codegen/abi", "pbc_traits/abi", "create_type_spec_derive/abi", "pbc_lib/abi"]
|
17
rust/upgradable-v1/README.md
Normal file
17
rust/upgradable-v1/README.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Upgradable v1
|
||||||
|
|
||||||
|
The simplest possible upgradable example contract that retains some amount of
|
||||||
|
security and usability.
|
||||||
|
|
||||||
|
The [`UpgradableV1State`] contains the address of the account or contract that is
|
||||||
|
allowed to upgrade this contract.
|
||||||
|
|
||||||
|
Contract can only be upgraded to a different contract, it cannot be upgraded to
|
||||||
|
itself, or from any other kind of contract.
|
||||||
|
|
||||||
|
## About upgrade governance
|
||||||
|
|
||||||
|
This contract is an example, and does not reflect what good upgrade logic for a
|
||||||
|
contract should look like. Please read documentation page for [upgradable smart
|
||||||
|
contracts](https://partisiablockchain.gitlab.io/documentation/smart-contracts/upgradable-smart-contracts.html)
|
||||||
|
for suggestion of how to implement the upgrade governance.
|
51
rust/upgradable-v1/src/lib.rs
Normal file
51
rust/upgradable-v1/src/lib.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate pbc_contract_codegen;
|
||||||
|
use pbc_contract_codegen::{init, state, upgrade_is_allowed};
|
||||||
|
|
||||||
|
use pbc_contract_common::address::Address;
|
||||||
|
use pbc_contract_common::context::ContractContext;
|
||||||
|
use pbc_contract_common::upgrade::ContractHashes;
|
||||||
|
|
||||||
|
/// Contract state.
|
||||||
|
#[state]
|
||||||
|
pub struct ContractState {
|
||||||
|
/// Contract or account allowed to upgrade this contract.
|
||||||
|
upgrader: Address,
|
||||||
|
/// Counter to demonstrate changes in behaviour
|
||||||
|
counter: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize contract with the upgrader address.
|
||||||
|
#[init]
|
||||||
|
pub fn initialize(_ctx: ContractContext, upgrader: Address) -> ContractState {
|
||||||
|
ContractState {
|
||||||
|
counter: 0,
|
||||||
|
upgrader,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether the upgrade is allowed.
|
||||||
|
///
|
||||||
|
/// This contract allows the [`ContractState::upgrader`] to upgrade the contract at any time.
|
||||||
|
#[upgrade_is_allowed]
|
||||||
|
pub fn is_upgrade_allowed(
|
||||||
|
context: ContractContext,
|
||||||
|
state: ContractState,
|
||||||
|
_old_contract_hashes: ContractHashes,
|
||||||
|
_new_contract_hashes: ContractHashes,
|
||||||
|
_new_contract_rpc: Vec<u8>,
|
||||||
|
) -> bool {
|
||||||
|
context.sender == state.upgrader
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increment the counter by one.
|
||||||
|
#[action(shortname = 0x01)]
|
||||||
|
pub fn increment_counter_by_one(
|
||||||
|
_context: ContractContext,
|
||||||
|
mut state: ContractState,
|
||||||
|
) -> ContractState {
|
||||||
|
state.counter += 1;
|
||||||
|
state
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user