1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

Merge branch 'testnet' into block-generation

This commit is contained in:
SpyCheese 2023-03-28 16:52:33 +03:00
commit d082ac36b0
261 changed files with 24449 additions and 1791 deletions

View file

@ -1,19 +0,0 @@
FROM ubuntu:18.04
RUN apt update
RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata
RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build
WORKDIR /
ARG BRANCH
RUN git clone --recurse-submodules https://github.com/ton-blockchain/ton.git && cd ton && git checkout $BRANCH
WORKDIR /ton
RUN mkdir /ton/build
WORKDIR /ton/build
ENV CC clang
ENV CXX clang++
ENV CCACHE_DISABLE 1
RUN cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= -DCMAKE_CXX_FLAGS="-mavx2" ..
RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func validator-engine validator-engine-console create-state generate-random-id create-hardfork dht-server lite-client

View file

@ -2,12 +2,13 @@ FROM ubuntu:20.04
RUN apt update
RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata
RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build
RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build pkg-config
WORKDIR /
ARG BRANCH
RUN git clone --recurse-submodules https://github.com/ton-blockchain/ton.git && cd ton && git checkout $BRANCH
ARG REPO
RUN git clone --recurse-submodules https://github.com/$REPO && cd ton && git checkout $BRANCH
WORKDIR /ton
RUN mkdir /ton/build

View file

@ -2,12 +2,13 @@ FROM ubuntu:22.04
RUN apt update
RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata
RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build
RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build pkg-config
WORKDIR /
ARG BRANCH
RUN git clone --recurse-submodules https://github.com/ton-blockchain/ton.git && cd ton && git checkout $BRANCH
ARG REPO
RUN git clone --recurse-submodules https://github.com/$REPO && cd ton && git checkout $BRANCH
WORKDIR /ton
RUN mkdir /ton/build

View file

@ -1,19 +0,0 @@
FROM ubuntu:18.04
RUN apt update
RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata
RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build
WORKDIR /
ARG BRANCH
RUN git clone --recurse-submodules https://github.com/ton-blockchain/ton.git && cd ton && git checkout $BRANCH
WORKDIR /ton
RUN mkdir /ton/build
WORKDIR /ton/build
ENV CC clang
ENV CXX clang++
ENV CCACHE_DISABLE 1
RUN cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= ..
RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func validator-engine validator-engine-console create-state generate-random-id dht-server lite-client

View file

@ -2,12 +2,13 @@ FROM ubuntu:20.04
RUN apt update
RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata
RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build
RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build pkg-config
WORKDIR /
ARG BRANCH
RUN git clone --recurse-submodules https://github.com/ton-blockchain/ton.git && cd ton && git checkout $BRANCH
ARG REPO
RUN git clone --recurse-submodules https://github.com/$REPO && cd ton && git checkout $BRANCH
WORKDIR /ton
RUN mkdir /ton/build

View file

@ -2,12 +2,13 @@ FROM ubuntu:22.04
RUN apt update
RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata
RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build
RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build pkg-config
WORKDIR /
ARG BRANCH
RUN git clone --recurse-submodules https://github.com/ton-blockchain/ton.git && cd ton && git checkout $BRANCH
ARG REPO
RUN git clone --recurse-submodules https://github.com/$REPO && cd ton && git checkout $BRANCH
WORKDIR /ton
RUN mkdir /ton/build

View file

@ -1,50 +1,48 @@
# The script build funcfift compiler to WASM
# The script builds funcfift compiler to WASM
# dependencies:
#sudo apt-get install -y build-essential git make cmake clang libgflags-dev zlib1g-dev libssl-dev libreadline-dev libmicrohttpd-dev pkg-config libgsl-dev python3 python3-dev python3-pip nodejs
#sudo apt-get install -y build-essential git make cmake clang libgflags-dev zlib1g-dev libssl-dev libreadline-dev libmicrohttpd-dev pkg-config libgsl-dev python3 python3-dev python3-pip nodejs libevent-dev
export CC=$(which clang)
export CXX=$(which clang++)
export CCACHE_DISABLE=1
cd ../..
rm -rf openssl zlib emsdk build
echo `pwd`
git clone https://github.com/openssl/openssl.git
cd openssl
git checkout OpenSSL_1_1_1j
./config
make -j4
make -j16
OPENSSL_DIR=`pwd`
cd ..
git clone https://github.com/madler/zlib.git
cd zlib
ZLIB_DIR=`pwd`
cd ..
# clone ton repo
git clone --recursive https://github.com/the-ton-tech/ton-blockchain.git
# only to generate auto-block.cpp
cd ton-blockchain
git pull
git checkout 1566a23b2bece49fd1de9ab2f35e88297d22829f
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DZLIB_LIBRARY=/usr/lib/x86_64-linux-gnu/libz.so -DZLIB_INCLUDE_DIR=$ZLIB_DIR -DOPENSSL_ROOT_DIR=$OPENSSL_DIR -DOPENSSL_INCLUDE_DIR=$OPENSSL_DIR/include -DOPENSSL_CRYPTO_LIBRARY=$OPENSSL_DIR/libcrypto.so -DOPENSSL_SSL_LIBRARY=$OPENSSL_DIR/libssl.so ..
make -j4 fift
cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DZLIB_LIBRARY=/usr/lib/x86_64-linux-gnu/libz.so -DZLIB_INCLUDE_DIR=$ZLIB_DIR -DOPENSSL_ROOT_DIR=$OPENSSL_DIR -DOPENSSL_INCLUDE_DIR=$OPENSSL_DIR/include -DOPENSSL_CRYPTO_LIBRARY=$OPENSSL_DIR/libcrypto.so -DOPENSSL_SSL_LIBRARY=$OPENSSL_DIR/libssl.so -DTON_USE_ABSEIL=OFF ..
test $? -eq 0 || { echo "Can't configure TON build"; exit 1; }
ninja fift smc-envelope
test $? -eq 0 || { echo "Can't compile fift "; exit 1; }
rm -rf *
cd ../..
cd ..
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
./emsdk install 3.1.19
./emsdk activate 3.1.19
EMSDK_DIR=`pwd`
source $EMSDK_DIR/emsdk_env.sh
@ -55,7 +53,8 @@ export CCACHE_DISABLE=1
cd ../zlib
emconfigure ./configure --static
emmake make -j4
emmake make -j16
test $? -eq 0 || { echo "Can't compile zlib with emmake "; exit 1; }
ZLIB_DIR=`pwd`
cd ../openssl
@ -66,14 +65,13 @@ sed -i 's/CROSS_COMPILE=.*/CROSS_COMPILE=/g' Makefile
sed -i 's/-ldl//g' Makefile
sed -i 's/-O3/-Os/g' Makefile
emmake make depend
emmake make -j4
cd ../ton-blockchain
cd build
emcmake cmake -DUSE_EMSCRIPTEN=ON -DCMAKE_BUILD_TYPE=Release -DZLIB_LIBRARY=$ZLIB_DIR/libz.a -DZLIB_INCLUDE_DIR=$ZLIB_DIR -DOPENSSL_ROOT_DIR=$OPENSSL_DIR -DOPENSSL_INCLUDE_DIR=$OPENSSL_DIR/include -DOPENSSL_CRYPTO_LIBRARY=$OPENSSL_DIR/libcrypto.a -DOPENSSL_SSL_LIBRARY=$OPENSSL_DIR/libssl.a -DCMAKE_TOOLCHAIN_FILE=$EMSDK_DIR/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_CXX_FLAGS="-pthread -sUSE_ZLIB=1" ..
emmake make -j16
test $? -eq 0 || { echo "Can't compile OpenSSL with emmake "; exit 1; }
cd ../build
emcmake cmake -DUSE_EMSCRIPTEN=ON -DCMAKE_BUILD_TYPE=Release -DZLIB_LIBRARY=$ZLIB_DIR/libz.a -DZLIB_INCLUDE_DIR=$ZLIB_DIR -DOPENSSL_ROOT_DIR=$OPENSSL_DIR -DOPENSSL_INCLUDE_DIR=$OPENSSL_DIR/include -DOPENSSL_CRYPTO_LIBRARY=$OPENSSL_DIR/libcrypto.a -DOPENSSL_SSL_LIBRARY=$OPENSSL_DIR/libssl.a -DCMAKE_TOOLCHAIN_FILE=$EMSDK_DIR/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_CXX_FLAGS="-sUSE_ZLIB=1" ..
test $? -eq 0 || { echo "Can't configure TON with with emmake "; exit 1; }
cp -R ../crypto/smartcont ../crypto/fift/lib crypto
emmake make -j4 funcfiftlib
emmake make -j16 funcfiftlib func fift tlbc emulator-emscripten

View file

@ -11,42 +11,69 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Show all artifacts
run: |
mkdir artifacts
ls -lart artifacts
- name: Download Ubuntu x86-64 artifacts
- name: Download Linux x86-64 artifacts
uses: dawidd6/action-download-artifact@v2
with:
workflow: ubuntu-compile.yml
workflow: ton-x86-64-linux.yml
path: artifacts
workflow_conclusion: success
skip_unpack: true
- name: Download Ubuntu arm64 artifacts
- name: Download and unzip Linux x86-64 artifacts
uses: dawidd6/action-download-artifact@v2
with:
workflow: docker-compile-ubuntu.yml
workflow: ton-x86-64-linux.yml
path: artifacts
workflow_conclusion: success
skip_unpack: false
# - name: Download Linux arm64 artifacts
# uses: dawidd6/action-download-artifact@v2
# with:
# workflow: ton-aarch64-linux.yml
# path: artifacts
# workflow_conclusion: success
# skip_unpack: true
#
# - name: Download and unzip Linux arm64 artifacts
# uses: dawidd6/action-download-artifact@v2
# with:
# workflow: ton-aarch64-linux.yml
# path: artifacts
# workflow_conclusion: success
# skip_unpack: false
- name: Download Mac x86-64 artifacts
uses: dawidd6/action-download-artifact@v2
with:
workflow: ton-x86-64-macos.yml
path: artifacts
workflow_conclusion: success
skip_unpack: true
- name: Download MacOS 11.7 artifacts
- name: Download and unzip Mac x86-64 artifacts
uses: dawidd6/action-download-artifact@v2
with:
workflow: macos-11.7-compile.yml
workflow: ton-x86-64-macos.yml
path: artifacts
workflow_conclusion: success
skip_unpack: true
skip_unpack: false
- name: Download MacOS 12.6 artifacts
uses: dawidd6/action-download-artifact@v2
with:
workflow: macos-12.6-compile.yml
path: artifacts
workflow_conclusion: success
skip_unpack: true
# - name: Download Mac arm64 artifacts
# uses: dawidd6/action-download-artifact@v2
# with:
# workflow: ton-aarch64-macos.yml
# path: artifacts
# workflow_conclusion: success
# skip_unpack: true
#
# - name: Download and unzip Mac arm64 artifacts
# uses: dawidd6/action-download-artifact@v2
# with:
# workflow: ton-aarch64-macos.yml
# path: artifacts
# workflow_conclusion: success
# skip_unpack: false
- name: Download Windows artifacts
uses: dawidd6/action-download-artifact@v2
@ -56,10 +83,27 @@ jobs:
workflow_conclusion: success
skip_unpack: true
- name: Download and unzip Windows artifacts
uses: dawidd6/action-download-artifact@v2
with:
workflow: win-2019-compile.yml
path: artifacts
workflow_conclusion: success
skip_unpack: false
- name: Download WASM artifacts
uses: dawidd6/action-download-artifact@v2
with:
workflow: ton-wasm-emscripten.yml
path: artifacts
workflow_conclusion: success
skip_unpack: true
- name: Show all artifacts
run: |
tree artifacts
# create release
- name: Read Changelog.md and use it as a body of new release
@ -79,7 +123,7 @@ jobs:
- name: Get registration token
id: getRegToken
run: |
curl -X POST -H \"Accept: application/vnd.github+json\" -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/neodix42/HardTestDevelopment/actions/runners/registration-token
curl -X POST -H \"Accept: application/vnd.github+json\" -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/ton-blockchain/ton/actions/runners/registration-token
- name: Create release
id: create_release
@ -94,74 +138,250 @@ jobs:
draft: false
prerelease: false
# upload
# win
- name: Upload Windows 2019 artifacts
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-win-binaries.zip
asset_name: ton-windows-2019-x86-64.zip
asset_name: ton-win-x86-64.zip
tag: v${{ steps.date.outputs.date }}
- name: Upload MacOS 11.7 x86-64 artifacts
- name: Upload Windows 2019 single artifact - fift
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-macos-11.7.zip
asset_name: ton-macos-11.7-x86-64.zip
file: artifacts/ton-win-binaries/fift.exe
asset_name: fift.exe
tag: v${{ steps.date.outputs.date }}
- name: Upload MacOS 12.6 x86-64 artifacts
- name: Upload Windows 2019 single artifact - func
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-macos-12.6.zip
asset_name: ton-macos-12.6-x86-64.zip
file: artifacts/ton-win-binaries/func.exe
asset_name: func.exe
tag: v${{ steps.date.outputs.date }}
- name: Upload Ubuntu 18.04 x86-64 artifacts
- name: Upload Windows 2019 single artifact - lite-client
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-binaries-ubuntu-18.04.zip
asset_name: ton-ubuntu-18.04-x86-64.zip
file: artifacts/ton-win-binaries/lite-client.exe
asset_name: lite-client.exe
tag: v${{ steps.date.outputs.date }}
- name: Upload Ubuntu 20.04 x86-64 artifacts
- name: Upload Windows 2019 single artifact - rldp-http-proxy
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-binaries-ubuntu-20.04.zip
asset_name: ton-ubuntu-20.04-x86-64.zip
file: artifacts/ton-win-binaries/rldp-http-proxy.exe
asset_name: rldp-http-proxy.exe
tag: v${{ steps.date.outputs.date }}
- name: Upload Ubuntu 22.04 x86-64 artifacts
- name: Upload Windows 2019 single artifact - http-proxy
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-binaries-ubuntu-22.04.zip
asset_name: ton-ubuntu-22.04-x86-64.zip
file: artifacts/ton-win-binaries/http-proxy.exe
asset_name: http-proxy.exe
tag: v${{ steps.date.outputs.date }}
- name: Upload Ubuntu 18.04 arm64 artifacts
- name: Upload Windows 2019 single artifact - storage-daemon-cli
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-ubuntu-18.04-arm64.zip
asset_name: ton-ubuntu-18.04-arm64.zip
file: artifacts/ton-win-binaries/storage-daemon-cli.exe
asset_name: storage-daemon-cli.exe
tag: v${{ steps.date.outputs.date }}
- name: Upload Ubuntu 20.04 arm64 artifacts
- name: Upload Windows 2019 single artifact - tonlibjson
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-ubuntu-20.04-arm64.zip
asset_name: ton-ubuntu-20.04-arm64.zip
file: artifacts/ton-win-binaries/tonlibjson.dll
asset_name: tonlibjson.dll
tag: v${{ steps.date.outputs.date }}
- name: Upload Ubuntu 22.04 arm64 artifacts
- name: Upload Windows 2019 single artifact - tonlib-cli
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-ubuntu-22.04-arm64.zip
asset_name: ton-ubuntu-22.04-arm64.zip
file: artifacts/ton-win-binaries/tonlib-cli.exe
asset_name: tonlib-cli.exe
tag: v${{ steps.date.outputs.date }}
# mac x86-64
- name: Upload Mac x86-64 artifacts
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-macos-binaries.zip
asset_name: ton-mac-x86-64.zip
tag: v${{ steps.date.outputs.date }}
- name: Upload Mac x86-64 single artifact - fift
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-macos-binaries/fift
asset_name: fift-mac-x86-64
tag: v${{ steps.date.outputs.date }}
- name: Upload Mac x86-64 single artifact - func
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-macos-binaries/func
asset_name: func-mac-x86-64
tag: v${{ steps.date.outputs.date }}
- name: Upload Mac x86-64 single artifact - lite-client
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-macos-binaries/lite-client
asset_name: lite-client-mac-x86-64
tag: v${{ steps.date.outputs.date }}
- name: Upload Mac x86-64 single artifact - rldp-http-proxy
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-macos-binaries/rldp-http-proxy
asset_name: rldp-http-proxy-mac-x86-64
tag: v${{ steps.date.outputs.date }}
- name: Upload Mac x86-64 single artifact - http-proxy
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-macos-binaries/http-proxy
asset_name: http-proxy-mac-x86-64
tag: v${{ steps.date.outputs.date }}
- name: Upload Mac x86-64 single artifact - storage-daemon-cli
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-macos-binaries/storage-daemon-cli
asset_name: storage-daemon-cli-mac-x86-64
tag: v${{ steps.date.outputs.date }}
- name: Upload Mac x86-64 single artifact - tonlibjson
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-macos-binaries/libtonlibjson.dylib
asset_name: tonlibjson-mac-x86-64.dylib
tag: v${{ steps.date.outputs.date }}
- name: Upload Mac x86-64 single artifact - tonlib-cli
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-macos-binaries/tonlib-cli
asset_name: tonlib-cli-mac-x86-64
tag: v${{ steps.date.outputs.date }}
# linux x86-64
- name: Upload Linux x86-64 artifacts
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-linux-binaries.zip
asset_name: ton-linux-x86_64.zip
tag: v${{ steps.date.outputs.date }}
- name: Upload Linux x86-64 single artifact - fift
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-linux-binaries/fift
asset_name: fift-linux-x86_64
tag: v${{ steps.date.outputs.date }}
- name: Upload Linux x86-64 single artifact - func
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-linux-binaries/func
asset_name: func-linux-x86_64
tag: v${{ steps.date.outputs.date }}
- name: Upload Linux x86-64 single artifact - lite-client
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-linux-binaries/lite-client
asset_name: lite-client-linux-x86_64
tag: v${{ steps.date.outputs.date }}
- name: Upload Linux x86-64 single artifact - rldp-http-proxy
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-linux-binaries/rldp-http-proxy
asset_name: rldp-http-proxy-linux-x86_64
tag: v${{ steps.date.outputs.date }}
- name: Upload Linux x86-64 single artifact - http-proxy
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-linux-binaries/http-proxy
asset_name: http-proxy-linux-x86_64
tag: v${{ steps.date.outputs.date }}
- name: Upload Linux x86-64 single artifact - storage-daemon-cli
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-linux-binaries/storage-daemon-cli
asset_name: storage-daemon-cli-linux-x86_64
tag: v${{ steps.date.outputs.date }}
- name: Upload Linux x86-64 single artifact - tonlibjson
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-linux-binaries/libtonlibjson.so.0.5
asset_name: tonlibjson-linux-x86_64.so
tag: v${{ steps.date.outputs.date }}
- name: Upload Linux x86-64 single artifact - tonlib-cli
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-x86_64-linux-binaries/tonlib-cli
asset_name: tonlib-cli-linux-x86_64
tag: v${{ steps.date.outputs.date }}
# - name: Upload Linux arm64 artifacts
# uses: svenstaro/upload-release-action@v2
# with:
# repo_token: ${{ secrets.GITHUB_TOKEN }}
# file: artifacts/ton-aarch64-linux-binaries.zip
# asset_name: ton-linux-arm64.zip
# tag: v${{ steps.date.outputs.date }}
#
# - name: Upload Mac arm64 artifacts
# uses: svenstaro/upload-release-action@v2
# with:
# repo_token: ${{ secrets.GITHUB_TOKEN }}
# file: artifacts/ton-aarch64-macos-binaries
# asset_name: ton-mac-arm64.zip
# tag: v${{ steps.date.outputs.date }}
- name: Upload WASM artifacts
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-wasm-binaries.zip
asset_name: ton-wasm-binaries.zip
tag: v${{ steps.date.outputs.date }}

View file

@ -9,7 +9,7 @@ jobs:
max-parallel: 3
matrix:
arch: [arm64]
ver: [22.04, 18.04, 20.04 ]
ver: [22.04, 20.04 ]
runs-on: ubuntu-22.04
steps:
@ -33,7 +33,7 @@ jobs:
run: |
mkdir build-${{matrix.ver}}-${{matrix.arch}}
docker buildx build --build-arg BRANCH=${{ steps.vars.outputs.short_ref }} --platform=linux/${{matrix.arch}} --progress=plain --load . -t build-${{matrix.ver}}-${{matrix.arch}} -f .github/script/${{matrix.arch}}-${{matrix.ver}}.Dockerfile
docker buildx build --build-arg REPO=${{ github.repository }} --build-arg BRANCH=${{ steps.vars.outputs.short_ref }} --platform=linux/${{matrix.arch}} --progress=plain --load . -t build-${{matrix.ver}}-${{matrix.arch}} -f .github/script/${{matrix.arch}}-${{matrix.ver}}.Dockerfile
container_id=$(docker create --platform=linux/${{matrix.arch}} build-${{matrix.ver}}-${{matrix.arch}})
docker cp $container_id:/ton/build/dht-server/dht-server build-${{matrix.ver}}-${{matrix.arch}}/
docker cp -a $container_id:/ton/build/validator-engine/validator-engine build-${{matrix.ver}}-${{matrix.arch}}/

View file

@ -23,18 +23,19 @@ jobs:
- name: Build all
run: |
export NONINTERACTIVE=1
brew install ninja
brew install ninja libmicrohttpd pkg-config
rootPath=`pwd`
mkdir build
cd build
cmake -GNinja -DOPENSSL_FOUND=1 -DOPENSSL_INCLUDE_DIR=$rootPath/openssl_1_1_1/include -DOPENSSL_CRYPTO_LIBRARY=$rootPath/openssl_1_1_1/libcrypto.a -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=11.7 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release ..
ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork tlbc
ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tonlib tonlibjson tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork tlbc
- name: Find & copy binaries
run: |
mkdir artifacts
cp build/storage/storage-daemon/storage-daemon artifacts/
cp build/storage/storage-daemon/storage-daemon-cli artifacts/
cp build/blockchain-explorer/blockchain-explorer artifacts/
cp build/crypto/fift artifacts/
cp build/crypto/func artifacts/
cp build/crypto/create-state artifacts/
@ -50,10 +51,18 @@ jobs:
cp build/utils/generate-random-id artifacts/
cp build/utils/json2tlo artifacts/
cp build/adnl/adnl-proxy artifacts/
chmod +x artifacts/*
rsync -r crypto/smartcont artifacts/
rsync -r crypto/fift/lib artifacts/
ls -laRt artifacts
- name: Simple binaries test
run: |
artifacts/validator-engine -V
artifacts/lite-client -V
artifacts/fift -V
artifacts/func -V
- name: Upload artifacts
uses: actions/upload-artifact@master
with:

View file

@ -23,18 +23,19 @@ jobs:
- name: Build all
run: |
export NONINTERACTIVE=1
brew install ninja
brew install ninja libmicrohttpd pkg-config
rootPath=`pwd`
mkdir build
cd build
cmake -GNinja -DOPENSSL_FOUND=1 -DOPENSSL_INCLUDE_DIR=$rootPath/openssl_1_1_1/include -DOPENSSL_CRYPTO_LIBRARY=$rootPath/openssl_1_1_1/libcrypto.a -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=12.6 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release ..
ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork tlbc
ninja storage-daemon storage-daemon-cli blockchain-explorer fift func tonlib tonlibjson tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork tlbc
- name: Find & copy binaries
run: |
mkdir artifacts
cp build/storage/storage-daemon/storage-daemon artifacts/
cp build/storage/storage-daemon/storage-daemon-cli artifacts/
cp build/blockchain-explorer/blockchain-explorer artifacts/
cp build/crypto/fift artifacts/
cp build/crypto/func artifacts/
cp build/crypto/create-state artifacts/
@ -50,10 +51,18 @@ jobs:
cp build/utils/generate-random-id artifacts/
cp build/utils/json2tlo artifacts/
cp build/adnl/adnl-proxy artifacts/
chmod +x artifacts/*
rsync -r crypto/smartcont artifacts/
rsync -r crypto/fift/lib artifacts/
ls -laRt artifacts
- name: Simple binaries test
run: |
artifacts/validator-engine -V
artifacts/lite-client -V
artifacts/fift -V
artifacts/func -V
- name: Upload artifacts
uses: actions/upload-artifact@master
with:

48
.github/workflows/ton-aarch64-linux.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name: "TON aarch64 Linux binaries"
on: [workflow_dispatch,workflow_call]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- run: |
sudo apt update
sudo apt install -y apt-utils
sudo apt install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: cachix/install-nix-action@v18
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Compile
run: nix build .?submodules=1#packages.aarch64-linux.ton-oldglibc_staticbinaries --print-build-logs --system aarch64-linux -o result-aarch64
- name: Copy binaries
run: |
ls -lart
mkdir artifacts
cp $PWD/result-aarch64-linux/bin/* artifacts/
chmod +x artifacts/*
cp $PWD/result-aarch64-linux/lib/libtonlibjson.so.0.5 artifacts/
cp -R crypto/smartcont artifacts/
cp -R crypto/fift/lib artifacts/
- name: Simple binaries test
run: |
artifacts/validator-engine -V
artifacts/lite-client -V
artifacts/fift -V
artifacts/func -V
- name: Upload artifacts
uses: actions/upload-artifact@master
with:
name: ton-aarch64-linux-binaries
path: artifacts

45
.github/workflows/ton-aarch64-macos.yml vendored Normal file
View file

@ -0,0 +1,45 @@
name: "TON aarch64 macOS binaries"
on: [workflow_dispatch,workflow_call]
jobs:
build:
runs-on: macos-12
steps:
- run: brew install qemu
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: cachix/install-nix-action@v18
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Compile
run: nix build .?submodules=1#packages.aarch64-darwin.ton-staticbin-dylib --print-build-logs -o result-aarch64-darwin
- name: Copy binaries
run: |
ls -lart
mkdir artifacts
cp $PWD/result-aarch64-darwin/bin/* artifacts/
chmod +x artifacts/*
cp $PWD/result-aarch64-darwin/lib/libtonlibjson* artifacts/
cp -R crypto/smartcont artifacts/
cp -R crypto/fift/lib artifacts/
- name: Simple binaries test
run: |
artifacts/validator-engine -V
artifacts/lite-client -V
artifacts/fift -V
artifacts/func -V
- name: Upload artifacts
uses: actions/upload-artifact@master
with:
name: ton-aarch64-macos-binaries
path: artifacts

View file

@ -5,7 +5,7 @@ on: [push,workflow_dispatch,workflow_call]
jobs:
build:
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
steps:
- name: Check out repository

View file

@ -0,0 +1,40 @@
name: TON WASM Compile
on: [push,workflow_dispatch,workflow_call]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: Check out repository
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Install libraries
run: |
sudo apt update
sudo apt install -y build-essential git make cmake ninja-build clang libgflags-dev zlib1g-dev libssl-dev libreadline-dev libmicrohttpd-dev pkg-config libgsl-dev python3 python3-dev python3-pip nodejs
- name: Configure & Build
run: |
cd .github/script
./fift-func-wasm-build-ubuntu.sh
- name: Find & copy binaries
run: |
mkdir artifacts
ls build/crypto
cp build/crypto/fift* artifacts
cp build/crypto/func* artifacts
cp build/crypto/tlbc* artifacts
cp build/emulator/emulator-emscripten* artifacts
cp -R crypto/smartcont artifacts
cp -R crypto/fift/lib artifacts
- name: Upload artifacts
uses: actions/upload-artifact@master
with:
name: ton-wasm-binaries
path: artifacts

47
.github/workflows/ton-x86-64-linux.yml vendored Normal file
View file

@ -0,0 +1,47 @@
name: "TON x86_64 Linux binaries"
on: [push,workflow_dispatch,workflow_call]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- run: |
sudo apt update
sudo apt install -y apt-utils
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: cachix/install-nix-action@v18
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Compile
run: nix build .?submodules=1#packages.x86_64-linux.ton-oldglibc_staticbinaries --print-build-logs --system x86_64-linux -o result-x86_64
- name: Copy binaries
run: |
ls -lart
mkdir artifacts
cp $PWD/result-x86_64/bin/* artifacts/
chmod +x artifacts/*
cp $PWD/result-x86_64/lib/libtonlibjson.so.0.5 artifacts/
cp -R crypto/smartcont artifacts/
cp -R crypto/fift/lib artifacts/
- name: Simple binaries test
run: |
artifacts/validator-engine -V
artifacts/lite-client -V
artifacts/fift -V
artifacts/func -V
- name: Upload artifacts
uses: actions/upload-artifact@master
with:
name: ton-x86_64-linux-binaries
path: artifacts

43
.github/workflows/ton-x86-64-macos.yml vendored Normal file
View file

@ -0,0 +1,43 @@
name: "TON x86_64 macOS binaries"
on: [push,workflow_dispatch,workflow_call]
jobs:
build:
runs-on: macos-12
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: cachix/install-nix-action@v18
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Compile
run: nix build .?submodules=1#packages.x86_64-darwin.ton-staticbin-dylib --print-build-logs -o result-x86_64-darwin
- name: Copy binaries
run: |
ls -lart
mkdir artifacts
cp $PWD/result-x86_64-darwin/bin/* artifacts/
chmod +x artifacts/*
cp $PWD/result-x86_64-darwin/lib/libtonlibjson.dylib artifacts/
cp -R crypto/smartcont artifacts/
cp -R crypto/fift/lib artifacts/
- name: Simple binaries test
run: |
artifacts/validator-engine -V
artifacts/lite-client -V
artifacts/fift -V
artifacts/func -V
- name: Upload artifacts
uses: actions/upload-artifact@master
with:
name: ton-x86_64-macos-binaries
path: artifacts

View file

@ -1,11 +1,11 @@
name: Ubuntu 18.04 Compile
name: Ubuntu 22.04 Compile
on: [push,workflow_dispatch,workflow_call]
jobs:
build:
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
steps:
- name: Check out repository
@ -36,9 +36,17 @@ jobs:
run: |
mkdir artifacts
cp build/storage/storage-daemon/storage-daemon build/storage/storage-daemon/storage-daemon-cli build/crypto/fift build/crypto/tlbc build/crypto/func build/crypto/create-state build/validator-engine-console/validator-engine-console build/tonlib/tonlib-cli build/tonlib/libtonlibjson.so.0.5 build/http/http-proxy build/rldp-http-proxy/rldp-http-proxy build/dht-server/dht-server build/lite-client/lite-client build/validator-engine/validator-engine build/utils/generate-random-id build/utils/json2tlo build/adnl/adnl-proxy artifacts
chmod +x artifacts/*
cp -R crypto/smartcont artifacts/
cp -R crypto/fift/lib artifacts/
- name: Simple binaries test
run: |
artifacts/validator-engine -V
artifacts/lite-client -V
artifacts/fift -V
artifacts/func -V
- name: Upload artifacts
uses: actions/upload-artifact@master
with:

View file

@ -7,7 +7,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-18.04, ubuntu-20.04, ubuntu-22.04]
os: [ubuntu-20.04, ubuntu-22.04]
runs-on: ${{ matrix.os }}
steps:
@ -39,8 +39,17 @@ jobs:
run: |
mkdir artifacts-${{ matrix.os }}
cp build-${{ matrix.os }}/storage/storage-daemon/storage-daemon build-${{ matrix.os }}/storage/storage-daemon/storage-daemon-cli build-${{ matrix.os }}/crypto/fift build-${{ matrix.os }}/crypto/tlbc build-${{ matrix.os }}/crypto/func build-${{ matrix.os }}/crypto/create-state build-${{ matrix.os }}/validator-engine-console/validator-engine-console build-${{ matrix.os }}/tonlib/tonlib-cli build-${{ matrix.os }}/tonlib/libtonlibjson.so.0.5 build-${{ matrix.os }}/http/http-proxy build-${{ matrix.os }}/rldp-http-proxy/rldp-http-proxy build-${{ matrix.os }}/dht-server/dht-server build-${{ matrix.os }}/lite-client/lite-client build-${{ matrix.os }}/validator-engine/validator-engine build-${{ matrix.os }}/utils/generate-random-id build-${{ matrix.os }}/utils/json2tlo build-${{ matrix.os }}/adnl/adnl-proxy artifacts-${{ matrix.os }}
chmod +x artifacts-${{ matrix.os }}/*
cp -R crypto/smartcont artifacts-${{ matrix.os }}
cp -R crypto/fift/lib artifacts-${{ matrix.os }}
- name: Simple binaries test
run: |
artifacts-${{ matrix.os }}/validator-engine -V
artifacts-${{ matrix.os }}/lite-client -V
artifacts-${{ matrix.os }}/fift -V
artifacts-${{ matrix.os }}/func -V
- name: Upload artifacts
uses: actions/upload-artifact@master
with:

View file

@ -28,7 +28,10 @@ jobs:
path: zlib
- name: Setup msbuild.exe
uses: microsoft/setup-msbuild@v1.0.2
uses: microsoft/setup-msbuild@v1.1
- name: Install Pkg-config Lite
run: choco install pkgconfiglite
- name: Compile zlib Win64
run: |
@ -57,7 +60,7 @@ jobs:
mkdir build
cd build
cmake -DREADLINE_INCLUDE_DIR=%root%\readline-5.0-1-lib\include\readline -DREADLINE_LIBRARY=%root%\readline-5.0-1-lib\lib\readline.lib -DZLIB_FOUND=1 -DMHD_FOUND=1 -DMHD_LIBRARY=%root%\libmicrohttpd-0.9.75-w32-bin\x86_64\VS2019\Release-static\libmicrohttpd.lib -DMHD_INCLUDE_DIR=%root%\libmicrohttpd-0.9.75-w32-bin\x86_64\VS2019\Release-static -DZLIB_INCLUDE_DIR=%root%\zlib -DZLIB_LIBRARY=%root%\zlib\contrib\vstudio\vc14\x64\ZlibStatReleaseWithoutAsm\zlibstat.lib -DOPENSSL_FOUND=1 -DOPENSSL_INCLUDE_DIR=%root%/openssl-1.1/x64/include -DOPENSSL_CRYPTO_LIBRARY=%root%/openssl-1.1/x64/lib/libcrypto.lib -DCMAKE_CXX_FLAGS="/DTD_WINDOWS=1 /EHsc /bigobj /W0" ..
cmake --build . --target storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork --config Release
cmake --build . --target storage-daemon storage-daemon-cli blockchain-explorer fift func tonlib tonlibjson tonlib-cli validator-engine lite-client pow-miner validator-engine-console generate-random-id json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork --config Release
- name: Show executables
run: |
@ -68,8 +71,7 @@ jobs:
- name: Check if validator-engine.exe exists
run: |
set root=%cd%
copy %root%\build\validator-engine\Release\validator-engine.exe test
copy %cd%\build\validator-engine\Release\validator-engine.exe test
- name: Find & copy binaries
run: |
@ -77,7 +79,7 @@ jobs:
mkdir artifacts\smartcont
mkdir artifacts\lib
for %%I in (build\storage\storage-daemon\Release\storage-daemon.exe build\storage\storage-daemon\Release\storage-daemon-cli.exe build\crypto\Release\fift.exe build\crypto\Release\tlbc.exe build\crypto\Release\func.exe build\crypto\Release\create-state.exe build\validator-engine-console\Release\validator-engine-console.exe build\tonlib\Release\tonlib-cli.exe build\tonlib\Release\tonlibjson.dll build\http\Release\http-proxy.exe build\rldp-http-proxy\Release\rldp-http-proxy.exe build\dht-server\Release\dht-server.exe build\lite-client\Release\lite-client.exe build\validator-engine\Release\validator-engine.exe build\utils\Release\generate-random-id.exe build\utils\Release\json2tlo.exe build\adnl\Release\adnl-proxy.exe) do copy %%I artifacts\
for %%I in (build\storage\storage-daemon\Release\storage-daemon.exe build\storage\storage-daemon\Release\storage-daemon-cli.exe build\blockchain-explorer\blockchain-explorer.exe build\crypto\Release\fift.exe build\crypto\Release\tlbc.exe build\crypto\Release\func.exe build\crypto\Release\create-state.exe build\validator-engine-console\Release\validator-engine-console.exe build\tonlib\Release\tonlib-cli.exe build\tonlib\Release\tonlibjson.dll build\http\Release\http-proxy.exe build\rldp-http-proxy\Release\rldp-http-proxy.exe build\dht-server\Release\dht-server.exe build\lite-client\Release\lite-client.exe build\validator-engine\Release\validator-engine.exe build\utils\Release\generate-random-id.exe build\utils\Release\json2tlo.exe build\adnl\Release\adnl-proxy.exe) do copy %%I artifacts\
xcopy /e /k /h /i crypto\smartcont artifacts\smartcont
xcopy /e /k /h /i crypto\fift\lib artifacts\lib

View file

@ -20,19 +20,6 @@ find_library(
set(MHD_INCLUDE_DIRS ${MHD_INCLUDE_DIR})
set(MHD_LIBRARIES ${MHD_LIBRARY})
# debug library on windows
# same naming convention as in qt (appending debug library with d)
# boost is using the same "hack" as us with "optimized" and "debug"
# official MHD project actually uses _d suffix
if (MSVC)
find_library(
MHD_LIBRARY_DEBUG
NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d
DOC "mhd debug library"
)
set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG})
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(mhd DEFAULT_MSG MHD_INCLUDE_DIR MHD_LIBRARY)
find_package_handle_standard_args(MHD DEFAULT_MSG MHD_INCLUDE_DIR MHD_LIBRARY)
mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY)

View file

@ -108,7 +108,8 @@ set(TON_ARCH "native" CACHE STRING "Architecture, will be passed to -march=")
#BEGIN M1 support
EXECUTE_PROCESS( COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE )
if ((ARCHITECTURE MATCHES "arm64") AND (CMAKE_SYSTEM_NAME STREQUAL "Darwin"))
if ((ARCHITECTURE MATCHES "arm64") AND (CMAKE_SYSTEM_NAME STREQUAL "Darwin") AND
(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0)) # only clang 13+ supports cpu=apple-m1
set(TON_ARCH "apple-m1")
endif()
#END M1 support
@ -135,7 +136,16 @@ set(CRC32C_BUILD_BENCHMARKS OFF CACHE BOOL "Build CRC32C's benchmarks")
set(CRC32C_USE_GLOG OFF CACHE BOOL "Build CRC32C's tests with Google Logging")
set(CRC32C_INSTALL OFF CACHE BOOL "Install CRC32C's header and library")
message("Add crc32c")
add_subdirectory(third-party/crc32c EXCLUDE_FROM_ALL)
if (NOT MSVC)
set(OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
# fix aarch64 build @ crc32c/src/crc32c_arm64_linux_check.h
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=address")
add_subdirectory(third-party/crc32c EXCLUDE_FROM_ALL)
set(CMAKE_CXX_FLAGS ${OLD_CMAKE_CXX_FLAGS})
unset(OLD_CMAKE_CXX_FLAGS)
else()
add_subdirectory(third-party/crc32c EXCLUDE_FROM_ALL)
endif()
set(CRC32C_FOUND 1)
if (TON_USE_ROCKSDB)
@ -198,6 +208,7 @@ include(CheckCXXCompilerFlag)
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(ZLIB REQUIRED)
if (TON_ARCH AND NOT MSVC)
@ -383,6 +394,7 @@ add_subdirectory(tl-utils)
add_subdirectory(adnl)
add_subdirectory(crypto)
add_subdirectory(lite-client)
add_subdirectory(emulator)
#BEGIN tonlib
add_subdirectory(tonlib)

View file

@ -1,43 +1,16 @@
## 05.2022 Update
* Initial synchronization improved: adjusted timeouts for state download and the way of choosing which state to download. Nodes with low network speed and/or bad connectivity will synchronize faster and consistently.
* Improved peer-to-peer network stability and DDoS resistance: now peers will only relay valid messages to the network. Large messages, which require splitting for relaying, will be retranslated as well, but only after the node gets all parts, and reassembles and checks them. Validators may sign certificates for network peers, which allow relaying large messages by parts without checks. It is used now by validators to faster relay new blocks. Sign and import certificate commands are exposed via `validator-engine-console`.
* Fixed some rare edge cases in TVM arithmetic operations related to big numbers (`2**63+`)
* Improved fixes used to combat wrong activate-destruct-activate contract behavior last November.
* Improved tonlib: support libraries (with client-side caching), getmethods completely fill c7 register, getmethods support slice arguments, improved messages listing for transactions, added extended block header params, added getConfig method.
* RocksDB updated to a newer version.
* Improved persistent state serialization: memory usage during serialization was optimized; the start of serialization on different nodes was sparsed.
* FunC update: support for string literals and constants (including precompiled constant expressions), semver, `include` expressions.
* Fixed rarely manifested bugs in `Asm.fif`.
* LiteClient supports key as cli parameter.
* Improved Liteserver DoS resistance for running getmethods.
## 03.2023 Update
1. Improvement of ADNL connection stability
2. Transaction emulator support and getAccountStateByTransaction method
3. Fixes of typos, undefined behavior and timer warnings
4. Handling incorrect integer literal values in funC; funC version bumped to 0.4.2
5. FunC Mathlib
Besides the work of the core team, this update is based on the efforts of @tvorogme (added support for slice arguments and noted bugs in Asm.fif), @akifoq (fixed bug in Asm.fif), @cryshado (noted strange behavior of LS, which, upon inspection, turned out to be a vector of DoS attack).
## 08.2022 Update
* Blockchain state serialization now works via separate db-handler which simplfies memory clearing after serialization
* CellDB now works asynchronously which substantially increase database access throughput
* Abseil-cpp and crc32 updated: solve issues with compilation on recent OS distributives
* Fixed a series of UBs and issues for exotic endianness hosts
* Added detailed network stats for overlays (can be accessed via `validator-console`)
* Improved auto-builds for wide range of systems.
* Added extended error information for unaccepted external messages: `exit_code` and TVM trace (where applicable).
* [Improved catchain DoS resistance](https://github.com/ton-blockchain/ton/blob/master/doc/catchain-dos.md)
* A series of FunC improvements, summarized [here](https://github.com/ton-blockchain/ton/pull/378)
#### Update delay
Update coincided with persistent state serialization event which lead to block production speed deterioration (issue substantially mitigated in update itself). This phenomena was aggravated by the fact that after update some validators lost ability to participate in block creation. The last was caused by threshold based hardcoded protocol version bump, where threshold was set in such manner (based on block height with value higher than 9m), that it eluded detection in private net tests. The update was temporarily paused and resumed after persistent state serialization ended and issues with block creation were resolved.
Besides the work of the core team, this update is based on the efforts of @awesome-doge (help with abseil-cpp upgrade), @rec00rsiff (noted issues for exotic endianess and implemented network stats) and third-party security auditors.
## 10.2022 Update
* Added extended block creation and general perfomance stats gathering
* Forbidden report data on blocks not committed to the master chain for LS
* Improved debug in TVM
* FunC 0.3.0: multi-line asms, bitwise operations for constants, duplication of identical definition for constants and asms now allowed
* New tonlib methods: sendMessageReturnHash, getTransactionsV2, getMasterchainBlockSignatures, getShardBlockProof, getLibraries.
* Fixed bugs related to invalid TVM output (c4, c5, libaries) and non-validated network data; avoided too deep recursion in libraries loading
* Fixed multiple undefined behavior issues
* Added build of FunC and Fift to WASM
Besides the work of the core team, this update is based on the efforts of @tvorogme (debug improvements), @AlexeyFSL (WASM builds) and third-party security auditors.
## 01.2023 Update
1. Added ConfigParam 44: `SuspendedAddressList`. Upon being set this config suspends initialisation of **uninit** addresses from the list for given time.
2. FunC: `v0.4.1` added pragmas for precise control of computation order
3. FunC: fixed compiler crashes for some exotic inputs
4. FunC: added legacy tester, a collection of smart-contracts which is used to check whether compilator update change compilation result
5. Improved archive manager: proper handling of recently garbage-collected blocks
## 12.2022 Update
Node update:
@ -53,3 +26,47 @@ Node update:
10. TON Storage: added storage-daemon (create, download bag of Files, storage-provider staff), added storage-daemon-cli
Besides the work of the core team, this update is based on the efforts of @vtamara (help with abseil-cpp upgrade), @krigga(in-place modification of global variables) and third-party security auditors.
## 10.2022 Update
* Added extended block creation and general perfomance stats gathering
* Forbidden report data on blocks not committed to the master chain for LS
* Improved debug in TVM
* FunC 0.3.0: multi-line asms, bitwise operations for constants, duplication of identical definition for constants and asms now allowed
* New tonlib methods: sendMessageReturnHash, getTransactionsV2, getMasterchainBlockSignatures, getShardBlockProof, getLibraries.
* Fixed bugs related to invalid TVM output (c4, c5, libaries) and non-validated network data; avoided too deep recursion in libraries loading
* Fixed multiple undefined behavior issues
* Added build of FunC and Fift to WASM
Besides the work of the core team, this update is based on the efforts of @tvorogme (debug improvements), @AlexeyFSL (WASM builds) and third-party security auditors.
## 08.2022 Update
* Blockchain state serialization now works via separate db-handler which simplfies memory clearing after serialization
* CellDB now works asynchronously which substantially increase database access throughput
* Abseil-cpp and crc32 updated: solve issues with compilation on recent OS distributives
* Fixed a series of UBs and issues for exotic endianness hosts
* Added detailed network stats for overlays (can be accessed via `validator-console`)
* Improved auto-builds for wide range of systems.
* Added extended error information for unaccepted external messages: `exit_code` and TVM trace (where applicable).
* [Improved catchain DoS resistance](https://github.com/ton-blockchain/ton/blob/master/doc/catchain-dos.md)
* A series of FunC improvements, summarized [here](https://github.com/ton-blockchain/ton/pull/378)
#### Update delay
Update coincided with persistent state serialization event which lead to block production speed deterioration (issue substantially mitigated in update itself). This phenomena was aggravated by the fact that after update some validators lost ability to participate in block creation. The last was caused by threshold based hardcoded protocol version bump, where threshold was set in such manner (based on block height with value higher than 9m), that it eluded detection in private net tests. The update was temporarily paused and resumed after persistent state serialization ended and issues with block creation were resolved.
Besides the work of the core team, this update is based on the efforts of @awesome-doge (help with abseil-cpp upgrade), @rec00rsiff (noted issues for exotic endianess and implemented network stats) and third-party security auditors.
## 05.2022 Update
* Initial synchronization improved: adjusted timeouts for state download and the way of choosing which state to download. Nodes with low network speed and/or bad connectivity will synchronize faster and consistently.
* Improved peer-to-peer network stability and DDoS resistance: now peers will only relay valid messages to the network. Large messages, which require splitting for relaying, will be retranslated as well, but only after the node gets all parts, and reassembles and checks them. Validators may sign certificates for network peers, which allow relaying large messages by parts without checks. It is used now by validators to faster relay new blocks. Sign and import certificate commands are exposed via `validator-engine-console`.
* Fixed some rare edge cases in TVM arithmetic operations related to big numbers (`2**63+`)
* Improved fixes used to combat wrong activate-destruct-activate contract behavior last November.
* Improved tonlib: support libraries (with client-side caching), getmethods completely fill c7 register, getmethods support slice arguments, improved messages listing for transactions, added extended block header params, added getConfig method.
* RocksDB updated to a newer version.
* Improved persistent state serialization: memory usage during serialization was optimized; the start of serialization on different nodes was sparsed.
* FunC update: support for string literals and constants (including precompiled constant expressions), semver, `include` expressions.
* Fixed rarely manifested bugs in `Asm.fif`.
* LiteClient supports key as cli parameter.
* Improved Liteserver DoS resistance for running getmethods.
Besides the work of the core team, this update is based on the efforts of @tvorogme (added support for slice arguments and noted bugs in Asm.fif), @akifoq (fixed bug in Asm.fif), @cryshado (noted strange behavior of LS, which, upon inspection, turned out to be a vector of DoS attack).

View file

@ -1,21 +1,28 @@
<div align="center">
<a href="https://ton.org">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://ton.org/download/ton_logo_dark_background.svg">
<img alt="TON logo" src="https://ton.org/download/ton_logo_light_background.svg">
</picture>
</a>
<h3>Reference implementation of TON Node and tools</h3>
<hr/>
</div>
##
[![TON Overflow Group][ton-overflow-badge]][ton-overflow-url]
[![Stack Overflow Group][stack-overflow-badge]][stack-overflow-url]
[![Telegram Foundation Group][telegram-foundation-badge]][telegram-foundation-url]
[![Telegram Community Chat][telegram-tondev-badge]][telegram-tondev-url]
[![Telegram Community Group][telegram-community-badge]][telegram-community-url]
[![Telegram Foundation Group][telegram-foundation-badge]][telegram-foundation-url]
[![Twitter Group][twitter-badge]][twitter-url]
[telegram-foundation-badge]: https://img.shields.io/badge/-TON%20Foundation-2CA5E0?style=flat&logo=telegram&logoColor=white
[telegram-community-badge]: https://img.shields.io/badge/-TON%20Community-2CA5E0?style=flat&logo=telegram&logoColor=white
[telegram-foundation-badge]: https://img.shields.io/badge/TON%20Foundation-2CA5E0?logo=telegram&logoColor=white&style=flat
[telegram-community-badge]: https://img.shields.io/badge/TON%20Community-2CA5E0?logo=telegram&logoColor=white&style=flat
[telegram-tondev-badge]: https://img.shields.io/badge/chat-TONDev-2CA5E0?logo=telegram&logoColor=white&style=flat
[telegram-foundation-url]: https://t.me/tonblockchain
[telegram-community-url]: https://t.me/toncoin
[telegram-tondev-url]: https://t.me/tondev_eng
[twitter-badge]: https://img.shields.io/twitter/follow/ton_blockchain
[twitter-url]: https://twitter.com/ton_blockchain
[stack-overflow-badge]: https://img.shields.io/badge/-Stack%20Overflow-FE7A16?style=flat&logo=stack-overflow&logoColor=white
@ -27,6 +34,15 @@
Main TON monorepo, which includes the code of the node/validator, lite-client, tonlib, FunC compiler, etc.
## The Open Network
__The Open Network (TON)__ is a fast, secure, scalable blockchain focused on handling _millions of transactions per second_ (TPS) with the goal of reaching hundreds of millions of blockchain users.
- To learn more about different aspects of TON blockchain and its underlying ecosystem check [documentation](ton.org/docs)
- To run node, validator or lite-server check [Participate section](https://ton.org/docs/participate/nodes/run-node)
- To develop decentralised apps check [Tutorials](https://ton.org/docs/develop/smart-contracts/), [FunC docs](https://ton.org/docs/develop/func/overview) and [DApp tutorials](https://ton.org/docs/develop/dapps/)
- To work on TON check [wallets](https://ton.app/wallets), [explorers](https://ton.app/explorers), [DEXes](https://ton.app/dex) and [utilities](https://ton.app/utilities)
- To interact with TON check [APIs](https://ton.org/docs/develop/dapps/apis/)
## Updates flow:
* **master branch** - mainnet is running on this stable branch.

View file

@ -99,6 +99,8 @@ target_link_libraries(adnl-pong PUBLIC tdactor ton_crypto tl_api tdnet common
add_library(adnltest STATIC ${ADNL_TEST_SOURCE})
target_include_directories(adnltest PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(adnltest PUBLIC adnl )
install(TARGETS adnl-proxy RUNTIME DESTINATION bin)
endif()
#END internal

View file

@ -68,7 +68,9 @@ void AdnlPeerPairImpl::alarm() {
}
if (retry_send_at_ && retry_send_at_.is_in_past()) {
retry_send_at_ = td::Timestamp::never();
send_messages_in(std::move(pending_messages_), false);
auto messages = std::move(pending_messages_);
pending_messages_.clear();
send_messages_in(std::move(messages), false);
}
alarm_timestamp().relax(next_dht_query_at_);
alarm_timestamp().relax(next_db_update_at_);
@ -113,6 +115,8 @@ void AdnlPeerPairImpl::discover() {
}
void AdnlPeerPairImpl::receive_packet_checked(AdnlPacket packet) {
last_received_packet_ = td::Timestamp::now();
try_reinit_at_ = td::Timestamp::never();
request_reverse_ping_after_ = td::Timestamp::in(15.0);
auto d = Adnl::adnl_start_time();
if (packet.dst_reinit_date() > d) {
@ -263,7 +267,9 @@ void AdnlPeerPairImpl::send_messages_in(std::vector<OutboundAdnlMessage> message
size_t ptr = 0;
bool first = true;
do {
size_t s = (channel_ready_ ? channel_packet_header_max_size() : packet_header_max_size());
bool try_reinit = try_reinit_at_ && try_reinit_at_.is_in_past();
bool via_channel = channel_ready_ && !try_reinit;
size_t s = (via_channel ? channel_packet_header_max_size() : packet_header_max_size());
if (first) {
s += 2 * addr_list_max_size();
}
@ -307,7 +313,7 @@ void AdnlPeerPairImpl::send_messages_in(std::vector<OutboundAdnlMessage> message
}
}
if (!channel_ready_) {
if (!via_channel) {
packet.set_reinit_date(Adnl::adnl_start_time(), reinit_date_);
packet.set_source(local_id_);
}
@ -330,7 +336,7 @@ void AdnlPeerPairImpl::send_messages_in(std::vector<OutboundAdnlMessage> message
packet.run_basic_checks().ensure();
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), conn, id = print_id(),
via_channel = channel_ready_](td::Result<AdnlPacket> res) {
via_channel](td::Result<AdnlPacket> res) {
if (res.is_error()) {
LOG(ERROR) << id << ": dropping OUT message: error while creating packet: " << res.move_as_error();
} else {
@ -339,9 +345,9 @@ void AdnlPeerPairImpl::send_messages_in(std::vector<OutboundAdnlMessage> message
});
td::actor::send_closure(local_actor_, &AdnlLocalId::update_packet, std::move(packet),
!channel_ready_ && ack_seqno_ == 0 && in_seqno_ == 0, !channel_ready_,
(!channel_ready_ && ack_seqno_ == 0 && in_seqno_ == 0) || try_reinit, !via_channel,
(first || s + addr_list_max_size() <= AdnlNetworkManager::get_mtu())
? peer_recv_addr_list_version_
? (try_reinit ? 0 : peer_recv_addr_list_version_)
: 0x7fffffff,
(first || s + 2 * addr_list_max_size() <= AdnlNetworkManager::get_mtu())
? peer_recv_priority_addr_list_version_
@ -388,6 +394,9 @@ void AdnlPeerPairImpl::send_messages(std::vector<OutboundAdnlMessage> messages)
void AdnlPeerPairImpl::send_packet_continue(AdnlPacket packet, td::actor::ActorId<AdnlNetworkConnection> conn,
bool via_channel) {
if (!try_reinit_at_ && last_received_packet_ < td::Timestamp::in(-5.0)) {
try_reinit_at_ = td::Timestamp::in(10.0);
}
packet.run_basic_checks().ensure();
auto B = serialize_tl_object(packet.tl(), true);
if (via_channel) {
@ -800,7 +809,9 @@ void AdnlPeerPairImpl::Conn::create_conn(td::actor::ActorId<AdnlPeerPairImpl> pe
void AdnlPeerPairImpl::conn_change_state(AdnlConnectionIdShort id, bool ready) {
if (ready) {
if (pending_messages_.size() > 0) {
send_messages_in(std::move(pending_messages_), true);
auto messages = std::move(pending_messages_);
pending_messages_.clear();
send_messages_in(std::move(messages), true);
}
}
}
@ -949,6 +960,7 @@ void AdnlPeerPairImpl::got_data_from_dht(td::Result<AdnlNode> R) {
CHECK(dht_query_active_);
dht_query_active_ = false;
next_dht_query_at_ = td::Timestamp::in(td::Random::fast(60.0, 120.0));
alarm_timestamp().relax(next_dht_query_at_);
if (R.is_error()) {
VLOG(ADNL_INFO) << this << ": dht query failed: " << R.move_as_error();
return;

View file

@ -255,6 +255,9 @@ class AdnlPeerPairImpl : public AdnlPeerPair {
td::Timestamp next_db_update_at_ = td::Timestamp::never();
td::Timestamp retry_send_at_ = td::Timestamp::never();
td::Timestamp last_received_packet_ = td::Timestamp::never();
td::Timestamp try_reinit_at_ = td::Timestamp::never();
bool has_reverse_addr_ = false;
td::Timestamp request_reverse_ping_after_ = td::Timestamp::now();
bool request_reverse_ping_active_ = false;

View file

@ -1,22 +1,28 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
option(NIX "Use \"ON\" for a static build." OFF)
find_package(MHD)
if (MHD_FOUND)
set(BLOCHAIN_EXPLORER_SOURCE
set(BLOCHAIN_EXPLORER_SOURCE
blockchain-explorer.cpp
blockchain-explorer.hpp
blockchain-explorer-http.cpp
blockchain-explorer-http.hpp
blockchain-explorer-query.cpp
blockchain-explorer-query.hpp
)
)
add_executable(blockchain-explorer ${BLOCHAIN_EXPLORER_SOURCE})
add_executable(blockchain-explorer ${BLOCHAIN_EXPLORER_SOURCE})
if (NIX)
find_package(PkgConfig REQUIRED)
pkg_check_modules(MHD libmicrohttpd)
target_include_directories(blockchain-explorer PUBLIC ${MHD_INCLUDE_DIRS} ${MHD_STATIC_INCLUDE_DIRS})
target_link_libraries(blockchain-explorer tdutils tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block ${MHD_LIBRARIES} ${MHD_STATIC_LIBRARIES})
else()
find_package(MHD)
target_include_directories(blockchain-explorer PUBLIC ${MHD_INCLUDE_DIRS})
target_link_libraries(blockchain-explorer tdutils tdactor adnllite tl_lite_api tl-lite-utils
ton_crypto ton_block ${MHD_LIBRARY})
target_link_libraries(blockchain-explorer tdutils tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block ${MHD_LIBRARIES})
endif()
install(TARGETS blockchain-explorer RUNTIME DESTINATION bin)

View file

@ -143,6 +143,7 @@ set(FIFT_SOURCE
fift/Dictionary.cpp
fift/Fift.cpp
fift/IntCtx.cpp
fift/HashMap.cpp
fift/Continuation.cpp
fift/SourceLookup.cpp
fift/utils.cpp
@ -151,6 +152,7 @@ set(FIFT_SOURCE
fift/Dictionary.h
fift/Fift.h
fift/IntCtx.h
fift/HashMap.h
fift/Continuation.h
fift/SourceLookup.h
fift/utils.h
@ -274,6 +276,10 @@ add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE})
target_include_directories(ton_crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(ton_crypto PUBLIC ${OPENSSL_CRYPTO_LIBRARY} tdutils tddb_utils)
if (USE_EMSCRIPTEN)
target_link_options(ton_crypto PRIVATE -fexceptions)
target_compile_options(ton_crypto PRIVATE -fexceptions)
endif()
if (NOT WIN32)
find_library(DL dl)
if (DL)
@ -296,6 +302,10 @@ target_link_libraries(test-ed25519-crypto PUBLIC ton_crypto)
add_library(fift-lib ${FIFT_SOURCE})
target_include_directories(fift-lib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(fift-lib PUBLIC ton_crypto ton_db tdutils ton_block)
if (USE_EMSCRIPTEN)
target_link_options(fift-lib PRIVATE -fexceptions)
target_compile_options(fift-lib PRIVATE -fexceptions)
endif()
set_target_properties(fift-lib PROPERTIES OUTPUT_NAME fift)
add_executable(fift fift/fift-main.cpp)
@ -324,17 +334,20 @@ if (USE_EMSCRIPTEN)
add_executable(funcfiftlib funcfiftlib/funcfiftlib.cpp ${FUNC_LIB_SOURCE})
target_include_directories(funcfiftlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(funcfiftlib PUBLIC fift-lib src_parser git)
target_link_options(funcfiftlib PRIVATE -sEXPORTED_RUNTIME_METHODS=FS,ccall,cwrap,_malloc,free,UTF8ToString,stringToUTF8)
target_link_options(funcfiftlib PRIVATE -sEXPORTED_FUNCTIONS=_func_compile,_version)
target_link_options(funcfiftlib PRIVATE -sEXPORTED_RUNTIME_METHODS=FS,ccall,cwrap,UTF8ToString,stringToUTF8,lengthBytesUTF8,addFunction,removeFunction,setValue)
target_link_options(funcfiftlib PRIVATE -sEXPORTED_FUNCTIONS=_func_compile,_version,_malloc,_free,_setThrew)
target_link_options(funcfiftlib PRIVATE -sEXPORT_NAME=CompilerModule)
target_link_options(funcfiftlib PRIVATE -sERROR_ON_UNDEFINED_SYMBOLS=0)
target_link_options(funcfiftlib PRIVATE -sFILESYSTEM=1)
target_link_options(funcfiftlib PRIVATE -sFILESYSTEM=1 -lnodefs.js)
target_link_options(funcfiftlib PRIVATE -Oz)
target_link_options(funcfiftlib PRIVATE -sIGNORE_MISSING_MAIN=1)
target_link_options(funcfiftlib PRIVATE -sAUTO_NATIVE_LIBRARIES=0)
target_link_options(funcfiftlib PRIVATE -sMODULARIZE=1)
target_link_options(funcfiftlib PRIVATE -sALLOW_MEMORY_GROWTH=1)
target_link_options(funcfiftlib PRIVATE -sALLOW_TABLE_GROWTH=1)
target_link_options(funcfiftlib PRIVATE --embed-file ${CMAKE_CURRENT_SOURCE_DIR}/fift/lib@/fiftlib)
target_compile_options(funcfiftlib PRIVATE -sDISABLE_EXCEPTION_CATCHING=0)
target_link_options(funcfiftlib PRIVATE -fexceptions)
target_compile_options(funcfiftlib PRIVATE -fexceptions)
endif()
add_executable(tlbc tl/tlbc.cpp)
@ -484,6 +497,6 @@ if (WINGETOPT_FOUND)
target_link_libraries_system(test-weight-distr wingetopt)
endif()
install(TARGETS fift func pow-miner RUNTIME DESTINATION bin)
install(TARGETS fift func create-state tlbc RUNTIME DESTINATION bin)
install(DIRECTORY fift/lib/ DESTINATION lib/fift)
install(DIRECTORY smartcont DESTINATION share/ton)

View file

@ -624,6 +624,7 @@ void BlockDbImpl::get_block_by_id(ton::BlockId blk_id, bool need_data, td::Promi
}
}
promise(it->second);
return;
}
promise(td::Status::Error(-666, "block not found in database"));
}
@ -642,6 +643,7 @@ void BlockDbImpl::get_state_by_id(ton::BlockId blk_id, bool need_data, td::Promi
}
}
promise(it->second);
return;
}
if (zerostate.not_null() && blk_id == zerostate->blk.id) {
LOG(DEBUG) << "get_state_by_id(): zerostate requested";
@ -666,6 +668,7 @@ void BlockDbImpl::get_out_queue_info_by_id(ton::BlockId blk_id, td::Promise<td::
if (it == state_info.end()) {
promise(td::Status::Error(
-666, std::string{"cannot obtain output queue info for block "} + blk_id.to_str() + " : cannot load state"));
return;
}
if (it->second->data.is_null()) {
LOG(DEBUG) << "loading data for state " << blk_id.to_str();
@ -679,6 +682,7 @@ void BlockDbImpl::get_out_queue_info_by_id(ton::BlockId blk_id, td::Promise<td::
if (it2 == block_info.end()) {
promise(td::Status::Error(-666, std::string{"cannot obtain output queue info for block "} + blk_id.to_str() +
" : cannot load block description"));
return;
}
vm::StaticBagOfCellsDbLazy::Options options;
auto res = vm::StaticBagOfCellsDbLazy::create(it->second->data.clone(), options);
@ -707,10 +711,12 @@ void BlockDbImpl::get_out_queue_info_by_id(ton::BlockId blk_id, td::Promise<td::
if (it->second->blk.root_hash != state_root->get_hash().bits()) {
promise(td::Status::Error(
-668, std::string{"state for block "} + blk_id.to_str() + " is invalid : state root hash mismatch"));
return;
}
vm::CellSlice cs = vm::load_cell_slice(state_root);
if (!cs.have(64, 1) || cs.prefetch_ulong(32) != 0x9023afde) {
promise(td::Status::Error(-668, std::string{"state for block "} + blk_id.to_str() + " is invalid"));
return;
}
auto out_queue_info = cs.prefetch_ref();
promise(Ref<OutputQueueInfoDescr>{true, blk_id, it2->second->blk.root_hash.cbits(), state_root->get_hash().bits(),
@ -758,6 +764,7 @@ void BlockDbImpl::save_new_block(ton::BlockIdExt id, td::BufferSlice data, int a
auto save_res = save_db_file(id.file_hash, data, FMode::chk_if_exists | FMode::overwrite | FMode::chk_file_hash);
if (save_res.is_error()) {
promise(std::move(save_res));
return;
}
auto sz = data.size();
auto lev = bb.alloc<log::NewBlock>(id.id, id.root_hash, id.file_hash, data.size(), authority & 0xff);
@ -780,6 +787,7 @@ void BlockDbImpl::save_new_state(ton::BlockIdExt id, td::BufferSlice data, int a
auto save_res = save_db_file(id.file_hash, data, FMode::chk_if_exists | FMode::overwrite | FMode::chk_file_hash);
if (save_res.is_error()) {
promise(std::move(save_res));
return;
}
auto sz = data.size();
auto lev = bb.alloc<log::NewState>(id.id, id.root_hash, id.file_hash, data.size(), authority & 0xff);

View file

@ -1000,7 +1000,7 @@ bool Account::skip_copy_depth_balance(vm::CellBuilder& cb, vm::CellSlice& cs) co
}
const Account t_Account, t_AccountE{true};
const RefTo<Account> t_Ref_Account;
const RefTo<Account> t_Ref_AccountE{true};
bool ShardAccount::extract_account_state(Ref<vm::CellSlice> cs_ref, Ref<vm::Cell>& acc_state) {
if (cs_ref.is_null()) {

View file

@ -536,7 +536,7 @@ struct Account final : TLB_Complex {
};
extern const Account t_Account, t_AccountE;
extern const RefTo<Account> t_Ref_Account;
extern const RefTo<Account> t_Ref_AccountE;
struct AccountStatus final : TLB {
enum { acc_state_uninit, acc_state_frozen, acc_state_active, acc_state_nonexist };
@ -572,7 +572,7 @@ struct ShardAccount final : TLB_Complex {
return cs.advance_ext(0x140, 1);
}
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return cs.advance(0x140) && t_Ref_Account.validate_skip(ops, cs, weak);
return cs.advance(0x140) && t_Ref_AccountE.validate_skip(ops, cs, weak);
}
static bool unpack(vm::CellSlice& cs, Record& info) {
return info.unpack(cs);

View file

@ -366,7 +366,7 @@ trans_merge_install$0111 split_info:SplitMergeInfo
smc_info#076ef1ea actions:uint16 msgs_sent:uint16
unixtime:uint32 block_lt:uint64 trans_lt:uint64
rand_seed:bits256 balance_remaining:CurrencyCollection
myself:MsgAddressInt = SmartContractInfo;
myself:MsgAddressInt global_config:(Maybe Cell) = SmartContractInfo;
//
//
out_list_empty$_ = OutList 0;

View file

@ -315,6 +315,113 @@ td::Result<TransactionList::Info> TransactionList::validate() const {
return std::move(res);
}
td::Result<BlockTransaction::Info> BlockTransaction::validate(bool check_proof) const {
if (root.is_null()) {
return td::Status::Error("transactions are expected to be non-empty");
}
if (check_proof && proof->get_hash().bits().compare(root->get_hash().bits(), 256)) {
return td::Status::Error(PSLICE() << "transaction hash mismatch: Merkle proof expects "
<< proof->get_hash().bits().to_hex(256)
<< " but received data has " << root->get_hash().bits().to_hex(256));
}
block::gen::Transaction::Record trans;
if (!tlb::unpack_cell(root, trans)) {
return td::Status::Error("cannot unpack transaction cell");
}
Info res;
res.blkid = blkid;
res.now = trans.now;
res.lt = trans.lt;
res.hash = root->get_hash().bits();
res.transaction = root;
return std::move(res);
}
td::Result<BlockTransactionList::Info> BlockTransactionList::validate(bool check_proof) const {
constexpr int max_answer_transactions = 256;
TRY_RESULT_PREFIX(list, vm::std_boc_deserialize_multi(std::move(transactions_boc)), "cannot deserialize transactions boc: ");
std::vector<td::Ref<vm::Cell>> tx_proofs(list.size());
if (check_proof) {
try {
TRY_RESULT(proof_cell, vm::std_boc_deserialize(std::move(proof_boc)));
auto virt_root = vm::MerkleProof::virtualize(proof_cell, 1);
if (blkid.root_hash != virt_root->get_hash().bits()) {
return td::Status::Error("Invalid block proof root hash");
}
block::gen::Block::Record blk;
block::gen::BlockExtra::Record extra;
if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(std::move(blk.extra), extra))) {
return td::Status::Error("Error unpacking proof cell");
}
vm::AugmentedDictionary acc_dict{vm::load_cell_slice_ref(extra.account_blocks), 256,
block::tlb::aug_ShardAccountBlocks};
bool eof = false;
ton::LogicalTime reverse = reverse_mode ? ~0ULL : 0;
ton::LogicalTime trans_lt = static_cast<ton::LogicalTime>(start_lt);
td::Bits256 cur_addr = start_addr;
bool allow_same = true;
int count = 0;
while (!eof && count < req_count && count < max_answer_transactions) {
auto value = acc_dict.extract_value(
acc_dict.vm::DictionaryFixed::lookup_nearest_key(cur_addr.bits(), 256, !reverse, allow_same));
if (value.is_null()) {
eof = true;
break;
}
allow_same = false;
if (cur_addr != start_addr) {
trans_lt = reverse;
}
block::gen::AccountBlock::Record acc_blk;
if (!tlb::csr_unpack(std::move(value), acc_blk) || acc_blk.account_addr != cur_addr) {
return td::Status::Error("Error unpacking proof account block");
}
vm::AugmentedDictionary trans_dict{vm::DictNonEmpty(), std::move(acc_blk.transactions), 64,
block::tlb::aug_AccountTransactions};
td::BitArray<64> cur_trans{(long long)trans_lt};
while (count < req_count && count < max_answer_transactions) {
auto tvalue = trans_dict.extract_value_ref(
trans_dict.vm::DictionaryFixed::lookup_nearest_key(cur_trans.bits(), 64, !reverse));
if (tvalue.is_null()) {
trans_lt = reverse;
break;
}
if (static_cast<size_t>(count) < tx_proofs.size()) {
tx_proofs[count] = std::move(tvalue);
}
count++;
}
}
if (static_cast<size_t>(count) != list.size()) {
return td::Status::Error(PSLICE() << "Txs count mismatch in proof (" << count << ") and response (" << list.size() << ")");
}
} catch (vm::VmError& err) {
return err.as_status("Couldn't verify proof: ");
} catch (vm::VmVirtError& err) {
return err.as_status("Couldn't verify proof: ");
} catch (...) {
return td::Status::Error("Unknown exception raised while verifying proof");
}
}
Info res;
for (int i = 0; i < static_cast<int>(list.size()); i++) {
auto& root = list[i];
BlockTransaction transaction;
transaction.root = root;
transaction.blkid = blkid;
transaction.proof = tx_proofs[i];
TRY_RESULT(info, transaction.validate(check_proof));
res.transactions.push_back(std::move(info));
}
return std::move(res);
}
td::Status BlockProofLink::validate(td::uint32* save_utime) const {
if (save_utime) {
*save_utime = 0;
@ -362,7 +469,7 @@ td::Status BlockProofLink::validate(td::uint32* save_utime) const {
if (to.seqno()) {
TRY_STATUS(check_block_header(vd_root, to));
if (!(tlb::unpack_cell(vd_root, blk) && tlb::unpack_cell(blk.info, info))) {
return td::Status::Error("cannot unpack header for block "s + from.to_str());
return td::Status::Error("cannot unpack header for block "s + to.to_str());
}
if (info.key_block != is_key) {
return td::Status::Error(PSTRING() << "incorrect is_key_block value " << is_key << " for destination block "

View file

@ -88,4 +88,36 @@ struct TransactionList {
td::Result<Info> validate() const;
};
struct BlockTransaction {
ton::BlockIdExt blkid;
td::Ref<vm::Cell> root;
td::Ref<vm::Cell> proof;
struct Info {
ton::BlockIdExt blkid;
td::uint32 now;
ton::LogicalTime lt;
ton::Bits256 hash;
td::Ref<vm::Cell> transaction;
};
td::Result<Info> validate(bool check_proof) const;
};
struct BlockTransactionList {
ton::BlockIdExt blkid;
td::BufferSlice transactions_boc;
td::BufferSlice proof_boc;
ton::LogicalTime start_lt;
td::Bits256 start_addr;
bool reverse_mode;
int req_count;
struct Info {
ton::BlockIdExt blkid;
std::vector<BlockTransaction::Info> transactions;
};
td::Result<Info> validate(bool check_proof) const;
};
} // namespace block

View file

@ -47,6 +47,7 @@
#include "fift/Fift.h"
#include "fift/Dictionary.h"
#include "fift/SourceLookup.h"
#include "fift/IntCtx.h"
#include "fift/words.h"
#include "td/utils/logging.h"
@ -308,7 +309,7 @@ td::RefInt256 create_smartcontract(td::RefInt256 smc_addr, Ref<vm::Cell> code, R
THRERR("cannot create smart-contract AccountStorage");
Ref<vm::DataCell> storage = cb.finalize();
vm::CellStorageStat stats;
PDO(stats.compute_used_storage(Ref<vm::Cell>(storage)));
PDO(stats.compute_used_storage(Ref<vm::Cell>(storage)).is_ok());
if (verbosity > 2) {
std::cerr << "storage is:\n";
vm::load_cell_slice(storage).print_rec(std::cerr);
@ -867,7 +868,8 @@ int main(int argc, char* const argv[]) {
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
break;
case 'V':
std::cout << "create-state build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
std::cout << "create-state build information: [ Commit: " << GitMetadata::CommitSHA1()
<< ", Date: " << GitMetadata::CommitDate() << "]\n";
std::exit(0);
break;
case 'h':

View file

@ -644,7 +644,6 @@ class Config {
static td::Result<std::vector<int>> unpack_param_dict(vm::Dictionary& dict);
static td::Result<std::vector<int>> unpack_param_dict(Ref<vm::Cell> dict_root);
protected:
Config(int _mode) : mode(_mode) {
config_addr.set_zero();
}

View file

@ -20,6 +20,7 @@
#include "block/block.h"
#include "block/block-parse.h"
#include "block/block-auto.h"
#include "crypto/openssl/rand.hpp"
#include "td/utils/bits.h"
#include "td/utils/uint128.h"
#include "ton/ton-shard.h"
@ -513,6 +514,7 @@ td::RefInt256 Account::compute_storage_fees(ton::UnixTime now, const std::vector
return StoragePrices::compute_storage_fees(now, pricing, storage_stat, last_paid, is_special, is_masterchain());
}
namespace transaction {
Transaction::Transaction(const Account& _account, int ttype, ton::LogicalTime req_start_lt, ton::UnixTime _now,
Ref<vm::Cell> _inmsg)
: trans_type(ttype)
@ -589,7 +591,7 @@ bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig*
in_msg_extern = true;
// compute forwarding fees for this external message
vm::CellStorageStat sstat; // for message size
sstat.compute_used_storage(cs); // message body
auto cell_info = sstat.compute_used_storage(cs).move_as_ok(); // message body
sstat.bits -= cs.size(); // bits in the root cells are free
sstat.cells--; // the root cell itself is not counted as a cell
LOG(DEBUG) << "storage paid for a message: " << sstat.cells << " cells, " << sstat.bits << " bits";
@ -597,6 +599,10 @@ bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig*
LOG(DEBUG) << "inbound external message too large, invalid";
return false;
}
if (cell_info.max_merkle_depth > max_allowed_merkle_depth) {
LOG(DEBUG) << "inbound external message has too big merkle depth, invalid";
return false;
}
// fetch message pricing info
CHECK(cfg);
const MsgPrices& msg_prices = cfg->fetch_msg_prices(account.is_masterchain());
@ -745,6 +751,7 @@ bool Transaction::prepare_credit_phase() {
total_fees += std::move(collected);
return true;
}
} // namespace transaction
bool ComputePhaseConfig::parse_GasLimitsPrices(Ref<vm::Cell> cell, td::RefInt256& freeze_due_limit,
td::RefInt256& delete_due_limit) {
@ -837,6 +844,7 @@ td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const {
: td::rshift(gas_price256 * (gas_used - flat_gas_limit), 16, 1) + flat_gas_price;
}
namespace transaction {
bool Transaction::compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg) {
// Compute gas limits
if (account.is_special) {
@ -1057,13 +1065,21 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
std::unique_ptr<StringLoggerTail> logger;
auto vm_log = vm::VmLog();
if (cfg.with_vm_log) {
logger = std::make_unique<StringLoggerTail>();
size_t log_max_size = cfg.vm_log_verbosity > 0 ? 1024 * 1024 : 256;
logger = std::make_unique<StringLoggerTail>(log_max_size);
vm_log.log_interface = logger.get();
vm_log.log_options = td::LogOptions(VERBOSITY_NAME(DEBUG), true, false);
if (cfg.vm_log_verbosity > 1) {
vm_log.log_mask |= vm::VmLog::ExecLocation;
if (cfg.vm_log_verbosity > 2) {
vm_log.log_mask |= vm::VmLog::DumpStack | vm::VmLog::GasRemaining;
}
}
}
vm::VmState vm{new_code, std::move(stack), gas, 1, new_data, vm_log, compute_vm_libraries(cfg)};
vm.set_max_data_depth(cfg.max_vm_data_depth);
vm.set_c7(prepare_vm_c7(cfg)); // tuple with SmartContractInfo
vm.set_chksig_always_succeed(cfg.ignore_chksig);
// vm.incr_stack_trace(1); // enable stack dump after each step
LOG(DEBUG) << "starting VM";
@ -1145,19 +1161,20 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
ap.reserved_balance.set_zero();
td::Ref<vm::Cell> old_code = new_code, old_data = new_data, old_library = new_library;
auto enforce_state_size_limits = [&]() {
auto enforce_state_limits = [&]() {
if (account.is_special) {
return true;
}
if (!check_state_size_limit(cfg)) {
auto S = check_state_limits(cfg);
if (S.is_error()) {
// Rollback changes to state, fail action phase
LOG(INFO) << "Account state size exceeded limits";
LOG(INFO) << "Account state size exceeded limits: " << S.move_as_error();
new_storage_stat.clear();
new_code = old_code;
new_data = old_data;
new_library = old_library;
ap.result_code = 50;
ap.state_size_too_big = true;
ap.state_exceeds_limits = true;
return false;
}
return true;
@ -1238,8 +1255,8 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
ap.no_funds = true;
}
LOG(DEBUG) << "invalid action " << ap.result_arg << " in action list: error code " << ap.result_code;
// This is reuqired here because changes to libraries are applied even if actipn phase fails
enforce_state_size_limits();
// This is reuqired here because changes to libraries are applied even if action phase fails
enforce_state_limits();
return true;
}
}
@ -1249,7 +1266,7 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
new_code = ap.new_code;
}
new_data = compute_phase->new_data; // tentative persistent data update applied
if (!enforce_state_size_limits()) {
if (!enforce_state_limits()) {
return true;
}
@ -1322,8 +1339,8 @@ int Transaction::try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, c
return 41;
}
vm::CellStorageStat sstat;
sstat.compute_used_storage(lib_ref);
if (sstat.cells > cfg.size_limits.max_library_cells) {
auto cell_info = sstat.compute_used_storage(lib_ref).move_as_ok();
if (sstat.cells > cfg.size_limits.max_library_cells || cell_info.max_merkle_depth > max_allowed_merkle_depth) {
return 43;
}
vm::CellBuilder cb;
@ -1338,6 +1355,7 @@ int Transaction::try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, c
ap.spec_actions++;
return 0;
}
} // namespace transaction
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
@ -1372,6 +1390,7 @@ td::RefInt256 MsgPrices::get_next_part(td::RefInt256 total) const {
return (std::move(total) * next_frac) >> 16;
}
namespace transaction {
bool Transaction::check_replace_src_addr(Ref<vm::CellSlice>& src_addr) const {
int t = (int)src_addr->prefetch_ulong(2);
if (!t && src_addr->size_ext() == 2) {
@ -1594,16 +1613,27 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
// compute size of message
vm::CellStorageStat sstat; // for message size
// preliminary storage estimation of the resulting message
sstat.add_used_storage(msg.init, true, 3); // message init
sstat.add_used_storage(msg.body, true, 3); // message body (the root cell itself is not counted)
unsigned max_merkle_depth = 0;
auto add_used_storage = [&](const auto& x, unsigned skip_root_count) {
if (x.not_null()) {
auto res = sstat.add_used_storage(x, true, skip_root_count).move_as_ok();
max_merkle_depth = std::max(max_merkle_depth, res.max_merkle_depth);
}
};
add_used_storage(msg.init, 3); // message init
add_used_storage(msg.body, 3); // message body (the root cell itself is not counted)
if (!ext_msg) {
sstat.add_used_storage(info.value->prefetch_ref());
add_used_storage(info.value->prefetch_ref(), 0);
}
LOG(DEBUG) << "storage paid for a message: " << sstat.cells << " cells, " << sstat.bits << " bits";
if (sstat.bits > cfg.size_limits.max_msg_bits || sstat.cells > cfg.size_limits.max_msg_cells) {
LOG(DEBUG) << "message too large, invalid";
return skip_invalid ? 0 : 40;
}
if (max_merkle_depth > max_allowed_merkle_depth) {
LOG(DEBUG) << "message has too big merkle depth, invalid";
return skip_invalid ? 0 : 40;
}
// compute forwarding fees
auto fees_c = msg_prices.compute_fwd_ihr_fees(sstat.cells, sstat.bits, info.ihr_disabled);
@ -1855,7 +1885,7 @@ int Transaction::try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap,
return 0;
}
bool Transaction::check_state_size_limit(const ActionPhaseConfig& cfg) {
td::Status Transaction::check_state_limits(const ActionPhaseConfig& cfg) {
auto cell_equal = [](const td::Ref<vm::Cell>& a, const td::Ref<vm::Cell>& b) -> bool {
if (a.is_null()) {
return b.is_null();
@ -1867,21 +1897,36 @@ bool Transaction::check_state_size_limit(const ActionPhaseConfig& cfg) {
};
if (cell_equal(account.code, new_code) && cell_equal(account.data, new_data) &&
cell_equal(account.library, new_library)) {
return true;
return td::Status::OK();
}
// new_storage_stat is used here beause these stats will be reused in compute_state()
new_storage_stat.limit_cells = cfg.size_limits.max_acc_state_cells;
new_storage_stat.limit_bits = cfg.size_limits.max_acc_state_bits;
new_storage_stat.add_used_storage(new_code);
new_storage_stat.add_used_storage(new_data);
new_storage_stat.add_used_storage(new_library);
td::Timer timer;
auto add_used_storage = [&](const td::Ref<vm::Cell>& cell) -> td::Status {
if (cell.not_null()) {
TRY_RESULT(res, new_storage_stat.add_used_storage(cell));
if (res.max_merkle_depth > max_allowed_merkle_depth) {
return td::Status::Error("too big merkle depth");
}
}
return td::Status::OK();
};
TRY_STATUS(add_used_storage(new_code));
TRY_STATUS(add_used_storage(new_data));
TRY_STATUS(add_used_storage(new_library));
if (timer.elapsed() > 0.1) {
LOG(INFO) << "Compute used storage took " << timer.elapsed() << "s";
}
if (acc_status == Account::acc_active) {
new_storage_stat.clear_limit();
} else {
new_storage_stat.clear();
}
return new_storage_stat.cells <= cfg.size_limits.max_acc_state_cells &&
new_storage_stat.bits <= cfg.size_limits.max_acc_state_bits;
new_storage_stat.bits <= cfg.size_limits.max_acc_state_bits
? td::Status::OK()
: td::Status::Error("state too big");
}
bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
@ -1978,6 +2023,7 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
bp.ok = true;
return true;
}
} // namespace transaction
/*
*
@ -2033,6 +2079,7 @@ static td::optional<vm::CellStorageStat> try_update_storage_stat(const vm::CellS
return new_stat;
}
namespace transaction {
bool Transaction::compute_state() {
if (new_total_state.not_null()) {
return true;
@ -2108,7 +2155,7 @@ bool Transaction::compute_state() {
stats = new_stats.unwrap();
} else {
td::Timer timer;
CHECK(stats.add_used_storage(Ref<vm::Cell>(storage)));
stats.add_used_storage(Ref<vm::Cell>(storage)).ensure();
if (timer.elapsed() > 0.1) {
LOG(INFO) << "Compute used storage took " << timer.elapsed() << "s";
}
@ -2460,6 +2507,7 @@ void Transaction::extract_out_msgs(std::vector<LtCellRef>& list) {
list.emplace_back(start_lt + i + 1, std::move(out_msgs[i]));
}
}
} // namespace transaction
void Account::push_transaction(Ref<vm::Cell> trans_root, ton::LogicalTime trans_lt) {
transactions.emplace_back(trans_lt, std::move(trans_root));
@ -2503,4 +2551,82 @@ bool Account::libraries_changed() const {
}
}
td::Status FetchConfigParams::fetch_config_params(const block::Config& config,
Ref<vm::Cell>* old_mparams,
std::vector<block::StoragePrices>* storage_prices,
block::StoragePhaseConfig* storage_phase_cfg,
td::BitArray<256>* rand_seed,
block::ComputePhaseConfig* compute_phase_cfg,
block::ActionPhaseConfig* action_phase_cfg,
td::RefInt256* masterchain_create_fee,
td::RefInt256* basechain_create_fee,
ton::WorkchainId wc,
ton::UnixTime now) {
*old_mparams = config.get_config_param(9);
{
auto res = config.get_storage_prices();
if (res.is_error()) {
return res.move_as_error();
}
*storage_prices = res.move_as_ok();
}
if (rand_seed->is_zero()) {
// generate rand seed
prng::rand_gen().strong_rand_bytes(rand_seed->data(), 32);
LOG(DEBUG) << "block random seed set to " << rand_seed->to_hex();
}
TRY_RESULT(size_limits, config.get_size_limits_config());
{
// compute compute_phase_cfg / storage_phase_cfg
auto cell = config.get_config_param(wc == ton::masterchainId ? 20 : 21);
if (cell.is_null()) {
return td::Status::Error(-668, "cannot fetch current gas prices and limits from masterchain configuration");
}
if (!compute_phase_cfg->parse_GasLimitsPrices(std::move(cell), storage_phase_cfg->freeze_due_limit,
storage_phase_cfg->delete_due_limit)) {
return td::Status::Error(-668, "cannot unpack current gas prices and limits from masterchain configuration");
}
compute_phase_cfg->block_rand_seed = *rand_seed;
compute_phase_cfg->max_vm_data_depth = size_limits.max_vm_data_depth;
compute_phase_cfg->global_config = config.get_root_cell();
compute_phase_cfg->suspended_addresses = config.get_suspended_addresses(now);
}
{
// compute action_phase_cfg
block::gen::MsgForwardPrices::Record rec;
auto cell = config.get_config_param(24);
if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) {
return td::Status::Error(-668, "cannot fetch masterchain message transfer prices from masterchain configuration");
}
action_phase_cfg->fwd_mc =
block::MsgPrices{rec.lump_price, rec.bit_price, rec.cell_price, rec.ihr_price_factor,
(unsigned)rec.first_frac, (unsigned)rec.next_frac};
cell = config.get_config_param(25);
if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) {
return td::Status::Error(-668, "cannot fetch standard message transfer prices from masterchain configuration");
}
action_phase_cfg->fwd_std =
block::MsgPrices{rec.lump_price, rec.bit_price, rec.cell_price, rec.ihr_price_factor,
(unsigned)rec.first_frac, (unsigned)rec.next_frac};
action_phase_cfg->workchains = &config.get_workchain_list();
action_phase_cfg->bounce_msg_body = (config.has_capability(ton::capBounceMsgBody) ? 256 : 0);
action_phase_cfg->size_limits = size_limits;
}
{
// fetch block_grams_created
auto cell = config.get_config_param(14);
if (cell.is_null()) {
*basechain_create_fee = *masterchain_create_fee = td::zero_refint();
} else {
block::gen::BlockCreateFees::Record create_fees;
if (!(tlb::unpack_cell(cell, create_fees) &&
block::tlb::t_Grams.as_integer_to(create_fees.masterchain_block_fee, *masterchain_create_fee) &&
block::tlb::t_Grams.as_integer_to(create_fees.basechain_block_fee, *basechain_create_fee))) {
return td::Status::Error(-668, "cannot unpack BlockCreateFees from configuration parameter #14");
}
}
}
return td::Status::OK();
}
} // namespace block

View file

@ -35,7 +35,10 @@ using td::Ref;
using LtCellRef = std::pair<ton::LogicalTime, Ref<vm::Cell>>;
struct Account;
namespace transaction {
struct Transaction;
} // namespace transaction
struct CollatorError {
std::string msg;
@ -106,9 +109,11 @@ struct ComputePhaseConfig {
std::unique_ptr<vm::Dictionary> libraries;
Ref<vm::Cell> global_config;
td::BitArray<256> block_rand_seed;
bool ignore_chksig{false};
bool with_vm_log{false};
td::uint16 max_vm_data_depth = 512;
std::unique_ptr<vm::Dictionary> suspended_addresses;
int vm_log_verbosity = 0;
ComputePhaseConfig(td::uint64 _gas_price = 0, td::uint64 _gas_limit = 0, td::uint64 _gas_credit = 0)
: gas_price(_gas_price), gas_limit(_gas_limit), special_gas_limit(_gas_limit), gas_credit(_gas_credit) {
compute_threshold();
@ -186,7 +191,7 @@ struct ActionPhase {
bool code_changed{false};
bool action_list_invalid{false};
bool acc_delete_req{false};
bool state_size_too_big{false};
bool state_exceeds_limits{false};
enum { acst_unchanged = 0, acst_frozen = 2, acst_deleted = 3 };
int acc_status_change{acst_unchanged};
td::RefInt256 total_fwd_fees; // all fees debited from the account
@ -273,7 +278,7 @@ struct Account {
bool create_account_block(vm::CellBuilder& cb); // stores an AccountBlock with all transactions
protected:
friend struct Transaction;
friend struct transaction::Transaction;
bool set_split_depth(int split_depth);
bool check_split_depth(int split_depth) const;
bool forget_split_depth();
@ -288,7 +293,9 @@ struct Account {
bool compute_my_addr(bool force = false);
};
namespace transaction {
struct Transaction {
static constexpr unsigned max_allowed_merkle_depth = 2;
enum {
tr_none,
tr_ord,
@ -354,7 +361,7 @@ struct Transaction {
std::vector<Ref<vm::Cell>> compute_vm_libraries(const ComputePhaseConfig& cfg);
bool prepare_compute_phase(const ComputePhaseConfig& cfg);
bool prepare_action_phase(const ActionPhaseConfig& cfg);
bool check_state_size_limit(const ActionPhaseConfig& cfg);
td::Status check_state_limits(const ActionPhaseConfig& cfg);
bool prepare_bounce_phase(const ActionPhaseConfig& cfg);
bool compute_state();
bool serialize();
@ -390,5 +397,20 @@ struct Transaction {
bool serialize_bounce_phase(vm::CellBuilder& cb);
bool unpack_msg_state(bool lib_only = false);
};
} // namespace transaction
struct FetchConfigParams {
static td::Status fetch_config_params(const block::Config& config,
Ref<vm::Cell>* old_mparams,
std::vector<block::StoragePrices>* storage_prices,
StoragePhaseConfig* storage_phase_cfg,
td::BitArray<256>* rand_seed,
ComputePhaseConfig* compute_phase_cfg,
ActionPhaseConfig* action_phase_cfg,
td::RefInt256* masterchain_create_fee,
td::RefInt256* basechain_create_fee,
ton::WorkchainId wc,
ton::UnixTime now);
};
} // namespace block

View file

@ -27,7 +27,7 @@ namespace fift {
//
bool FiftCont::print_dict_name(std::ostream& os, const IntCtx& ctx) const {
std::string word_name;
if (ctx.dictionary && ctx.dictionary->lookup_def(this, &word_name)) {
if (ctx.dictionary.lookup_def(this, &word_name)) {
if (word_name.size() && word_name.back() == ' ') {
word_name.pop_back();
}
@ -39,7 +39,7 @@ bool FiftCont::print_dict_name(std::ostream& os, const IntCtx& ctx) const {
std::string FiftCont::get_dict_name(const IntCtx& ctx) const {
std::string word_name;
if (ctx.dictionary && ctx.dictionary->lookup_def(this, &word_name)) {
if (ctx.dictionary.lookup_def(this, &word_name)) {
if (word_name.size() && word_name.back() == ' ') {
word_name.pop_back();
}
@ -63,6 +63,140 @@ bool FiftCont::dump(std::ostream& os, const IntCtx& ctx) const {
return ok;
}
//
// StackWord
//
Ref<FiftCont> StackWord::run_tail(IntCtx& ctx) const {
f(ctx.stack);
return {};
}
//
// CtxWord
//
Ref<FiftCont> CtxWord::run_tail(IntCtx& ctx) const {
f(ctx);
return {};
}
//
// CtxTailWord
//
Ref<FiftCont> CtxTailWord::run_tail(IntCtx& ctx) const {
return f(ctx);
}
//
// WordList
//
WordList::WordList(std::vector<Ref<FiftCont>>&& _list) : list(std::move(_list)) {
}
WordList::WordList(const std::vector<Ref<FiftCont>>& _list) : list(_list) {
}
WordList& WordList::push_back(Ref<FiftCont> word_def) {
list.push_back(std::move(word_def));
return *this;
}
WordList& WordList::push_back(FiftCont& wd) {
list.emplace_back(&wd);
return *this;
}
Ref<FiftCont> WordList::run_tail(IntCtx& ctx) const {
if (list.empty()) {
return {};
}
if (list.size() > 1) {
ctx.next = td::make_ref<ListCont>(std::move(ctx.next), Ref<WordList>(this), 1);
}
return list[0];
}
void WordList::close() {
list.shrink_to_fit();
}
WordList& WordList::append(const std::vector<Ref<FiftCont>>& other) {
list.insert(list.end(), other.begin(), other.end());
return *this;
}
WordList& WordList::append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end) {
list.insert(list.end(), begin, end);
return *this;
}
bool WordList::dump(std::ostream& os, const IntCtx& ctx) const {
os << "{";
for (auto entry : list) {
os << ' ';
entry->print_name(os, ctx);
}
os << " }" << std::endl;
return true;
}
//
// ListCont
//
Ref<FiftCont> ListCont::run_tail(IntCtx& ctx) const {
auto sz = list->size();
if (pos >= sz) {
return std::move(ctx.next);
} else if (ctx.next.not_null()) {
ctx.next = td::make_ref<ListCont>(SeqCont::seq(next, std::move(ctx.next)), list, pos + 1);
} else if (pos + 1 == sz) {
ctx.next = next;
} else {
ctx.next = td::make_ref<ListCont>(next, list, pos + 1);
}
return list->at(pos);
}
Ref<FiftCont> ListCont::run_modify(IntCtx& ctx) {
auto sz = list->size();
if (pos >= sz) {
return std::move(ctx.next);
}
auto cur = list->at(pos++);
if (ctx.next.not_null()) {
next = SeqCont::seq(next, std::move(ctx.next));
}
if (pos == sz) {
ctx.next = std::move(next);
} else {
ctx.next = self();
}
return cur;
}
bool ListCont::dump(std::ostream& os, const IntCtx& ctx) const {
std::string dict_name = list->get_dict_name(ctx);
if (!dict_name.empty()) {
os << "[in " << dict_name << ":] ";
}
std::size_t sz = list->size(), i, a = (pos >= 16 ? pos - 16 : 0), b = std::min(pos + 16, sz);
if (a > 0) {
os << "... ";
}
for (i = a; i < b; i++) {
if (i == pos) {
os << "**HERE** ";
}
list->at(i)->print_name(os, ctx);
os << ' ';
}
if (b < sz) {
os << "...";
}
os << std::endl;
return true;
}
//
// QuitCont
//
@ -295,12 +429,15 @@ bool GenericLitCont::print_name(std::ostream& os, const IntCtx& ctx) const {
bool sp = false;
for (auto entry : list) {
if (sp) {
os << sp;
os << ' ';
}
sp = true;
int tp = entry.type();
if (entry.is_int() || entry.is(vm::StackEntry::t_string) || entry.is(vm::StackEntry::t_bytes)) {
entry.dump(os);
} else if (entry.is_atom()) {
os << '`';
entry.dump(os);
} else {
auto cont_lit = entry.as_object<FiftCont>();
if (cont_lit.not_null()) {

View file

@ -17,6 +17,7 @@
Copyright 2020 Telegram Systems LLP
*/
#pragma once
#include <functional>
#include "common/refcnt.hpp"
#include "common/refint.h"
#include "vm/stack.hpp"
@ -76,6 +77,101 @@ class FiftCont : public td::CntObject {
}
};
typedef std::function<void(vm::Stack&)> StackWordFunc;
typedef std::function<void(IntCtx&)> CtxWordFunc;
typedef std::function<Ref<FiftCont>(IntCtx&)> CtxTailWordFunc;
class NopWord : public FiftCont {
public:
NopWord() = default;
~NopWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override {
return {};
}
};
class StackWord : public FiftCont {
StackWordFunc f;
public:
StackWord(StackWordFunc _f) : f(std::move(_f)) {
}
~StackWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class CtxWord : public FiftCont {
CtxWordFunc f;
public:
CtxWord(CtxWordFunc _f) : f(std::move(_f)) {
}
~CtxWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class CtxTailWord : public FiftCont {
CtxTailWordFunc f;
public:
CtxTailWord(CtxTailWordFunc _f) : f(std::move(_f)) {
}
~CtxTailWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class WordList : public FiftCont {
std::vector<Ref<FiftCont>> list;
public:
~WordList() override = default;
WordList() = default;
WordList(std::vector<Ref<FiftCont>>&& _list);
WordList(const std::vector<Ref<FiftCont>>& _list);
WordList& push_back(Ref<FiftCont> word_def);
WordList& push_back(FiftCont& wd);
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
void close();
bool is_list() const override {
return true;
}
long long list_size() const override {
return (long long)list.size();
}
std::size_t size() const {
return list.size();
}
const Ref<FiftCont>& at(std::size_t idx) const {
return list.at(idx);
}
const Ref<FiftCont>* get_list() const override {
return list.data();
}
WordList& append(const std::vector<Ref<FiftCont>>& other);
WordList& append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end);
WordList* make_copy() const override {
return new WordList(list);
}
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class ListCont : public FiftCont {
Ref<FiftCont> next;
Ref<WordList> list;
std::size_t pos;
public:
ListCont(Ref<FiftCont> nxt, Ref<WordList> wl, std::size_t p = 0) : next(std::move(nxt)), list(std::move(wl)), pos(p) {
}
~ListCont() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
Ref<FiftCont> up() const override {
return next;
}
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class QuitCont : public FiftCont {
int exit_code;

View file

@ -17,143 +17,10 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#include "Dictionary.h"
#include "IntCtx.h"
namespace fift {
//
// StackWord
//
Ref<FiftCont> StackWord::run_tail(IntCtx& ctx) const {
f(ctx.stack);
return {};
}
//
// CtxWord
//
Ref<FiftCont> CtxWord::run_tail(IntCtx& ctx) const {
f(ctx);
return {};
}
//
// CtxTailWord
//
Ref<FiftCont> CtxTailWord::run_tail(IntCtx& ctx) const {
return f(ctx);
}
//
// WordList
//
WordList::WordList(std::vector<Ref<FiftCont>>&& _list) : list(std::move(_list)) {
}
WordList::WordList(const std::vector<Ref<FiftCont>>& _list) : list(_list) {
}
WordList& WordList::push_back(Ref<FiftCont> word_def) {
list.push_back(std::move(word_def));
return *this;
}
WordList& WordList::push_back(FiftCont& wd) {
list.emplace_back(&wd);
return *this;
}
Ref<FiftCont> WordList::run_tail(IntCtx& ctx) const {
if (list.empty()) {
return {};
}
if (list.size() > 1) {
ctx.next = td::make_ref<ListCont>(std::move(ctx.next), Ref<WordList>(this), 1);
}
return list[0];
}
void WordList::close() {
list.shrink_to_fit();
}
WordList& WordList::append(const std::vector<Ref<FiftCont>>& other) {
list.insert(list.end(), other.begin(), other.end());
return *this;
}
WordList& WordList::append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end) {
list.insert(list.end(), begin, end);
return *this;
}
bool WordList::dump(std::ostream& os, const IntCtx& ctx) const {
os << "{";
for (auto entry : list) {
os << ' ';
entry->print_name(os, ctx);
}
os << " }" << std::endl;
return true;
}
//
// ListCont
//
Ref<FiftCont> ListCont::run_tail(IntCtx& ctx) const {
auto sz = list->size();
if (pos >= sz) {
return std::move(ctx.next);
} else if (ctx.next.not_null()) {
ctx.next = td::make_ref<ListCont>(SeqCont::seq(next, std::move(ctx.next)), list, pos + 1);
} else if (pos + 1 == sz) {
ctx.next = next;
} else {
ctx.next = td::make_ref<ListCont>(next, list, pos + 1);
}
return list->at(pos);
}
Ref<FiftCont> ListCont::run_modify(IntCtx& ctx) {
auto sz = list->size();
if (pos >= sz) {
return std::move(ctx.next);
}
auto cur = list->at(pos++);
if (ctx.next.not_null()) {
next = SeqCont::seq(next, std::move(ctx.next));
}
if (pos == sz) {
ctx.next = std::move(next);
} else {
ctx.next = self();
}
return cur;
}
bool ListCont::dump(std::ostream& os, const IntCtx& ctx) const {
std::string dict_name = list->get_dict_name(ctx);
if (!dict_name.empty()) {
os << "[in " << dict_name << ":] ";
}
std::size_t sz = list->size(), i, a = (pos >= 16 ? pos - 16 : 0), b = std::min(pos + 16, sz);
if (a > 0) {
os << "... ";
}
for (i = a; i < b; i++) {
if (i == pos) {
os << "**HERE** ";
}
list->at(i)->print_name(os, ctx);
os << ' ';
}
if (b < sz) {
os << "...";
}
os << std::endl;
return true;
}
//
// DictEntry
//
@ -167,15 +34,49 @@ DictEntry::DictEntry(CtxWordFunc func, bool _act) : def(Ref<CtxWord>{true, std::
DictEntry::DictEntry(CtxTailWordFunc func, bool _act) : def(Ref<CtxTailWord>{true, std::move(func)}), active(_act) {
}
DictEntry DictEntry::create_from(vm::StackEntry se) {
if (se.is_tuple()) {
auto& tuple = *se.as_tuple();
if (tuple.size() == 1) {
auto def = tuple[0].as_object<FiftCont>();
if (def.not_null()) {
return DictEntry{std::move(def), true};
}
}
} else {
auto def = std::move(se).as_object<FiftCont>();
if (def.not_null()) {
return DictEntry{std::move(def)};
}
}
return {};
}
DictEntry::operator vm::StackEntry() const& {
if (def.is_null()) {
return {};
} else if (active) {
return vm::make_tuple_ref(vm::StackEntry{vm::from_object, def});
} else {
return {vm::from_object, def};
}
}
DictEntry::operator vm::StackEntry() && {
if (def.is_null()) {
return {};
} else if (active) {
return vm::make_tuple_ref(vm::StackEntry{vm::from_object, std::move(def)});
} else {
return {vm::from_object, std::move(def)};
}
}
//
// Dictionary
//
DictEntry* Dictionary::lookup(td::Slice name) {
auto it = words_.find(name);
if (it == words_.end()) {
return nullptr;
}
return &it->second;
DictEntry Dictionary::lookup(std::string name) const {
return DictEntry::create_from(words().get(name));
}
void Dictionary::def_ctx_word(std::string name, CtxWordFunc func) {
@ -196,26 +97,27 @@ void Dictionary::def_ctx_tail_word(std::string name, CtxTailWordFunc func) {
}
void Dictionary::def_word(std::string name, DictEntry word) {
auto res = words_.emplace(name, std::move(word));
LOG_IF(FATAL, !res.second) << "Cannot redefine word: " << name;
auto dict = words();
dict.set(std::move(name), vm::StackEntry(std::move(word)));
set_words(dict);
}
void Dictionary::undef_word(td::Slice name) {
auto it = words_.find(name);
if (it == words_.end()) {
return;
void Dictionary::undef_word(std::string name) {
auto dict = words();
if (dict.remove(name)) {
set_words(dict);
}
words_.erase(it);
}
bool Dictionary::lookup_def(const FiftCont* cont, std::string* word_ptr) const {
if (!cont) {
return false;
}
for (const auto& entry : words_) {
if (entry.second.get_def().get() == cont) {
for (auto entry : words()) {
auto val = DictEntry::create_from(entry.value());
if (val.get_def().get() == cont && entry.key().is_string()) {
if (word_ptr) {
*word_ptr = entry.first;
*word_ptr = vm::StackEntry(entry.key()).as_string();
}
return true;
}
@ -223,35 +125,4 @@ bool Dictionary::lookup_def(const FiftCont* cont, std::string* word_ptr) const {
return false;
}
void interpret_nop(vm::Stack& stack) {
}
Ref<FiftCont> Dictionary::nop_word_def = Ref<StackWord>{true, interpret_nop};
//
// functions for wordef
//
Ref<FiftCont> pop_exec_token(vm::Stack& stack) {
stack.check_underflow(1);
auto wd_ref = stack.pop().as_object<FiftCont>();
if (wd_ref.is_null()) {
throw IntError{"execution token expected"};
}
return wd_ref;
}
Ref<WordList> pop_word_list(vm::Stack& stack) {
stack.check_underflow(1);
auto wl_ref = stack.pop().as_object<WordList>();
if (wl_ref.is_null()) {
throw IntError{"word list expected"};
}
return wl_ref;
}
void push_argcount(vm::Stack& stack, int args) {
stack.push_smallint(args);
stack.push({vm::from_object, Dictionary::nop_word_def});
}
} // namespace fift

View file

@ -17,115 +17,27 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include <functional>
#include <map>
#include "IntCtx.h"
#include "Continuation.h"
#include "HashMap.h"
#include "vm/box.hpp"
namespace fift {
using td::Ref;
struct IntCtx;
/*
*
* WORD CLASSES
*
*/
typedef std::function<void(vm::Stack&)> StackWordFunc;
typedef std::function<void(IntCtx&)> CtxWordFunc;
class StackWord : public FiftCont {
StackWordFunc f;
public:
StackWord(StackWordFunc _f) : f(std::move(_f)) {
}
~StackWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class CtxWord : public FiftCont {
CtxWordFunc f;
public:
CtxWord(CtxWordFunc _f) : f(std::move(_f)) {
}
~CtxWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
typedef std::function<Ref<FiftCont>(IntCtx&)> CtxTailWordFunc;
class CtxTailWord : public FiftCont {
CtxTailWordFunc f;
public:
CtxTailWord(CtxTailWordFunc _f) : f(std::move(_f)) {
}
~CtxTailWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class WordList : public FiftCont {
std::vector<Ref<FiftCont>> list;
public:
~WordList() override = default;
WordList() = default;
WordList(std::vector<Ref<FiftCont>>&& _list);
WordList(const std::vector<Ref<FiftCont>>& _list);
WordList& push_back(Ref<FiftCont> word_def);
WordList& push_back(FiftCont& wd);
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
void close();
bool is_list() const override {
return true;
}
long long list_size() const override {
return (long long)list.size();
}
std::size_t size() const {
return list.size();
}
const Ref<FiftCont>& at(std::size_t idx) const {
return list.at(idx);
}
const Ref<FiftCont>* get_list() const override {
return list.data();
}
WordList& append(const std::vector<Ref<FiftCont>>& other);
WordList& append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end);
WordList* make_copy() const override {
return new WordList(list);
}
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class ListCont : public FiftCont {
Ref<FiftCont> next;
Ref<WordList> list;
std::size_t pos;
public:
ListCont(Ref<FiftCont> nxt, Ref<WordList> wl, std::size_t p = 0) : next(std::move(nxt)), list(std::move(wl)), pos(p) {
}
~ListCont() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
Ref<FiftCont> up() const override {
return next;
}
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class DictEntry {
Ref<FiftCont> def;
bool active;
bool active{false};
public:
DictEntry() = delete;
DictEntry() = default;
DictEntry(const DictEntry& ref) = default;
DictEntry(DictEntry&& ref) = default;
DictEntry(Ref<FiftCont> _def, bool _act = false) : def(std::move(_def)), active(_act) {
@ -137,6 +49,9 @@ class DictEntry {
//DictEntry(std::vector<Ref<FiftCont>>&& word_list);
DictEntry& operator=(const DictEntry&) = default;
DictEntry& operator=(DictEntry&&) = default;
static DictEntry create_from(vm::StackEntry se);
explicit operator vm::StackEntry() const&;
explicit operator vm::StackEntry() &&;
Ref<FiftCont> get_def() const& {
return def;
}
@ -146,16 +61,17 @@ class DictEntry {
bool is_active() const {
return active;
}
bool empty() const {
return def.is_null();
}
explicit operator bool() const {
return def.not_null();
}
bool operator!() const {
return def.is_null();
}
};
/*
DictEntry::DictEntry(const std::vector<Ref<FiftCont>>& word_list) : def(Ref<WordList>{true, word_list}) {
}
DictEntry::DictEntry(std::vector<Ref<FiftCont>>&& word_list) : def(Ref<WordList>{true, std::move(word_list)}) {
}
*/
/*
*
* DICTIONARIES
@ -164,37 +80,52 @@ DictEntry::DictEntry(std::vector<Ref<FiftCont>>&& word_list) : def(Ref<WordList>
class Dictionary {
public:
DictEntry* lookup(td::Slice name);
Dictionary() : box_(true) {
}
Dictionary(Ref<vm::Box> box) : box_(std::move(box)) {
}
Dictionary(Ref<Hashmap> hmap) : box_(true, vm::from_object, std::move(hmap)) {
}
DictEntry lookup(std::string name) const;
void def_ctx_word(std::string name, CtxWordFunc func);
void def_ctx_tail_word(std::string name, CtxTailWordFunc func);
void def_active_word(std::string name, CtxWordFunc func);
void def_stack_word(std::string name, StackWordFunc func);
void def_word(std::string name, DictEntry word);
void undef_word(td::Slice name);
void undef_word(std::string name);
bool lookup_def(const FiftCont* cont, std::string* word_ptr = nullptr) const;
bool lookup_def(Ref<FiftCont> cont, std::string* word_ptr = nullptr) const {
return lookup_def(cont.get(), word_ptr);
}
auto begin() const {
return words_.begin();
return words().begin();
}
auto end() const {
return words_.end();
return words().end();
}
HashmapKeeper words() const {
if (box_->empty()) {
return {};
} else {
return box_->get().as_object<Hashmap>();
}
}
Ref<vm::Box> get_box() const {
return box_;
}
void set_words(Ref<Hashmap> new_words) {
box_->set(vm::StackEntry{vm::from_object, std::move(new_words)});
}
bool operator==(const Dictionary& other) const {
return box_ == other.box_;
}
bool operator!=(const Dictionary& other) const {
return box_ != other.box_;
}
static Ref<FiftCont> nop_word_def;
private:
std::map<std::string, DictEntry, std::less<>> words_;
Ref<vm::Box> box_;
};
/*
*
* AUX FUNCTIONS FOR WORD DEFS
*
*/
Ref<FiftCont> pop_exec_token(vm::Stack& stack);
Ref<WordList> pop_word_list(vm::Stack& stack);
void push_argcount(vm::Stack& stack, int args);
} // namespace fift

View file

@ -17,7 +17,7 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#include "Fift.h"
#include "IntCtx.h"
#include "words.h"
#include "td/utils/PathView.h"
@ -51,7 +51,7 @@ td::Result<int> Fift::interpret_istream(std::istream& stream, std::string curren
td::Result<int> Fift::do_interpret(IntCtx& ctx, bool is_interactive) {
ctx.ton_db = &config_.ton_db;
ctx.source_lookup = &config_.source_lookup;
ctx.dictionary = &config_.dictionary;
ctx.dictionary = ctx.main_dictionary = ctx.context = config_.dictionary;
ctx.output_stream = config_.output_stream;
ctx.error_stream = config_.error_stream;
if (!ctx.output_stream) {
@ -71,7 +71,7 @@ td::Result<int> Fift::do_interpret(IntCtx& ctx, bool is_interactive) {
ctx.top_ctx();
ctx.clear_error();
ctx.stack.clear();
ctx.load_next_line();
ctx.parser->load_next_line();
continue;
}
}

371
crypto/fift/HashMap.cpp Normal file
View file

@ -0,0 +1,371 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "HashMap.h"
#include "td/utils/Random.h"
#include "IntCtx.h"
namespace fift {
using td::Ref;
DictKey::DictKey(vm::StackEntry se) {
auto tp = tp_ = se.type();
switch (tp) {
case Type::t_int:
ref_ = se.as_int();
break;
case Type::t_atom:
ref_ = se.as_atom();
break;
case Type::t_string:
ref_ = se.as_string_ref();
break;
case Type::t_bytes:
ref_ = se.as_bytes_ref();
break;
case Type::t_null:
break;
default:
throw IntError{"unsupported key type"};
}
compute_hash();
}
DictKey::operator vm::StackEntry() const& {
switch (tp_) {
case Type::t_int:
return value<td::CntInt256>();
case Type::t_atom:
return value<vm::Atom>();
case Type::t_string:
case Type::t_bytes:
return {value<td::Cnt<std::string>>(), tp_ == Type::t_bytes};
default:
return {};
}
}
DictKey::operator vm::StackEntry() && {
switch (tp_) {
case Type::t_int:
return move_value<td::CntInt256>();
case Type::t_atom:
return move_value<vm::Atom>();
case Type::t_string:
case Type::t_bytes:
return {move_value<td::Cnt<std::string>>(), tp_ == Type::t_bytes};
default:
return {};
}
}
std::ostream& operator<<(std::ostream& os, const DictKey& dkey) {
return os << vm::StackEntry(dkey).to_string();
}
int DictKey::cmp_internal(const DictKey& other) const {
if (tp_ != other.tp_) {
return tp_ < other.tp_ ? -1 : 1;
}
switch (tp_) {
case Type::t_int:
return td::cmp(value<td::CntInt256>(), other.value<td::CntInt256>());
case Type::t_atom: {
int u = value<vm::Atom>()->index(), v = other.value<vm::Atom>()->index();
return u == v ? 0 : (u < v ? -1 : 1);
}
case Type::t_string:
case Type::t_bytes:
return value<td::Cnt<std::string>>()->compare(*other.value<td::Cnt<std::string>>());
default:
return 0;
}
}
int DictKey::cmp(const DictKey& other) const {
if (hash_ < other.hash_) {
return -1;
} else if (hash_ > other.hash_) {
return 1;
} else {
return cmp_internal(other);
}
}
DictKey::keyhash_t DictKey::compute_str_hash(DictKey::keyhash_t h, const char* str, std::size_t len) {
const char* end = str + len;
while (str < end) {
h = h * StrHash + (unsigned char)*str++;
}
return h;
}
DictKey::keyhash_t DictKey::compute_int_hash(td::AnyIntView<> x) {
keyhash_t h = IntHash0;
for (int i = 0; i < x.size(); i++) {
h = h * MixConst3 + x.digits[i];
}
return h * MixConst4;
}
DictKey::keyhash_t DictKey::compute_hash() {
switch (tp_) {
case Type::t_int:
return hash_ = compute_int_hash(value<td::CntInt256>()->as_any_int());
case Type::t_atom:
return hash_ = value<vm::Atom>()->index() * MixConst1 + MixConst2;
case Type::t_string:
case Type::t_bytes: {
auto ref = value<td::Cnt<std::string>>();
return hash_ = compute_str_hash(tp_, ref->data(), ref->size());
}
default:
return hash_ = 0;
}
}
const Hashmap* Hashmap::lookup_key_aux(const Hashmap* root, const DictKey& key) {
if (key.is_null()) {
return nullptr;
}
while (root) {
int r = key.cmp(root->key_);
if (!r) {
break;
}
root = (r < 0 ? root->left_.get() : root->right_.get());
}
return root;
}
Ref<Hashmap> Hashmap::lookup_key(Ref<Hashmap> root, const DictKey& key) {
return Ref<Hashmap>(lookup_key_aux(root.get(), key));
}
vm::StackEntry Hashmap::get_key(Ref<Hashmap> root, const DictKey& key) {
auto node = lookup_key_aux(root.get(), key);
if (node) {
return node->value_;
} else {
return {};
}
}
std::pair<Ref<Hashmap>, vm::StackEntry> Hashmap::get_remove_key(Ref<Hashmap> root, const DictKey& key) {
if (root.is_null() || key.is_null()) {
return {std::move(root), {}};
}
vm::StackEntry val;
auto res = root->get_remove_internal(key, val);
if (val.is_null()) {
return {std::move(root), {}};
} else {
return {std::move(res), std::move(val)};
}
}
Ref<Hashmap> Hashmap::remove_key(Ref<Hashmap> root, const DictKey& key) {
if (root.is_null() || key.is_null()) {
return root;
}
vm::StackEntry val;
auto res = root->get_remove_internal(key, val);
if (val.is_null()) {
return root;
} else {
return res;
}
}
Ref<Hashmap> Hashmap::get_remove_internal(const DictKey& key, vm::StackEntry& val) const {
int r = key.cmp(key_);
if (!r) {
val = value_;
return merge(left_, right_);
} else if (r < 0) {
if (left_.is_null()) {
return {};
} else {
auto res = left_->get_remove_internal(key, val);
if (val.is_null()) {
return res;
} else {
return td::make_ref<Hashmap>(key_, value_, std::move(res), right_, y_);
}
}
} else if (right_.is_null()) {
return {};
} else {
auto res = right_->get_remove_internal(key, val);
if (val.is_null()) {
return res;
} else {
return td::make_ref<Hashmap>(key_, value_, left_, std::move(res), y_);
}
}
}
Ref<Hashmap> Hashmap::merge(Ref<Hashmap> a, Ref<Hashmap> b) {
if (a.is_null()) {
return b;
} else if (b.is_null()) {
return a;
} else if (a->y_ > b->y_) {
auto& aa = a.write();
aa.right_ = merge(std::move(aa.right_), std::move(b));
return a;
} else {
auto& bb = b.write();
bb.left_ = merge(std::move(a), std::move(bb.left_));
return b;
}
}
Ref<Hashmap> Hashmap::set(Ref<Hashmap> root, const DictKey& key, vm::StackEntry value) {
if (!key.is_null() && !replace(root, key, value) && !value.is_null()) {
insert(root, key, value, new_y());
}
return root;
}
bool Hashmap::replace(Ref<Hashmap>& root, const DictKey& key, vm::StackEntry value) {
if (root.is_null() || key.is_null()) {
return false;
}
if (value.is_null()) {
auto res = root->get_remove_internal(key, value);
if (value.is_null()) {
return false;
} else {
root = std::move(res);
return true;
}
}
bool found = false;
auto res = root->replace_internal(key, std::move(value), found);
if (found) {
root = std::move(res);
}
return found;
}
Ref<Hashmap> Hashmap::replace_internal(const DictKey& key, const vm::StackEntry& value, bool& found) const {
int r = key.cmp(key_);
if (!r) {
found = true;
return td::make_ref<Hashmap>(key_, value, left_, right_, y_);
} else if (r < 0) {
if (left_.is_null()) {
found = false;
return {};
}
auto res = left_->replace_internal(key, value, found);
if (!found) {
return {};
}
return td::make_ref<Hashmap>(key_, value_, std::move(res), right_, y_);
} else {
if (right_.is_null()) {
found = false;
return {};
}
auto res = right_->replace_internal(key, value, found);
if (!found) {
return {};
}
return td::make_ref<Hashmap>(key_, value_, left_, std::move(res), y_);
}
}
void Hashmap::insert(Ref<Hashmap>& root, const DictKey& key, vm::StackEntry value, long long y) {
if (root.is_null()) {
root = td::make_ref<Hashmap>(key, std::move(value), empty(), empty(), y);
return;
}
if (root->y_ <= y) {
auto res = split(std::move(root), key);
root = td::make_ref<Hashmap>(key, std::move(value), std::move(res.first), std::move(res.second), y);
return;
}
int r = key.cmp(root->key_);
CHECK(r);
insert(r < 0 ? root.write().left_ : root.write().right_, key, std::move(value), y);
}
std::pair<Ref<Hashmap>, Ref<Hashmap>> Hashmap::split(Ref<Hashmap> root, const DictKey& key, bool cmpv) {
if (root.is_null()) {
return {{}, {}};
}
int r = key.cmp(root->key_);
if (r < (int)cmpv) {
if (root->left_.is_null()) {
return {{}, std::move(root)};
}
auto res = split(root->left_, key, cmpv);
return {std::move(res.first),
td::make_ref<Hashmap>(root->key_, root->value_, std::move(res.second), root->right_, root->y_)};
} else {
if (root->right_.is_null()) {
return {std::move(root), {}};
}
auto res = split(root->right_, key, cmpv);
return {td::make_ref<Hashmap>(root->key_, root->value_, root->left_, std::move(res.first), root->y_),
std::move(res.second)};
}
}
long long Hashmap::new_y() {
return td::Random::fast_uint64();
}
bool HashmapIterator::unwind(Ref<Hashmap> root) {
if (root.is_null()) {
return false;
}
while (true) {
auto left = root->lr(down_);
if (left.is_null()) {
cur_ = std::move(root);
return true;
}
stack_.push_back(std::move(root));
root = std::move(left);
}
}
bool HashmapIterator::next() {
if (cur_.not_null()) {
cur_ = cur_->rl(down_);
if (cur_.not_null()) {
while (true) {
auto left = cur_->lr(down_);
if (left.is_null()) {
return true;
}
stack_.push_back(std::move(cur_));
cur_ = std::move(left);
}
}
}
if (stack_.empty()) {
return false;
}
cur_ = std::move(stack_.back());
stack_.pop_back();
return true;
}
} // namespace fift

306
crypto/fift/HashMap.h Normal file
View file

@ -0,0 +1,306 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/refcnt.hpp"
#include "vm/stack.hpp"
#include "vm/atom.h"
namespace fift {
using td::Ref;
using td::RefAny;
class DictKey {
public:
typedef vm::StackEntry::Type Type;
typedef unsigned long long keyhash_t;
private:
RefAny ref_;
Type tp_ = Type::t_null;
keyhash_t hash_ = 0;
static constexpr keyhash_t IntHash0 = 0xce6ab89d724409ed, MixConst1 = 0xcd5c126501510979,
MixConst2 = 0xb8f44d7fd6274ad1, MixConst3 = 0xd08726ea2422e405,
MixConst4 = 0x6407d2aeb5039dfb, StrHash = 0x93ff128344add06d;
keyhash_t compute_hash();
static keyhash_t compute_str_hash(DictKey::keyhash_t h, const char* str, std::size_t len);
static keyhash_t compute_int_hash(td::AnyIntView<> x);
int cmp_internal(const DictKey& other) const;
template <typename T>
Ref<T> value() const {
return Ref<T>{td::static_cast_ref(), ref_};
}
template <typename T>
Ref<T> move_value() {
return Ref<T>{td::static_cast_ref(), std::move(ref_)};
}
public:
DictKey() : ref_(), tp_(Type::t_null) {
}
DictKey(const DictKey& other) = default;
DictKey(DictKey&& other) = default;
DictKey& operator=(const DictKey& other) = default;
DictKey& operator=(DictKey&& other) = default;
DictKey(Ref<vm::Atom> atom_ref) : ref_(std::move(atom_ref)), tp_(Type::t_atom) {
compute_hash();
}
DictKey(td::RefInt256 int_ref) : ref_(std::move(int_ref)), tp_(Type::t_int) {
compute_hash();
}
explicit DictKey(vm::StackEntry se);
DictKey(std::string str, bool bytes = false) : ref_(), tp_(bytes ? Type::t_bytes : Type::t_string) {
ref_ = Ref<td::Cnt<std::string>>{true, std::move(str)};
compute_hash();
}
Type type() const {
return tp_;
}
void swap(DictKey& other) {
ref_.swap(other.ref_);
std::swap(tp_, other.tp_);
}
operator vm::StackEntry() const&;
operator vm::StackEntry() &&;
int cmp(const DictKey& other) const;
bool operator==(const DictKey& other) const {
return hash_ == other.hash_ && !cmp_internal(other);
}
bool operator!=(const DictKey& other) const {
return hash_ != other.hash_ || cmp_internal(other);
}
bool operator<(const DictKey& other) const {
return hash_ < other.hash_ || (hash_ == other.hash_ && cmp_internal(other) < 0);
}
bool is_null() const {
return tp_ == Type::t_null;
}
bool is_string() const {
return tp_ == Type::t_string;
}
};
std::ostream& operator<<(std::ostream& os, const DictKey& dkey);
class Hashmap : public td::CntObject {
DictKey key_;
vm::StackEntry value_;
Ref<Hashmap> left_;
Ref<Hashmap> right_;
long long y_;
public:
Hashmap(DictKey key, vm::StackEntry value, Ref<Hashmap> left, Ref<Hashmap> right, long long y)
: key_(std::move(key)), value_(std::move(value)), left_(std::move(left)), right_(std::move(right)), y_(y) {
}
Hashmap(const Hashmap& other) = default;
Hashmap(Hashmap&& other) = default;
virtual ~Hashmap() {
}
Hashmap* make_copy() const override {
return new Hashmap(*this);
}
const DictKey& key() const& {
return key_;
}
DictKey key() && {
return std::move(key_);
}
const vm::StackEntry& value() const& {
return value_;
}
vm::StackEntry value() && {
return std::move(value_);
}
Ref<Hashmap> left() const {
return left_;
}
Ref<Hashmap> right() const {
return right_;
}
Ref<Hashmap> lr(bool branch) const {
return branch ? right_ : left_;
}
Ref<Hashmap> rl(bool branch) const {
return branch ? left_ : right_;
}
static Ref<Hashmap> lookup_key(Ref<Hashmap> root, const DictKey& key);
template <typename... Args>
static Ref<Hashmap> lookup(Ref<Hashmap> root, Args&&... args) {
return lookup_key(std::move(root), DictKey{std::forward<Args>(args)...});
}
static vm::StackEntry get_key(Ref<Hashmap> root, const DictKey& key);
template <typename... Args>
static vm::StackEntry get(Ref<Hashmap> root, Args&&... args) {
return get_key(std::move(root), DictKey{std::forward<Args>(args)...});
}
static Ref<Hashmap> remove_key(Ref<Hashmap> root, const DictKey& key);
template <typename... Args>
static Ref<Hashmap> remove(Ref<Hashmap> root, Args&&... args) {
return remove_key(std::move(root), DictKey{std::forward<Args>(args)...});
}
static std::pair<Ref<Hashmap>, vm::StackEntry> get_remove_key(Ref<Hashmap> root, const DictKey& key);
template <typename... Args>
static std::pair<Ref<Hashmap>, vm::StackEntry> get_remove(Ref<Hashmap> root, Args&&... args) {
return get_remove_key(std::move(root), DictKey{std::forward<Args>(args)...});
}
static Ref<Hashmap> set(Ref<Hashmap> root, const DictKey& key, vm::StackEntry value);
static bool replace(Ref<Hashmap>& root, const DictKey& key, vm::StackEntry value);
static std::pair<Ref<Hashmap>, Ref<Hashmap>> split(Ref<Hashmap> root, const DictKey& key, bool eq_left = false);
static Ref<Hashmap> empty() {
return {};
}
private:
static Ref<Hashmap> merge(Ref<Hashmap> a, Ref<Hashmap> b);
static const Hashmap* lookup_key_aux(const Hashmap* root, const DictKey& key);
Ref<Hashmap> get_remove_internal(const DictKey& key, vm::StackEntry& val) const;
Ref<Hashmap> replace_internal(const DictKey& key, const vm::StackEntry& value, bool& found) const;
static void insert(Ref<Hashmap>& root, const DictKey& key, vm::StackEntry value, long long y);
static long long new_y();
};
struct HashmapIdx {
Ref<Hashmap>& root_;
DictKey idx_;
template <typename... Args>
HashmapIdx(Ref<Hashmap>& root, Args&&... args) : root_(root), idx_(std::forward<Args>(args)...) {
}
operator vm::StackEntry() const {
return Hashmap::get(root_, idx_);
}
template <typename T>
HashmapIdx& operator=(T&& value) {
root_ = Hashmap::set(root_, idx_, vm::StackEntry(std::forward<T>(value)));
return *this;
}
};
class HashmapIterator {
std::vector<Ref<Hashmap>> stack_;
Ref<Hashmap> cur_;
const bool down_{false};
bool unwind(Ref<Hashmap> root);
public:
HashmapIterator() = default;
HashmapIterator(Ref<Hashmap> root, bool down = false) : down_(down) {
unwind(std::move(root));
}
const Hashmap& operator*() const {
return *cur_;
}
const Hashmap* operator->() const {
return cur_.get();
}
bool eof() {
return cur_.is_null();
}
bool next();
bool operator<(const HashmapIterator& other) const {
if (other.cur_.is_null()) {
return cur_.not_null();
} else if (cur_.is_null()) {
return false;
} else {
return cur_->key().cmp(other.cur_->key()) * (down_ ? -1 : 1) < 0;
}
}
bool operator==(const HashmapIterator& other) const {
return other.cur_.is_null() ? cur_.is_null() : (cur_.not_null() && cur_->key() == other.cur_->key());
}
bool operator!=(const HashmapIterator& other) const {
return other.cur_.is_null() ? cur_.not_null() : (cur_.is_null() || cur_->key() != other.cur_->key());
}
HashmapIterator& operator++() {
next();
return *this;
}
};
struct HashmapKeeper {
Ref<Hashmap> root;
HashmapKeeper() = default;
HashmapKeeper(Ref<Hashmap> _root) : root(std::move(_root)) {
}
Ref<Hashmap> extract() {
return std::move(root);
}
operator Ref<Hashmap>() const& {
return root;
}
operator Ref<Hashmap>() && {
return std::move(root);
}
template <typename... Args>
HashmapIdx operator[](Args&&... args) {
return HashmapIdx{root, DictKey{std::forward<Args>(args)...}};
}
template <typename... Args>
vm::StackEntry operator[](Args&&... args) const {
return Hashmap::get(root, DictKey{std::forward<Args>(args)...});
}
vm::StackEntry get_key(const DictKey& key) const {
return Hashmap::get(root, key);
}
template <typename... Args>
vm::StackEntry get(Args&&... args) const {
return Hashmap::get(root, DictKey{std::forward<Args>(args)...});
}
vm::StackEntry get_remove_key(const DictKey& key) {
auto res = Hashmap::get_remove_key(root, key);
root = std::move(res.first);
return std::move(res.second);
}
template <typename... Args>
vm::StackEntry get_remove(Args&&... args) {
return get_remove_key(DictKey{std::forward<Args>(args)...});
}
bool remove_key(const DictKey& key) {
auto res = Hashmap::get_remove(root, key);
root = std::move(res.first);
return !res.second.is_null();
}
template <typename... Args>
bool remove(Args&&... args) {
return remove_key(DictKey{std::forward<Args>(args)...});
}
template <typename T>
void set(T key, vm::StackEntry value) {
root = Hashmap::set(root, DictKey(key), std::move(value));
}
template <typename T>
bool replace(T key, vm::StackEntry value) {
return Hashmap::replace(root, DictKey(key), std::move(value));
}
HashmapIterator begin(bool reverse = false) const {
return HashmapIterator{root, reverse};
}
HashmapIterator end() const {
return HashmapIterator{};
}
HashmapIterator rbegin() const {
return HashmapIterator{root, true};
}
HashmapIterator rend() const {
return HashmapIterator{};
}
};
} // namespace fift

View file

@ -20,7 +20,7 @@
namespace fift {
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx) {
td::StringBuilder& operator<<(td::StringBuilder& os, const ParseCtx& ctx) {
if (ctx.include_depth) {
return os << ctx.filename << ":" << ctx.line_no << ": ";
} else {
@ -28,7 +28,7 @@ td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx) {
}
}
std::ostream& operator<<(std::ostream& os, const IntCtx& ctx) {
std::ostream& operator<<(std::ostream& os, const ParseCtx& ctx) {
return os << (PSLICE() << ctx).c_str();
}
@ -67,73 +67,7 @@ void CharClassifier::set_char_class(int c, int cl) {
*p = static_cast<unsigned char>((*p & ~mask) | cl);
}
IntCtx::Savepoint::Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
std::unique_ptr<std::istream> new_input_stream)
: ctx(_ctx)
, old_line_no(_ctx.line_no)
, old_need_line(_ctx.need_line)
, old_filename(_ctx.filename)
, old_current_dir(_ctx.currentd_dir)
, old_input_stream(_ctx.input_stream)
, old_input_stream_holder(std::move(_ctx.input_stream_holder))
, old_curline(_ctx.str)
, old_curpos(_ctx.input_ptr - _ctx.str.c_str())
, old_word(_ctx.word) {
ctx.line_no = 0;
ctx.filename = new_filename;
ctx.currentd_dir = new_current_dir;
ctx.input_stream = new_input_stream.get();
ctx.input_stream_holder = std::move(new_input_stream);
ctx.str = "";
ctx.input_ptr = 0;
++(ctx.include_depth);
}
bool IntCtx::Savepoint::restore(IntCtx& _ctx) {
if (restored || &ctx != &_ctx) {
return false;
}
ctx.line_no = old_line_no;
ctx.need_line = old_need_line;
ctx.filename = old_filename;
ctx.currentd_dir = old_current_dir;
ctx.input_stream = old_input_stream;
ctx.input_stream_holder = std::move(old_input_stream_holder);
ctx.str = old_curline;
ctx.input_ptr = ctx.str.c_str() + old_curpos;
ctx.word = old_word;
--(ctx.include_depth);
return restored = true;
}
bool IntCtx::enter_ctx(std::string new_filename, std::string new_current_dir,
std::unique_ptr<std::istream> new_input_stream) {
if (!new_input_stream) {
return false;
}
ctx_save_stack.emplace_back(*this, std::move(new_filename), std::move(new_current_dir), std::move(new_input_stream));
return true;
}
bool IntCtx::leave_ctx() {
if (ctx_save_stack.empty()) {
return false;
}
bool ok = ctx_save_stack.back().restore(*this);
ctx_save_stack.pop_back();
return ok;
}
bool IntCtx::top_ctx() {
while (!ctx_save_stack.empty()) {
if (!leave_ctx()) {
return false;
}
}
return true;
}
bool IntCtx::load_next_line() {
bool ParseCtx::load_next_line() {
if (!std::getline(*input_stream, str)) {
return false;
}
@ -145,11 +79,11 @@ bool IntCtx::load_next_line() {
return true;
}
bool IntCtx::is_sb() const {
bool ParseCtx::is_sb() const {
return !eof() && line_no == 1 && *input_ptr == '#' && input_ptr[1] == '!';
}
td::Slice IntCtx::scan_word_to(char delim, bool err_endl) {
td::Slice ParseCtx::scan_word_to(char delim, bool err_endl) {
load_next_line_ifreq();
auto ptr = input_ptr;
while (*ptr && *ptr != delim) {
@ -167,7 +101,7 @@ td::Slice IntCtx::scan_word_to(char delim, bool err_endl) {
}
}
td::Slice IntCtx::scan_word() {
td::Slice ParseCtx::scan_word() {
skipspc(true);
auto ptr = input_ptr;
while (*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '\r') {
@ -179,7 +113,7 @@ td::Slice IntCtx::scan_word() {
return td::Slice{ptr, ptr2};
}
td::Slice IntCtx::scan_word_ext(const CharClassifier& classifier) {
td::Slice ParseCtx::scan_word_ext(const CharClassifier& classifier) {
skipspc(true);
auto ptr = input_ptr;
while (*ptr && *ptr != '\r' && *ptr != '\n') {
@ -196,7 +130,7 @@ td::Slice IntCtx::scan_word_ext(const CharClassifier& classifier) {
return td::Slice{ptr, input_ptr};
}
void IntCtx::skipspc(bool skip_eol) {
void ParseCtx::skipspc(bool skip_eol) {
do {
while (*input_ptr == ' ' || *input_ptr == '\t' || *input_ptr == '\r') {
++input_ptr;
@ -207,6 +141,45 @@ void IntCtx::skipspc(bool skip_eol) {
} while (load_next_line());
}
bool IntCtx::enter_ctx(std::unique_ptr<ParseCtx> new_parser) {
if (!new_parser) {
return false;
}
if (parser) {
parser_save_stack.push_back(std::move(parser));
}
parser = std::move(new_parser);
return true;
}
bool IntCtx::enter_ctx(std::string new_filename, std::string new_current_dir,
std::unique_ptr<std::istream> new_input_stream) {
if (!new_input_stream) {
return false;
} else {
return enter_ctx(
std::make_unique<ParseCtx>(std::move(new_input_stream), new_filename, new_current_dir, include_depth() + 1));
}
}
bool IntCtx::leave_ctx() {
if (parser_save_stack.empty()) {
return false;
} else {
parser = std::move(parser_save_stack.back());
parser_save_stack.pop_back();
return true;
}
}
bool IntCtx::top_ctx() {
if (!parser_save_stack.empty()) {
parser = std::move(parser_save_stack[0]);
parser_save_stack.clear();
}
return true;
}
void IntCtx::check_compile() const {
if (state <= 0) {
throw IntError{"compilation mode only"};
@ -283,15 +256,20 @@ td::Result<int> IntCtx::get_result() {
}
}
td::Status IntCtx::add_error_loc(td::Status err) const {
if (err.is_error()) {
std::ostringstream os;
std::ostream& ParseCtx::show_context(std::ostream& os) const {
if (include_depth && line_no) {
os << filename << ":" << line_no << ":\t";
}
if (!word.empty()) {
os << word << ":";
}
return os;
}
td::Status IntCtx::add_error_loc(td::Status err) const {
if (err.is_error() && parser) {
std::ostringstream os;
parser->show_context(os);
return err.move_as_error_prefix(os.str());
} else {
return err;

View file

@ -18,8 +18,8 @@
*/
#pragma once
#include "crypto/vm/db/TonDb.h" // FIXME
#include "crypto/vm/stack.hpp"
#include "crypto/vm/box.hpp"
#include "crypto/common/bitstring.h"
#include "td/utils/Status.h"
@ -32,6 +32,11 @@
#include <iostream>
#include <string>
namespace vm {
class TonDbImpl; // from crypto/vm/db/TonDb.h
using TonDb = std::unique_ptr<TonDbImpl>;
} // namespace vm
namespace fift {
class Dictionary;
class SourceLookup;
@ -68,71 +73,36 @@ class CharClassifier {
}
};
struct IntCtx {
vm::Stack stack;
Ref<FiftCont> next, exc_handler;
Ref<FiftCont> exc_cont, exc_next;
int state{0};
struct ParseCtx {
int include_depth{0};
int line_no{0};
int exit_code{0};
td::Status error;
bool need_line{true};
std::string filename;
std::string currentd_dir;
std::istream* input_stream{nullptr};
std::unique_ptr<std::istream> input_stream_holder;
std::ostream* output_stream{nullptr};
std::ostream* error_stream{nullptr};
vm::TonDb* ton_db{nullptr};
Dictionary* dictionary{nullptr};
SourceLookup* source_lookup{nullptr};
int* now{nullptr};
std::string word;
private:
std::string str;
const char* input_ptr = nullptr;
class Savepoint {
IntCtx& ctx;
int old_line_no;
bool old_need_line;
bool restored{false};
std::string old_filename;
std::string old_current_dir;
std::istream* old_input_stream;
std::unique_ptr<std::istream> old_input_stream_holder;
std::string old_curline;
std::ptrdiff_t old_curpos;
std::string old_word;
public:
Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
std::unique_ptr<std::istream> new_input_stream);
bool restore(IntCtx& _ctx);
};
std::vector<Savepoint> ctx_save_stack;
public:
IntCtx() = default;
IntCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0)
ParseCtx() = default;
ParseCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0)
: include_depth(_depth)
, filename(std::move(_filename))
, currentd_dir(std::move(_curdir))
, input_stream(&_istream) {
}
operator vm::Stack&() {
return stack;
ParseCtx(std::unique_ptr<std::istream> _istream_ptr, std::string _filename, std::string _curdir = "", int _depth = 0)
: include_depth(_depth)
, filename(std::move(_filename))
, currentd_dir(std::move(_curdir))
, input_stream(_istream_ptr.get())
, input_stream_holder(std::move(_istream_ptr)) {
}
bool enter_ctx(std::string new_filename, std::string new_current_dir, std::unique_ptr<std::istream> new_input_stream);
bool leave_ctx();
bool top_ctx();
td::Slice scan_word_to(char delim, bool err_endl = true);
td::Slice scan_word();
td::Slice scan_word_ext(const CharClassifier& classifier);
@ -165,6 +135,50 @@ struct IntCtx {
bool is_sb() const;
std::ostream& show_context(std::ostream& os) const;
};
struct IntCtx {
vm::Stack stack;
Ref<FiftCont> next, exc_handler;
Ref<FiftCont> exc_cont, exc_next;
int state{0};
int exit_code{0};
td::Status error;
std::unique_ptr<ParseCtx> parser;
std::vector<std::unique_ptr<ParseCtx>> parser_save_stack;
std::ostream* output_stream{nullptr}; // move to OutCtx?
std::ostream* error_stream{nullptr};
vm::TonDb* ton_db{nullptr};
SourceLookup* source_lookup{nullptr};
int* now{nullptr};
Dictionary dictionary, main_dictionary, context;
public:
IntCtx() = default;
IntCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0) {
parser = std::make_unique<ParseCtx>(_istream, _filename, _curdir, _depth);
}
IntCtx(std::unique_ptr<std::istream> _istream, std::string _filename, std::string _curdir = "", int _depth = 0) {
parser = std::make_unique<ParseCtx>(std::move(_istream), _filename, _curdir, _depth);
}
bool enter_ctx(std::unique_ptr<ParseCtx> new_ctx);
bool enter_ctx(std::string new_filename, std::string new_current_dir, std::unique_ptr<std::istream> new_input_stream);
bool leave_ctx();
bool top_ctx();
int include_depth() const {
return parser ? parser->include_depth : -1;
}
operator vm::Stack &() {
return stack;
}
void clear() {
state = 0;
stack.clear();
@ -194,6 +208,6 @@ struct IntCtx {
td::Result<int> run(Ref<FiftCont> cont);
};
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx);
std::ostream& operator<<(std::ostream& os, const IntCtx& ctx);
td::StringBuilder& operator<<(td::StringBuilder& os, const ParseCtx& ctx);
std::ostream& operator<<(std::ostream& os, const ParseCtx& ctx);
} // namespace fift

View file

@ -1,12 +1,16 @@
library TVM_Asm
// simple TVM Assembler
namespace Asm
Asm definitions
"0.4.3" constant asm-fif-version
variable @atend
variable @was-split
false @was-split !
{ "not in asm context" abort } @atend !
{ `normal eq? not abort"must be terminated by }>" } : @normal?
{ @atend @ 1 { @atend ! @normal? } does @atend ! } : @pushatend
{ @pushatend <b } : <{
{ context@ @atend @ 2 { @atend ! context! @normal? } does @atend ! } : @pushatend
{ @pushatend Asm <b } : <{
{ @atend @ execute } : @endblk
{ false @was-split ! `normal @endblk } : }>
{ }> b> } : }>c
@ -251,7 +255,7 @@ x{7F} @Defop TRUE
} cond
} cond
@addopb } dup : PUSHINT : INT
{ <b x{83} s, swap 1- 8 u, @addopb } : PUSHPOW2
{ dup 256 = abort"use PUSHNAN instead of 256 PUSHPOW2" <b x{83} s, swap 1- 8 u, @addopb } : PUSHPOW2
x{83FF} @Defop PUSHNAN
{ <b x{84} s, swap 1- 8 u, @addopb } : PUSHPOW2DEC
{ <b x{85} s, swap 1- 8 u, @addopb } : PUSHNEGPOW2
@ -324,38 +328,85 @@ x{A7} x{A8} @Defop(8i,alt) MULCONST
' SUBCONST : SUBINT
' MULCONST : MULINT
x{A8} @Defop MUL
x{A904} @Defop DIV
x{A905} @Defop DIVR
x{A906} @Defop DIVC
x{A908} @Defop MOD
x{A909} @Defop MODR
x{A90A} @Defop MODC
x{A90C} @Defop DIVMOD
x{A90D} @Defop DIVMODR
x{A90E} @Defop DIVMODC
x{A925} @Defop RSHIFTR
x{A926} @Defop RSHIFTC
x{A928} @Defop MODPOW2
x{A929} @Defop MODPOW2R
x{A92A} @Defop MODPOW2C
x{A92C} @Defop RSHIFTMOD
x{A92D} @Defop RSHIFTMODR
x{A92E} @Defop RSHIFTMODC
x{A935} @Defop(8u+1) RSHIFTR#
x{A936} @Defop(8u+1) RSHIFTC#
x{A938} @Defop(8u+1) MODPOW2#
x{A939} @Defop(8u+1) MODPOW2R#
x{A93A} @Defop(8u+1) MODPOW2C#
x{A93C} @Defop(8u+1) RSHIFT#MOD
x{A93D} @Defop(8u+1) RSHIFTR#MOD
x{A93E} @Defop(8u+1) RSHIFTC#MOD
x{A984} @Defop MULDIV
x{A985} @Defop MULDIVR
x{A986} @Defop MULDIVC
x{A988} @Defop MULMOD
x{A989} @Defop MULMODR
x{A98A} @Defop MULMODC
x{A98C} @Defop MULDIVMOD
x{A98D} @Defop MULDIVMODR
x{A98E} @Defop MULDIVMODC
x{A9A4} @Defop MULRSHIFT
x{A9A5} @Defop MULRSHIFTR
x{A9A6} @Defop MULRSHIFTC
x{A9A8} @Defop MULMODPOW2
x{A9A9} @Defop MULMODPOW2R
x{A9AA} @Defop MULMODPOW2C
x{A9AC} @Defop MULRSHIFTMOD
x{A9AD} @Defop MULRSHIFTRMOD
x{A9AE} @Defop MULRSHIFTCMOD
x{A9B4} @Defop(8u+1) MULRSHIFT#
x{A9B5} @Defop(8u+1) MULRSHIFTR#
x{A9B6} @Defop(8u+1) MULRSHIFTC#
x{A9B8} @Defop(8u+1) MULMODPOW2#
x{A9B9} @Defop(8u+1) MULMODPOW2R#
x{A9BA} @Defop(8u+1) MULMODPOW2C#
x{A9BC} @Defop(8u+1) MULRSHIFT#MOD
x{A9BD} @Defop(8u+1) MULRSHIFTR#MOD
x{A9BE} @Defop(8u+1) MULRSHIFTC#MOD
x{A9C4} @Defop LSHIFTDIV
x{A9C5} @Defop LSHIFTDIVR
x{A9C6} @Defop LSHIFTDIVC
x{A9C8} @Defop LSHIFTMOD
x{A9C9} @Defop LSHIFTMODR
x{A9CA} @Defop LSHIFTMODC
x{A9CC} @Defop LSHIFTDIVMOD
x{A9CD} @Defop LSHIFTDIVMODR
x{A9CE} @Defop LSHIFTDIVMODC
x{A9D4} @Defop(8u+1) LSHIFT#DIV
x{A9D5} @Defop(8u+1) LSHIFT#DIVR
x{A9D6} @Defop(8u+1) LSHIFT#DIVC
x{A9D8} @Defop(8u+1) LSHIFT#MOD
x{A9D9} @Defop(8u+1) LSHIFT#MODR
x{A9DA} @Defop(8u+1) LSHIFT#MODC
x{A9DC} @Defop(8u+1) LSHIFT#DIVMOD
x{A9DD} @Defop(8u+1) LSHIFT#DIVMODR
x{A9DE} @Defop(8u+1) LSHIFT#DIVMODC
x{AA} @Defop(8u+1) LSHIFT#
x{AB} @Defop(8u+1) RSHIFT#
x{AC} @Defop LSHIFT
@ -504,6 +555,7 @@ x{CF1E} @Defop STSLICERQ
x{CF1F} dup @Defop STBRQ @Defop BCONCATQ
x{CF20} @Defop(ref) STREFCONST
{ <b x{CF21} s, rot ref, swap ref, @addopb } : STREF2CONST
x{CF23} @Defop ENDXC
x{CF28} @Defop STILE4
x{CF29} @Defop STULE4
x{CF2A} @Defop STILE8
@ -606,6 +658,9 @@ x{D733} @Defop SSKIPLAST
x{D734} @Defop SUBSLICE
x{D736} @Defop SPLIT
x{D737} @Defop SPLITQ
x{D739} @Defop XCTOS
x{D73A} @Defop XLOAD
x{D73B} @Defop XLOADQ
x{D741} @Defop SCHKBITS
x{D742} @Defop SCHKREFS
x{D743} @Defop SCHKBITREFS
@ -875,9 +930,15 @@ x{EDFB} @Defop SAMEALTSAVE
} dup : PREPARE : PREPAREDICT
//
// inline support
{ dup sbits { @addop } {
dup srefs 1- abort"exactly one reference expected in inline"
ref@ CALLREF } cond
{ dup sbits
{ @addop }
{
dup srefs //
{ ref@ CALLREF }
{ drop }
cond
}
cond
} : INLINE
//
// throwing and handling exceptions
@ -1186,6 +1247,8 @@ x{FFF0} @Defop SETCPX
255 and <b x{FF} s, swap 8 u, @addopb
} : SETCP
' @addop : CUSTOMOP
//
// provisions for defining programs consisting of several mutually-recursive procedures
//
@ -1194,6 +1257,7 @@ variable @proclist
variable @procdict
variable @procinfo
variable @gvarcnt
variable @parent-state
variable asm-mode 1 asm-mode !
19 constant @procdictkeylen
32 constant @zcount
@ -1213,7 +1277,13 @@ variable asm-mode 1 asm-mode !
{ 1 'nop does swap 0 (create) } : @declglobvar
{ @proccnt @ 1+ dup @proccnt ! 1 @declproc } : @newproc
{ @gvarcnt @ 1+ dup @gvarcnt ! @declglobvar } : @newglobvar
{ 0 =: main @proclist null! @proccnt 0! @gvarcnt 0!
variable @oldcurrent variable @oldctx
Fift-wordlist dup @oldcurrent ! @oldctx !
{ current@ @oldcurrent ! context@ @oldctx ! Asm definitions
@proccnt @ @proclist @ @procdict @ @procinfo @ @gvarcnt @ @parent-state @ current@ @oldcurrent @ @oldctx @
9 tuple @parent-state !
hole current!
0 =: main @proclist null! @proccnt 0! @gvarcnt 0!
{ bl word @newproc } : NEWPROC
{ bl word dup (def?) ' drop ' @newproc cond } : DECLPROC
{ bl word dup find
@ -1287,6 +1357,11 @@ variable asm-mode 1 asm-mode !
} while
drop @proclist null! @procinfo null! @proccnt 0!
@procdict dup @ swap null!
@parent-state @ dup null? { drop } {
9 untuple
@oldctx ! @oldcurrent ! current! @parent-state ! @gvarcnt ! @procinfo ! @procdict ! @proclist ! @proccnt !
} cond
@oldctx @ context! @oldcurrent @ current!
} : }END
forget @proclist forget @proccnt
{ }END <{ SETCP0 swap @procdictkeylen DICTPUSHCONST DICTIGETJMPZ 11 THROWARG }> } : }END>
@ -1313,3 +1388,44 @@ forget @proclist forget @proccnt
{ <b 2 8 u, swap 256 u, b>spec } : hash>libref
// ( c -- c' )
{ hash hash>libref } : >libref
{ dup "." $pos dup -1 =
{ drop 0 }
{ $| 1 $| nip swap (number) 1- abort"invalid version"
dup dup 0 < swap 999 > or abort"invalid version"
}
cond
} : parse-version-level
{
0 swap
"." $+
{ swap 1000 * swap parse-version-level rot + swap } 3 times
"" $= not abort"invalid version"
} : parse-asm-fif-version
{
dup =: required-version parse-asm-fif-version
asm-fif-version parse-asm-fif-version
= 1+ {
"Required Asm.fif version: " @' required-version "; actual Asm.fif version: " asm-fif-version $+ $+ $+ abort
} if
} : require-asm-fif-version
{
dup =: required-version parse-asm-fif-version
asm-fif-version parse-asm-fif-version
swap
>= 1+ {
"Required Asm.fif version: " @' required-version "; actual Asm.fif version: " asm-fif-version $+ $+ $+ abort
} if
} : require-asm-fif-version>=
Fift definitions Asm
' <{ : <{
' PROGRAM{ : PROGRAM{
' asm-fif-version : asm-fif-version
' require-asm-fif-version : require-asm-fif-version
' require-asm-fif-version>= : require-asm-fif-version>=
Fift

141
crypto/fift/lib/Disasm.fif Normal file
View file

@ -0,0 +1,141 @@
library TVM_Disasm
// simple TVM Disassembler
"Lists.fif" include
variable 'disasm
{ 'disasm @ execute } : disasm // disassemble a slice
// usage: x{74B0} disasm
variable @dismode @dismode 0!
{ rot over @ and rot xor swap ! } : andxor!
{ -2 0 @dismode andxor! } : stack-disasm // output 's1 s4 XCHG'
{ -2 1 @dismode andxor! } : std-disasm // output 'XCHG s1, s4'
{ -3 2 @dismode andxor! } : show-vm-code
{ -3 0 @dismode andxor! } : hide-vm-code
{ @dismode @ 1 and 0= } : stack-disasm?
variable @indent @indent 0!
{ ' space @indent @ 2* times } : .indent
{ @indent 1+! } : +indent
{ @indent 1-! } : -indent
{ " " $pos } : spc-pos
{ dup " " $pos swap "," $pos dup 0< { drop } {
over 0< { nip } { min } cond } cond
} : spc-comma-pos
{ { dup spc-pos 0= } { 1 $| nip } while } : -leading
{ -leading -trailing dup spc-pos dup 0< {
drop dup $len { atom single } { drop nil } cond } {
$| swap atom swap -leading 2 { over spc-comma-pos dup 0>= } {
swap 1+ -rot $| 1 $| nip -leading rot
} while drop tuple
} cond
} : parse-op
{ dup "s-1" $= { drop "s(-1)" true } {
dup "s-2" $= { drop "s(-2)" true } {
dup 1 $| swap "x" $= { nip "x{" swap $+ +"}" true } {
2drop false } cond } cond } cond
} : adj-op-arg
{ over count over <= { drop } { 2dup [] adj-op-arg { swap []= } { drop } cond } cond } : adj-arg[]
{ 1 adj-arg[] 2 adj-arg[] 3 adj-arg[]
dup first
dup `XCHG eq? {
drop dup count 2 = { tpop swap "s0" , swap , } if } {
dup `LSHIFT eq? {
drop dup count 2 = stack-disasm? and { second `LSHIFT# swap pair } if } {
dup `RSHIFT eq? {
drop dup count 2 = stack-disasm? and { second `RSHIFT# swap pair } if } {
drop
} cond } cond } cond
} : adjust-op
variable @cp @cp 0!
variable @curop
variable @contX variable @contY variable @cdict
{ atom>$ type } : .atom
{ dup first .atom dup count 1 > { space 0 over count 2- { 1+ 2dup [] type .", " } swap times 1+ [] type } { drop } cond } : std-show-op
{ 0 over count 1- { 1+ 2dup [] type space } swap times drop first .atom } : stk-show-op
{ @dismode @ 2 and { .indent ."// " @curop @ csr. } if } : .curop?
{ .curop? .indent @dismode @ 1 and ' std-show-op ' stk-show-op cond cr
} : show-simple-op
{ dup 4 u@ 9 = { 8 u@+ swap 15 and 3 << s@ } {
dup 7 u@ 0x47 = { 7 u@+ nip 2 u@+ 7 u@+ -rot 3 << swap sr@ } {
dup 8 u@ 0x8A = { ref@ <s } {
abort"invalid PUSHCONT"
} cond } cond } cond
} : get-cont-body
{ 14 u@+ nip 10 u@+ ref@ dup rot pair swap <s empty? { drop null } if } : get-const-dict
{ @contX @ @contY @ @contX ! @contY ! } : scont-swap
{ .indent swap type type cr @contY @ @contY null! @contX @ @contX null!
+indent disasm -indent @contY !
} : show-cont-bodyx
{ ":<{" show-cont-bodyx .indent ."}>" cr } : show-cont-op
{ swap scont-swap ":<{" show-cont-bodyx scont-swap
"" show-cont-bodyx .indent ."}>" cr } : show-cont2-op
{ @contX @ null? { "CONT" show-cont-op } ifnot
} : flush-contX
{ @contY @ null? { scont-swap "CONT" show-cont-op scont-swap } ifnot
} : flush-contY
{ flush-contY flush-contX } : flush-cont
{ @contX @ null? not } : have-cont?
{ @contY @ null? not } : have-cont2?
{ flush-contY @contY ! scont-swap } : save-cont-body
{ @cdict ! } : save-const-dict
{ @cdict null! } : flush-dict
{ @cdict @ null? not } : have-dict?
{ flush-cont .indent type .":<{" cr
@curop @ ref@ <s +indent disasm -indent .indent ."}>" cr
} : show-ref-op
{ flush-contY .indent rot type .":<{" cr
@curop @ ref@ <s @contX @ @contX null! rot ' swap if
+indent disasm -indent .indent swap type cr
+indent disasm -indent .indent ."}>" cr
} : show-cont-ref-op
{ flush-cont .indent swap type .":<{" cr
@curop @ ref@+ <s +indent disasm -indent .indent swap type cr
ref@ <s +indent disasm -indent .indent ."}>" cr
} : show-ref2-op
{ flush-cont first atom>$ dup 5 $| drop "DICTI" $= swap
.indent type ." {" cr +indent @cdict @ @cdict null! unpair
rot {
swap .indent . ."=> <{" cr +indent disasm -indent .indent ."}>" cr true
} swap ' idictforeach ' dictforeach cond drop
-indent .indent ."}" cr
} : show-const-dict-op
( `PUSHCONT `PUSHREFCONT ) constant @PushContL
( `REPEAT `UNTIL `IF `IFNOT `IFJMP `IFNOTJMP ) constant @CmdC1
( `IFREF `IFNOTREF `IFJMPREF `IFNOTJMPREF `CALLREF `JMPREF ) constant @CmdR1
( `DICTIGETJMP `DICTIGETJMPZ `DICTUGETJMP `DICTUGETJMPZ `DICTIGETEXEC `DICTUGETEXEC ) constant @JmpDictL
{ dup first `DICTPUSHCONST eq? {
flush-cont @curop @ get-const-dict save-const-dict show-simple-op } {
dup first @JmpDictL list-member? have-dict? and {
flush-cont show-const-dict-op } {
flush-dict
dup first @PushContL list-member? {
drop @curop @ get-cont-body save-cont-body } {
dup first @CmdC1 list-member? have-cont? and {
flush-contY first atom>$ .curop? show-cont-op } {
dup first @CmdR1 list-member? {
flush-cont first atom>$ dup $len 3 - $| drop .curop? show-ref-op } {
dup first `WHILE eq? have-cont2? and {
drop "WHILE" "}>DO<{" .curop? show-cont2-op } {
dup first `IFELSE eq? have-cont2? and {
drop "IF" "}>ELSE<{" .curop? show-cont2-op } {
dup first dup `IFREFELSE eq? swap `IFELSEREF eq? or have-cont? and {
first `IFREFELSE eq? "IF" "}>ELSE<{" rot .curop? show-cont-ref-op } {
dup first `IFREFELSEREF eq? {
drop "IF" "}>ELSE<{" .curop? show-ref2-op } {
flush-cont show-simple-op
} cond } cond } cond } cond } cond } cond } cond } cond } cond
} : show-op
{ dup @cp @ (vmoplen) dup 0> { 65536 /mod swap sr@+ swap dup @cp @ (vmopdump) parse-op swap s> true } { drop false } cond } : fetch-one-op
{ { fetch-one-op } { swap @curop ! adjust-op show-op } while } : disasm-slice
{ { disasm-slice dup sbitrefs 1- or 0= } { ref@ <s } while flush-dict flush-cont } : disasm-chain
{ @curop @ swap disasm-chain dup sbitrefs or { .indent ."Cannot disassemble: " csr. } { drop } cond @curop ! }
'disasm !

View file

@ -76,6 +76,8 @@ variable base
{ def? ' skip-to-eof if } : skip-ifdef
{ bl word dup (def?) { drop skip-to-eof } { 'nop swap 0 (create) } cond } : library
{ bl word dup (def?) { 2drop skip-to-eof } { swap 1 'nop does swap 0 (create) } cond } : library-version
{ hole dup 1 'nop does swap 1 { context! } does bl word tuck 0 (create) +"-wordlist" 0 (create) } : namespace
{ context@ current! } : definitions
{ char ) word "$" swap $+ 1 { find 0= abort"undefined parameter" execute } } ::_ $(
// b s -- ?
{ sbitrefs rot brembitrefs rot >= -rot <= and } : s-fits?

118
crypto/fift/lib/FiftExt.fif Normal file
View file

@ -0,0 +1,118 @@
{ ?dup { 1+ { execute } { 0 swap } cond }
{ (number) ?dup 0= abort"-?" 'nop } cond
} : (interpret-prepare)
{ { include-depth 0= (seekeof?) not } {
(word-prefix-find) (interpret-prepare) (execute)
} while
} : interpret
{ ({)
{ 0 (seekeof?) abort"no }" (word-prefix-find) (interpret-prepare) (compile) over atom? not } until
(}) swap execute
} : begin-block
{ swap 0 'nop } : end-block
{ { 1 'nop } `{ begin-block }
{ { swap `{ eq? not abort"} without {" swap execute } end-block }
:: } :: {
// if{ ... }then{ ... }elseif{ ... }then{ ... }else{ ... }
{ eq? not abort"unexpected" } : ?pairs
{ dup `if eq? swap `ifnot eq? over or not abort"without if{" } : if-ifnot?
// cond then ? -- exec
{ { ' if } { ' ifnot } cond rot ({) 0 rot (compile) -rot 1 swap (compile) (})
} : (make-if)
// cond then else -- exec
{ rot ({) 0 rot (compile) -rot 2 ' cond (compile) (})
} : (make-cond)
{ `noelse `if begin-block } :: if{
{ `noelse `ifnot begin-block } :: ifnot{
{ 1 ' end-block does } : end-block-does
{ { over `else eq? } {
nip rot if-ifnot? ' swap ifnot (make-cond)
} while
swap `noelse ?pairs 0 swap
} : finish-else-chain
{ swap dup if-ifnot? drop `then {
swap `then ?pairs
swap if-ifnot? (make-if) finish-else-chain
} `{ begin-block
} end-block-does :: }then{
{ swap `{ ?pairs nip
swap `then eq? not abort"without }then{" `else
} : ?else-ok
{ ?else-ok { finish-else-chain } `{ begin-block } end-block-does :: }else{
{ ?else-ok `if begin-block } end-block-does :: }elseif{
{ ?else-ok `ifnot begin-block } end-block-does :: }elseifnot{
// while{ ... }do{ ... }
{ 2 ' while does } : (make-while)
{ `while begin-block } :: while{
{ swap `while eq? not abort"without while{" `while-do {
swap `while-do ?pairs (make-while) 0 swap
} `{ begin-block
} end-block-does :: }do{
// repeat{ ... }until{ ... }
{ swap ({) 0 rot (compile) 0 rot (compile) (}) 1 ' until does } : (make-until)
{ `repeat begin-block } :: repeat{
{ swap `repeat eq? not abort"without repeat{" `until {
swap `until ?pairs (make-until) 0 swap
} `{ begin-block
} end-block-does :: }until{
// def <name> { ... } instead of { ... } : <name>
{ bl word swap bl word "{" $cmp abort"{ expected" `def {
swap `def ?pairs -rot 3 ' (create)
} `{ begin-block
} : (def)
{ 0 (def) } :: def
{ 1 (def) } :: def::
// defrec <name> { ... } instead of recursive <name> { ... } swap !
{ recursive bl word "{" $cmp abort"{ expected" `defrec {
swap `defrec ?pairs swap ! 0 'nop
} `{ begin-block
} :: defrec
def .sgn {
if{ ?dup 0= }then{
."zero"
}elseif{ 0> }then{
."positive"
}else{
."negative"
}
cr
}
// equivalent to: { ?dup 0= { ."zero" } { 0> { ."positive" } { ."negative" } cond } cond cr } : .sgn
defrec fact {
if{ dup }then{
dup 1- fact *
}else{
drop 1
}
}
// equivalent to: recursive fact { dup { dup 1- fact * } { drop 1 } cond } swap !
// [[ ... ]] computes arbitrary constants inside definitions
// { [[ 5 dup * ]] + } : add25
// is equivalent to
// { 25 + } : add25
{ "without [[" abort } box constant ']]
{ ']] @ execute } : ]]
{ { ']] @ 2 { ']] ! call/cc } does ']] !
interpret 'nop ']] ! "]] not found" abort
} call/cc
drop 1 'nop
} :: [[
{ { over @ swap 2 { call/cc } does swap !
interpret "literal to eof" abort
} call/cc
drop execute 1 'nop
} : interpret-literal-to
// use next line only if Lists.fif is loaded (or move it to Lists.fif if FiftExt.fif becomes part of Fift.fif)
// { ( ') interpret-literal-to } :: '(
// then you can use list literals '( a b c ... ) inside definitions:
// { '( 1 2 3 ) } : test
// { '( ( `a { ."A" } ) ( `b { ."B" } ) ) assoc { cadr execute } { ."???" } cond } : test2

View file

@ -59,6 +59,12 @@ td::Result<std::string> load_GetOpt_fif(std::string dir = "") {
td::Result<std::string> load_wallet3_code_fif(std::string dir = "") {
return td::read_file_str(smartcont_dir(dir) + "wallet-v3-code.fif");
}
td::Result<std::string> load_FiftExt_fif(std::string dir = "") {
return load_source("FiftExt.fif", dir);
}
td::Result<std::string> load_Disasm_fif(std::string dir = "") {
return load_source("Disasm.fif", dir);
}
class MemoryFileLoader : public fift::FileLoader {
public:
@ -108,7 +114,8 @@ class MemoryFileLoader : public fift::FileLoader {
td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_preamble = true, bool need_asm = true,
bool need_ton_util = true, bool need_lisp = true,
bool need_w3_code = true, std::string dir = "") {
bool need_w3_code = true, bool need_fift_ext = true,
bool need_disasm = true, std::string dir = "") {
auto loader = std::make_unique<MemoryFileLoader>();
loader->add_file("/main.fif", std::move(main));
if (need_preamble) {
@ -141,6 +148,14 @@ td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_
TRY_RESULT(f, load_wallet3_code_fif(dir));
loader->add_file("/wallet-v3-code.fif", std::move(f));
}
if (need_fift_ext) {
TRY_RESULT(f, load_FiftExt_fif(dir));
loader->add_file("/FiftExt.fif", std::move(f));
}
if (need_disasm) {
TRY_RESULT(f, load_Disasm_fif(dir));
loader->add_file("/Disasm.fif", std::move(f));
}
auto res = fift::SourceLookup(std::move(loader));
res.add_include_path("/");
return std::move(res);
@ -172,7 +187,7 @@ td::Result<fift::SourceLookup> run_fift(fift::SourceLookup source_lookup, std::o
} // namespace
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args, std::string fift_dir) {
std::stringstream ss;
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, true, fift_dir));
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, true, true, true, fift_dir));
TRY_RESULT_ASSIGN(source_lookup, run_fift(std::move(source_lookup), &ss, true, std::move(args)));
FiftOutput res;
res.source_lookup = std::move(source_lookup);
@ -190,7 +205,8 @@ td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std:
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir, bool need_preamble,
bool need_asm, bool need_ton_util, bool need_lisp,
bool need_w3_code) {
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, fift_dir);
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, false, false,
fift_dir);
}
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir, bool is_raw) {
@ -198,7 +214,7 @@ td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_d
TRY_RESULT(source_lookup,
create_source_lookup(PSTRING() << "\"Asm.fif\" include\n " << (is_raw ? "<{" : "") << asm_code << "\n"
<< (is_raw ? "}>c" : "") << " boc>B \"res\" B>file",
true, true, true, false, false, fift_dir));
true, true, true, false, false, false, false, fift_dir));
TRY_RESULT(res, run_fift(std::move(source_lookup), &ss));
TRY_RESULT(boc, res.read_file("res"));
return vm::std_boc_deserialize(std::move(boc.data));

View file

@ -21,6 +21,7 @@
#include "Dictionary.h"
#include "IntCtx.h"
#include "SourceLookup.h"
#include "HashMap.h"
#include "common/refcnt.hpp"
#include "common/bigint.hpp"
@ -42,6 +43,8 @@
#include "vm/box.hpp"
#include "vm/atom.h"
#include "vm/db/TonDb.h" // only for interpret_db_run_vm{,_parallel}
#include "block/block.h"
#include "td/utils/filesystem.h"
@ -58,12 +61,31 @@
namespace fift {
void show_total_cells(std::ostream& stream) {
stream << "total cells = " << vm::DataCell::get_total_data_cells() << std::endl;
const Ref<FiftCont> nop_word_def = Ref<NopWord>{true};
//
// functions for wordef
//
Ref<FiftCont> pop_exec_token(vm::Stack& stack) {
auto wd_ref = stack.pop_chk().as_object<FiftCont>();
if (wd_ref.is_null()) {
throw IntError{"execution token expected"};
}
return wd_ref;
}
void do_compile(vm::Stack& stack, Ref<FiftCont> word_def);
void do_compile_literals(vm::Stack& stack, int count);
Ref<WordList> pop_word_list(vm::Stack& stack) {
auto wl_ref = stack.pop_chk().as_object<WordList>();
if (wl_ref.is_null()) {
throw IntError{"word list expected"};
}
return wl_ref;
}
void push_argcount(vm::Stack& stack, int args) {
stack.push_smallint(args);
stack.push_object(nop_word_def);
}
void interpret_dot(IntCtx& ctx, bool space_after) {
*ctx.output_stream << dec_string2(ctx.stack.pop_int()) << (space_after ? " " : "");
@ -121,7 +143,7 @@ void interpret_print_list(IntCtx& ctx) {
}
void interpret_dottc(IntCtx& ctx) {
show_total_cells(*ctx.output_stream);
*ctx.output_stream << "total cells = " << vm::DataCell::get_total_data_cells() << std::endl;
}
void interpret_dot_internal(vm::Stack& stack) {
@ -461,7 +483,7 @@ void interpret_make_xchg(vm::Stack& stack) {
if (x) {
stack.push_object(td::Ref<StackWord>{true, std::bind(interpret_xchg, _1, x, y)});
} else if (y <= 1) {
stack.push_object(y ? swap_word_def : Dictionary::nop_word_def);
stack.push_object(y ? swap_word_def : nop_word_def);
} else {
stack.push_object(td::Ref<StackWord>{true, std::bind(interpret_xchg0, _1, y)});
}
@ -486,7 +508,7 @@ void interpret_make_pop(vm::Stack& stack) {
}
void interpret_is_string(vm::Stack& stack) {
stack.push_bool(stack.pop().type() == vm::StackEntry::t_string);
stack.push_bool(stack.pop_chk().type() == vm::StackEntry::t_string);
}
int make_utf8_char(char buffer[4], int x) {
@ -1080,6 +1102,31 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) {
}
}
void interpret_fetch_slice(vm::Stack& stack, int mode) {
unsigned refs = ((mode & 1) ? stack.pop_smallint_range(4) : 0);
unsigned bits = stack.pop_smallint_range(1023);
auto cs = stack.pop_cellslice();
if (!cs->have(bits, refs)) {
if (mode & 2) {
stack.push(std::move(cs));
}
stack.push_bool(false);
if (!(mode & 4)) {
throw IntError{"end of data while fetching subslice from cell"};
}
} else {
if (mode & 2) {
stack.push(cs.write().fetch_subslice(bits, refs));
stack.push(std::move(cs));
} else {
stack.push(cs->prefetch_subslice(bits, refs));
}
if (mode & 4) {
stack.push_bool(true);
}
}
}
void interpret_cell_empty(vm::Stack& stack) {
auto cs = stack.pop_cellslice();
stack.push_bool(cs->empty_ext());
@ -1285,7 +1332,7 @@ void interpret_atom_anon(vm::Stack& stack) {
}
void interpret_is_atom(vm::Stack& stack) {
stack.push_bool(stack.pop().is_atom());
stack.push_bool(stack.pop_chk().is_atom());
}
bool are_eqv(vm::StackEntry x, vm::StackEntry y) {
@ -1307,11 +1354,13 @@ bool are_eqv(vm::StackEntry x, vm::StackEntry y) {
}
void interpret_is_eqv(vm::Stack& stack) {
stack.check_underflow(2);
auto y = stack.pop(), x = stack.pop();
stack.push_bool(are_eqv(std::move(x), std::move(y)));
}
void interpret_is_eq(vm::Stack& stack) {
stack.check_underflow(2);
auto y = stack.pop(), x = stack.pop();
stack.push_bool(x == y);
}
@ -1481,6 +1530,150 @@ void interpret_crc32c(vm::Stack& stack) {
stack.push_smallint(td::crc32c(td::Slice{str}));
}
// Fift hashmaps
void push_hmap(vm::Stack& stack, Ref<Hashmap> hmap) {
if (hmap.not_null()) {
stack.push_object(std::move(hmap));
} else {
stack.push({});
}
}
void push_hmap(vm::Stack& stack, HashmapKeeper hmap_keep) {
push_hmap(stack, hmap_keep.extract());
}
Ref<Hashmap> pop_hmap(vm::Stack& stack) {
stack.check_underflow(1);
auto se = stack.pop();
if (se.is_null()) {
return {};
}
auto hmap_ref = std::move(se).as_object<Hashmap>();
if (hmap_ref.is_null()) {
throw IntError{"hashmap expected"};
}
return hmap_ref;
}
HashmapKeeper pop_hmap_keeper(vm::Stack& stack) {
return HashmapKeeper{pop_hmap(stack)};
}
void interpret_hmap_new(vm::Stack& stack) {
stack.push({});
}
void interpret_hmap_fetch(vm::Stack& stack, int mode) {
auto hmap = pop_hmap(stack);
auto value = Hashmap::get(std::move(hmap), stack.pop_chk());
bool found = !value.is_null();
if ((mode & 8) && !found) {
throw IntError{"hashmap key not found"};
}
if (mode & (2 << (int)found)) {
stack.push(std::move(value));
}
if (mode & 1) {
stack.push_bool(found);
}
}
void interpret_hmap_delete(vm::Stack& stack, int mode) {
auto hmap = pop_hmap(stack);
auto res = Hashmap::get_remove(std::move(hmap), stack.pop_chk());
push_hmap(stack, std::move(res.first));
bool found = !res.second.is_null();
if ((mode & 8) && !found) {
throw IntError{"hashmap key not found"};
}
if (mode & (2 << (int)found)) {
stack.push(std::move(res.second));
}
if (mode & 1) {
stack.push_bool(found);
}
}
void interpret_hmap_store(vm::Stack& stack, int mode) {
stack.check_underflow(3);
auto hmap = pop_hmap_keeper(stack);
auto key = stack.pop(), value = stack.pop();
bool ok = true;
if (mode & 1) {
hmap.set(std::move(key), std::move(value));
} else {
ok = hmap.replace(std::move(key), std::move(value));
}
push_hmap(stack, std::move(hmap));
if (mode & 2) {
stack.push_bool(ok);
}
}
void interpret_hmap_is_empty(vm::Stack& stack) {
stack.push_bool(pop_hmap(stack).is_null());
}
void interpret_hmap_decompose(vm::Stack& stack, int mode) {
auto hmap = pop_hmap(stack);
if (hmap.is_null()) {
if (mode & 1) {
stack.push_bool(false);
} else {
throw IntError{"empty hmap"};
}
return;
}
stack.push(hmap->key());
stack.push(hmap->value());
push_hmap(stack, hmap->left());
push_hmap(stack, hmap->right());
if (mode & 1) {
stack.push_bool(true);
}
}
class HmapIterCont : public LoopCont {
HashmapIterator it;
bool ok;
public:
HmapIterCont(Ref<FiftCont> _func, Ref<FiftCont> _after, HashmapIterator _it)
: LoopCont(std::move(_func), std::move(_after)), it(std::move(_it)), ok(true) {
}
HmapIterCont(const HmapIterCont&) = default;
HmapIterCont* make_copy() const override {
return new HmapIterCont(*this);
}
bool init(IntCtx& ctx) override {
return true;
}
bool pre_exec(IntCtx& ctx) override {
if (it.eof()) {
return false;
} else {
ctx.stack.push(it->key());
ctx.stack.push(it->value());
return true;
}
}
bool post_exec(IntCtx& ctx) override {
ok = ctx.stack.pop_bool();
return ok && it.next();
}
bool finalize(IntCtx& ctx) override {
ctx.stack.push_bool(ok);
return true;
}
};
Ref<FiftCont> interpret_hmap_foreach(IntCtx& ctx, int mode) {
auto func = pop_exec_token(ctx);
return td::make_ref<HmapIterCont>(std::move(func), std::move(ctx.next), pop_hmap_keeper(ctx).begin(mode & 1));
}
// vm dictionaries
void interpret_dict_new(vm::Stack& stack) {
stack.push({});
@ -1880,7 +2073,7 @@ void interpret_pfx_dict_get(vm::Stack& stack) {
}
void interpret_bitstring_hex_literal(IntCtx& ctx) {
auto s = ctx.scan_word_to('}');
auto s = ctx.parser->scan_word_to('}');
unsigned char buff[128];
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), s.begin(), s.end());
if (bits < 0) {
@ -1888,11 +2081,11 @@ void interpret_bitstring_hex_literal(IntCtx& ctx) {
}
auto cs = Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
ctx.stack.push(std::move(cs));
push_argcount(ctx.stack, 1);
push_argcount(ctx, 1);
}
void interpret_bitstring_binary_literal(IntCtx& ctx) {
auto s = ctx.scan_word_to('}');
auto s = ctx.parser->scan_word_to('}');
unsigned char buff[128];
int bits = (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), s.begin(), s.end());
if (bits < 0) {
@ -1900,12 +2093,12 @@ void interpret_bitstring_binary_literal(IntCtx& ctx) {
}
auto cs = Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
ctx.stack.push(std::move(cs));
push_argcount(ctx.stack, 1);
push_argcount(ctx, 1);
}
void interpret_word(IntCtx& ctx) {
char sep = (char)ctx.stack.pop_smallint_range(127);
auto word = (sep != ' ' ? ctx.scan_word_to(sep, true) : ctx.scan_word());
auto word = (sep != ' ' ? ctx.parser->scan_word_to(sep, true) : ctx.parser->scan_word());
ctx.stack.push_string(word);
}
@ -1913,17 +2106,17 @@ void interpret_word_ext(IntCtx& ctx) {
int mode = ctx.stack.pop_smallint_range(11);
auto delims = ctx.stack.pop_string();
if (mode & 8) {
ctx.skipspc(mode & 4);
ctx.parser->skipspc(mode & 4);
}
ctx.stack.push_string(ctx.scan_word_ext(CharClassifier{delims, mode & 3}));
ctx.stack.push_string(ctx.parser->scan_word_ext(CharClassifier{delims, mode & 3}));
}
void interpret_skipspc(IntCtx& ctx) {
ctx.skipspc();
ctx.parser->skipspc();
}
void interpret_wordlist_begin_aux(vm::Stack& stack) {
stack.push({vm::from_object, Ref<WordList>{true}});
stack.push_make_object<WordList>();
}
void interpret_wordlist_begin(IntCtx& ctx) {
@ -1936,7 +2129,7 @@ void interpret_wordlist_begin(IntCtx& ctx) {
void interpret_wordlist_end_aux(vm::Stack& stack) {
Ref<WordList> wordlist_ref = pop_word_list(stack);
wordlist_ref.write().close();
stack.push({vm::from_object, Ref<FiftCont>{wordlist_ref}});
stack.push_object(std::move(wordlist_ref));
}
void interpret_wordlist_end(IntCtx& ctx) {
@ -1955,7 +2148,7 @@ void interpret_internal_interpret_begin(IntCtx& ctx) {
void interpret_internal_interpret_end(IntCtx& ctx) {
ctx.check_int_exec();
ctx.state = -ctx.state;
ctx.stack.push({vm::from_object, Dictionary::nop_word_def});
ctx.stack.push_object(nop_word_def);
}
// (create)
@ -1972,18 +2165,12 @@ void interpret_create_aux(IntCtx& ctx, int mode) {
if (!(mode & 2)) {
word += ' ';
}
bool active = (mode & 1);
auto entry = ctx.dictionary->lookup(word);
if (entry) {
*entry = DictEntry{std::move(wd_ref), active}; // redefine word
} else {
ctx.dictionary->def_word(std::move(word), {std::move(wd_ref), active});
}
ctx.dictionary.def_word(std::move(word), {std::move(wd_ref), (bool)(mode & 1)});
}
// { bl word 0 (create) } : create
void interpret_create(IntCtx& ctx) {
auto word = ctx.scan_word();
auto word = ctx.parser->scan_word();
if (!word.size()) {
throw IntError{"non-empty word name expected"};
}
@ -1995,10 +2182,10 @@ Ref<FiftCont> create_aux_wd{Ref<CtxWord>{true, std::bind(interpret_create_aux, s
// { bl word <mode> 2 ' (create) } :: :
void interpret_colon(IntCtx& ctx, int mode) {
ctx.stack.push_string(ctx.scan_word());
ctx.stack.push_string(ctx.parser->scan_word());
ctx.stack.push_smallint(mode);
ctx.stack.push_smallint(2);
ctx.stack.push({vm::from_object, create_aux_wd});
ctx.stack.push_object(create_aux_wd);
//push_argcount(ctx, 2, create_wd);
}
@ -2006,27 +2193,27 @@ void interpret_colon(IntCtx& ctx, int mode) {
void interpret_forget_aux(IntCtx& ctx) {
std::string s = ctx.stack.pop_string();
auto s_copy = s;
auto entry = ctx.dictionary->lookup(s);
auto entry = ctx.dictionary.lookup(s);
if (!entry) {
s += " ";
entry = ctx.dictionary->lookup(s);
entry = ctx.dictionary.lookup(s);
}
if (!entry) {
throw IntError{"`" + s_copy + "` not found"};
} else {
ctx.dictionary->undef_word(s);
ctx.dictionary.undef_word(s);
}
}
// { bl word (forget) } : forget
void interpret_forget(IntCtx& ctx) {
ctx.stack.push_string(ctx.scan_word());
ctx.stack.push_string(ctx.parser->scan_word());
interpret_forget_aux(ctx);
}
void interpret_quote_str(IntCtx& ctx) {
ctx.stack.push_string(ctx.scan_word_to('"'));
push_argcount(ctx.stack, 1);
ctx.stack.push_string(ctx.parser->scan_word_to('"'));
push_argcount(ctx, 1);
}
int str_utf8_code(const char* str, int& len) {
@ -2054,7 +2241,7 @@ int str_utf8_code(const char* str, int& len) {
}
void interpret_char(IntCtx& ctx) {
auto s = ctx.scan_word();
auto s = ctx.parser->scan_word();
int len = (s.size() < 10 ? (int)s.size() : 10);
int code = str_utf8_code(s.data(), len);
if (code < 0 || s.size() != (unsigned)len) {
@ -2195,6 +2382,12 @@ Ref<FiftCont> interpret_execute(IntCtx& ctx) {
return pop_exec_token(ctx);
}
Ref<FiftCont> interpret_call_cc(IntCtx& ctx) {
auto next = pop_exec_token(ctx);
ctx.stack.push_object(std::move(ctx.next));
return next;
}
Ref<FiftCont> interpret_execute_times(IntCtx& ctx) {
int count = ctx.stack.pop_smallint_range(1000000000);
auto body = pop_exec_token(ctx);
@ -2249,30 +2442,46 @@ Ref<FiftCont> interpret_until(IntCtx& ctx) {
return body;
}
void interpret_tick(IntCtx& ctx) {
std::string word = ctx.scan_word().str();
auto entry = ctx.dictionary->lookup(word);
DictEntry context_lookup(IntCtx& ctx, std::string word, bool append_space = true) {
if (append_space) {
auto entry = context_lookup(ctx, word, false);
if (!entry) {
entry = ctx.dictionary->lookup(word + ' ');
entry = context_lookup(ctx, word + ' ', false);
}
return entry;
}
auto entry = ctx.context.lookup(word);
if (!entry && ctx.context != ctx.dictionary) {
entry = ctx.dictionary.lookup(word);
}
if (!entry && ctx.main_dictionary != ctx.context && ctx.main_dictionary != ctx.dictionary) {
entry = ctx.main_dictionary.lookup(word);
}
return entry;
}
void interpret_tick(IntCtx& ctx) {
std::string word = ctx.parser->scan_word().str();
auto entry = context_lookup(ctx, word);
if (!entry) {
throw IntError{"word `" + word + "` undefined"};
}
}
ctx.stack.push({vm::from_object, entry->get_def()});
ctx.stack.push_object(entry.get_def());
push_argcount(ctx, 1);
}
void interpret_find(IntCtx& ctx) {
void interpret_find(IntCtx& ctx, int mode) {
std::string word = ctx.stack.pop_string();
auto entry = ctx.dictionary->lookup(word);
if (!entry) {
entry = ctx.dictionary->lookup(word + ' ');
}
auto entry = context_lookup(ctx, word, !(mode & 2));
if (!entry) {
ctx.stack.push_bool(false);
} else {
ctx.stack.push({vm::from_object, entry->get_def()});
ctx.stack.push_object(entry.get_def());
if (!(mode & 1) || !entry.is_active()) {
ctx.stack.push_bool(true);
} else {
ctx.stack.push_smallint(1);
}
}
}
@ -2282,9 +2491,13 @@ void interpret_leave_source(IntCtx& ctx) {
}
}
void interpret_include_depth(IntCtx& ctx) {
ctx.stack.push_smallint(ctx.include_depth());
}
Ref<FiftCont> interpret_include(IntCtx& ctx) {
auto fname = ctx.stack.pop_string();
auto r_file = ctx.source_lookup->lookup_source(fname, ctx.currentd_dir);
auto r_file = ctx.source_lookup->lookup_source(fname, ctx.parser->currentd_dir);
if (r_file.is_error()) {
throw IntError{"cannot locate file `" + fname + "`"};
}
@ -2312,12 +2525,32 @@ Ref<FiftCont> interpret_skip_source(IntCtx& ctx) {
}
void interpret_words(IntCtx& ctx) {
for (const auto& x : *ctx.dictionary) {
*ctx.output_stream << x.first << " ";
for (const auto& x : ctx.dictionary) {
*ctx.output_stream << vm::StackEntry(x.key()).as_string() << " ";
}
*ctx.output_stream << std::endl;
}
void interpret_get_current(IntCtx& ctx) {
ctx.stack.push(ctx.dictionary.get_box());
}
void interpret_set_current(IntCtx& ctx) {
ctx.dictionary = ctx.stack.pop_box();
}
void interpret_get_context(IntCtx& ctx) {
ctx.stack.push(ctx.context.get_box());
}
void interpret_set_context(IntCtx& ctx) {
ctx.context = ctx.stack.pop_box();
}
void interpret_set_context_to(IntCtx& ctx, Ref<vm::Box> box) {
ctx.context = std::move(box);
}
void interpret_print_backtrace(IntCtx& ctx) {
ctx.print_backtrace(*ctx.output_stream, ctx.next);
}
@ -2463,6 +2696,28 @@ void interpret_run_vm(IntCtx& ctx, int mode) {
}
}
void interpret_vmop_len(vm::Stack& stack) {
int cp = stack.pop_smallint_range(0x7fffffff, -0x80000000);
auto cs = stack.pop_cellslice();
auto dispatch = vm::DispatchTable::get_table(cp);
if (!dispatch) {
throw IntError{"unknown vm codepage"};
}
stack.push_smallint(dispatch->instr_len(*cs));
}
void interpret_vmop_dump(vm::Stack& stack) {
int cp = stack.pop_smallint_range(0x7fffffff, -0x80000000);
auto cs = stack.pop_cellslice();
auto dispatch = vm::DispatchTable::get_table(cp);
if (!dispatch) {
throw IntError{"unknown vm codepage"};
}
auto dump = dispatch->dump_instr(cs.write());
stack.push_cellslice(std::move(cs));
stack.push_string(std::move(dump));
}
void do_interpret_db_run_vm_parallel(std::ostream* stream, vm::Stack& stack, vm::TonDb* ton_db_ptr, int threads_n,
int tasks_n) {
if (!ton_db_ptr || !*ton_db_ptr) {
@ -2621,11 +2876,11 @@ Ref<FiftCont> interpret_get_cmdline_arg(IntCtx& ctx) {
interpret_get_fixed_cmdline_arg(ctx.stack, n);
return {};
}
auto entry = ctx.dictionary->lookup("$0 ");
auto entry = ctx.dictionary.lookup("$0 ");
if (!entry) {
throw IntError{"-?"};
} else {
return entry->get_def();
return entry.get_def();
}
}
@ -2665,29 +2920,19 @@ Ref<FiftCont> interpret_execute_internal(IntCtx& ctx) {
return word_def;
}
// wl x1 .. xn n 'w --> wl'
void interpret_compile_internal(vm::Stack& stack) {
Ref<FiftCont> word_def = pop_exec_token(stack);
int count = stack.pop_smallint_range(255);
do_compile_literals(stack, count);
if (word_def != Dictionary::nop_word_def) {
do_compile(stack, word_def);
}
}
void do_compile(vm::Stack& stack, Ref<FiftCont> word_def) {
Ref<WordList> wl_ref = pop_word_list(stack);
if (word_def != Dictionary::nop_word_def) {
if (word_def != nop_word_def) {
auto list_size = word_def->list_size();
if ((td::uint64)list_size <= 1) {
// inline short definitions
if (list_size >= 0 && (list_size <= 2 || word_def.is_unique())) {
// inline short and unique definitions
auto list = word_def->get_list();
wl_ref.write().append(list, list + list_size);
} else {
wl_ref.write().push_back(word_def);
wl_ref.write().push_back(std::move(word_def));
}
}
stack.push({vm::from_object, wl_ref});
stack.push_object(std::move(wl_ref));
}
void compile_one_literal(WordList& wlist, vm::StackEntry val) {
@ -2715,12 +2960,146 @@ void do_compile_literals(vm::Stack& stack, int count) {
}
}
stack.pop_many(count + 1);
stack.push({vm::from_object, wl_ref});
stack.push_object(std::move(wl_ref));
}
// wl x1 .. xn n 'w --> wl'
void interpret_compile_internal(vm::Stack& stack) {
Ref<FiftCont> word_def = pop_exec_token(stack);
int count = stack.pop_smallint_range(255);
do_compile_literals(stack, count);
if (word_def != nop_word_def) {
do_compile(stack, std::move(word_def));
}
}
Ref<FiftCont> interpret_compile_execute(IntCtx& ctx) {
if (ctx.state > 0) {
interpret_compile_internal(ctx.stack);
return {};
} else {
return interpret_execute_internal(ctx);
}
}
void interpret_seekeof(IntCtx& ctx, int mode) {
if (mode == -1) {
mode = ctx.stack.pop_smallint_range(3, -1);
}
bool eof = true;
if (ctx.parser && (ctx.parser->get_input() || ctx.parser->load_next_line())) {
while (true) {
if (!ctx.parser->is_sb()) {
ctx.parser->skipspc();
if (*ctx.parser->get_input()) {
eof = false;
break;
}
}
if (mode & 1) {
*ctx.output_stream << " ok" << std::endl;
}
if (!ctx.parser->load_next_line()) {
break;
}
}
}
ctx.stack.push_bool(eof);
}
void interpret_word_prefix_find(IntCtx& ctx, int mode) {
const char *ptr = ctx.parser->get_input(), *start = ptr;
if (!ptr) {
ctx.stack.push_string(std::string{});
ctx.stack.push_bool(false);
return;
}
while (*ptr && *ptr != ' ' && *ptr != '\t') {
ptr++;
}
std::string Word{start, ptr};
Word.push_back(' ');
auto entry = context_lookup(ctx, Word, false);
Word.pop_back();
if (entry) {
ctx.parser->set_input(ptr);
ctx.parser->skipspc();
} else {
const char* ptr_end = ptr;
while (true) {
entry = context_lookup(ctx, Word, false);
if (entry) {
ctx.parser->set_input(ptr);
break;
}
if (ptr == start) {
Word = std::string{start, ptr_end};
ctx.parser->set_input(ptr_end);
ctx.parser->skipspc();
break;
}
Word.pop_back();
--ptr;
}
}
ctx.parser->word = Word;
if (!(mode & 2) || !entry) {
ctx.stack.push_string(std::move(Word));
}
if (mode & 1) {
if (!entry) {
ctx.stack.push_bool(false);
} else {
ctx.stack.push_object(entry.get_def());
ctx.stack.push_smallint(entry.is_active() ? 1 : -1);
}
}
}
// equivalent to
// { ?dup { 1+ { execute } { 0 swap } cond } { (number) ?dup 0= abort"-?" 'nop } cond
// } : (interpret-prepare)
Ref<FiftCont> interpret_prepare(IntCtx& ctx) {
int found = ctx.stack.pop_smallint_range(1, -1);
if (!found) {
// numbers
interpret_parse_number(ctx); // (number)
interpret_cond_dup(ctx); // ?dup
auto res = ctx.stack.pop_int(); // 0= abort"-?"
if (res == 0) {
throw IntError{"-?"};
}
ctx.stack.push_object(nop_word_def); // 'nop
return {};
} else if (found == -1) {
// ordinary word
ctx.stack.push_smallint(0); // 0
interpret_swap(ctx); // swap
return {};
} else {
// active word
return pop_exec_token(ctx); // execute
}
}
Ref<FiftCont> InterpretCont::run_tail(IntCtx& ctx) const {
static Ref<FiftCont> interpret_prepare_ref = td::make_ref<CtxTailWord>(interpret_prepare);
static Ref<FiftCont> compile_exec_ref = td::make_ref<CtxTailWord>(interpret_compile_execute);
interpret_seekeof(ctx, !ctx.state && !ctx.include_depth()); // seekeof
if (ctx.stack.pop_bool()) {
exit_interpret->clear();
return {}; // exit loop
}
exit_interpret->set({vm::from_object, ctx.next}); // set 'exit-interpret to current continuation
interpret_word_prefix_find(ctx, 3); // (word-prefix-find)
// (interpet-prepare) (compile-execute) and schedule next loop iteration
ctx.next = SeqCont::seq(compile_exec_ref, SeqCont::seq(self(), std::move(ctx.next)));
return interpret_prepare_ref; // (interpret-prepare)
}
void init_words_common(Dictionary& d) {
using namespace std::placeholders;
d.def_word("nop ", Dictionary::nop_word_def);
d.def_word("nop ", nop_word_def);
// stack print/dump words
d.def_ctx_word(". ", std::bind(interpret_dot, _1, true));
d.def_ctx_word("._ ", std::bind(interpret_dot, _1, false));
@ -2940,6 +3319,14 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("ref@+ ", std::bind(interpret_fetch_ref, _1, 2));
d.def_stack_word("ref@? ", std::bind(interpret_fetch_ref, _1, 4));
d.def_stack_word("ref@?+ ", std::bind(interpret_fetch_ref, _1, 6));
d.def_stack_word("s@ ", std::bind(interpret_fetch_slice, _1, 0));
d.def_stack_word("sr@ ", std::bind(interpret_fetch_slice, _1, 1));
d.def_stack_word("s@+ ", std::bind(interpret_fetch_slice, _1, 2));
d.def_stack_word("sr@+ ", std::bind(interpret_fetch_slice, _1, 3));
d.def_stack_word("s@? ", std::bind(interpret_fetch_slice, _1, 4));
d.def_stack_word("sr@? ", std::bind(interpret_fetch_slice, _1, 5));
d.def_stack_word("s@?+ ", std::bind(interpret_fetch_slice, _1, 6));
d.def_stack_word("sr@?+ ", std::bind(interpret_fetch_slice, _1, 7));
d.def_stack_word("s> ", interpret_cell_check_empty);
d.def_stack_word("empty? ", interpret_cell_empty);
d.def_stack_word("remaining ", interpret_cell_remaining);
@ -2968,6 +3355,18 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("crc16 ", interpret_crc16);
d.def_stack_word("crc32 ", interpret_crc32);
d.def_stack_word("crc32c ", interpret_crc32c);
// hashmaps
d.def_stack_word("hmapnew ", interpret_hmap_new);
d.def_stack_word("hmap@ ", std::bind(interpret_hmap_fetch, _1, 6));
d.def_stack_word("hmap@? ", std::bind(interpret_hmap_fetch, _1, 5));
d.def_stack_word("hmap- ", std::bind(interpret_hmap_delete, _1, 0));
d.def_stack_word("hmap-? ", std::bind(interpret_hmap_delete, _1, 1));
d.def_stack_word("hmap@- ", std::bind(interpret_hmap_delete, _1, 6));
d.def_stack_word("hmap! ", std::bind(interpret_hmap_store, _1, 0));
d.def_stack_word("hmap!+ ", std::bind(interpret_hmap_store, _1, 1));
d.def_stack_word("hmapempty? ", interpret_hmap_is_empty);
d.def_stack_word("hmapunpack ", std::bind(interpret_hmap_decompose, _1, 1));
d.def_ctx_tail_word("hmapforeach ", std::bind(interpret_hmap_foreach, _1, 0));
// vm dictionaries
d.def_stack_word("dictnew ", interpret_dict_new);
d.def_stack_word("dict>s ", interpret_dict_to_slice);
@ -3039,6 +3438,7 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("atom? ", interpret_is_atom);
// execution control
d.def_ctx_tail_word("execute ", interpret_execute);
d.def_ctx_tail_word("call/cc ", interpret_call_cc);
d.def_ctx_tail_word("times ", interpret_execute_times);
d.def_ctx_tail_word("if ", interpret_if);
d.def_ctx_tail_word("ifnot ", interpret_ifnot);
@ -3054,10 +3454,12 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("(}) ", interpret_wordlist_end_aux);
d.def_stack_word("(compile) ", interpret_compile_internal);
d.def_ctx_tail_word("(execute) ", interpret_execute_internal);
d.def_ctx_tail_word("(interpret-prepare) ", interpret_prepare);
d.def_active_word("' ", interpret_tick);
d.def_word("'nop ", LitCont::literal({vm::from_object, Dictionary::nop_word_def}));
d.def_word("'nop ", LitCont::literal({vm::from_object, nop_word_def}));
// dictionary manipulation
d.def_ctx_word("find ", interpret_find);
d.def_ctx_word("find ", std::bind(interpret_find, _1, 1));
d.def_ctx_word("(word-prefix-find) ", std::bind(interpret_word_prefix_find, _1, 3));
d.def_ctx_word("create ", interpret_create);
d.def_ctx_word("(create) ", std::bind(interpret_create_aux, _1, -1));
d.def_active_word(": ", std::bind(interpret_colon, _1, 0));
@ -3067,12 +3469,21 @@ void init_words_common(Dictionary& d) {
d.def_ctx_word("(forget) ", interpret_forget_aux);
d.def_ctx_word("forget ", interpret_forget);
d.def_ctx_word("words ", interpret_words);
d.def_word("Fift-wordlist ", LitCont::literal(d.get_box()));
d.def_ctx_word("Fift ", std::bind(interpret_set_context_to, _1, d.get_box()));
d.def_ctx_word("current@ ", interpret_get_current);
d.def_ctx_word("current! ", interpret_set_current);
d.def_ctx_word("context@ ", interpret_get_context);
d.def_ctx_word("context! ", interpret_set_context);
d.def_ctx_word(".bt ", interpret_print_backtrace);
d.def_ctx_word("cont. ", interpret_print_continuation);
// input parse
d.def_ctx_word("word ", interpret_word);
d.def_ctx_word("(word) ", interpret_word_ext);
d.def_ctx_word("skipspc ", interpret_skipspc);
d.def_ctx_word("seekeof? ", std::bind(interpret_seekeof, _1, 1));
d.def_ctx_word("(seekeof?) ", std::bind(interpret_seekeof, _1, -1));
d.def_ctx_word("include-depth ", interpret_include_depth);
d.def_ctx_tail_word("include ", interpret_include);
d.def_ctx_tail_word("skip-to-eof ", interpret_skip_source);
d.def_word("'exit-interpret ", LitCont::literal(exit_interpret));
@ -3108,6 +3519,8 @@ void init_words_vm(Dictionary& d, bool enable_debug) {
d.def_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel);
d.def_stack_word("vmcont, ", interpret_store_vm_cont);
d.def_stack_word("vmcont@ ", interpret_fetch_vm_cont);
d.def_stack_word("(vmoplen) ", interpret_vmop_len);
d.def_stack_word("(vmopdump) ", interpret_vmop_dump);
}
void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]) {
@ -3126,99 +3539,4 @@ void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* con
}
}
std::pair<td::RefInt256, td::RefInt256> numeric_value_ext(std::string s, bool allow_frac = true) {
td::RefInt256 num, denom;
int res = parse_number(s, num, denom, allow_frac);
if (res <= 0) {
throw IntError{"-?"};
}
return std::make_pair(std::move(num), res == 2 ? std::move(denom) : td::RefInt256{});
}
td::RefInt256 numeric_value(std::string s) {
td::RefInt256 num, denom;
int res = parse_number(s, num, denom, false);
if (res != 1) {
throw IntError{"-?"};
}
return num;
}
Ref<FiftCont> interpret_compile_execute(IntCtx& ctx) {
if (ctx.state > 0) {
interpret_compile_internal(ctx.stack);
return {};
} else {
return interpret_execute_internal(ctx);
}
}
Ref<FiftCont> InterpretCont::run_tail(IntCtx& ctx) const {
if (!ctx.get_input() && !ctx.load_next_line()) {
return {};
}
while (true) {
if (!ctx.is_sb()) {
ctx.skipspc();
if (*ctx.get_input()) {
break;
}
}
if (!ctx.state && !ctx.include_depth) {
*ctx.output_stream << " ok" << std::endl;
}
if (!ctx.load_next_line()) {
return {};
}
}
const char* ptr = ctx.get_input();
std::string Word;
Word.reserve(128);
auto entry = ctx.dictionary->lookup("");
std::string entry_word;
const char* ptr_end = ptr;
while (*ptr && *ptr != ' ' && *ptr != '\t') {
Word += *ptr++;
auto cur = ctx.dictionary->lookup(Word);
if (cur) {
entry = cur;
entry_word = Word;
ptr_end = ptr;
}
}
auto cur = ctx.dictionary->lookup(Word + " ");
if (cur || !entry) {
entry = std::move(cur);
ctx.set_input(ptr);
ctx.skipspc();
} else {
Word = entry_word;
ctx.set_input(ptr_end);
}
ctx.word = Word;
static Ref<FiftCont> compile_exec_ref = td::make_ref<CtxTailWord>(interpret_compile_execute);
if (!entry) {
// numbers
auto res = numeric_value_ext(Word);
ctx.stack.push(std::move(res.first));
if (res.second.not_null()) {
ctx.stack.push(std::move(res.second));
push_argcount(ctx, 2);
} else {
push_argcount(ctx, 1);
}
} else if (!entry->is_active()) {
// ordinary word
ctx.stack.push_smallint(0);
ctx.stack.push({vm::from_object, entry->get_def()});
} else {
// active word
ctx.next = SeqCont::seq(compile_exec_ref, SeqCont::seq(self(), std::move(ctx.next)));
return entry->get_def();
}
exit_interpret->set({vm::from_object, ctx.next});
ctx.next = SeqCont::seq(self(), std::move(ctx.next));
return compile_exec_ref;
}
} // namespace fift

View file

@ -158,9 +158,9 @@ void VarDescr::set_const(td::RefInt256 value) {
} else if (s > 0) {
val |= _NonZero | _Pos | _Finite;
} else if (!s) {
if (*int_const == 1) {
val |= _Bit;
}
//if (*int_const == 1) {
// val |= _Bit;
//}
val |= _Zero | _Neg | _Pos | _Finite | _Bool | _Bit;
}
if (val & _Finite) {

View file

@ -735,7 +735,7 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
res.emplace_back(i);
}
AsmOpList tmp;
func->compile(tmp, res, args); // abstract interpretation of res := f (args)
func->compile(tmp, res, args, where); // abstract interpretation of res := f (args)
int j = 0;
for (var_idx_t i : left) {
values.add_newval(i).set_value(res[j++]);

View file

@ -0,0 +1,153 @@
import os
import os.path
import subprocess
import sys
import tempfile
import shutil
add_pragmas = [] #["allow-post-modification", "compute-asm-ltr"];
tests = [
# note, that deployed version of elector,config and multisig differ since it is compilled with func-0.1.0.
# Newer compillators optimize arithmetic and logic expression that can be calculated at the compile time
["elector/elector-code.fc", 115226404411715505328583639896096915745686314074575650766750648324043316883483],
["config/config-code.fc", 10913070768607625342121305745084703121685937915388357634624451844356456145601],
["eth-bridge-multisig/multisig-code.fc", 101509909129354488841890823627011033360100627957439967918234053299675481277954],
["bsc-bridge-collector/votes-collector.fc", 62190447221288642706570413295807615918589884489514159926097051017036969900417],
["uni-lock-wallet/uni-lockup-wallet.fc", 61959738324779104851267145467044677651344601417998258530238254441977103654381],
["nft-collection/nft-collection-editable.fc", 45561997735512210616567774035540357815786262097548276229169737015839077731274],
["dns-collection/nft-collection.fc", 107999822699841936063083742021519765435859194241091312445235370766165379261859],
# note, that deployed version of tele-nft-item differs since it is compilled with func-0.3.0.
# After introducing of try/catch construction, c2 register is not always the default one.
# Thus it is necessary to save it upon jumps, differences of deployed and below compilled is that
# "c2 SAVE" is added to the beginning of recv_internal. It does not change behavior.
["tele-nft-item/nft-item.fc", 69777543125381987786450436977742010705076866061362104025338034583422166453344],
["storage/storage-contract.fc", 91377830060355733016937375216020277778264560226873154627574229667513068328151],
["storage/storage-provider.fc", 13618336676213331164384407184540461509022654507176709588621016553953760588122],
["nominator-pool/pool.fc", 69767057279163099864792356875696330339149706521019810113334238732928422055375],
["jetton-minter/jetton-minter.fc", 9028309926287301331466371999814928201427184114165428257502393474125007156494],
["gg-marketplace/nft-marketplace-v2.fc", 92199806964112524639740773542356508485601908152150843819273107618799016205930],
["jetton-wallet/jetton-wallet.fc", 86251125787443633057458168028617933212663498001665054651523310772884328206542],
["whales-nominators/nominators.fc", 8941364499854379927692172316865293429893094891593442801401542636695127885153],
["tact-examples/treasure_Treasure.code.fc", 13962538639825790677138656603323869918938565499584297120566680287245364723897],
["tact-examples/jetton_SampleJetton.code.fc", 94076762218493729104783735200107713211245710256802265203823917715299139499110],
["tact-examples/jetton_JettonDefaultWallet.code.fc", 29421313492520031238091587108198906058157443241743283101866538036369069620563],
["tact-examples/maps_MapTestContract.code.fc", 22556550222249123835909180266811414538971143565993192846012583552876721649744],
]
def getenv(name, default=None):
if name in os.environ:
return os.environ[name]
if default is None:
print("Environment variable", name, "is not set", file=sys.stderr)
exit(1)
return default
FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
FIFT_LIBS = getenv("FIFTPATH")
TMP_DIR = tempfile.mkdtemp()
COMPILED_FIF = os.path.join(TMP_DIR, "compiled.fif")
RUNNER_FIF = os.path.join(TMP_DIR, "runner.fif")
TESTS_DIR = "legacy_tests"
class ExecutionError(Exception):
pass
def pre_process_func(f):
shutil.copyfile(f, f+"_backup")
with open(f, "r") as src:
sources = src.read()
with open(f, "w") as src:
for pragma in add_pragmas:
src.write("#pragma %s;\n"%pragma)
src.write(sources)
def post_process_func(f):
shutil.move(f+"_backup", f)
def compile_func(f):
res = None
try:
pre_process_func(f)
if "storage-provider.fc" in f :
# This contract requires building of storage-contract to include it as ref
with open(f, "r") as src:
sources = src.read()
COMPILED_ST_BOC = os.path.join(TMP_DIR, "storage-contract-code.boc")
sources = sources.replace("storage-contract-code.boc", COMPILED_ST_BOC)
with open(f, "w") as src:
src.write(sources)
COMPILED_ST_FIF = os.path.join(TMP_DIR, "storage-contract.fif")
COMPILED_ST_BOC = os.path.join(TMP_DIR, "storage-contract-code.boc")
COMPILED_BUILD_BOC = os.path.join(TMP_DIR, "build-boc.fif")
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_ST_FIF, "-SPA", f.replace("storage-provider.fc","storage-contract.fc")], capture_output=False, timeout=10)
with open(COMPILED_BUILD_BOC, "w") as scr:
scr.write("\"%s\" include boc>B \"%s\" B>file "%(COMPILED_ST_FIF, COMPILED_ST_BOC))
res = subprocess.run([FIFT_EXECUTABLE, COMPILED_BUILD_BOC ], capture_output=True, timeout=10)
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_FIF, "-SPA", f], capture_output=True, timeout=10)
except Exception as e:
post_process_func(f)
raise e
else:
post_process_func(f)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
def run_runner():
res = subprocess.run([FIFT_EXECUTABLE, "-I", FIFT_LIBS, RUNNER_FIF], capture_output=True, timeout=10)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
s = str(res.stdout, "utf-8")
s = s.strip()
return int(s)
def get_version():
res = subprocess.run([FUNC_EXECUTABLE, "-s"], capture_output=True, timeout=10)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
s = str(res.stdout, "utf-8")
return s.strip()
success = 0
for ti, t in enumerate(tests):
tf, th = t
print(" Running test %d/%d: %s" % (ti + 1, len(tests), tf), file=sys.stderr)
tf = os.path.join(TESTS_DIR, tf)
try:
compile_func(tf)
except ExecutionError as e:
print(file=sys.stderr)
print("Compilation error", file=sys.stderr)
print(e, file=sys.stderr)
exit(2)
with open(RUNNER_FIF, "w") as f:
print("\"%s\" include hash .s" % COMPILED_FIF , file=f)
try:
func_out = run_runner()
if func_out != th:
raise ExecutionError("Error : expected '%d', found '%d'" % (th, func_out))
success += 1
except ExecutionError as e:
print(e, file=sys.stderr)
#print("Compiled:", file=sys.stderr)
#with open(COMPILED_FIF, "r") as f:
# print(f.read(), file=sys.stderr)
#exit(2)
print(" OK ", file=sys.stderr)
print(get_version())
print("Done: Success %d, Error: %d"%(success, len(tests)-success), file=sys.stderr)

View file

@ -0,0 +1,13 @@
(int, int, cell) get_bridge_config() impure inline_ref {
cell bridge_config = config_param(72);
if (bridge_config.cell_null?()) {
bridge_config = config_param(-72);
}
throw_if(666, bridge_config.cell_null?());
slice ds = bridge_config.begin_parse();
;; wc always equals to -1
int bridge_address = ds~load_uint(256);
int oracles_address = ds~load_uint(256);
cell oracles = ds~load_dict();
return (bridge_address, oracles_address, oracles);
}

View file

@ -0,0 +1,38 @@
() send_receipt_message(addr, ans_tag, query_id, body, grams, mode) impure inline_ref {
;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(addr)
.store_grams(grams)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(ans_tag, 32)
.store_uint(query_id, 64);
if (body >= 0) {
msg~store_uint(body, 256);
}
send_raw_message(msg.end_cell(), mode);
}
() send_text_receipt_message(addr, grams, mode) impure inline_ref {
;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(addr)
.store_grams(grams)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(0, 32)
.store_uint(0x4f4b, 16); ;; "OK"
send_raw_message(msg.end_cell(), mode);
}
() emit_log_simple (int event_id, slice data) impure inline {
var msg = begin_cell()
.store_uint (12, 4) ;; ext_out_msg_info$11 src:MsgAddressInt ()
.store_uint (1, 2)
.store_uint (256, 9)
.store_uint(event_id, 256)
.store_uint(0, 64 + 32 + 2) ;; created_lt, created_at, init:Maybe, body:Either
.store_slice(data)
.end_cell();
send_raw_message(msg, 0);
}

View file

@ -0,0 +1,209 @@
;; Standard library for funC
;;
forall X -> tuple cons(X head, tuple tail) asm "CONS";
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
int cell_hash(cell c) asm "HASHCU";
int slice_hash(slice s) asm "HASHSU";
int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
cont get_c3() impure asm "c3 PUSH";
() set_c3(cont c) impure asm "c3 POP";
cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
cell preload_ref(slice s) asm "PLDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
builder store_slice(builder b, slice s) asm "STSLICER";
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_dict(builder b, cell c) asm(c b) "STDICT";
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
tuple parse_addr(slice s) asm "PARSEMSGADDR";
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";
int random() impure asm "RANDU256";
int rand(int range) impure asm "RAND";
int get_seed() impure asm "RANDSEED";
int set_seed() impure asm "SETRAND";
() randomize(int x) impure asm "ADDRAND";
() randomize_lt() impure asm "LTIME" "ADDRAND";

View file

@ -0,0 +1,118 @@
#include "stdlib.fc";
#include "bridge-config.fc";
#include "message_utils.fc";
cell load_data() inline_ref {
var ds = get_data().begin_parse();
return ds~load_dict();
}
() save_data(cell external_votings) impure inline_ref {
var st = begin_cell().store_dict(external_votings).end_cell();
set_data(st);
}
() vote_on_external_chain(slice s_addr, int query_id, int voting_id, slice signature) impure {
cell external_votings = load_data();
(_, int oracles_address, cell oracles) = get_bridge_config();
(int wc, int addr) = parse_std_addr(s_addr);
throw_if(301, wc + 1);
(slice key, int found?) = oracles.udict_get?(256, addr);
throw_unless(304, found?);
(slice old_voting_data, int voting_found?) = external_votings.udict_get?(256, voting_id);
cell signatures = new_dict();
if (voting_found?) {
(_, int old_oracles_address, signatures) = (old_voting_data~load_uint(32),
old_voting_data~load_uint(256),
old_voting_data~load_dict());
if (old_oracles_address != oracles_address) {
signatures = new_dict();
}
}
int secp_key = key~load_uint(256);
int success? = signatures~udict_add?(256, secp_key, signature);
throw_unless(324, success?);
builder new_voting_data = begin_cell()
.store_uint(now(), 32)
.store_uint(oracles_address, 256)
.store_dict(signatures);
external_votings~udict_set_builder(256, voting_id, new_voting_data);
save_data(external_votings);
return send_receipt_message(s_addr, 0x10000 + 5, query_id, voting_id, 0, 64);
}
() remove_outdated_votings(slice s_addr, int query_id, slice external_ids) impure {
cell external_votings = load_data();
int bound = now() - 60 * 60 * 24 * 7;
while (~ external_ids.slice_empty?()) {
if (external_ids.slice_data_empty?()) {
external_ids = external_ids.preload_ref().begin_parse();
}
int voting_id = external_ids~load_uint(256);
(cell external_votings', slice voting, int voting_found?) = external_votings.udict_delete_get?(256, voting_id);
if (voting_found?) {
int last_update = voting~load_uint(32);
if (bound > last_update) {
;; remove only old votings
external_votings = external_votings';
}
}
}
save_data(external_votings);
return send_receipt_message(s_addr, 0x10000 + 6, query_id, 0, 0, 64); ;; thanks
}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
var cs = in_msg_cell.begin_parse();
var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
if (flags & 1) {
;; ignore all bounced messages
return ();
}
slice s_addr = cs~load_msg_addr();
if (in_msg.slice_empty?()) {
;; inbound message has empty body
return ();
}
int op = in_msg~load_uint(32);
if (op == 0) {
return ();
}
int query_id = in_msg~load_uint(64);
if (op == 5) { ;; submit signatures
int voting_id = in_msg~load_uint(256);
slice signature = in_msg~load_bits(520);
return vote_on_external_chain(s_addr, query_id, voting_id, signature);
}
if (op == 6) { ;; remove old swaps
return remove_outdated_votings(s_addr, query_id, in_msg);
}
}
(tuple) get_external_voting_data(int voting_id) method_id {
cell external_votings = load_data();
(slice voting_data, int found?) = external_votings.udict_get?(256, voting_id);
throw_unless(309, found?);
(int time, int old_oracles_address, cell signatures) = (voting_data~load_uint(32),
voting_data~load_uint(256),
voting_data~load_dict());
tuple list = null();
int secp_key = -1;
do {
(secp_key, slice sig, int found?) = signatures.udict_get_next?(256, secp_key);
if (found?) {
(int r, int s, int v) = (sig~load_uint(256),
sig~load_uint(256),
sig~load_uint(8));
list = cons( pair( secp_key, triple(r,s,v)), list);
}
} until (~ found?);
return (list);
}

View file

@ -0,0 +1,643 @@
;; Simple configuration smart contract
#include "stdlib.fc";
() set_conf_param(int index, cell value) impure {
var cs = get_data().begin_parse();
var cfg_dict = cs~load_ref();
cfg_dict~idict_set_ref(32, index, value);
set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell());
}
(cell, int, int, cell) load_data() inline {
var cs = get_data().begin_parse();
var res = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256), cs~load_dict());
cs.end_parse();
return res;
}
() store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline {
set_data(begin_cell()
.store_ref(cfg_dict)
.store_uint(stored_seqno, 32)
.store_uint(public_key, 256)
.store_dict(vote_dict)
.end_cell());
}
;; (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price)
_ parse_vote_config(cell c) inline {
var cs = c.begin_parse();
throw_unless(44, cs~load_uint(8) == 0x36);
var res = (cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
cs.end_parse();
return res;
}
;; cfg_vote_setup#91 normal_params:^ConfigProposalSetup critical_params:^ConfigProposalSetup = ConfigVotingSetup;
_ get_vote_config_internal(int critical?, cell cparam11) inline_ref {
var cs = cparam11.begin_parse();
throw_unless(44, cs~load_uint(8) == 0x91);
if (critical?) {
cs~load_ref();
}
return parse_vote_config(cs.preload_ref());
}
_ get_vote_config(int critical?) inline {
return get_vote_config_internal(critical?, config_param(11));
}
(int, int) check_validator_set(cell vset) {
var cs = vset.begin_parse();
throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only
int utime_since = cs~load_uint(32);
int utime_until = cs~load_uint(32);
int total = cs~load_uint(16);
int main = cs~load_uint(16);
throw_unless(9, main > 0);
throw_unless(9, total >= main);
return (utime_since, utime_until);
}
() send_answer(addr, query_id, ans_tag, mode) impure {
;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
send_raw_message(begin_cell()
.store_uint(0x18, 6)
.store_slice(addr)
.store_uint(0, 5 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(ans_tag, 32)
.store_uint(query_id, 64)
.end_cell(), mode);
}
() send_confirmation(addr, query_id, ans_tag) impure inline {
return send_answer(addr, query_id, ans_tag, 64);
}
() send_error(addr, query_id, ans_tag) impure inline {
return send_answer(addr, query_id, ans_tag, 64);
}
;; forward a message to elector smart contract to make it upgrade its code
() change_elector_code(slice cs) impure {
var dest_addr = config_param(1).begin_parse().preload_uint(256);
var query_id = now();
send_raw_message(begin_cell()
.store_uint(0xc4ff, 17)
.store_uint(dest_addr, 256)
.store_grams(1 << 30) ;; ~ 1 Gram (will be returned back)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(0x4e436f64, 32) ;; action
.store_uint(query_id, 64)
.store_slice(cs)
.end_cell(), 0);
}
() after_code_upgrade(slice param, cont old_code) impure method_id(1666) {
}
_ perform_action(cfg_dict, public_key, action, cs) inline_ref {
if (action == 0x43665021) {
;; change one configuration parameter
var param_index = cs~load_int(32);
var param_value = cs~load_ref();
cs.end_parse();
cfg_dict~idict_set_ref(32, param_index, param_value);
return (cfg_dict, public_key);
} elseif (action == 0x4e436f64) {
;; change configuration smart contract code
var new_code = cs~load_ref();
set_code(new_code);
var old_code = get_c3();
set_c3(new_code.begin_parse().bless());
after_code_upgrade(cs, old_code);
throw(0);
return (cfg_dict, public_key);
} elseif (action == 0x50624b21) {
;; change configuration master public key
public_key = cs~load_uint(256);
cs.end_parse();
return (cfg_dict, public_key);
} elseif (action == 0x4e43ef05) {
;; change election smart contract code
change_elector_code(cs);
return (cfg_dict, public_key);
} else {
throw_if(32, action);
return (cfg_dict, public_key);
}
}
(cell, int, cell) get_current_vset() inline_ref {
var vset = config_param(34);
var cs = begin_parse(vset);
;; validators_ext#12 utime_since:uint32 utime_until:uint32
;; total:(## 16) main:(## 16) { main <= total } { main >= 1 }
;; total_weight:uint64
throw_unless(40, cs~load_uint(8) == 0x12);
cs~skip_bits(32 + 32 + 16 + 16);
var (total_weight, dict) = (cs~load_uint(64), cs~load_dict());
cs.end_parse();
return (vset, total_weight, dict);
}
(slice, int) get_validator_descr(int idx) inline_ref {
var (vset, total_weight, dict) = get_current_vset();
var (value, _) = dict.udict_get?(16, idx);
return (value, total_weight);
}
(int, int) unpack_validator_descr(slice cs) inline {
;; ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey;
;; validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr;
;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr;
throw_unless(41, (cs~load_uint(8) & ~ 0x20) == 0x53);
throw_unless(41, cs~load_uint(32) == 0x8e81278a);
return (cs~load_uint(256), cs~load_uint(64));
}
;; cfg_proposal#f3 param_id:int32 param_value:(Maybe ^Cell) if_hash_equal:(Maybe uint256)
;; c -> (param-id param-cell maybe-hash)
(int, cell, int) parse_config_proposal(cell c) inline_ref {
var cs = c.begin_parse();
throw_unless(44, cs~load_int(8) == 0xf3 - 0x100);
var (id, val, hash) = (cs~load_int(32), cs~load_maybe_ref(), cs~load_int(1));
if (hash) {
hash = cs~load_uint(256);
} else {
hash = -1;
}
cs.end_parse();
return (id, val, hash);
}
(cell, int, cell) accept_proposal(cell cfg_dict, cell proposal, int critical?) inline_ref {
var (param_id, param_val, req_hash) = parse_config_proposal(proposal);
cell cur_val = cfg_dict.idict_get_ref(32, param_id);
int cur_hash = null?(cur_val) ? 0 : cell_hash(cur_val);
if ((cur_hash != req_hash) & (req_hash >= 0)) {
;; current value has incorrect hash, do not apply changes
return (cfg_dict, 0, null());
}
cell mparams = cfg_dict.idict_get_ref(32, 9); ;; mandatory parameters
var (_, found?) = mparams.idict_get?(32, param_id);
if (found? & param_val.null?()) {
;; cannot set a mandatory parameter to (null)
return (cfg_dict, 0, null());
}
cell cparams = cfg_dict.idict_get_ref(32, 10); ;; critical parameters
(_, found?) = cparams.idict_get?(32, param_id);
if (found? < critical?) {
;; trying to set a critical parameter after a non-critical voting
return (cfg_dict, 0, null());
}
;; CHANGE ONE CONFIGURATION PARAMETER (!)
cfg_dict~idict_set_ref(32, param_id, param_val);
return (cfg_dict, param_id, param_val);
}
(cell, int) perform_proposed_action(cell cfg_dict, int public_key, int param_id, cell param_val) inline_ref {
if (param_id == -999) {
;; appoint or depose dictator
return (cfg_dict, param_val.null?() ? 0 : param_val.begin_parse().preload_uint(256));
}
if (param_val.null?()) {
return (cfg_dict, public_key);
}
if (param_id == -1000) {
;; upgrade code
var cs = param_val.begin_parse();
var new_code = cs~load_ref();
set_code(new_code);
var old_code = get_c3();
set_c3(new_code.begin_parse().bless());
after_code_upgrade(cs, old_code);
throw(0);
return (cfg_dict, public_key);
}
if (param_id == -1001) {
;; update elector code
var cs = param_val.begin_parse();
change_elector_code(cs);
}
return (cfg_dict, public_key);
}
;; cfg_proposal_status#ce expires:uint32 proposal:^ConfigProposal is_critical:Bool
;; voters:(HashmapE 16 True) remaining_weight:int64 validator_set_id:uint256
;; rounds_remaining:uint8 wins:uint8 losses:uint8 = ConfigProposalStatus;
(int, cell, int, cell, int, int, slice) unpack_proposal_status(slice cs) inline_ref {
throw_unless(44, cs~load_int(8) == 0xce - 0x100);
return (cs~load_uint(32), cs~load_ref(), cs~load_int(1), cs~load_dict(), cs~load_int(64), cs~load_uint(256), cs);
}
slice update_proposal_status(slice rest, int weight_remaining, int critical?) inline_ref {
var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _) = get_vote_config(critical?);
var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
losses -= (weight_remaining >= 0);
if (losses > max_losses) {
;; lost too many times
return null();
}
rounds_remaining -= 1;
if (rounds_remaining < 0) {
;; existed for too many rounds
return null();
}
return begin_cell()
.store_uint(rounds_remaining, 8)
.store_uint(wins, 8)
.store_uint(losses, 8)
.end_cell().begin_parse();
}
builder begin_pack_proposal_status(int expires, cell proposal, int critical?, cell voters, int weight_remaining, int vset_id) inline {
return begin_cell()
.store_int(0xce - 0x100, 8)
.store_uint(expires, 32)
.store_ref(proposal)
.store_int(critical?, 1)
.store_dict(voters)
.store_int(weight_remaining, 64)
.store_uint(vset_id, 256);
}
(cell, cell, int) register_vote(vote_dict, phash, idx, weight) inline_ref {
var (pstatus, found?) = vote_dict.udict_get?(256, phash);
ifnot (found?) {
;; config proposal not found
return (vote_dict, null(), -1);
}
var (cur_vset, total_weight, _) = get_current_vset();
int cur_vset_id = cur_vset.cell_hash();
var (expires, proposal, critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
if (expires <= now()) {
;; config proposal expired, delete and report not found
vote_dict~udict_delete?(256, phash);
return (vote_dict, null(), -1);
}
if (vset_id != cur_vset_id) {
;; config proposal belongs to a previous validator set
vset_id = cur_vset_id;
rest = update_proposal_status(rest, weight_remaining, critical?);
voters = null();
weight_remaining = muldiv(total_weight, 3, 4);
}
if (rest.null?()) {
;; discard proposal (existed for too many rounds, or too many losses)
vote_dict~udict_delete?(256, phash);
return (vote_dict, null(), -1);
}
var (_, found?) = voters.udict_get?(16, idx);
if (found?) {
;; already voted for this proposal, ignore vote
return (vote_dict, null(), -2);
}
;; register vote
voters~udict_set_builder(16, idx, begin_cell().store_uint(now(), 32));
int old_wr = weight_remaining;
weight_remaining -= weight;
if ((weight_remaining ^ old_wr) >= 0) {
;; not enough votes, or proposal already accepted in this round
;; simply update weight_remaining
vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id).store_slice(rest));
return (vote_dict, null(), 2);
}
;; proposal wins in this round
var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _) = get_vote_config(critical?);
var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
wins += 1;
if (wins >= min_wins) {
;; proposal is accepted, remove and process
vote_dict~udict_delete?(256, phash);
return (vote_dict, proposal, 6 - critical?);
}
;; update proposal info
vote_dict~udict_set_builder(256, phash,
begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id)
.store_uint(rounds_remaining, 8)
.store_uint(wins, 8)
.store_uint(losses, 8));
return (vote_dict, null(), 2);
}
int proceed_register_vote(phash, idx, weight) impure inline_ref {
var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
(vote_dict, var accepted_proposal, var status) = register_vote(vote_dict, phash, idx, weight);
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
ifnot (accepted_proposal.null?()) {
var critical? = 6 - status;
(cfg_dict, var param_id, var param_val) = accept_proposal(cfg_dict, accepted_proposal, critical?);
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
if (param_id) {
commit();
(cfg_dict, public_key) = perform_proposed_action(cfg_dict, public_key, param_id, param_val);
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
}
}
return status;
}
(slice, int) scan_proposal(int phash, slice pstatus) inline_ref {
var (cur_vset, total_weight, _) = get_current_vset();
int cur_vset_id = cur_vset.cell_hash();
var (expires, proposal, critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
if (expires <= now()) {
;; config proposal expired, delete
return (null(), true);
}
if (vset_id == cur_vset_id) {
;; config proposal already processed or voted for in this round, change nothing
return (pstatus, false);
}
;; config proposal belongs to a previous validator set
vset_id = cur_vset_id;
rest = update_proposal_status(rest, weight_remaining, critical?);
voters = null();
weight_remaining = muldiv(total_weight, 3, 4);
if (rest.null?()) {
;; discard proposal (existed for too many rounds, or too many losses)
return (null(), true);
}
;; return updated proposal
return (begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id).store_slice(rest).end_cell().begin_parse(), true);
}
cell scan_random_proposal(cell vote_dict) inline_ref {
var (phash, pstatus, found?) = vote_dict.udict_get_nexteq?(256, random());
ifnot (found?) {
return vote_dict;
}
(pstatus, var changed?) = scan_proposal(phash, pstatus);
if (changed?) {
if (pstatus.null?()) {
vote_dict~udict_delete?(256, phash);
} else {
vote_dict~udict_set(256, phash, pstatus);
}
}
return vote_dict;
}
int register_voting_proposal(slice cs, int msg_value) impure inline_ref {
var (expire_at, proposal, critical?) = (cs~load_uint(32), cs~load_ref(), cs~load_int(1));
if (expire_at >> 30) {
expire_at -= now();
}
var (param_id, param_val, hash) = parse_config_proposal(proposal);
if (hash >= 0) {
cell cur_val = config_param(param_id);
int cur_hash = null?(cur_val) ? 0 : cell_hash(cur_val);
if (cur_hash != hash) {
hash = -0xe2646356; ;; bad current value
}
} else {
var m_params = config_param(9);
var (_, found?) = m_params.idict_get?(32, param_id);
if (found?) {
hash = -0xcd506e6c; ;; cannot set mandatory parameter to null
}
}
if (param_val.cell_depth() >= 256) {
hash = -0xc2616456; ;; bad value
}
if (hash < -1) {
return hash; ;; return error if any
}
ifnot (critical?) {
var crit_params = config_param(10);
var (_, found?) = crit_params.idict_get?(32, param_id);
if (found?) {
hash = -0xc3726954; ;; trying to set a critical parameter without critical flag
}
}
if (hash < -1) {
return hash;
}
;; obtain vote proposal configuration
var vote_cfg = get_vote_config(critical?);
var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price) = vote_cfg;
if (expire_at < min_store_sec) {
return -0xc5787069; ;; expired
}
expire_at = min(expire_at, max_store_sec);
;; compute price
var (_, bits, refs) = compute_data_size(param_val, 1024);
var pps = bit_price * (bits + 1024) + cell_price * (refs + 2);
var price = pps * expire_at;
expire_at += now();
var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
int phash = proposal.cell_hash();
var (pstatus, found?) = vote_dict.udict_get?(256, phash);
if (found?) {
;; proposal already exists; we can only extend it
var (expires, r_proposal, r_critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
if (r_critical? != critical?) {
return -0xc3726955; ;; cannot upgrade critical parameter to non-critical...
}
if (expires >= expire_at) {
return -0xc16c7245; ;; proposal already exists
}
;; recompute price
price = pps * (expire_at - expires + 16384);
if (msg_value - price < (1 << 30)) {
return -0xf0617924; ;; need more money
}
;; update expiration time
vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expire_at, r_proposal, r_critical?, voters, weight_remaining, vset_id).store_slice(rest));
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
return price;
}
if (msg_value - price < (1 << 30)) {
return -0xf0617924; ;; need more money
}
;; obtain current validator set data
var (vset, total_weight, _) = get_current_vset();
int weight_remaining = muldiv(total_weight, 3, 4);
;; create new proposal
vote_dict~udict_set_builder(256, phash,
begin_pack_proposal_status(expire_at, proposal, critical?, null(), weight_remaining, vset.cell_hash())
.store_uint(max_tot_rounds, 8).store_uint(0, 16));
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
return price;
}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
var cs = in_msg_cell.begin_parse();
var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
var s_addr = cs~load_msg_addr();
(int src_wc, int src_addr) = s_addr.parse_std_addr();
if ((src_wc + 1) | (flags & 1) | in_msg.slice_empty?()) {
;; source not in masterchain, or a bounced message, or a simple transfer
return ();
}
int tag = in_msg~load_uint(32);
int query_id = in_msg~load_uint(64);
if (tag == 0x4e565354) {
;; set next validator set
var vset = in_msg~load_ref();
in_msg.end_parse();
var elector_param = config_param(1);
var elector_addr = cell_null?(elector_param) ? -1 : elector_param.begin_parse().preload_uint(256);
var ok = false;
if (src_addr == elector_addr) {
;; message from elector smart contract
;; set next validator set
(var t_since, var t_until) = check_validator_set(vset);
var t = now();
ok = (t_since > t) & (t_until > t_since);
}
if (ok) {
set_conf_param(36, vset);
;; send confirmation
return send_confirmation(s_addr, query_id, 0xee764f4b);
} else {
return send_error(s_addr, query_id, 0xee764f6f);
}
}
if (tag == 0x6e565052) {
;; new voting proposal
var price = register_voting_proposal(in_msg, msg_value);
int mode = 64;
int ans_tag = - price;
if (price >= 0) {
;; ok, debit price
raw_reserve(price, 4);
ans_tag = 0xee565052;
mode = 128;
}
return send_answer(s_addr, query_id, ans_tag, mode);
}
if (tag == 0x566f7465) {
;; vote for a configuration proposal
var signature = in_msg~load_bits(512);
var msg_body = in_msg;
var (sign_tag, idx, phash) = (in_msg~load_uint(32), in_msg~load_uint(16), in_msg~load_uint(256));
in_msg.end_parse();
throw_unless(37, sign_tag == 0x566f7445);
var (vdescr, total_weight) = get_validator_descr(idx);
var (val_pubkey, weight) = unpack_validator_descr(vdescr);
throw_unless(34, check_data_signature(msg_body, signature, val_pubkey));
int res = proceed_register_vote(phash, idx, weight);
return send_confirmation(s_addr, query_id, res + 0xd6745240);
}
;; if tag is non-zero and its higher bit is zero, throw an exception (the message is an unsupported query)
;; to bounce message back to sender
throw_unless(37, (tag == 0) | (tag & (1 << 31)));
;; do nothing for other internal messages
}
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512);
var cs = in_msg;
int action = cs~load_uint(32);
int msg_seqno = cs~load_uint(32);
var valid_until = cs~load_uint(32);
throw_if(35, valid_until < now());
throw_if(39, slice_depth(cs) > 128);
var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
throw_unless(33, msg_seqno == stored_seqno);
if (action == 0x566f7465) {
;; vote for a configuration proposal
var (idx, phash) = (cs~load_uint(16), cs~load_uint(256));
cs.end_parse();
var (vdescr, total_weight) = get_validator_descr(idx);
var (val_pubkey, weight) = unpack_validator_descr(vdescr);
throw_unless(34, check_data_signature(in_msg, signature, val_pubkey));
accept_message();
stored_seqno = (stored_seqno + 1) % (1 << 32);
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
commit();
proceed_register_vote(phash, idx, weight);
return ();
}
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
accept_message();
stored_seqno = (stored_seqno + 1) % (1 << 32);
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
commit();
(cfg_dict, public_key) = perform_action(cfg_dict, public_key, action, cs);
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
}
() run_ticktock(int is_tock) impure {
var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
int kl = 32;
var next_vset = cfg_dict.idict_get_ref(kl, 36);
var updated? = false;
ifnot (next_vset.null?()) {
;; check whether we have to set next_vset as the current validator set
var ds = next_vset.begin_parse();
if (ds.slice_bits() >= 40) {
var tag = ds~load_uint(8);
var since = ds.preload_uint(32);
if ((since <= now()) & (tag == 0x12)) {
;; next validator set becomes active!
var cur_vset = cfg_dict~idict_set_get_ref(kl, 34, next_vset); ;; next_vset -> cur_vset
cfg_dict~idict_set_get_ref(kl, 32, cur_vset); ;; cur_vset -> prev_vset
cfg_dict~idict_delete?(kl, 36); ;; (null) -> next_vset
updated? = true;
}
}
}
ifnot (updated?) {
;; if nothing has been done so far, scan a random voting proposal instead
vote_dict = scan_random_proposal(vote_dict);
}
;; save data and return
return store_data(cfg_dict, stored_seqno, public_key, vote_dict);
}
int seqno() method_id {
return get_data().begin_parse().preload_uint(32);
}
_ unpack_proposal(slice pstatus) inline_ref {
(int expires, cell proposal, int critical?, cell voters, int weight_remaining, int vset_id, slice rest) = unpack_proposal_status(pstatus);
var voters_list = null();
var voter_id = (1 << 32);
do {
(voter_id, _, var f) = voters.udict_get_prev?(16, voter_id);
if (f) {
voters_list = cons(voter_id, voters_list);
}
} until (~ f);
var (rounds_remaining, losses, wins) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
rest.end_parse();
var (param_id, param_val, param_hash) = parse_config_proposal(proposal);
return [expires, critical?, [param_id, param_val, param_hash], vset_id, voters_list, weight_remaining, rounds_remaining, losses, wins];
}
_ get_proposal(int phash) method_id {
(_, _, _, var vote_dict) = load_data();
var (pstatus, found?) = vote_dict.udict_get?(256, phash);
ifnot (found?) {
return null();
}
return unpack_proposal(pstatus);
}
_ list_proposals() method_id {
(_, _, _, var vote_dict) = load_data();
var phash = (1 << 255) + ((1 << 255) - 1);
var list = null();
do {
(phash, var pstatus, var f) = vote_dict.udict_get_prev?(256, phash);
if (f) {
list = cons([phash, unpack_proposal(pstatus)], list);
}
} until (~ f);
return list;
}
_ proposal_storage_price(int critical?, int seconds, int bits, int refs) method_id {
var cfg_dict = get_data().begin_parse().preload_ref();
var cparam11 = cfg_dict.idict_get_ref(32, 11);
var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price) = get_vote_config_internal(critical?, cparam11);
if (seconds < min_store_sec) {
return -1;
}
seconds = min(seconds, max_store_sec);
return (bit_price * (bits + 1024) + cell_price * (refs + 2)) * seconds;
}

View file

@ -0,0 +1,208 @@
;; Standard library for funC
;;
forall X -> tuple cons(X head, tuple tail) asm "CONS";
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
int cell_hash(cell c) asm "HASHCU";
int slice_hash(slice s) asm "HASHSU";
int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
cont get_c3() impure asm "c3 PUSH";
() set_c3(cont c) impure asm "c3 POP";
cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
cell preload_ref(slice s) asm "PLDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
builder store_slice(builder b, slice s) asm "STSLICER";
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_dict(builder b, cell c) asm(c b) "STDICT";
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
tuple parse_addr(slice s) asm "PARSEMSGADDR";
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";
int random() impure asm "RANDU256";
int rand(int range) impure asm "RAND";
int get_seed() impure asm "RANDSEED";
int set_seed() impure asm "SETRAND";
() randomize(int x) impure asm "ADDRAND";
() randomize_lt() impure asm "LTIME" "ADDRAND";

View file

@ -0,0 +1,110 @@
const int one_month = 2592000; ;; 1 month in seconds = 60 * 60 * 24 * 30
const int one_year = 31622400; ;; 1 year in seconds = 60 * 60 * 24 * 366
const int auction_start_time = 1659171600; ;; GMT: Monday, 30 July 2022 г., 09:00:00
const int one_ton = 1000000000;
const int dns_next_resolver_prefix = 0xba93; ;; dns_next_resolver prefix - https://github.com/ton-blockchain/ton/blob/7e3df93ca2ab336716a230fceb1726d81bac0a06/crypto/block/block.tlb#L819
const int dns_config_id = 80; ;; dns black list config param; in testnet -80
const int op::fill_up = 0x370fec51;
const int op::outbid_notification = 0x557cea20;
const int op::change_dns_record = 0x4eb1f0f9;
const int op::process_governance_decision = 0x44beae41;
const int op::dns_balance_release = 0x4ed14b65;
int mod(int x, int y) asm "MOD";
slice zero_address() {
return begin_cell().store_uint(0, 2).end_cell().begin_parse();
}
;; "ton\0test\0" -> "ton"
int get_top_domain_bits(slice domain) {
int i = 0;
int need_break = 0;
do {
int char = domain~load_uint(8); ;; we do not check domain.length because it MUST contains \0 character
need_break = char == 0;
if (~ need_break) {
i += 8;
}
} until (need_break);
throw_if(201, i == 0); ;; starts with \0
return i;
}
slice read_domain_from_comment(slice in_msg_body) {
int need_break = 0;
builder result = begin_cell();
do {
result = result.store_slice(in_msg_body~load_bits(in_msg_body.slice_bits()));
int refs_len = in_msg_body.slice_refs();
need_break = refs_len == 0;
if (~ need_break) {
throw_unless(202, refs_len == 1);
in_msg_body = in_msg_body~load_ref().begin_parse();
}
} until (need_break);
return result.end_cell().begin_parse();
}
int check_domain_string(slice domain) {
int i = 0;
int len = slice_bits(domain);
int need_break = 0;
do {
need_break = i == len;
if (~ need_break) {
int char = domain~load_uint(8);
;; we can do it because additional UTF-8 character's octets >= 128 -- https://www.ietf.org/rfc/rfc3629.txt
int is_hyphen = (char == 45);
int valid_char = (is_hyphen & (i > 0) & (i < len - 8)) | ((char >= 48) & (char <= 57)) | ((char >= 97) & (char <= 122)); ;; '-' or 0-9 or a-z
need_break = ~ valid_char;
if (~ need_break) {
i += 8;
}
}
} until (need_break);
return i == len;
}
(int, int) get_min_price_config(int domain_char_count) {
if (domain_char_count == 4) {
return (1000, 100);
}
if (domain_char_count == 5) {
return (500, 50);
}
if (domain_char_count == 6) {
return (400, 40);
}
if (domain_char_count == 7) {
return (300, 30);
}
if (domain_char_count == 8) {
return (200, 20);
}
if (domain_char_count == 9) {
return (100, 10);
}
if (domain_char_count == 10) {
return (50, 5);
}
return (10, 1);
}
int get_min_price(int domain_bits_length, int now_time) {
(int start_min_price, int end_min_price) = get_min_price_config(domain_bits_length / 8);
start_min_price *= one_ton;
end_min_price *= one_ton;
int seconds = now_time - auction_start_time;
int months = seconds / one_month;
if (months > 21) {
return end_min_price;
}
repeat (months) {
start_min_price = start_min_price * 90 / 100;
}
return start_min_price;
}

View file

@ -0,0 +1,144 @@
;; DNS resolver smart contract (implements NFT Collection interface)
#include "stdlib.fc";
#include "params.fc";
#include "dns-utils.fc";
;; storage scheme
;; storage#_ collection_content:^Cell
;; nft_item_code:^Cell
;; = Storage;
(cell, cell) load_data() inline {
var ds = get_data().begin_parse();
return (
ds~load_ref(), ;; content
ds~load_ref() ;; nft_item_code
);
}
() save_data(cell content, cell nft_item_code) impure inline {
set_data(begin_cell()
.store_ref(content)
.store_ref(nft_item_code)
.end_cell());
}
cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {
cell data = begin_cell().store_uint(item_index, 256).store_slice(my_address()).end_cell();
return begin_cell().store_uint(0, 2).store_dict(nft_item_code).store_dict(data).store_uint(0, 1).end_cell();
}
slice calculate_nft_item_address(int wc, cell state_init) {
return begin_cell()
.store_uint(4, 3)
.store_int(wc, 8)
.store_uint(cell_hash(state_init), 256)
.end_cell()
.begin_parse();
}
() deploy_nft_item(int item_index, cell nft_item_code, cell nft_content) impure {
cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);
slice nft_address = calculate_nft_item_address(workchain(), state_init);
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(nft_address)
.store_coins(0)
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
.store_ref(state_init)
.store_ref(nft_content);
send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message, fee deducted from amount
}
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) { ;; bounce back empty messages
throw(0xffff);
}
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) { ;; ignore all bounced messages
return ();
}
slice sender_address = cs~load_msg_addr();
int op = in_msg_body~load_uint(32);
var (content, nft_item_code) = load_data();
if (op == 0) { ;; deploy new nft
int now_time = now();
throw_unless(199, now_time > auction_start_time); ;; start of auction
slice domain = read_domain_from_comment(in_msg_body);
int len = slice_bits(domain);
throw_unless(200, len > 3 * 8); ;; minimum 4 characters
throw_unless(201, len <= 126 * 8); ;; maxmimum 126 characters
throw_unless(202, mod(len, 8) == 0);
throw_unless(203, check_domain_string(domain));
int min_price = get_min_price(len, now_time);
throw_unless(204, msg_value >= min_price);
int item_index = slice_hash(domain);
cell config_cell = config_param(dns_config_id);
if (~ cell_null?(config_cell)) {
slice config_cs = config_cell.begin_parse();
cell config = config_cs~load_dict();
(slice config_value, int found) = config.udict_get?(256, item_index);
throw_if(205, found);
}
cell nft_content = begin_cell()
.store_slice(sender_address)
.store_ref(begin_cell().store_slice(domain).end_cell())
.end_cell();
deploy_nft_item(item_index, nft_item_code, nft_content);
return ();
}
if (op == op::fill_up) { ;; just fill-up balance
return ();
}
throw(0xffff);
}
;; Get methods
(int, cell, slice) get_collection_data() method_id {
var (content, nft_item_code) = load_data();
return (-1, content, zero_address());
}
slice get_nft_address_by_index(int index) method_id {
var (content, nft_item_code) = load_data();
cell state_init = calculate_nft_item_state_init(index, nft_item_code);
return calculate_nft_item_address(workchain(), state_init);
}
cell get_nft_content(int index, cell individual_nft_content) method_id {
return individual_nft_content;
}
(int, cell) dnsresolve(slice subdomain, int category) method_id {
throw_unless(70, mod(slice_bits(subdomain), 8) == 0);
int starts_with_zero_byte = subdomain.preload_int(8) == 0;
if (starts_with_zero_byte & (slice_bits(subdomain) == 8)) { ;; "." requested
return (8, null()); ;; resolved but no dns-records
}
if (starts_with_zero_byte) {
subdomain~load_uint(8);
}
int top_subdomain_bits = get_top_domain_bits(subdomain);
slice top_subdomain = subdomain~load_bits(top_subdomain_bits);
int item_index = slice_hash(top_subdomain);
cell result = begin_cell()
.store_uint(dns_next_resolver_prefix, 16)
.store_slice(get_nft_address_by_index(item_index))
.end_cell();
return (top_subdomain_bits + (starts_with_zero_byte ? 8 : 0), result);
}

View file

@ -0,0 +1,6 @@
int workchain() asm "0 PUSHINT";
() force_chain(slice addr) impure {
(int wc, _) = parse_std_addr(addr);
throw_unless(333, wc == workchain());
}

View file

@ -0,0 +1,216 @@
;; Standard library for funC
;;
forall X -> tuple cons(X head, tuple tail) asm "CONS";
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
int cell_hash(cell c) asm "HASHCU";
int slice_hash(slice s) asm "HASHSU";
int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
cont get_c3() impure asm "c3 PUSH";
() set_c3(cont c) impure asm "c3 POP";
cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
cell preload_ref(slice s) asm "PLDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
builder store_slice(builder b, slice s) asm "STSLICER";
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_dict(builder b, cell c) asm(c b) "STDICT";
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
tuple parse_addr(slice s) asm "PARSEMSGADDR";
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF" "NULLSWAPIFNOT";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF" "NULLSWAPIFNOT";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";
int random() impure asm "RANDU256";
int rand(int range) impure asm "RAND";
int get_seed() impure asm "RANDSEED";
int set_seed() impure asm "SETRAND";
() randomize(int x) impure asm "ADDRAND";
() randomize_lt() impure asm "LTIME" "ADDRAND";
builder store_coins(builder b, int x) asm "STVARUINT16";
(slice, int) load_coins(slice s) asm( -> 1 0) "LDVARUINT16";
int equal_slices (slice a, slice b) asm "SDEQ";
int builder_null?(builder b) asm "ISNULL";
builder store_builder(builder to, builder from) asm "STBR";

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,208 @@
;; Standard library for funC
;;
forall X -> tuple cons(X head, tuple tail) asm "CONS";
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
int cell_hash(cell c) asm "HASHCU";
int slice_hash(slice s) asm "HASHSU";
int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
cont get_c3() impure asm "c3 PUSH";
() set_c3(cont c) impure asm "c3 POP";
cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
cell preload_ref(slice s) asm "PLDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
builder store_slice(builder b, slice s) asm "STSLICER";
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_dict(builder b, cell c) asm(c b) "STDICT";
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
tuple parse_addr(slice s) asm "PARSEMSGADDR";
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";
int random() impure asm "RANDU256";
int rand(int range) impure asm "RAND";
int get_seed() impure asm "RANDSEED";
int set_seed() impure asm "SETRAND";
() randomize(int x) impure asm "ADDRAND";
() randomize_lt() impure asm "LTIME" "ADDRAND";

View file

@ -0,0 +1,382 @@
;; Simple wallet smart contract
#include "stdlib.fc";
(int, int) get_bridge_config() impure inline_ref {
cell bridge_config = config_param(71);
if (bridge_config.cell_null?()) {
bridge_config = config_param(-71);
}
if (bridge_config.cell_null?()) {
return (0, 0);
}
slice ds = bridge_config.begin_parse();
if (ds.slice_bits() < 512) {
return (0, 0);
}
;; wc always equals to -1
int bridge_address = ds~load_uint(256);
int oracles_address = ds~load_uint(256);
return (bridge_address, oracles_address);
}
_ unpack_state() inline_ref {
var ds = begin_parse(get_data());
var res = (ds~load_uint(32), ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict(), ds~load_uint(32));
ds.end_parse();
return res;
}
_ pack_state(cell pending_queries, cell owner_infos, int last_cleaned, int k, int n, int wallet_id, int spend_delay) inline_ref {
return begin_cell()
.store_uint(wallet_id, 32)
.store_uint(n, 8)
.store_uint(k, 8)
.store_uint(last_cleaned, 64)
.store_dict(owner_infos)
.store_dict(pending_queries)
.store_uint(spend_delay,32)
.end_cell();
}
_ pack_owner_info(int public_key, int flood) inline_ref {
return begin_cell()
.store_uint(public_key, 256)
.store_uint(flood, 8);
}
_ unpack_owner_info(slice cs) inline_ref {
return (cs~load_uint(256), cs~load_uint(8));
}
(int, int) check_signatures(cell public_keys, cell signatures, int hash, int cnt_bits) inline_ref {
int cnt = 0;
do {
slice cs = signatures.begin_parse();
slice signature = cs~load_bits(512);
int i = cs~load_uint(8);
signatures = cs~load_dict();
(slice public_key, var found?) = public_keys.udict_get?(8, i);
throw_unless(37, found?);
throw_unless(38, check_signature(hash, signature, public_key.preload_uint(256)));
int mask = (1 << i);
int old_cnt_bits = cnt_bits;
cnt_bits |= mask;
int should_check = cnt_bits != old_cnt_bits;
cnt -= should_check;
} until (cell_null?(signatures));
return (cnt, cnt_bits);
}
() recv_internal(slice in_msg) impure {
;; do nothing for internal messages
}
(int, cell, int, int) parse_msg(slice in_msg) inline_ref {
int mode = in_msg~load_uint(8);
var msg = in_msg~load_ref();
var msg' = msg.begin_parse();
msg'~load_uint(4); ;; flags
msg'~load_msg_addr(); ;; src
(int wc, int addr) = parse_std_addr(msg'~load_msg_addr()); ;; dest
return (mode, msg, wc, addr);
}
() check_proposed_query(slice in_msg) impure inline {
throw_unless(43, (slice_refs(in_msg) == 1) & (slice_bits(in_msg) == 8));
(_, _, int wc, _) = parse_msg(in_msg);
wc~impure_touch();
}
(int, int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?, int root_i) inline_ref {
if (found?) {
throw_unless(35, query~load_int(1));
(int creator_i, int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(8), query~load_uint(n), query);
throw_unless(36, slice_hash(msg) == slice_hash(in_msg));
return (creator_i, cnt, cnt_bits, msg);
}
check_proposed_query(in_msg);
return (root_i, 0, 0, in_msg);
}
(cell, ()) dec_flood(cell owner_infos, int creator_i) {
(slice owner_info, var found?) = owner_infos.udict_get?(8, creator_i);
(int public_key, int flood) = unpack_owner_info(owner_info);
owner_infos~udict_set_builder(8, creator_i, pack_owner_info(public_key, flood - 1));
return (owner_infos, ());
}
() try_init() impure inline_ref {
;; first query without signatures is always accepted
(int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries, int spend_delay) = unpack_state();
throw_if(37, last_cleaned);
accept_message();
set_data(pack_state(pending_queries, owner_infos, 1, k, n, wallet_id, spend_delay));
}
(cell, cell) update_pending_queries(cell pending_queries, cell owner_infos, slice msg, int query_id, int creator_i, int cnt, int cnt_bits, int n, int k) impure inline_ref {
if (cnt >= k) {
accept_message();
(int bridge_address, int oracles_address) = get_bridge_config();
(_, int my_addr) = parse_std_addr(my_address());
var (mode, msg', wc, addr) = parse_msg(msg);
if ( ((wc == -1) & (addr == bridge_address)) | (oracles_address != my_addr) ) {
send_raw_message(msg', mode);
}
pending_queries~udict_set_builder(64, query_id, begin_cell().store_int(0, 1));
owner_infos~dec_flood(creator_i);
} else {
pending_queries~udict_set_builder(64, query_id, begin_cell()
.store_uint(1, 1)
.store_uint(creator_i, 8)
.store_uint(cnt, 8)
.store_uint(cnt_bits, n)
.store_slice(msg));
}
return (pending_queries, owner_infos);
}
(int, int) calc_boc_size(int cells, int bits, slice root) {
cells += 1;
bits += root.slice_bits();
while (root.slice_refs()) {
(cells, bits) = calc_boc_size(cells, bits, root~load_ref().begin_parse());
}
return (cells, bits);
}
() recv_external(slice in_msg) impure {
;; empty message triggers init
if (slice_empty?(in_msg)) {
return try_init();
}
;; Check root signature
slice root_signature = in_msg~load_bits(512);
int root_hash = slice_hash(in_msg);
int root_i = in_msg~load_uint(8);
(int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries, int spend_delay) = unpack_state();
throw_unless(38, now() > spend_delay);
last_cleaned -= last_cleaned == 0;
(slice owner_info, var found?) = owner_infos.udict_get?(8, root_i);
throw_unless(31, found?);
(int public_key, int flood) = unpack_owner_info(owner_info);
throw_unless(32, check_signature(root_hash, root_signature, public_key));
cell signatures = in_msg~load_dict();
var hash = slice_hash(in_msg);
int query_wallet_id = in_msg~load_uint(32);
throw_unless(42, query_wallet_id == wallet_id);
int query_id = in_msg~load_uint(64);
(int cnt, int bits) = calc_boc_size(0, 0, in_msg);
throw_if(40, (cnt > 8) | (bits > 2048));
(slice query, var found?) = pending_queries.udict_get?(64, query_id);
ifnot (found?) {
flood += 1;
throw_if(39, flood > 10);
}
var bound = (now() << 32);
throw_if(33, query_id < bound);
(int creator_i, int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?, root_i);
int mask = 1 << root_i;
throw_if(34, cnt_bits & mask);
cnt_bits |= mask;
cnt += 1;
throw_if(41, ~ found? & (cnt < k) & (bound + ((60 * 60) << 32) > query_id));
set_gas_limit(100000);
ifnot (found?) {
owner_infos~udict_set_builder(8, root_i, pack_owner_info(public_key, flood));
}
(pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k);
set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id, spend_delay));
commit();
int need_save = 0;
ifnot (cell_null?(signatures) | (cnt >= k)) {
(int new_cnt, cnt_bits) = check_signatures(owner_infos, signatures, hash, cnt_bits);
cnt += new_cnt;
(pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k);
need_save = -1;
}
accept_message();
bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
int old_last_cleaned = last_cleaned;
do {
var (pending_queries', i, query, f) = pending_queries.udict_delete_get_min(64);
f~touch();
if (f) {
f = (i < bound);
}
if (f) {
if (query~load_int(1)) {
owner_infos~dec_flood(query~load_uint(8));
}
pending_queries = pending_queries';
last_cleaned = i;
need_save = -1;
}
} until (~ f);
if (need_save) {
set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id, spend_delay));
}
}
;; Get methods
;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten)
(int, int) get_query_state(int query_id) method_id {
(_, int n, _, int last_cleaned, _, cell pending_queries, _) = unpack_state();
(slice cs, var found) = pending_queries.udict_get?(64, query_id);
if (found) {
if (cs~load_int(1)) {
cs~load_uint(8 + 8);
return (0, cs~load_uint(n));
} else {
return (-1, 0);
}
} else {
return (-(query_id <= last_cleaned), 0);
}
}
int processed?(int query_id) method_id {
(int x, _) = get_query_state(query_id);
return x;
}
cell create_init_state(int wallet_id, int n, int k, cell owners_info, int spend_delay) method_id {
return pack_state(new_dict(), owners_info, 0, k, n, wallet_id, spend_delay);
}
cell merge_list(cell a, cell b) {
if (cell_null?(a)) {
return b;
}
if (cell_null?(b)) {
return a;
}
slice as = a.begin_parse();
if (as.slice_refs() != 0) {
cell tail = merge_list(as~load_ref(), b);
return begin_cell().store_slice(as).store_ref(tail).end_cell();
}
as~skip_last_bits(1);
;; as~skip_bits(1);
return begin_cell().store_slice(as).store_dict(b).end_cell();
}
cell get_public_keys() method_id {
(_, _, _, _, cell public_keys, _, _) = unpack_state();
return public_keys;
}
(int, int) check_query_signatures(cell query) method_id {
slice cs = query.begin_parse();
slice root_signature = cs~load_bits(512);
int root_hash = slice_hash(cs);
int root_i = cs~load_uint(8);
cell public_keys = get_public_keys();
(slice public_key, var found?) = public_keys.udict_get?(8, root_i);
throw_unless(31, found?);
throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256)));
int mask = 1 << root_i;
cell signatures = cs~load_dict();
if (cell_null?(signatures)) {
return (1, mask);
}
(int cnt, mask) = check_signatures(public_keys, signatures, slice_hash(cs), mask);
return (cnt + 1, mask);
}
int message_signed_by_id?(int id, int query_id) method_id {
(_, int n, _, _, _, cell pending_queries, _) = unpack_state();
(var cs, var f) = pending_queries.udict_get?(64, query_id);
if (f) {
if (cs~load_int(1)) {
int cnt_bits = cs.skip_bits(8 + 8).preload_uint(n);
if (cnt_bits & (1 << id)) {
return -1;
}
return 0;
}
return -1;
}
return 0;
}
cell messages_by_mask(int mask) method_id {
(_, int n, _, _, _, cell pending_queries, _) = unpack_state();
int i = -1;
cell a = new_dict();
do {
(i, var cs, var f) = pending_queries.udict_get_next?(64, i);
if (f) {
if (cs~load_int(1)) {
int cnt_bits = cs.skip_bits(8 + 8).preload_uint(n);
if (cnt_bits & mask) {
a~udict_set_builder(64, i, begin_cell().store_slice(cs));
}
}
}
} until (~ f);
return a;
}
cell get_messages_unsigned_by_id(int id) method_id {
return messages_by_mask(1 << id);
}
cell get_messages_unsigned() method_id {
return messages_by_mask(~ 0);
}
(int, int) get_n_k() method_id {
(_, int n, int k, _, _, _, _) = unpack_state();
return (n, k);
}
cell merge_inner_queries(cell a, cell b) method_id {
slice ca = a.begin_parse();
slice cb = b.begin_parse();
cell list_a = ca~load_dict();
cell list_b = cb~load_dict();
throw_unless(31, slice_hash(ca) == slice_hash(cb));
return begin_cell()
.store_dict(merge_list(list_a, list_b))
.store_slice(ca)
.end_cell();
}
int get_lock_timeout() method_id {
(_, _, _, _, _, _, int spend_delay) = unpack_state();
return spend_delay;
}

View file

@ -0,0 +1,209 @@
;; Standard library for funC
;;
forall X -> tuple cons(X head, tuple tail) asm "CONS";
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
int cell_hash(cell c) asm "HASHCU";
int slice_hash(slice s) asm "HASHSU";
int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
cont get_c3() impure asm "c3 PUSH";
() set_c3(cont c) impure asm "c3 POP";
cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
cell preload_ref(slice s) asm "PLDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
builder store_slice(builder b, slice s) asm "STSLICER";
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_dict(builder b, cell c) asm(c b) "STDICT";
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
tuple parse_addr(slice s) asm "PARSEMSGADDR";
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";
int random() impure asm "RANDU256";
int rand(int range) impure asm "RAND";
int get_seed() impure asm "RANDSEED";
int set_seed() impure asm "SETRAND";
() randomize(int x) impure asm "ADDRAND";
() randomize_lt() impure asm "LTIME" "ADDRAND";

View file

@ -0,0 +1,110 @@
;; NFT marketplace smart contract v2
;; Extends wallet v3r2 & adds ability to deploy sales
#include "stdlib.fc";
;;
;; storage scheme
;;
;; storage#_ seqno:uint32 subwallet:uint32 public_key:uint25
;; = Storage;
;;
_ load_data() {
var ds = get_data().begin_parse();
return (
ds~load_uint(32), ;; seqno
ds~load_uint(32), ;; subwallet
ds~load_uint(256) ;; public_key
);
}
() store_data(var data) impure {
(
int seqno,
int subwallet,
int public_key
) = data;
set_data(
begin_cell()
.store_uint(seqno, 32)
.store_uint(subwallet, 32)
.store_uint(public_key, 256)
.end_cell()
);
}
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) { ;; ignore empty messages
return ();
}
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) { ;; ignore all bounced messages
return ();
}
slice sender_address = cs~load_msg_addr();
var (seqno, subwallet, public_key) = load_data();
int op = in_msg_body~load_uint(32);
if (op == 1) { ;; deploy new signed sale
var signature = in_msg_body~load_bits(512);
throw_unless(35, check_signature(slice_hash(in_msg_body), signature, public_key));
(cell state_init, cell body) = (in_msg_body~load_ref(), in_msg_body~load_ref());
int state_init_hash = cell_hash(state_init);
slice dest_address = begin_cell().store_int(0, 8).store_uint(state_init_hash, 256).end_cell().begin_parse();
var msg = begin_cell()
.store_uint(0x18, 6)
.store_uint(4, 3).store_slice(dest_address)
.store_grams(0)
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
.store_ref(state_init)
.store_ref(body);
send_raw_message(msg.end_cell(), 64); ;; carry remaining value of message
return ();
}
return ();
}
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512);
var cs = in_msg;
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
throw_if(35, valid_until <= now());
var (seqno, subwallet, public_key) = load_data();
throw_unless(33, msg_seqno == seqno);
throw_unless(34, subwallet_id == subwallet);
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
accept_message();
cs~touch();
while (cs.slice_refs()) {
var mode = cs~load_uint(8);
send_raw_message(cs~load_ref(), mode);
}
store_data(
seqno + 1,
subwallet,
public_key
);
}
;; Get methods
int seqno() method_id {
return get_data().begin_parse().preload_uint(32);
}
int get_public_key() method_id {
var cs = get_data().begin_parse();
cs~load_uint(64);
return cs.preload_uint(256);
}

View file

@ -0,0 +1,215 @@
;; Standard library for funC
;;
forall X -> tuple cons(X head, tuple tail) asm "CONS";
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
int cell_hash(cell c) asm "HASHCU";
int slice_hash(slice s) asm "HASHSU";
int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
cont get_c3() impure asm "c3 PUSH";
() set_c3(cont c) impure asm "c3 POP";
cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
cell preload_ref(slice s) asm "PLDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
builder store_slice(builder b, slice s) asm "STSLICER";
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_dict(builder b, cell c) asm(c b) "STDICT";
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
tuple parse_addr(slice s) asm "PARSEMSGADDR";
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";
int random() impure asm "RANDU256";
int rand(int range) impure asm "RAND";
int get_seed() impure asm "RANDSEED";
int set_seed() impure asm "SETRAND";
() randomize(int x) impure asm "ADDRAND";
() randomize_lt() impure asm "LTIME" "ADDRAND";
builder store_coins(builder b, int x) asm "STVARUINT16";
(slice, int) load_coins(slice s) asm( -> 1 0) "LDVARUINT16";
int equal_slices (slice a, slice b) asm "SDEQ";
int builder_null?(builder b) asm "ISNULL";
builder store_builder(builder to, builder from) asm "STBR";

View file

@ -0,0 +1,13 @@
;; operations (constant values taken from crc32 on op message in the companion .tlb files and appear during build)
int op::increment() asm "0x37491f2f PUSHINT";
int op::deposit() asm "0x47d54391 PUSHINT";
int op::withdraw() asm "0x41836980 PUSHINT";
int op::transfer_ownership() asm "0x2da38aaf PUSHINT";
;; errors
int error::unknown_op() asm "101 PUSHINT";
int error::access_denied() asm "102 PUSHINT";
int error::insufficient_balance() asm "103 PUSHINT";
;; other
int const::min_tons_for_storage() asm "10000000 PUSHINT"; ;; 0.01 TON

View file

@ -0,0 +1,30 @@
cell pack_jetton_wallet_data(int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) inline {
return begin_cell()
.store_coins(balance)
.store_slice(owner_address)
.store_slice(jetton_master_address)
.store_ref(jetton_wallet_code)
.end_cell();
}
cell calculate_jetton_wallet_state_init(slice owner_address, slice jetton_master_address, cell jetton_wallet_code) inline {
return begin_cell()
.store_uint(0, 2)
.store_dict(jetton_wallet_code)
.store_dict(pack_jetton_wallet_data(0, owner_address, jetton_master_address, jetton_wallet_code))
.store_uint(0, 1)
.end_cell();
}
slice calculate_jetton_wallet_address(cell state_init) inline {
return begin_cell().store_uint(4, 3)
.store_int(workchain(), 8)
.store_uint(cell_hash(state_init), 256)
.end_cell()
.begin_parse();
}
slice calculate_user_jetton_wallet_address(slice owner_address, slice jetton_master_address, cell jetton_wallet_code) inline {
return calculate_jetton_wallet_address(calculate_jetton_wallet_state_init(owner_address, jetton_master_address, jetton_wallet_code));
}

View file

@ -0,0 +1,9 @@
int op::transfer() asm "0xf8a7ea5 PUSHINT";
int op::transfer_notification() asm "0x7362d09c PUSHINT";
int op::internal_transfer() asm "0x178d4519 PUSHINT";
int op::excesses() asm "0xd53276db PUSHINT";
int op::burn() asm "0x595f07bc PUSHINT";
int op::burn_notification() asm "0x7bdd97de PUSHINT";
;; Minter
int op::mint() asm "21 PUSHINT";

View file

@ -0,0 +1,6 @@
int workchain() asm "0 PUSHINT";
() force_chain(slice addr) impure {
(int wc, _) = parse_std_addr(addr);
throw_unless(333, wc == workchain());
}

View file

@ -0,0 +1,215 @@
;; Standard library for funC
;;
forall X -> tuple cons(X head, tuple tail) asm "CONS";
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
int cell_hash(cell c) asm "HASHCU";
int slice_hash(slice s) asm "HASHSU";
int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
cont get_c3() impure asm "c3 PUSH";
() set_c3(cont c) impure asm "c3 POP";
cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
cell preload_ref(slice s) asm "PLDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
builder store_slice(builder b, slice s) asm "STSLICER";
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_dict(builder b, cell c) asm(c b) "STDICT";
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
tuple parse_addr(slice s) asm "PARSEMSGADDR";
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";
int random() impure asm "RANDU256";
int rand(int range) impure asm "RAND";
int get_seed() impure asm "RANDSEED";
int set_seed() impure asm "SETRAND";
() randomize(int x) impure asm "ADDRAND";
() randomize_lt() impure asm "LTIME" "ADDRAND";
builder store_coins(builder b, int x) asm "STVARUINT16";
(slice, int) load_coins(slice s) asm( -> 1 0) "LDVARUINT16";
int equal_slices (slice a, slice b) asm "SDEQ";
int builder_null?(builder b) asm "ISNULL";
builder store_builder(builder to, builder from) asm "STBR";

View file

@ -0,0 +1,9 @@
() send_grams(slice address, int amount) impure {
cell msg = begin_cell()
.store_uint (0x18, 6) ;; bounce
.store_slice(address) ;; 267 bit address
.store_grams(amount)
.store_uint(0, 107) ;; 106 zeroes + 0 as an indicator that there is no cell with the data
.end_cell();
send_raw_message(msg, 3); ;; mode, 2 for ignoring errors, 1 for sender pays fees, 64 for returning inbound message value
}

View file

@ -0,0 +1,122 @@
;; Jettons minter smart contract
;; storage scheme
;; storage#_ total_supply:Coins admin_address:MsgAddress content:^Cell jetton_wallet_code:^Cell = Storage;
#include "imports/stdlib.fc";
#include "imports/params.fc";
#include "imports/constants.fc";
#include "imports/jetton-utils.fc";
#include "imports/op-codes.fc";
#include "imports/utils.fc";
#pragma version >=0.2.0;
(int, slice, cell, cell) load_data() inline {
slice ds = get_data().begin_parse();
return (
ds~load_coins(), ;; total_supply
ds~load_msg_addr(), ;; admin_address
ds~load_ref(), ;; content
ds~load_ref() ;; jetton_wallet_code
);
}
() save_data(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) impure inline {
set_data(begin_cell()
.store_coins(total_supply)
.store_slice(admin_address)
.store_ref(content)
.store_ref(jetton_wallet_code)
.end_cell()
);
}
() mint_tokens(slice to_address, cell jetton_wallet_code, int amount, cell master_msg) impure {
cell state_init = calculate_jetton_wallet_state_init(to_address, my_address(), jetton_wallet_code);
slice to_wallet_address = calculate_jetton_wallet_address(state_init);
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(to_wallet_address)
.store_coins(amount)
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
.store_ref(state_init)
.store_ref(master_msg);
send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
}
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) { ;; ignore empty messages
return ();
}
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) { ;; ignore all bounced messages
return ();
}
slice sender_address = cs~load_msg_addr();
int op = in_msg_body~load_uint(32);
int query_id = in_msg_body~load_uint(64);
(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data();
if (op == op::mint()) {
throw_unless(73, equal_slices(sender_address, admin_address));
slice to_address = in_msg_body~load_msg_addr();
int amount = in_msg_body~load_coins();
cell master_msg = in_msg_body~load_ref();
slice master_msg_cs = master_msg.begin_parse();
master_msg_cs~skip_bits(32 + 64); ;; op + query_id
int jetton_amount = master_msg_cs~load_coins();
mint_tokens(to_address, jetton_wallet_code, amount, master_msg);
save_data(total_supply + jetton_amount, admin_address, content, jetton_wallet_code);
return ();
}
if (op == op::burn_notification()) {
int jetton_amount = in_msg_body~load_coins();
slice from_address = in_msg_body~load_msg_addr();
throw_unless(74,
equal_slices(calculate_user_jetton_wallet_address(from_address, my_address(), jetton_wallet_code), sender_address)
);
save_data(total_supply - jetton_amount, admin_address, content, jetton_wallet_code);
slice response_address = in_msg_body~load_msg_addr();
if (response_address.preload_uint(2) != 0) {
var msg = begin_cell()
.store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
.store_slice(response_address)
.store_coins(0)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(op::excesses(), 32)
.store_uint(query_id, 64);
send_raw_message(msg.end_cell(), 2 + 64);
}
return ();
}
if (op == 3) { ;; change admin
throw_unless(73, equal_slices(sender_address, admin_address));
slice new_admin_address = in_msg_body~load_msg_addr();
save_data(total_supply, new_admin_address, content, jetton_wallet_code);
return ();
}
if (op == 4) { ;; change content, delete this for immutable tokens
throw_unless(73, equal_slices(sender_address, admin_address));
save_data(total_supply, admin_address, in_msg_body~load_ref(), jetton_wallet_code);
return ();
}
throw(0xffff);
}
(int, int, slice, cell, cell) get_jetton_data() method_id {
(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data();
return (total_supply, -1, admin_address, content, jetton_wallet_code);
}
slice get_wallet_address(slice owner_address) method_id {
(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) = load_data();
return calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code);
}

View file

@ -0,0 +1,17 @@
;; operations (constant values taken from crc32 on op message in the companion .tlb files and appear during build)
int op::increment() asm "0x37491f2f PUSHINT";
int op::deposit() asm "0x47d54391 PUSHINT";
int op::withdraw() asm "0x41836980 PUSHINT";
int op::transfer_ownership() asm "0x2da38aaf PUSHINT";
;; errors
int error::unknown_op() asm "101 PUSHINT";
int error::access_denied() asm "102 PUSHINT";
int error::insufficient_balance() asm "103 PUSHINT";
;; other
int const::min_tons_for_storage() asm "10000000 PUSHINT"; ;; 0.01 TON
;; 6905(computational_gas_price) * 1000(cur_gas_price) = 6905000
;; ceil(6905000) = 10000000 ~= 0.01 TON
int const::provide_address_gas_consumption() asm "10000000 PUSHINT";

View file

@ -0,0 +1,30 @@
cell pack_jetton_wallet_data(int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) inline {
return begin_cell()
.store_coins(balance)
.store_slice(owner_address)
.store_slice(jetton_master_address)
.store_ref(jetton_wallet_code)
.end_cell();
}
cell calculate_jetton_wallet_state_init(slice owner_address, slice jetton_master_address, cell jetton_wallet_code) inline {
return begin_cell()
.store_uint(0, 2)
.store_dict(jetton_wallet_code)
.store_dict(pack_jetton_wallet_data(0, owner_address, jetton_master_address, jetton_wallet_code))
.store_uint(0, 1)
.end_cell();
}
slice calculate_jetton_wallet_address(cell state_init) inline {
return begin_cell().store_uint(4, 3)
.store_int(workchain(), 8)
.store_uint(cell_hash(state_init), 256)
.end_cell()
.begin_parse();
}
slice calculate_user_jetton_wallet_address(slice owner_address, slice jetton_master_address, cell jetton_wallet_code) inline {
return calculate_jetton_wallet_address(calculate_jetton_wallet_state_init(owner_address, jetton_master_address, jetton_wallet_code));
}

View file

@ -0,0 +1,9 @@
int op::transfer() asm "0xf8a7ea5 PUSHINT";
int op::transfer_notification() asm "0x7362d09c PUSHINT";
int op::internal_transfer() asm "0x178d4519 PUSHINT";
int op::excesses() asm "0xd53276db PUSHINT";
int op::burn() asm "0x595f07bc PUSHINT";
int op::burn_notification() asm "0x7bdd97de PUSHINT";
;; Minter
int op::mint() asm "21 PUSHINT";

View file

@ -0,0 +1,6 @@
int workchain() asm "0 PUSHINT";
() force_chain(slice addr) impure {
(int wc, _) = parse_std_addr(addr);
throw_unless(333, wc == workchain());
}

View file

@ -0,0 +1,215 @@
;; Standard library for funC
;;
forall X -> tuple cons(X head, tuple tail) asm "CONS";
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
int cell_hash(cell c) asm "HASHCU";
int slice_hash(slice s) asm "HASHSU";
int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
cont get_c3() impure asm "c3 PUSH";
() set_c3(cont c) impure asm "c3 POP";
cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
cell preload_ref(slice s) asm "PLDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
builder store_slice(builder b, slice s) asm "STSLICER";
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_dict(builder b, cell c) asm(c b) "STDICT";
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
tuple parse_addr(slice s) asm "PARSEMSGADDR";
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";
int random() impure asm "RANDU256";
int rand(int range) impure asm "RAND";
int get_seed() impure asm "RANDSEED";
int set_seed() impure asm "SETRAND";
() randomize(int x) impure asm "ADDRAND";
() randomize_lt() impure asm "LTIME" "ADDRAND";
builder store_coins(builder b, int x) asm "STVARUINT16";
(slice, int) load_coins(slice s) asm( -> 1 0) "LDVARUINT16";
int equal_slices (slice a, slice b) asm "SDEQ";
int builder_null?(builder b) asm "ISNULL";
builder store_builder(builder to, builder from) asm "STBR";

View file

@ -0,0 +1,9 @@
() send_grams(slice address, int amount) impure {
cell msg = begin_cell()
.store_uint (0x18, 6) ;; bounce
.store_slice(address) ;; 267 bit address
.store_grams(amount)
.store_uint(0, 107) ;; 106 zeroes + 0 as an indicator that there is no cell with the data
.end_cell();
send_raw_message(msg, 3); ;; mode, 2 for ignoring errors, 1 for sender pays fees, 64 for returning inbound message value
}

View file

@ -0,0 +1,250 @@
;; Jetton Wallet Smart Contract
#include "imports/stdlib.fc";
#include "imports/params.fc";
#include "imports/constants.fc";
#include "imports/jetton-utils.fc";
#include "imports/op-codes.fc";
#include "imports/utils.fc";
#pragma version >=0.2.0;
{-
NOTE that this tokens can be transferred within the same workchain.
This is suitable for most tokens, if you need tokens transferable between workchains there are two solutions:
1) use more expensive but universal function to calculate message forward fee for arbitrary destination (see `misc/forward-fee-calc.cs`)
2) use token holder proxies in target workchain (that way even 'non-universal' token can be used from any workchain)
-}
const min_tons_for_storage = 10000000; ;; 0.01 TON
const gas_consumption = 10000000; ;; 0.01 TON
{-
Storage
storage#_ balance:Coins owner_address:MsgAddressInt jetton_master_address:MsgAddressInt jetton_wallet_code:^Cell = Storage;
-}
(int, slice, slice, cell) load_data() inline {
slice ds = get_data().begin_parse();
return (ds~load_coins(), ds~load_msg_addr(), ds~load_msg_addr(), ds~load_ref());
}
() save_data (int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) impure inline {
set_data(pack_jetton_wallet_data(balance, owner_address, jetton_master_address, jetton_wallet_code));
}
{-
transfer query_id:uint64 amount:(VarUInteger 16) destination:MsgAddress
response_destination:MsgAddress custom_payload:(Maybe ^Cell)
forward_ton_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell)
= InternalMsgBody;
internal_transfer query_id:uint64 amount:(VarUInteger 16) from:MsgAddress
response_address:MsgAddress
forward_ton_amount:(VarUInteger 16)
forward_payload:(Either Cell ^Cell)
= InternalMsgBody;
-}
() send_tokens (slice in_msg_body, slice sender_address, int msg_value, int fwd_fee) impure {
int query_id = in_msg_body~load_uint(64);
int jetton_amount = in_msg_body~load_coins();
slice to_owner_address = in_msg_body~load_msg_addr();
force_chain(to_owner_address);
(int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
balance -= jetton_amount;
throw_unless(705, equal_slices(owner_address, sender_address));
throw_unless(706, balance >= 0);
cell state_init = calculate_jetton_wallet_state_init(to_owner_address, jetton_master_address, jetton_wallet_code);
slice to_wallet_address = calculate_jetton_wallet_address(state_init);
slice response_address = in_msg_body~load_msg_addr();
cell custom_payload = in_msg_body~load_dict();
int forward_ton_amount = in_msg_body~load_coins();
throw_unless(708, slice_bits(in_msg_body) >= 1);
slice either_forward_payload = in_msg_body;
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(to_wallet_address)
.store_coins(0)
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
.store_ref(state_init);
var msg_body = begin_cell()
.store_uint(op::internal_transfer(), 32)
.store_uint(query_id, 64)
.store_coins(jetton_amount)
.store_slice(owner_address)
.store_slice(response_address)
.store_coins(forward_ton_amount)
.store_slice(either_forward_payload)
.end_cell();
msg = msg.store_ref(msg_body);
int fwd_count = forward_ton_amount ? 2 : 1;
throw_unless(709, msg_value >
forward_ton_amount +
;; 3 messages: wal1->wal2, wal2->owner, wal2->response
;; but last one is optional (it is ok if it fails)
fwd_count * fwd_fee +
(2 * gas_consumption + min_tons_for_storage)); ;; TODO(shahar) ?
;; universal message send fee calculation may be activated here
;; by using this instead of fwd_fee
;; msg_fwd_fee(to_wallet, msg_body, state_init, 15)
send_raw_message(msg.end_cell(), 64); ;; revert on errors
save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
}
{-
internal_transfer query_id:uint64 amount:(VarUInteger 16) from:MsgAddress
response_address:MsgAddress
forward_ton_amount:(VarUInteger 16)
forward_payload:(Either Cell ^Cell)
= InternalMsgBody;
-}
() receive_tokens (slice in_msg_body, slice sender_address, int my_ton_balance, int fwd_fee, int msg_value) impure {
;; NOTE we can not allow fails in action phase since in that case there will be
;; no bounce. Thus check and throw in computation phase.
(int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
int query_id = in_msg_body~load_uint(64);
int jetton_amount = in_msg_body~load_coins();
balance += jetton_amount;
slice from_address = in_msg_body~load_msg_addr();
slice response_address = in_msg_body~load_msg_addr();
throw_unless(707,
equal_slices(jetton_master_address, sender_address)
|
equal_slices(calculate_user_jetton_wallet_address(from_address, jetton_master_address, jetton_wallet_code), sender_address)
);
int forward_ton_amount = in_msg_body~load_coins();
int ton_balance_before_msg = my_ton_balance - msg_value;
int storage_fee = min_tons_for_storage - min(ton_balance_before_msg, min_tons_for_storage);
msg_value -= (storage_fee + gas_consumption);
if(forward_ton_amount) {
msg_value -= (forward_ton_amount + fwd_fee);
slice either_forward_payload = in_msg_body;
var msg_body = begin_cell()
.store_uint(op::transfer_notification(), 32)
.store_uint(query_id, 64)
.store_coins(jetton_amount)
.store_slice(from_address)
.store_slice(either_forward_payload)
.end_cell();
var msg = begin_cell()
.store_uint(0x10, 6) ;; we should not bounce here cause receiver can have uninitialized contract
.store_slice(owner_address)
.store_coins(forward_ton_amount)
.store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_ref(msg_body);
send_raw_message(msg.end_cell(), 1);
}
if ((response_address.preload_uint(2) != 0) & (msg_value > 0)) {
var msg = begin_cell()
.store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 010000
.store_slice(response_address)
.store_coins(msg_value)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(op::excesses(), 32)
.store_uint(query_id, 64);
send_raw_message(msg.end_cell(), 2);
}
save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
}
() burn_tokens (slice in_msg_body, slice sender_address, int msg_value, int fwd_fee) impure {
;; NOTE we can not allow fails in action phase since in that case there will be
;; no bounce. Thus check and throw in computation phase.
(int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
int query_id = in_msg_body~load_uint(64);
int jetton_amount = in_msg_body~load_coins();
slice response_address = in_msg_body~load_msg_addr();
;; ignore custom payload
;; slice custom_payload = in_msg_body~load_dict();
balance -= jetton_amount;
throw_unless(705, equal_slices(owner_address, sender_address));
throw_unless(706, balance >= 0);
throw_unless(707, msg_value > fwd_fee + 2 * gas_consumption);
var msg_body = begin_cell()
.store_uint(op::burn_notification(), 32)
.store_uint(query_id, 64)
.store_coins(jetton_amount)
.store_slice(owner_address)
.store_slice(response_address)
.end_cell();
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(jetton_master_address)
.store_coins(0)
.store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_ref(msg_body);
send_raw_message(msg.end_cell(), 64);
save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
}
() on_bounce (slice in_msg_body) impure {
in_msg_body~skip_bits(32); ;; 0xFFFFFFFF
(int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
int op = in_msg_body~load_uint(32);
throw_unless(709, (op == op::internal_transfer()) | (op == op::burn_notification()));
int query_id = in_msg_body~load_uint(64);
int jetton_amount = in_msg_body~load_coins();
balance += jetton_amount;
save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
}
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) { ;; ignore empty messages
return ();
}
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) {
on_bounce(in_msg_body);
return ();
}
slice sender_address = cs~load_msg_addr();
cs~load_msg_addr(); ;; skip dst
cs~load_coins(); ;; skip value
cs~skip_bits(1); ;; skip extracurrency collection
cs~load_coins(); ;; skip ihr_fee
int fwd_fee = muldiv(cs~load_coins(), 3, 2); ;; we use message fwd_fee for estimation of forward_payload costs
int op = in_msg_body~load_uint(32);
if (op == op::transfer()) { ;; outgoing transfer
send_tokens(in_msg_body, sender_address, msg_value, fwd_fee);
return ();
}
if (op == op::internal_transfer()) { ;; incoming transfer
receive_tokens(in_msg_body, sender_address, my_balance, fwd_fee, msg_value);
return ();
}
if (op == op::burn()) { ;; burn
burn_tokens(in_msg_body, sender_address, msg_value, fwd_fee);
return ();
}
throw(0xffff);
}
(int, slice, slice, cell) get_wallet_data() method_id {
return load_data();
}

View file

@ -0,0 +1,173 @@
;; NFT collection smart contract
;; storage scheme
;; default#_ royalty_factor:uint16 royalty_base:uint16 royalty_address:MsgAddress = RoyaltyParams;
;; storage#_ owner_address:MsgAddress next_item_index:uint64
;; ^[collection_content:^Cell common_content:^Cell]
;; nft_item_code:^Cell
;; royalty_params:^RoyaltyParams
;; = Storage;
#include "op-codes.fc";
#include "stdlib.fc";
#include "params.fc";
(slice, int, cell, cell, cell) load_data() inline {
var ds = get_data().begin_parse();
return
(ds~load_msg_addr(), ;; owner_address
ds~load_uint(64), ;; next_item_index
ds~load_ref(), ;; content
ds~load_ref(), ;; nft_item_code
ds~load_ref() ;; royalty_params
);
}
() save_data(slice owner_address, int next_item_index, cell content, cell nft_item_code, cell royalty_params) impure inline {
set_data(begin_cell()
.store_slice(owner_address)
.store_uint(next_item_index, 64)
.store_ref(content)
.store_ref(nft_item_code)
.store_ref(royalty_params)
.end_cell());
}
cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {
cell data = begin_cell().store_uint(item_index, 64).store_slice(my_address()).end_cell();
return begin_cell().store_uint(0, 2).store_dict(nft_item_code).store_dict(data).store_uint(0, 1).end_cell();
}
slice calculate_nft_item_address(int wc, cell state_init) {
return begin_cell().store_uint(4, 3)
.store_int(wc, 8)
.store_uint(cell_hash(state_init), 256)
.end_cell()
.begin_parse();
}
() deploy_nft_item(int item_index, cell nft_item_code, int amount, cell nft_content) impure {
cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);
slice nft_address = calculate_nft_item_address(workchain(), state_init);
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(nft_address)
.store_coins(amount)
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
.store_ref(state_init)
.store_ref(nft_content);
send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
}
() send_royalty_params(slice to_address, int query_id, slice data) impure inline {
var msg = begin_cell()
.store_uint(0x10, 6) ;; nobounce - int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool packages:MsgAddress -> 011000
.store_slice(to_address)
.store_coins(0)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(op::report_royalty_params(), 32)
.store_uint(query_id, 64)
.store_slice(data);
send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message
}
() recv_internal(cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) { ;; ignore empty messages
return ();
}
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) { ;; ignore all bounced messages
return ();
}
slice sender_address = cs~load_msg_addr();
int op = in_msg_body~load_uint(32);
int query_id = in_msg_body~load_uint(64);
var (owner_address, next_item_index, content, nft_item_code, royalty_params) = load_data();
if (op == op::get_royalty_params()) {
send_royalty_params(sender_address, query_id, royalty_params.begin_parse());
return ();
}
throw_unless(401, equal_slices(sender_address, owner_address));
if (op == 1) { ;; deploy new nft
int item_index = in_msg_body~load_uint(64);
throw_unless(402, item_index <= next_item_index);
var is_last = item_index == next_item_index;
deploy_nft_item(item_index, nft_item_code, in_msg_body~load_coins(), in_msg_body~load_ref());
if (is_last) {
next_item_index += 1;
save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);
}
return ();
}
if (op == 2) { ;; batch deploy of new nfts
int counter = 0;
cell deploy_list = in_msg_body~load_ref();
do {
var (item_index, item, f?) = deploy_list~udict::delete_get_min(64);
if (f?) {
counter += 1;
if (counter >= 250) { ;; Limit due to limits of action list size
throw(399);
}
throw_unless(403 + counter, item_index <= next_item_index);
deploy_nft_item(item_index, nft_item_code, item~load_coins(), item~load_ref());
if (item_index == next_item_index) {
next_item_index += 1;
}
}
} until ( ~ f?);
save_data(owner_address, next_item_index, content, nft_item_code, royalty_params);
return ();
}
if (op == 3) { ;; change owner
slice new_owner = in_msg_body~load_msg_addr();
save_data(new_owner, next_item_index, content, nft_item_code, royalty_params);
return ();
}
if (op == 4) { ;; change content
save_data(owner_address, next_item_index, in_msg_body~load_ref(), nft_item_code, in_msg_body~load_ref());
return ();
}
throw(0xffff);
}
;; Get methods
(int, cell, slice) get_collection_data() method_id {
var (owner_address, next_item_index, content, _, _) = load_data();
slice cs = content.begin_parse();
return (next_item_index, cs~load_ref(), owner_address);
}
slice get_nft_address_by_index(int index) method_id {
var (_, _, _, nft_item_code, _) = load_data();
cell state_init = calculate_nft_item_state_init(index, nft_item_code);
return calculate_nft_item_address(0, state_init);
}
(int, int, slice) royalty_params() method_id {
var (_, _, _, _, royalty) = load_data();
slice rs = royalty.begin_parse();
return (rs~load_uint(16), rs~load_uint(16), rs~load_msg_addr());
}
cell get_nft_content(int index, cell individual_nft_content) method_id {
var (_, _, content, _, _) = load_data();
slice cs = content.begin_parse();
cs~load_ref();
slice common_content = cs~load_ref().begin_parse();
return (begin_cell()
.store_uint(1, 8) ;; offchain tag
.store_slice(common_content)
.store_ref(individual_nft_content)
.end_cell());
}

View file

@ -0,0 +1,24 @@
int op::transfer() asm "0x5fcc3d14 PUSHINT";
int op::ownership_assigned() asm "0x05138d91 PUSHINT";
int op::excesses() asm "0xd53276db PUSHINT";
int op::get_static_data() asm "0x2fcb26a2 PUSHINT";
int op::report_static_data() asm "0x8b771735 PUSHINT";
int op::get_royalty_params() asm "0x693d3950 PUSHINT";
int op::report_royalty_params() asm "0xa8cb00ad PUSHINT";
;; NFTEditable
int op::edit_content() asm "0x1a0b9d51 PUSHINT";
int op::transfer_editorship() asm "0x1c04412a PUSHINT";
int op::editorship_assigned() asm "0x511a4463 PUSHINT";
;; SBT
int op::request_owner() asm "0xd0c3bfea PUSHINT";
int op::owner_info() asm "0x0dd607e3 PUSHINT";
int op::prove_ownership() asm "0x04ded148 PUSHINT";
int op::ownership_proof() asm "0x0524c7ae PUSHINT";
int op::ownership_proof_bounced() asm "0xc18e86d2 PUSHINT";
int op::destroy() asm "0x1f04537a PUSHINT";
int op::revoke() asm "0x6f89f5e3 PUSHINT";
int op::take_excess() asm "0xd136d3b3 PUSHINT";

View file

@ -0,0 +1,10 @@
int workchain() asm "0 PUSHINT";
() force_chain(slice addr) impure {
(int wc, _) = parse_std_addr(addr);
throw_unless(333, wc == workchain());
}
slice null_addr() asm "b{00} PUSHSLICE";
int flag::regular() asm "0x10 PUSHINT";
int flag::bounce() asm "0x8 PUSHINT";

View file

@ -0,0 +1,215 @@
;; Standard library for funC
;;
forall X -> tuple cons(X head, tuple tail) asm "CONS";
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
int cell_hash(cell c) asm "HASHCU";
int slice_hash(slice s) asm "HASHSU";
int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
cont get_c3() impure asm "c3 PUSH";
() set_c3(cont c) impure asm "c3 POP";
cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
cell preload_ref(slice s) asm "PLDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
builder store_slice(builder b, slice s) asm "STSLICER";
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_dict(builder b, cell c) asm(c b) "STDICT";
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
tuple parse_addr(slice s) asm "PARSEMSGADDR";
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";
int random() impure asm "RANDU256";
int rand(int range) impure asm "RAND";
int get_seed() impure asm "RANDSEED";
int set_seed() impure asm "SETRAND";
() randomize(int x) impure asm "ADDRAND";
() randomize_lt() impure asm "LTIME" "ADDRAND";
builder store_coins(builder b, int x) asm "STVARUINT16";
(slice, int) load_coins(slice s) asm( -> 1 0) "LDVARUINT16";
int equal_slices (slice a, slice b) asm "SDEQ";
int builder_null?(builder b) asm "ISNULL";
builder store_builder(builder to, builder from) asm "STBR";

View file

@ -0,0 +1,746 @@
;; The validator has his own wallet in the masterchain, on which he holds his own coins for operating.
;; From this wallet he sends commands to this nominator pool (mostly `new_stake`, `update_validator_set` and `recover_stake`).
;; Register/vote_for complaints and register/vote_for config proposals are sent from validator's wallet.
;;
;; Pool contract must be in masterchain.
;; Nominators' wallets must be in the basechain.
;; The validator in most cases have two pools (for even and odd validation rounds).
#include "stdlib.fc";
int op::new_stake() asm "0x4e73744b PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L621
int op::new_stake_error() asm "0xee6f454c PUSHINT"; ;; return_stake https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L169
int op::new_stake_ok() asm "0xf374484c PUSHINT"; ;; send_confirmation https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L173
int op::recover_stake() asm "0x47657424 PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L625
int op::recover_stake_error() asm "0xfffffffe PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L407
int op::recover_stake_ok() asm "0xf96f7324 PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L426
int ADDR_SIZE() asm "256 PUSHINT";
int BOUNCEABLE() asm "0x18 PUSHINT";
int NON_BOUNCEABLE() asm "0x10 PUSHINT";
int SEND_MODE_PAY_FEE_SEPARATELY() asm "1 PUSHINT"; ;; means that the sender wants to pay transfer fees separately
int SEND_MODE_IGNORE_ERRORS() asm "2 PUSHINT"; ;; means that any errors arising while processing this message during the action phase should be ignored
int SEND_MODE_REMAINING_AMOUNT() asm "64 PUSHINT"; ;; is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message
int ONE_TON() asm "1000000000 PUSHINT";
int MIN_TONS_FOR_STORAGE() asm "10000000000 PUSHINT"; ;; 10 TON
int DEPOSIT_PROCESSING_FEE() asm "1000000000 PUSHINT"; ;; 1 TON
int MIN_STAKE_TO_SEND() asm "500000000000 PUSHINT"; ;; 500 TON
int VOTES_LIFETIME() asm "2592000 PUSHINT"; ;; 30 days
int binary_log_ceil(int x) asm "UBITSIZE";
;; hex parse same with bridge https://github.com/ton-blockchain/bridge-func/blob/d03dbdbe9236e01efe7f5d344831bf770ac4c613/func/text_utils.fc
(slice, int) ~load_hex_symbol(slice comment) {
int n = comment~load_uint(8);
n = n - 48;
throw_unless(329, n >= 0);
if (n < 10) {
return (comment, (n));
}
n = n - 7;
throw_unless(329, n >= 0);
if (n < 16) {
return (comment, (n));
}
n = n - 32;
throw_unless(329, (n >= 0) & (n < 16));
return (comment, n);
}
(slice, int) ~load_text_hex_number(slice comment, int byte_length) {
int current_slice_length = comment.slice_bits() / 8;
int result = 0;
int counter = 0;
repeat (2 * byte_length) {
result = result * 16 + comment~load_hex_symbol();
counter = counter + 1;
if (counter == current_slice_length) {
if (comment.slice_refs() == 1) {
cell _cont = comment~load_ref();
comment = _cont.begin_parse();
current_slice_length = comment.slice_bits() / 8;
counter = 0;
}
}
}
return (comment, result);
}
slice make_address(int wc, int addr) inline_ref {
return begin_cell()
.store_uint(4, 3).store_int(wc, 8).store_uint(addr, ADDR_SIZE()).end_cell().begin_parse();
}
;; https://github.com/ton-blockchain/ton/blob/ae5c0720143e231c32c3d2034cfe4e533a16d969/crypto/block/block.tlb#L584
int is_elector_address(int wc, int addr) inline_ref {
return (wc == -1) & (config_param(1).begin_parse().preload_uint(ADDR_SIZE()) == addr);
}
slice elector_address() inline_ref {
int elector = config_param(1).begin_parse().preload_uint(ADDR_SIZE());
return make_address(-1, elector);
}
;; https://github.com/ton-blockchain/ton/blob/ae5c0720143e231c32c3d2034cfe4e533a16d969/crypto/block/block.tlb#L721
int max_recommended_punishment_for_validator_misbehaviour(int stake) inline_ref {
cell cp = config_param(40);
if (cell_null?(cp)) {
return 101000000000; ;; 101 TON - https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/lite-client/lite-client.cpp#L3678
}
slice cs = cp.begin_parse();
(int prefix,
int default_flat_fine, int default_proportional_fine,
int severity_flat_mult, int severity_proportional_mult,
int unpunishable_interval,
int long_interval, int long_flat_mult, int long_proportional_mult) =
(cs~load_uint(8),
cs~load_coins(), cs~load_uint(32),
cs~load_uint(16), cs~load_uint(16),
cs~load_uint(16),
cs~load_uint(16), cs~load_uint(16), cs~load_uint(16)
);
;; https://github.com/ton-blockchain/ton/blob/master/lite-client/lite-client.cpp#L3721
int fine = default_flat_fine;
int fine_part = default_proportional_fine;
fine *= severity_flat_mult; fine >>= 8;
fine_part *= severity_proportional_mult; fine_part >>= 8;
fine *= long_flat_mult; fine >>= 8;
fine_part *= long_proportional_mult; fine_part >>= 8;
return min(stake, fine + muldiv(stake, fine_part, 1 << 32)); ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L529
}
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/block/block.tlb#L632
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L118
int get_validator_config() inline_ref {
slice cs = config_param(15).begin_parse();
(int validators_elected_for, int elections_start_before, int elections_end_before, int stake_held_for) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32), cs.preload_uint(32));
return stake_held_for;
}
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/block/block.tlb#L712
(int, int, cell) get_current_validator_set() inline_ref {
cell vset = config_param(34); ;; current validator set
slice cs = vset.begin_parse();
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/block/block.tlb#L579
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/config-code.fc#L49
throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only
int utime_since = cs~load_uint(32); ;; actual start unixtime of current validation round
int utime_until = cs~load_uint(32); ;; supposed end unixtime of current validation round (utime_until = utime_since + validators_elected_for); unfreeze_at = utime_until + stake_held_for
return (utime_since, utime_until, vset);
}
;; check the validity of the new_stake message
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L208
int check_new_stake_msg(slice cs) impure inline_ref {
var validator_pubkey = cs~load_uint(256);
var stake_at = cs~load_uint(32);
var max_factor = cs~load_uint(32);
var adnl_addr = cs~load_uint(256);
var signature = cs~load_ref().begin_parse().preload_bits(512);
cs.end_parse();
return stake_at; ;; supposed start of next validation round (utime_since)
}
builder pack_nominator(int amount, int pending_deposit_amount) inline_ref {
return begin_cell().store_coins(amount).store_coins(pending_deposit_amount);
}
(int, int) unpack_nominator(slice ds) inline_ref {
return (
ds~load_coins(), ;; amount
ds~load_coins() ;; pending_deposit_amount
);
}
cell pack_config(int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake) inline_ref {
return begin_cell()
.store_uint(validator_address, ADDR_SIZE())
.store_uint(validator_reward_share, 16)
.store_uint(max_nominators_count, 16)
.store_coins(min_validator_stake)
.store_coins(min_nominator_stake)
.end_cell();
}
(int, int, int, int, int) unpack_config(slice ds) inline_ref {
return (
ds~load_uint(ADDR_SIZE()), ;; validator_address
ds~load_uint(16), ;; validator_reward_share
ds~load_uint(16), ;; max_nominators_count
ds~load_coins(), ;; min_validator_stake
ds~load_coins() ;; min_nominator_stake
);
}
() save_data(int state, int nominators_count, int stake_amount_sent, int validator_amount, cell config, cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) impure inline_ref {
set_data(begin_cell()
.store_uint(state, 8)
.store_uint(nominators_count, 16)
.store_coins(stake_amount_sent)
.store_coins(validator_amount)
.store_ref(config)
.store_dict(nominators)
.store_dict(withdraw_requests)
.store_uint(stake_at, 32)
.store_uint(saved_validator_set_hash, 256)
.store_uint(validator_set_changes_count, 8)
.store_uint(validator_set_change_time, 32)
.store_uint(stake_held_for, 32)
.store_dict(config_proposal_votings)
.end_cell());
}
(int, int, int, int, (int, int, int, int, int), cell, cell, int, int, int, int, int, cell) load_data() inline_ref {
slice ds = get_data().begin_parse();
return (
ds~load_uint(8), ;; state
ds~load_uint(16), ;; nominators_count
ds~load_coins(), ;; stake_amount_sent
ds~load_coins(), ;; validator_amount
unpack_config(ds~load_ref().begin_parse()), ;; config
ds~load_dict(), ;; nominators
ds~load_dict(), ;; withdraw_requests
ds~load_uint(32), ;; stake_at
ds~load_uint(256), ;; saved_validator_set_hash
ds~load_uint(8), ;; validator_set_changes_count
ds~load_uint(32), ;; validator_set_change_time
ds~load_uint(32), ;; stake_held_for
ds~load_dict() ;; config_proposal_votings
);
}
() send_msg(slice to_address, int amount, cell payload, int flags, int send_mode) impure inline_ref {
int has_payload = ~ cell_null?(payload);
builder msg = begin_cell()
.store_uint(flags, 6)
.store_slice(to_address)
.store_coins(amount)
.store_uint(has_payload ? 1 : 0, 1 + 4 + 4 + 64 + 32 + 1 + 1);
if (has_payload) {
msg = msg.store_ref(payload);
}
send_raw_message(msg.end_cell(), send_mode);
}
() send_excesses(slice sender_address) impure inline_ref {
send_msg(sender_address, 0, null(), NON_BOUNCEABLE(), SEND_MODE_REMAINING_AMOUNT() + SEND_MODE_IGNORE_ERRORS()); ;; non-bouneable, remaining inbound message amount, fee deducted from amount, ignore errors
}
(cell, cell, int, int) withdraw_nominator(int address, cell nominators, cell withdraw_requests, int balance, int nominators_count) impure inline_ref {
(slice nominator, int found) = nominators.udict_get?(ADDR_SIZE(), address);
throw_unless(60, found);
(int amount, int pending_deposit_amount) = unpack_nominator(nominator);
int withdraw_amount = amount + pending_deposit_amount;
if (withdraw_amount > balance - MIN_TONS_FOR_STORAGE()) {
return (nominators, withdraw_requests, balance, nominators_count);
}
nominators~udict_delete?(ADDR_SIZE(), address);
withdraw_requests~udict_delete?(ADDR_SIZE(), address);
nominators_count -= 1;
balance -= withdraw_amount;
if (withdraw_amount >= ONE_TON()) {
send_msg(make_address(0, address), withdraw_amount, null(), NON_BOUNCEABLE(), 0); ;; non-bouneable, fee deducted from amount, revert on errors
}
return (nominators, withdraw_requests, balance, nominators_count);
}
(cell, cell, int, int) process_withdraw_requests(cell nominators, cell withdraw_requests, int balance, int nominators_count, int limit) impure inline_ref {
int count = 0;
int address = -1;
int need_break = 0;
do {
(address, slice cs, int f) = withdraw_requests.udict_get_next?(ADDR_SIZE(), address);
if (f) {
(nominators, withdraw_requests, int new_balance, nominators_count) = withdraw_nominator(address, nominators, withdraw_requests, balance, nominators_count);
need_break = (new_balance == balance);
balance = new_balance;
count += 1;
if (count >= limit) {
need_break = -1;
}
}
} until ((~ f) | (need_break));
return (nominators, withdraw_requests, nominators_count, balance);
}
int calculate_total_nominators_amount(cell nominators) inline_ref {
int total = 0;
int address = -1;
do {
(address, slice cs, int f) = nominators.udict_get_next?(ADDR_SIZE(), address);
if (f) {
(int amount, int pending_deposit_amount) = unpack_nominator(cs);
total += (amount + pending_deposit_amount);
}
} until (~ f);
return total;
}
cell distribute_share(int reward, cell nominators) inline_ref {
int total_amount = 0;
int address = -1;
do {
(address, slice cs, int f) = nominators.udict_get_next?(ADDR_SIZE(), address);
if (f) {
(int amount, int pending_deposit_amount) = unpack_nominator(cs);
total_amount += amount;
}
} until (~ f);
cell new_nominators = new_dict();
address = -1;
do {
(address, slice cs, int f) = nominators.udict_get_next?(ADDR_SIZE(), address);
if (f) {
(int amount, int pending_deposit_amount) = unpack_nominator(cs);
if (total_amount > 0) {
amount += muldiv(reward, amount, total_amount);
if (amount < 0) {
amount = 0;
}
}
amount += pending_deposit_amount;
new_nominators~udict_set_builder(ADDR_SIZE(), address, pack_nominator(amount, 0));
}
} until (~ f);
return new_nominators;
}
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
int balance = pair_first(get_balance());
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
slice sender_address = cs~load_msg_addr();
(int sender_wc, int sender_addr) = parse_std_addr(sender_address);
if (flags & 1) { ;; bounced messages
if (in_msg_body.slice_bits() >= 64) {
in_msg_body~skip_bits(32); ;; skip 0xFFFFFFFF bounced prefix
int op = in_msg_body~load_uint(32);
if ((op == op::new_stake()) & (is_elector_address(sender_wc, sender_addr))) {
;; `new_stake` from nominator-pool should always be handled without throws by elector
;; because nominator-pool do `check_new_stake_msg` and `msg_value` checks before sending `new_stake`.
;; If the stake is not accepted elector will send `new_stake_error` response message.
;; Nevertheless we do process theoretically possible bounced `new_stake`.
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
if (state == 1) {
state = 0;
}
save_data(
state,
nominators_count,
stake_amount_sent,
validator_amount,
pack_config(validator_address, validator_reward_share, max_nominators_count, min_validator_stake, min_nominator_stake),
nominators,
withdraw_requests,
stake_at,
saved_validator_set_hash,
validator_set_changes_count,
validator_set_change_time,
stake_held_for,
config_proposal_votings
);
}
}
return (); ;; ignore other bounces messages
}
int op = in_msg_body~load_uint(32);
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
if (op == 0) {
;; We use simple text comments for nominator operations so nominators can do it from any wallet app.
;; In other cases, they will need to put a stake on a browser extension, or use scripts, which can be inconvenient.
;; Throw on any unexpected request so that the stake is bounced back to the nominator in case of a typo.
int action = in_msg_body~load_uint(8);
int is_vote = (action == 121) | (action == 110); ;; "y" or "n"
throw_unless(64, (action == 100) | (action == 119) | is_vote); ;; "d" or "w" or "y" or "n"
if (~ is_vote) {
in_msg_body.end_parse();
throw_unless(61, sender_wc == 0); ;; nominators only in basechain
throw_unless(62, sender_addr != validator_address);
}
if (action == 100) { ;; "d" - deposit nominator (any time, will take effect in the next round)
(slice nominator, int found) = nominators.udict_get?(ADDR_SIZE(), sender_addr);
if (~ found) {
nominators_count += 1;
}
throw_unless(65, nominators_count <= max_nominators_count);
msg_value -= DEPOSIT_PROCESSING_FEE();
throw_unless(66, msg_value > 0);
(int amount, int pending_deposit_amount) = found ? unpack_nominator(nominator) : (0, 0);
if (state == 0) {
amount += msg_value;
} else {
pending_deposit_amount += msg_value;
}
throw_unless(67, amount + pending_deposit_amount >= min_nominator_stake);
throw_unless(68, cell_depth(nominators) < max(5, binary_log_ceil(nominators_count) * 2) ); ;; prevent dict depth ddos
nominators~udict_set_builder(ADDR_SIZE(), sender_addr, pack_nominator(amount, pending_deposit_amount));
}
if (action == 119) { ;; "w" - withdraw request (any time)
if (state == 0) {
(nominators, withdraw_requests, int new_balance, nominators_count) = withdraw_nominator(sender_addr, nominators, withdraw_requests, balance, nominators_count);
if (new_balance - msg_value >= MIN_TONS_FOR_STORAGE()) {
send_excesses(sender_address);
}
} else {
(slice nominator, int found) = nominators.udict_get?(ADDR_SIZE(), sender_addr);
throw_unless(69, found);
withdraw_requests~udict_set_builder(ADDR_SIZE(), sender_addr, begin_cell());
send_excesses(sender_address);
}
}
if (is_vote) {
int authorized = (sender_wc == -1) & (sender_addr == validator_address);
if (~ authorized) {
throw_unless(121, sender_wc == 0);
(slice nominator, authorized) = nominators.udict_get?(ADDR_SIZE(), sender_addr);
throw_unless(122, authorized);
(int amount, int pending_deposit_amount) = unpack_nominator(nominator);
throw_unless(123, amount > 0);
}
int proposal_hash = in_msg_body~load_text_hex_number(32);
in_msg_body.end_parse();
int support = action == 121;
(slice votes_slice, int found) = config_proposal_votings.udict_get?(256, proposal_hash);
if (~ found) {
;; require higher fee to prevent dictionary spam
int fee = ONE_TON();
int power = cell_depth(config_proposal_votings);
repeat (power) {
fee = muldiv(fee, 15, 10);
}
throw_unless(123, msg_value >= fee);
}
(cell votes_dict, int votes_create_time) = found ? (votes_slice~load_dict(), votes_slice~load_uint(32)) : (new_dict(), now());
(_, int vote_found) = votes_dict.udict_get?(256, sender_addr);
throw_if(124, vote_found);
votes_dict~udict_set_builder(256, sender_addr, begin_cell().store_int(support, 1).store_uint(now(), 32));
builder new_votes = begin_cell().store_dict(votes_dict).store_uint(votes_create_time, 32);
config_proposal_votings~udict_set_builder(256, proposal_hash, new_votes);
if (found) {
send_excesses(sender_address);
}
}
} else {
int query_id = in_msg_body~load_uint(64);
if (is_elector_address(sender_wc, sender_addr)) { ;; response from elector
accept_message();
if (op == op::recover_stake_ok()) {
state = 0;
int reward = msg_value - stake_amount_sent;
int nominators_reward = 0;
if (reward <= 0) {
validator_amount += reward;
if (validator_amount < 0) {
;; even this should never happen
nominators_reward = validator_amount;
validator_amount = 0;
}
} else {
int validator_reward = (reward * validator_reward_share) / 10000;
if (validator_reward > reward) { ;; Theoretical invalid case if validator_reward_share > 10000
validator_reward = reward;
}
validator_amount += validator_reward;
nominators_reward = reward - validator_reward;
}
nominators = distribute_share(nominators_reward, nominators); ;; call even if there was no reward to process deposit requests
stake_amount_sent = 0;
}
if (state == 1) {
if (op == op::new_stake_error()) { ;; error when new_stake; stake returned
state = 0;
}
if (op == op::new_stake_ok()) {
state = 2;
}
}
;; else just accept coins from elector
} else {
;; throw on any unexpected request so that the coins is bounced back to the sender in case of a typo
throw_unless(70, ((op >= 1) & (op <= 7)) | (op == op::recover_stake()) | (op == op::new_stake()));
if (op == 1) {
;; just accept coins
}
if (op == 2) { ;; process withdraw requests (at any time while the balance is enough)
int limit = in_msg_body~load_uint(8);
(nominators, withdraw_requests, nominators_count, int new_balance) = process_withdraw_requests(nominators, withdraw_requests, balance, nominators_count, limit);
if (new_balance - msg_value >= MIN_TONS_FOR_STORAGE()) {
send_excesses(sender_address);
}
}
if (op == 3) { ;; emergency process withdraw request (at any time if the balance is enough)
int request_address = in_msg_body~load_uint(ADDR_SIZE());
(slice withdraw_request, int found) = withdraw_requests.udict_get?(ADDR_SIZE(), request_address);
throw_unless(71, found);
(nominators, withdraw_requests, int new_balance, nominators_count) = withdraw_nominator(request_address, nominators, withdraw_requests, balance, nominators_count);
if (new_balance - msg_value >= MIN_TONS_FOR_STORAGE()) {
send_excesses(sender_address);
}
}
if (op == 6) { ;; update current valudator set hash (anyone can invoke)
throw_unless(113, validator_set_changes_count < 3);
(int utime_since, int utime_until, cell vset) = get_current_validator_set();
int current_hash = cell_hash(vset);
if (saved_validator_set_hash != current_hash) {
saved_validator_set_hash = current_hash;
validator_set_changes_count += 1;
validator_set_change_time = now();
}
send_excesses(sender_address);
}
if (op == 7) { ;; clean up outdating votings
int t = now();
int proposal_hash = -1;
do {
(proposal_hash, slice votes_slice, int found) = config_proposal_votings.udict_get_next?(256, proposal_hash);
if (found) {
(cell votes_dict, int votes_create_time) = (votes_slice~load_dict(), votes_slice~load_uint(32));
if (t - votes_create_time > VOTES_LIFETIME()) {
config_proposal_votings~udict_delete?(256, proposal_hash);
}
}
} until (~ found);
send_excesses(sender_address);
}
if (op == op::recover_stake()) { ;; send recover_stake to elector (anyone can send)
;; We need to take all credits from the elector at once,
;; because if we do not take all at once, then it will be processed as a fine by pool.
;; In the elector, credits (`credit_to`) are accrued in three places:
;; 1) return of surplus stake in elections (`try_elect`)
;; 2) reward for complaint when punish (`punish`) - before unfreezing
;; 3) unfreeze round (`unfreeze_without_bonuses`/`unfreeze_with_bonuses`)
;; We need to be guaranteed to wait for unfreezing round and only then send `recover_stake`.
;; So we are waiting for the change of 3 validator sets.
;; ADDITIONAL NOTE:
;; In a special case (if the network was down), the config theoretically can refuse the elector to save a new round after election - https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/config-code.fc#L494
;; and the elector will start a new election - https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L364
;; in this case, our pool will have to skip the round, but it will be able to recover stake later
throw_unless(111, validator_set_changes_count >= 2);
throw_unless(112, (validator_set_changes_count > 2) | (now() - validator_set_change_time > stake_held_for + 60));
;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L887
cell payload = begin_cell().store_uint(op::recover_stake(), 32).store_uint(query_id, 64).end_cell();
send_msg(elector_address(), 0, payload, BOUNCEABLE(), SEND_MODE_REMAINING_AMOUNT()); ;; bounceable, carry all the remaining value of the inbound message, fee deducted from amount, revert on errors
}
;; message from validator
if (op == 4) { ;; deposit validator (any time)
throw_unless(73, (sender_wc == -1) & (sender_addr == validator_address));
msg_value -= DEPOSIT_PROCESSING_FEE();
throw_unless(74, msg_value > 0);
validator_amount += msg_value;
}
if (op == 5) { ;; withdraw validator (after recover_stake and before new_stake)
throw_unless(74, state == 0); ;; no withdraw request because validator software can wait right time
throw_unless(75, (sender_wc == -1) & (sender_addr == validator_address));
int request_amount = in_msg_body~load_coins();
throw_unless(78, request_amount > 0);
int total_nominators_amount = calculate_total_nominators_amount(nominators);
;; the validator can withdraw everything that does not belong to the nominators
throw_unless(76, request_amount <= balance - MIN_TONS_FOR_STORAGE() - total_nominators_amount);
validator_amount -= request_amount;
if (validator_amount < 0) {
validator_amount = 0;
}
send_msg(make_address(-1, validator_address), request_amount, null(), NON_BOUNCEABLE(), 0); ;; non-bouneable, fee deducted from amount, revert on errors
int new_balance = balance - request_amount;
if (new_balance - msg_value >= MIN_TONS_FOR_STORAGE()) {
send_excesses(sender_address);
}
}
if (op == op::new_stake()) {
throw_unless(78, (sender_wc == -1) & (sender_addr == validator_address));
throw_unless(79, state == 0);
throw_unless(80, query_id); ;; query_id must be greater then 0 to receive confirmation message from elector
throw_unless(86, msg_value >= ONE_TON()); ;; must be greater then new_stake sending to elector fee
int value = in_msg_body~load_coins();
slice msg = in_msg_body;
stake_at = check_new_stake_msg(in_msg_body);
stake_amount_sent = value - ONE_TON();
throw_unless(81, value >= MIN_STAKE_TO_SEND());
throw_unless(82, value <= balance - MIN_TONS_FOR_STORAGE());
throw_unless(83, validator_amount >= min_validator_stake);
throw_unless(84, validator_amount >= max_recommended_punishment_for_validator_misbehaviour(stake_amount_sent));
throw_unless(85, cell_null?(withdraw_requests)); ;; no withdraw requests
state = 1;
(int utime_since, int utime_until, cell vset) = get_current_validator_set();
saved_validator_set_hash = cell_hash(vset); ;; current validator set, we will be in next validator set
validator_set_changes_count = 0;
validator_set_change_time = utime_since;
stake_held_for = get_validator_config(); ;; save `stake_held_for` in case the config changes in the process
send_msg(elector_address(), value, begin_cell().store_uint(op, 32).store_uint(query_id, 64).store_slice(msg).end_cell(), BOUNCEABLE(), SEND_MODE_PAY_FEE_SEPARATELY()); ;; pay fee separately, rever on errors
}
}
}
save_data(
state,
nominators_count,
stake_amount_sent,
validator_amount,
pack_config(validator_address, validator_reward_share, max_nominators_count, min_validator_stake, min_nominator_stake),
nominators,
withdraw_requests,
stake_at,
saved_validator_set_hash,
validator_set_changes_count,
validator_set_change_time,
stake_held_for,
config_proposal_votings
);
}
;; Get methods
_ get_pool_data() method_id {
return load_data();
}
int has_withdraw_requests() method_id {
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
return ~ cell_null?(withdraw_requests);
}
(int, int, int) get_nominator_data(int nominator_address) method_id {
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
(slice nominator, int found) = nominators.udict_get?(ADDR_SIZE(), nominator_address);
throw_unless(86, found);
(int amount, int pending_deposit_amount) = unpack_nominator(nominator);
(slice withdraw_request, int withdraw_found) = withdraw_requests.udict_get?(ADDR_SIZE(), nominator_address);
return (amount, pending_deposit_amount, withdraw_found);
}
int get_max_punishment(int stake) method_id {
return max_recommended_punishment_for_validator_misbehaviour(stake);
}
tuple list_nominators() method_id {
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
var list = null();
int address = -1;
do {
(address, slice nominator, int found) = nominators.udict_get_next?(ADDR_SIZE(), address);
if (found) {
(int amount, int pending_deposit_amount) = unpack_nominator(nominator);
(_, int withdraw_requested) = withdraw_requests.udict_get?(ADDR_SIZE(), address);
list = cons(tuple4(address, amount, pending_deposit_amount, withdraw_requested), list);
}
} until (~ found);
return list;
}
tuple list_votes() method_id {
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
var list = null();
int proposal_hash = -1;
do {
(proposal_hash, slice votes_slice, int found) = config_proposal_votings.udict_get_next?(256, proposal_hash);
if (found) {
(cell votes_dict, int votes_create_time) = (votes_slice~load_dict(), votes_slice~load_uint(32));
list = cons(pair(proposal_hash, votes_create_time), list);
}
} until (~ found);
return list;
}
tuple list_voters(int proposal_hash) method_id {
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
var list = null();
(slice votes_slice, int found) = config_proposal_votings.udict_get?(256, proposal_hash);
throw_unless(133, found);
cell votes_dict = votes_slice~load_dict();
int address = -1;
do {
(address, slice cs, int found) = votes_dict.udict_get_next?(ADDR_SIZE(), address);
if (found) {
(int support, int vote_time) = (cs~load_int(1), cs~load_uint(32));
list = cons(triple(address, support, vote_time), list);
}
} until (~ found);
return list;
}

View file

@ -0,0 +1,211 @@
;; Standard library for funC
;;
forall X -> tuple cons(X head, tuple tail) asm "CONS";
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
int cell_hash(cell c) asm "HASHCU";
int slice_hash(slice s) asm "HASHSU";
int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
cont get_c3() impure asm "c3 PUSH";
() set_c3(cont c) impure asm "c3 POP";
cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
cell preload_ref(slice s) asm "PLDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
builder store_slice(builder b, slice s) asm "STSLICER";
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_dict(builder b, cell c) asm(c b) "STDICT";
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
tuple parse_addr(slice s) asm "PARSEMSGADDR";
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";
int random() impure asm "RANDU256";
int rand(int range) impure asm "RAND";
int get_seed() impure asm "RANDSEED";
int set_seed() impure asm "SETRAND";
() randomize(int x) impure asm "ADDRAND";
() randomize_lt() impure asm "LTIME" "ADDRAND";
builder store_coins(builder b, int x) asm "STVARUINT16";
(slice, int) load_coins(slice s) asm( -> 1 0) "LDVARUINT16";

View file

@ -0,0 +1,23 @@
const op::offer_storage_contract = 0x107c49ef;
const op::close_contract = 0x79f937ea;
const op::contract_deployed = 0xbf7bd0c1;
const op::storage_contract_confirmed = 0xd4caedcd;
const op::reward_withdrawal = 0xa91baf56;
const op::storage_contract_terminated = 0xb6236d63;
const op::accept_storage_contract = 0x7a361688;
const op::withdraw = 0x46ed2e94;
const op::proof_storage = 0x419d5d4d;
const op::update_pubkey = 0x53f34cd6;
const op::update_storage_params = 0x54cbf19b;
const error::not_enough_money = 1001;
const error::unauthorized = 401;
const error::wrong_proof = 1002;
const error::contract_not_active = 1003;
const error::file_too_small = 1004;
const error::file_too_big = 1005;
const error::no_new_contracts = 1006;
const error::contract_already_active = 1007;
const error::no_microchunk_hash = 1008;
const error::provider_params_changed = 1009;

View file

@ -0,0 +1,625 @@
;; Standard library for funC
;;
{-
# Tuple manipulation primitives
The names and the types are mostly self-explaining.
See [polymorhism with forall](https://ton.org/docs/#/func/functions?id=polymorphism-with-forall)
for more info on the polymorphic functions.
Note that currently values of atomic type `tuple` can't be cast to composite tuple type (e.g. `[int, cell]`)
and vise versa.
-}
{-
# Lisp-style lists
Lists can be represented as nested 2-elements tuples.
Empty list is conventionally represented as TVM `null` value (it can be obtained by calling [null()]).
For example, tuple `(1, (2, (3, null)))` represents list `[1, 2, 3]`. Elements of a list can be of different types.
-}
;;; Adds an element to the beginning of lisp-style list.
forall X -> tuple cons(X head, tuple tail) asm "CONS";
;;; Extracts the head and the tail of lisp-style list.
forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
;;; Extracts the tail and the head of lisp-style list.
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
;;; Returns the head of lisp-style list.
forall X -> X car(tuple list) asm "CAR";
;;; Returns the tail of lisp-style list.
tuple cdr(tuple list) asm "CDR";
;;; Creates tuple with zero elements.
tuple empty_tuple() asm "NIL";
;;; Appends a value `x` to a `Tuple t = (x1, ..., xn)`, but only if the resulting `Tuple t' = (x1, ..., xn, x)`
;;; is of length at most 255. Otherwise throws a type check exception.
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
;;; Creates a tuple of length one with given argument as element.
forall X -> [X] single(X x) asm "SINGLE";
;;; Unpacks a tuple of length one
forall X -> X unsingle([X] t) asm "UNSINGLE";
;;; Creates a tuple of length two with given arguments as elements.
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
;;; Unpacks a tuple of length two
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
;;; Creates a tuple of length three with given arguments as elements.
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
;;; Unpacks a tuple of length three
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
;;; Creates a tuple of length four with given arguments as elements.
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
;;; Unpacks a tuple of length four
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
;;; Returns the first element of a tuple (with unknown element types).
forall X -> X first(tuple t) asm "FIRST";
;;; Returns the second element of a tuple (with unknown element types).
forall X -> X second(tuple t) asm "SECOND";
;;; Returns the third element of a tuple (with unknown element types).
forall X -> X third(tuple t) asm "THIRD";
;;; Returns the fourth element of a tuple (with unknown element types).
forall X -> X fourth(tuple t) asm "3 INDEX";
;;; Returns the first element of a pair tuple.
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
;;; Returns the second element of a pair tuple.
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
;;; Returns the first element of a triple tuple.
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
;;; Returns the second element of a triple tuple.
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
;;; Returns the third element of a triple tuple.
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
;;; Push null element (casted to given type)
;;; By the TVM type `Null` FunC represents absence of a value of some atomic type.
;;; So `null` can actually have any atomic type.
forall X -> X null() asm "PUSHNULL";
;;; Moves a variable [x] to the top of the stack
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
;;; Returns the current Unix time as an Integer
int now() asm "NOW";
;;; Returns the internal address of the current smart contract as a Slice with a `MsgAddressInt`.
;;; If necessary, it can be parsed further using primitives such as [parse_std_addr].
slice my_address() asm "MYADDR";
;;; Returns the balance of the smart contract as a tuple consisting of an int
;;; (balance in nanotoncoins) and a `cell`
;;; (a dictionary with 32-bit keys representing the balance of "extra currencies")
;;; at the start of Computation Phase.
;;; Note that RAW primitives such as [send_raw_message] do not update this field.
[int, cell] get_balance() asm "BALANCE";
;;; Returns the logical time of the current transaction.
int cur_lt() asm "LTIME";
;;; Returns the starting logical time of the current block.
int block_lt() asm "BLOCKLT";
;;; Computes the representation hash of a `cell` [c] and returns it as a 256-bit unsigned integer `x`.
;;; Useful for signing and checking signatures of arbitrary entities represented by a tree of cells.
int cell_hash(cell c) asm "HASHCU";
;;; Computes the hash of a `slice s` and returns it as a 256-bit unsigned integer `x`.
;;; The result is the same as if an ordinary cell containing only data and references from `s` had been created
;;; and its hash computed by [cell_hash].
int slice_hash(slice s) asm "HASHSU";
;;; Computes sha256 of the data bits of `slice` [s]. If the bit length of `s` is not divisible by eight,
;;; throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`.
int string_hash(slice s) asm "SHA256U";
{-
# Signature checks
-}
;;; Checks the Ed25519-`signature` of a `hash` (a 256-bit unsigned integer, usually computed as the hash of some data)
;;; using [public_key] (also represented by a 256-bit unsigned integer).
;;; The signature must contain at least 512 data bits; only the first 512 bits are used.
;;; The result is `1` if the signature is valid, `0` otherwise.
;;; Note that `CHKSIGNU` creates a 256-bit slice with the hash and calls `CHKSIGNS`.
;;; That is, if [hash] is computed as the hash of some data, these data are hashed twice,
;;; the second hashing occurring inside `CHKSIGNS`.
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
;;; Checks whether [signature] is a valid Ed25519-signature of the data portion of `slice data` using `public_key`,
;;; similarly to [check_signature].
;;; If the bit length of [data] is not divisible by eight, throws a cell underflow exception.
;;; The verification of Ed25519 signatures is the standard one,
;;; with sha256 used to reduce [data] to the 256-bit number that is actually signed.
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
{---
# Computation of boc size
The primitives below may be useful for computing storage fees of user-provided data.
-}
;;; Returns `(x, y, z, -1)` or `(null, null, null, 0)`.
;;; Recursively computes the count of distinct cells `x`, data bits `y`, and cell references `z`
;;; in the DAG rooted at `cell` [c], effectively returning the total storage used by this DAG taking into account
;;; the identification of equal cells.
;;; The values of `x`, `y`, and `z` are computed by a depth-first traversal of this DAG,
;;; with a hash table of visited cell hashes used to prevent visits of already-visited cells.
;;; The total count of visited cells `x` cannot exceed non-negative [max_cells];
;;; otherwise the computation is aborted before visiting the `(max_cells + 1)`-st cell and
;;; a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`.
(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";
;;; Similar to [compute_data_size?], but accepting a `slice` [s] instead of a `cell`.
;;; The returned value of `x` does not take into account the cell that contains the `slice` [s] itself;
;;; however, the data bits and the cell references of [s] are accounted for in `y` and `z`.
(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";
;;; A non-quiet version of [compute_data_size?] that throws a cell overflow exception (`8`) on failure.
(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;;; A non-quiet version of [slice_compute_data_size?] that throws a cell overflow exception (8) on failure.
(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
;;; Throws an exception with exit_code excno if cond is not 0 (commented since implemented in compilator)
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
{--
# Debug primitives
Only works for local TVM execution with debug level verbosity
-}
;;; Dumps the stack (at most the top 255 values) and shows the total stack depth.
() dump_stack() impure asm "DUMPSTK";
{-
# Persistent storage save and load
-}
;;; Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later.
cell get_data() asm "c4 PUSH";
;;; Sets `cell` [c] as persistent contract data. You can update persistent contract storage with this primitive.
() set_data(cell c) impure asm "c4 POP";
{-
# Continuation primitives
-}
;;; Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls.
;;; The primitive returns the current value of `c3`.
cont get_c3() impure asm "c3 PUSH";
;;; Updates the current value of `c3`. Usually, it is used for updating smart contract code in run-time.
;;; Note that after execution of this primitive the current code
;;; (and the stack of recursive function calls) won't change,
;;; but any other function call will use a function from the new code.
() set_c3(cont c) impure asm "c3 POP";
;;; Transforms a `slice` [s] into a simple ordinary continuation `c`, with `c.code = s` and an empty stack and savelist.
cont bless(slice s) impure asm "BLESS";
{---
# Gas related primitives
-}
;;; Sets current gas limit `gl` to its maximal allowed value `gm`, and resets the gas credit `gc` to zero,
;;; decreasing the value of `gr` by `gc` in the process.
;;; In other words, the current smart contract agrees to buy some gas to finish the current transaction.
;;; This action is required to process external messages, which bring no value (hence no gas) with themselves.
;;;
;;; For more details check [accept_message effects](https://ton.org/docs/#/smart-contracts/accept).
() accept_message() impure asm "ACCEPT";
;;; Sets current gas limit `gl` to the minimum of limit and `gm`, and resets the gas credit `gc` to zero.
;;; If the gas consumed so far (including the present instruction) exceeds the resulting value of `gl`,
;;; an (unhandled) out of gas exception is thrown before setting new gas limits.
;;; Notice that [set_gas_limit] with an argument `limit ≥ 2^63 1` is equivalent to [accept_message].
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
;;; Commits the current state of registers `c4` (“persistent data”) and `c5` (“actions”)
;;; so that the current execution is considered “successful” with the saved values even if an exception
;;; in Computation Phase is thrown later.
() commit() impure asm "COMMIT";
;;; Not implemented
;;() buy_gas(int gram) impure asm "BUYGAS";
;;; Computes the amount of gas that can be bought for `amount` nanoTONs,
;;; and sets `gl` accordingly in the same way as [set_gas_limit].
() buy_gas(int amount) impure asm "BUYGAS";
;;; Computes the minimum of two integers [x] and [y].
int min(int x, int y) asm "MIN";
;;; Computes the maximum of two integers [x] and [y].
int max(int x, int y) asm "MAX";
;;; Sorts two integers.
(int, int) minmax(int x, int y) asm "MINMAX";
;;; Computes the absolute value of an integer [x].
int abs(int x) asm "ABS";
{-
# Slice primitives
It is said that a primitive _loads_ some data,
if it returns the data and the remainder of the slice
(so it can also be used as [modifying method](https://ton.org/docs/#/func/statements?id=modifying-methods)).
It is said that a primitive _preloads_ some data, if it returns only the data
(it can be used as [non-modifying method](https://ton.org/docs/#/func/statements?id=non-modifying-methods)).
Unless otherwise stated, loading and preloading primitives read the data from a prefix of the slice.
-}
;;; Converts a `cell` [c] into a `slice`. Notice that [c] must be either an ordinary cell,
;;; or an exotic cell (see [TVM.pdf](https://ton-blockchain.github.io/docs/tvm.pdf), 3.1.2)
;;; which is automatically loaded to yield an ordinary cell `c'`, converted into a `slice` afterwards.
slice begin_parse(cell c) asm "CTOS";
;;; Checks if [s] is empty. If not, throws an exception.
() end_parse(slice s) impure asm "ENDS";
;;; Loads the first reference from the slice.
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
;;; Preloads the first reference from the slice.
cell preload_ref(slice s) asm "PLDREF";
{- Functions below are commented because are implemented on compilator level for optimisation -}
;;; Loads a signed [len]-bit integer from a slice [s].
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;;; Loads an unsigned [len]-bit integer from a slice [s].
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;;; Preloads a signed [len]-bit integer from a slice [s].
;; int preload_int(slice s, int len) asm "PLDIX";
;;; Preloads an unsigned [len]-bit integer from a slice [s].
;; int preload_uint(slice s, int len) asm "PLDUX";
;;; Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`.
;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;;; Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`.
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
;;; Loads serialized amount of TonCoins (any unsigned integer up to `2^128 - 1`).
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
(slice, int) load_coins(slice s) asm( -> 1 0) "LDGRAMS";
;;; Returns all but the first `0 ≤ len ≤ 1023` bits of `slice` [s].
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
;;; Returns the first `0 ≤ len ≤ 1023` bits of `slice` [s].
slice first_bits(slice s, int len) asm "SDCUTFIRST";
;;; Returns all but the last `0 ≤ len ≤ 1023` bits of `slice` [s].
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
;;; Returns the last `0 ≤ len ≤ 1023` bits of `slice` [s].
slice slice_last(slice s, int len) asm "SDCUTLAST";
;;; Loads a dictionary `D` (HashMapE) from `slice` [s].
;;; (returns `null` if `nothing` constructor is used).
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
;;; Preloads a dictionary `D` from `slice` [s].
cell preload_dict(slice s) asm "PLDDICT";
;;; Loads a dictionary as [load_dict], but returns only the remainder of the slice.
slice skip_dict(slice s) asm "SKIPDICT";
;;; Loads (Maybe ^Cell) from `slice` [s].
;;; In other words loads 1 bit and if it is true
;;; loads first ref and return it with slice remainder
;;; otherwise returns `null` and slice remainder
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
;;; Preloads (Maybe ^Cell) from `slice` [s].
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
;;; Returns the depth of `cell` [c].
;;; If [c] has no references, then return `0`;
;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [c].
;;; If [c] is a `null` instead of a cell, returns zero.
int cell_depth(cell c) asm "CDEPTH";
{-
# Slice size primitives
-}
;;; Returns the number of references in `slice` [s].
int slice_refs(slice s) asm "SREFS";
;;; Returns the number of data bits in `slice` [s].
int slice_bits(slice s) asm "SBITS";
;;; Returns both the number of data bits and the number of references in `slice` [s].
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
;;; Checks whether a `slice` [s] is empty (i.e., contains no bits of data and no cell references).
int slice_empty?(slice s) asm "SEMPTY";
;;; Checks whether `slice` [s] has no bits of data.
int slice_data_empty?(slice s) asm "SDEMPTY";
;;; Checks whether `slice` [s] has no references.
int slice_refs_empty?(slice s) asm "SREMPTY";
;;; Returns the depth of `slice` [s].
;;; If [s] has no references, then returns `0`;
;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [s].
int slice_depth(slice s) asm "SDEPTH";
{-
# Builder size primitives
-}
;;; Returns the number of cell references already stored in `builder` [b]
int builder_refs(builder b) asm "BREFS";
;;; Returns the number of data bits already stored in `builder` [b].
int builder_bits(builder b) asm "BBITS";
;;; Returns the depth of `builder` [b].
;;; If no cell references are stored in [b], then returns 0;
;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [b].
int builder_depth(builder b) asm "BDEPTH";
{-
# Builder primitives
It is said that a primitive _stores_ a value `x` into a builder `b`
if it returns a modified version of the builder `b'` with the value `x` stored at the end of it.
It can be used as [non-modifying method](https://ton.org/docs/#/func/statements?id=non-modifying-methods).
All the primitives below first check whether there is enough space in the `builder`,
and only then check the range of the value being serialized.
-}
;;; Creates a new empty `builder`.
builder begin_cell() asm "NEWC";
;;; Converts a `builder` into an ordinary `cell`.
cell end_cell(builder b) asm "ENDC";
;;; Stores a reference to `cell` [c] into `builder` [b].
builder store_ref(builder b, cell c) asm(c b) "STREF";
;;; Stores an unsigned [len]-bit integer `x` into `b` for `0 ≤ len ≤ 256`.
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;;; Stores a signed [len]-bit integer `x` into `b` for` 0 ≤ len ≤ 257`.
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
;;; Stores `slice` [s] into `builder` [b]
builder store_slice(builder b, slice s) asm "STSLICER";
;;; Stores (serializes) an integer [x] in the range `0..2^128 1` into `builder` [b].
;;; The serialization of [x] consists of a 4-bit unsigned big-endian integer `l`,
;;; which is the smallest integer `l ≥ 0`, such that `x < 2^8l`,
;;; followed by an `8l`-bit unsigned big-endian representation of [x].
;;; If [x] does not belong to the supported range, a range check exception is thrown.
;;;
;;; Store amounts of TonCoins to the builder as VarUInteger 16
builder store_grams(builder b, int x) asm "STGRAMS";
builder store_coins(builder b, int x) asm "STGRAMS";
;;; Stores dictionary `D` represented by `cell` [c] or `null` into `builder` [b].
;;; In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise.
builder store_dict(builder b, cell c) asm(c b) "STDICT";
;;; Stores (Maybe ^Cell) to builder:
;;; if cell is null store 1 zero bit
;;; otherwise store 1 true bit and ref to cell
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
{-
# Address manipulation primitives
The address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme:
```TL-B
addr_none$00 = MsgAddressExt;
addr_extern$01 len:(## 8) external_address:(bits len)
= MsgAddressExt;
anycast_info$_ depth:(#<= 30) { depth >= 1 }
rewrite_pfx:(bits depth) = Anycast;
addr_std$10 anycast:(Maybe Anycast)
workchain_id:int8 address:bits256 = MsgAddressInt;
addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
_ _:MsgAddressInt = MsgAddress;
_ _:MsgAddressExt = MsgAddress;
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
src:MsgAddress dest:MsgAddressInt
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
```
A deserialized `MsgAddress` is represented by a tuple `t` as follows:
- `addr_none` is represented by `t = (0)`,
i.e., a tuple containing exactly one integer equal to zero.
- `addr_extern` is represented by `t = (1, s)`,
where slice `s` contains the field `external_address`. In other words, `
t` is a pair (a tuple consisting of two entries), containing an integer equal to one and slice `s`.
- `addr_std` is represented by `t = (2, u, x, s)`,
where `u` is either a `null` (if `anycast` is absent) or a slice `s'` containing `rewrite_pfx` (if anycast is present).
Next, integer `x` is the `workchain_id`, and slice `s` contains the address.
- `addr_var` is represented by `t = (3, u, x, s)`,
where `u`, `x`, and `s` have the same meaning as for `addr_std`.
-}
;;; Loads from slice [s] the only prefix that is a valid `MsgAddress`,
;;; and returns both this prefix `s'` and the remainder `s''` of [s] as slices.
(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";
;;; Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`.
;;; If [s] is not a valid `MsgAddress`, a cell deserialization exception is thrown.
tuple parse_addr(slice s) asm "PARSEMSGADDR";
;;; Parses slice [s] containing a valid `MsgAddressInt` (usually a `msg_addr_std`),
;;; applies rewriting from the anycast (if present) to the same-length prefix of the address,
;;; and returns both the workchain and the 256-bit address as integers.
;;; If the address is not 256-bit, or if [s] is not a valid serialization of `MsgAddressInt`,
;;; throws a cell deserialization exception.
(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";
;;; A variant of [parse_std_addr] that returns the (rewritten) address as a slice [s],
;;; even if it is not exactly 256 bit long (represented by a `msg_addr_var`).
(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";
{-
# Dictionary primitives
-}
;;; Sets the value associated with [key_len]-bit key signed index in dictionary [dict] to [value] (cell),
;;; and returns the resulting dictionary.
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
;;; Sets the value associated with [key_len]-bit key unsigned index in dictionary [dict] to [value] (cell),
;;; and returns the resulting dictionary.
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF" "NULLSWAPIFNOT";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF" "NULLSWAPIFNOT";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
;;; Creates an empty dictionary, which is actually a null value. Equivalent to PUSHNULL
cell new_dict() asm "NEWDICT";
;;; Checks whether a dictionary is empty. Equivalent to cell_null?.
int dict_empty?(cell c) asm "DICTEMPTY";
{- Prefix dictionary primitives -}
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
;;; Returns the value of the global configuration parameter with integer index `i` as a `cell` or `null` value.
cell config_param(int x) asm "CONFIGOPTPARAM";
;;; Checks whether c is a null. Note, that FunC also has polymorphic null? built-in.
int cell_null?(cell c) asm "ISNULL";
;;; Creates an output action which would reserve exactly amount nanotoncoins (if mode = 0), at most amount nanotoncoins (if mode = 2), or all but amount nanotoncoins (if mode = 1 or mode = 3), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying amount nanotoncoins (or b amount nanotoncoins, where b is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit +2 in mode means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit +8 in mode means `amount <- -amount` before performing any further actions. Bit +4 in mode means that amount is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently, amount must be a non-negative integer, and mode must be in the range 0..15.
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
;;; Similar to raw_reserve, but also accepts a dictionary extra_amount (represented by a cell or null) with extra currencies. In this way currencies other than TonCoin can be reserved.
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
;;; Sends a raw message contained in msg, which should contain a correctly serialized object Message X, with the only exception that the source address is allowed to have dummy value addr_none (to be automatically replaced with the current smart contract address), and ihr_fee, fwd_fee, created_lt and created_at fields can have arbitrary values (to be rewritten with correct values during the action phase of the current transaction). Integer parameter mode contains the flags. Currently mode = 0 is used for ordinary messages; mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message); mode = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message (if bit 0 is not set, the gas fees are deducted from this amount); mode' = mode + 1 means that the sender wants to pay transfer fees separately; mode' = mode + 2 means that any errors arising while processing this message during the action phase should be ignored. Finally, mode' = mode + 32 means that the current account must be destroyed if its resulting balance is zero. This flag is usually employed together with +128.
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
;;; Creates an output action that would change this smart contract code to that given by cell new_code. Notice that this change will take effect only after the successful termination of the current run of the smart contract
() set_code(cell new_code) impure asm "SETCODE";
;;; Generates a new pseudo-random unsigned 256-bit integer x. The algorithm is as follows: if r is the old value of the random seed, considered as a 32-byte array (by constructing the big-endian representation of an unsigned 256-bit integer), then its sha512(r) is computed; the first 32 bytes of this hash are stored as the new value r' of the random seed, and the remaining 32 bytes are returned as the next random value x.
int random() impure asm "RANDU256";
;;; Generates a new pseudo-random integer z in the range 0..range1 (or range..1, if range < 0). More precisely, an unsigned random value x is generated as in random; then z := x * range / 2^256 is computed.
int rand(int range) impure asm "RAND";
;;; Returns the current random seed as an unsigned 256-bit Integer.
int get_seed() impure asm "RANDSEED";
;;; Sets the random seed to unsigned 256-bit seed.
() set_seed(int) impure asm "SETRAND";
;;; Mixes unsigned 256-bit integer x into the random seed r by setting the random seed to sha256 of the concatenation of two 32-byte strings: the first with the big-endian representation of the old seed r, and the second with the big-endian representation of x.
() randomize(int x) impure asm "ADDRAND";
;;; Equivalent to randomize(cur_lt());.
() randomize_lt() impure asm "LTIME" "ADDRAND";
;;; Checks whether the data parts of two slices coinside
int equal_slice_bits (slice a, slice b) asm "SDEQ";
;;; Concatenates two builders
builder store_builder(builder to, builder from) asm "STBR";

View file

@ -0,0 +1,266 @@
#include "stdlib.fc";
#include "constants.fc";
const CHUNK_SIZE = 64;
const fee::receipt_value = 20000000;
const fee::storage = 10000000;
{-
storage#_ active:Bool
balance:Coins provider:MsgAddress
merkle_hash:uint256 file_size:uint64 next_proof_byte:uint64
rate_per_mb_day:Coins
max_span:uint32 last_proof_time:uint32
^[client:MsgAddress torrent_hash:uint256] = Storage;
-}
(slice, int) begin_parse_special(cell c) asm "x{D739} s,";
int check_proof(int merkle_hash, int byte_to_proof, int file_size, cell file_dict_proof) {
(slice cs, int special) = file_dict_proof.begin_parse_special();
if (~ special) {
return false;
}
if (cs~load_uint(8) != 3) { ;; Merkle proof
return false;
}
if (cs~load_uint(256) != merkle_hash) {
return false;
}
cell file_dict = cs~load_ref();
int key_len = 0;
while ((CHUNK_SIZE << key_len) < file_size) {
key_len += 1;
}
(slice data, int found?) = file_dict.udict_get?(key_len, byte_to_proof / CHUNK_SIZE);
if(found?) {
return true;
}
return false;
}
() add_to_balance(int amount) impure inline_ref {
var ds = get_data().begin_parse();
var (active, balance, residue) = (ds~load_int(1), ds~load_grams(), ds);
balance += amount;
begin_cell()
.store_int(active, 1)
.store_coins(balance)
.store_slice(residue)
.end_cell().set_data();
}
(slice, int) get_client_data(ds) {
ds = ds.preload_ref().begin_parse();
return (ds~load_msg_addr(), ds~load_uint(256));
}
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) { ;; ignore all bounced messages
return ();
}
slice sender_address = cs~load_msg_addr();
if (in_msg_body.slice_empty?()) {
return add_to_balance(msg_value);
}
int op = in_msg_body~load_uint(32);
if (op == 0) {
return add_to_balance(msg_value);
}
int query_id = in_msg_body~load_uint(64);
if(op == op::offer_storage_contract) {
add_to_balance(msg_value - 2 * fee::receipt_value);
var (client, torrent_hash) = get_client_data(get_data().begin_parse());
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(client)
.store_coins(fee::receipt_value)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(op::contract_deployed, 32)
.store_uint(query_id, 64)
.store_uint(torrent_hash, 256)
.end_cell();
send_raw_message(msg, 0);
}
if (op == op::accept_storage_contract) {
var ds = get_data().begin_parse();
(int active, int balance, slice provider, slice rest) =
(ds~load_int(1), ds~load_coins(), ds~load_msg_addr(), ds);
throw_unless(error::contract_already_active, ~ active);
throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider));
begin_cell()
.store_int(true, 1)
.store_coins(balance)
.store_slice(provider)
.store_slice(rest)
.end_cell().set_data();
var (client, torrent_hash) = get_client_data(rest);
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(client)
.store_coins(fee::receipt_value)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(op::storage_contract_confirmed, 32)
.store_uint(cur_lt(), 64)
.store_uint(torrent_hash, 256)
.end_cell();
send_raw_message(msg, 0);
}
if (op == op::close_contract) {
var ds = get_data().begin_parse();
(int active, int balance, slice provider, slice rest) =
(ds~load_int(1), ds~load_coins(), ds~load_msg_addr(), ds);
var (client, torrent_hash) = get_client_data(rest);
throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider) | equal_slice_bits(sender_address, client));
var client_msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(client)
.store_coins(balance)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(op::storage_contract_terminated, 32)
.store_uint(cur_lt(), 64)
.store_uint(torrent_hash, 256)
.end_cell();
if(~ active) {
return send_raw_message(client_msg, 128 + 32);
}
send_raw_message(client_msg, 64);
var provider_msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(provider)
.store_coins(0)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(op::storage_contract_terminated, 32)
.store_uint(cur_lt(), 64)
.store_uint(torrent_hash, 256)
.end_cell();
return send_raw_message(provider_msg, 128 + 32);
}
if (op == op::withdraw) {
var ds = get_data().begin_parse();
(int active, int balance, slice provider) = (ds~load_int(1), ds~load_coins(), ds~load_msg_addr());
throw_unless(error::contract_not_active, active);
throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider));
if(balance > 0) {
raw_reserve(balance + fee::storage, 2);
}
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(provider)
.store_coins(fee::receipt_value)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(op::reward_withdrawal, 32)
.store_uint(query_id, 64)
.end_cell();
send_raw_message(msg, 128 + 32);
}
if (op == op::proof_storage) {
cell file_dict_proof = in_msg_body~load_ref();
var ds = get_data().begin_parse();
var (active,
balance,
provider,
merkle_hash,
file_size,
next_proof,
rate_per_mb_day,
max_span,
last_proof_time,
client_data) = (ds~load_int(1),
ds~load_coins(),
ds~load_msg_addr(),
ds~load_uint(256),
ds~load_uint(64),
ds~load_uint(64),
ds~load_coins(),
ds~load_uint(32),
ds~load_uint(32),
ds~load_ref());
throw_unless(error::contract_not_active, active);
throw_unless(error::unauthorized, equal_slice_bits(sender_address, provider));
throw_unless(error::wrong_proof, check_proof(merkle_hash, next_proof, file_size, file_dict_proof));
next_proof = rand(file_size);
int actual_span = min(now() - last_proof_time, max_span);
int bounty = muldiv(file_size * rate_per_mb_day, actual_span, 24 * 60 * 60 * 1024 * 1024);
balance = max(0, balance - bounty);
last_proof_time = now();
begin_cell()
.store_int(true, 1)
.store_coins(balance)
.store_slice(provider)
.store_uint(merkle_hash, 256)
.store_uint(file_size, 64)
.store_uint(next_proof, 64)
.store_coins(rate_per_mb_day)
.store_uint(max_span, 32)
.store_uint(last_proof_time, 32)
.store_ref(client_data)
.end_cell().set_data();
;; Send remaining balance back
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(sender_address)
.store_uint(0, 4 + 1 + 4 + 4 + 64 + 32 + 1 + 1)
.end_cell();
send_raw_message(msg, 64 + 2);
}
}
_ get_storage_contract_data() method_id {
var ds = get_data().begin_parse();
var (active,
balance,
provider,
merkle_hash,
file_size,
next_proof,
rate_per_mb_day,
max_span,
last_proof_time,
rest) = (ds~load_int(1),
ds~load_coins(),
ds~load_msg_addr(),
ds~load_uint(256),
ds~load_uint(64),
ds~load_uint(64),
ds~load_coins(),
ds~load_uint(32),
ds~load_uint(32),
ds);
var (client, torrent_hash) = get_client_data(rest);
return (active, balance, provider, merkle_hash, file_size,
next_proof, rate_per_mb_day, max_span, last_proof_time,
client, torrent_hash);
}
_ get_torrent_hash() method_id {
var (active, balance, provider, merkle_hash, file_size,
next_proof, rate_per_mb_day, max_span, last_proof_time,
client, torrent_hash) = get_storage_contract_data();
return torrent_hash;
}
_ is_active() method_id {
return get_data().begin_parse().preload_int(1);
}
;; next_proof, last_proof_time, max_span
_ get_next_proof_info() method_id {
var (active, balance, provider, merkle_hash, file_size,
next_proof, rate_per_mb_day, max_span, last_proof_time,
client, torrent_hash) = get_storage_contract_data();
return (next_proof, last_proof_time, max_span);
}

View file

@ -0,0 +1,228 @@
;; Storage contract fabric
#include "stdlib.fc";
#include "constants.fc";
const min_deploy_amount = 50000000;
cell storage_contract_code() asm """ "storage-contract-code.boc" file>B B>boc PUSHREF """;
slice calculate_address_by_stateinit(cell state_init) {
return begin_cell().store_uint(4, 3)
.store_int(0, 8)
.store_uint(cell_hash(state_init), 256)
.end_cell()
.begin_parse();
}
cell build_storage_contract_stateinit(int merkle_hash, int file_size, int rate_per_mb_day,
int max_span, slice client, int torrent_hash) {
cell data = begin_cell()
.store_int(0, 1) ;; active
.store_coins(0) ;; client balance
.store_slice(my_address())
.store_uint(merkle_hash, 256)
.store_uint(file_size, 64)
.store_uint(0, 64) ;; next_proof
.store_coins(rate_per_mb_day)
.store_uint(max_span, 32)
.store_uint(now(), 32) ;; last_proof_time
.store_ref(begin_cell()
.store_slice(client)
.store_uint(torrent_hash, 256)
.end_cell())
.end_cell();
cell state_init = begin_cell()
.store_uint(0, 2)
.store_maybe_ref(storage_contract_code())
.store_maybe_ref(data)
.store_uint(0, 1) .end_cell();
return state_init;
}
() deploy_storage_contract (slice client, int query_id, int file_size, int merkle_hash, int torrent_hash,
int expected_rate, int expected_max_span) impure {
var ds = get_data().begin_parse();
var (wallet_data,
accept_new_contracts?,
rate_per_mb_day,
max_span,
minimal_file_size,
maximal_file_size) = (ds~load_bits(32 + 32 + 256),
ds~load_int(1),
ds~load_coins(),
ds~load_uint(32),
ds~load_uint(64),
ds~load_uint(64));
throw_unless(error::no_new_contracts, accept_new_contracts?);
throw_unless(error::file_too_small, file_size >= minimal_file_size);
throw_unless(error::file_too_big, file_size <= maximal_file_size);
throw_unless(error::provider_params_changed, expected_rate == rate_per_mb_day);
throw_unless(error::provider_params_changed, expected_max_span == max_span);
cell state_init = build_storage_contract_stateinit(merkle_hash, file_size, rate_per_mb_day,
max_span, client, torrent_hash);
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(calculate_address_by_stateinit(state_init))
.store_coins(0)
.store_uint(4 + 2, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
.store_ref(state_init)
.store_uint(op::offer_storage_contract, 32)
.store_uint(query_id, 64)
.end_cell();
send_raw_message(msg, 64);
}
() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure {
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if ((flags & 1) | in_msg_body.slice_empty?()) { ;; ignore all bounced and empty messages
return ();
}
slice sender_address = cs~load_msg_addr();
int op = in_msg_body~load_uint(32);
if (op == 0) { ;; transfer with text message
return ();
}
int query_id = in_msg_body~load_uint(64);
if(op == op::offer_storage_contract) {
throw_unless(error::not_enough_money, msg_value >= min_deploy_amount);
;; torrent_info piece_size:uint32 file_size:uint64 root_hash:(## 256) header_size:uint64 header_hash:(## 256)
;; microchunk_hash:(Maybe (## 256)) description:Text = TorrentInfo;
;;
;; new_storage_contract#00000001 query_id:uint64 info:(^ TorrentInfo) microchunk_hash:uint256
;; expected_rate:Coins expected_max_span:uint32 = NewStorageContract;
cell torrent_info = in_msg_body~load_ref();
int torrent_hash = cell_hash(torrent_info);
slice info_cs = torrent_info.begin_parse();
info_cs~skip_bits(32);
int file_size = info_cs~load_uint(64);
int merkle_hash = in_msg_body~load_uint(256);
int expected_rate = in_msg_body~load_coins();
int expected_max_span = in_msg_body~load_uint(32);
deploy_storage_contract(sender_address, query_id, file_size, merkle_hash, torrent_hash,
expected_rate, expected_max_span);
return ();
}
if(op == op::storage_contract_terminated) {
return ();
}
if(op == op::update_pubkey) {
if(~ equal_slice_bits(my_address(), sender_address)) {
return ();
}
var ds = get_data().begin_parse();
var (seqno_subwallet,
_,
non_wallet_data) = (ds~load_bits(32 + 32),
ds~load_uint(256),
ds);
int new_pubkey = in_msg_body~load_uint(256);
set_data(begin_cell()
.store_slice(seqno_subwallet)
.store_uint(new_pubkey, 256)
.store_slice(non_wallet_data)
.end_cell());
}
if(op == op::update_storage_params) {
if(~ equal_slice_bits(my_address(), sender_address)) {
return ();
}
var ds = get_data().begin_parse();
var wallet_data = ds~load_bits(32 + 32 + 256);
var(accept_new_contracts?,
rate_per_mb_day,
max_span,
minimal_file_size,
maximal_file_size) = (in_msg_body~load_int(1),
in_msg_body~load_coins(),
in_msg_body~load_uint(32),
in_msg_body~load_uint(64),
in_msg_body~load_uint(64));
set_data(begin_cell()
.store_slice(wallet_data)
.store_int(accept_new_contracts?, 1)
.store_coins(rate_per_mb_day)
.store_uint(max_span, 32)
.store_uint(minimal_file_size, 64)
.store_uint(maximal_file_size, 64)
.end_cell());
}
}
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512);
var cs = in_msg;
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
throw_if(35, valid_until <= now());
var ds = get_data().begin_parse();
var (stored_seqno,
stored_subwallet,
public_key,
non_wallet_data) = (ds~load_uint(32),
ds~load_uint(32),
ds~load_uint(256),
ds);
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, subwallet_id == stored_subwallet);
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
accept_message();
cs~touch();
while (cs.slice_refs()) {
var mode = cs~load_uint(8);
send_raw_message(cs~load_ref(), mode);
}
set_data(begin_cell()
.store_uint(stored_seqno + 1, 32)
.store_uint(stored_subwallet, 32)
.store_uint(public_key, 256)
.store_slice(non_wallet_data)
.end_cell());
}
;; Get methods
int seqno() method_id {
return get_data().begin_parse().preload_uint(32);
}
int get_public_key() method_id {
var cs = get_data().begin_parse();
cs~load_uint(64);
return cs.preload_uint(256);
}
;; seqno, subwallet, key
_ get_wallet_params() method_id {
var ds = get_data().begin_parse();
var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
return (stored_seqno, stored_subwallet, public_key);
}
_ get_storage_params() method_id {
var ds = get_data().begin_parse();
var (wallet_data,
accept_new_contracts?,
rate_per_mb_day,
max_span,
minimal_file_size,
maximal_file_size) = (ds~load_bits(32 + 32 + 256),
ds~load_int(1),
ds~load_coins(),
ds~load_uint(32),
ds~load_uint(64),
ds~load_uint(64));
return (accept_new_contracts?, rate_per_mb_day, max_span, minimal_file_size, maximal_file_size);
}
slice get_storage_contract_address(int merkle_hash, int file_size, slice client, int torrent_hash) method_id {
var (_, rate_per_mb_day, max_span, _, _) = get_storage_params();
cell state_init = build_storage_contract_stateinit(merkle_hash, file_size, rate_per_mb_day, max_span, client, torrent_hash);
return calculate_address_by_stateinit(state_init);
}

View file

@ -0,0 +1,439 @@
#include "stdlib.fc";
int __tact_my_balance() inline {
return pair_first(get_balance());
}
forall X -> X __tact_not_null(X x) { throw_if(128, null?(x)); return x; }
global (int, slice, int, slice) __tact_context;
global cell __tact_context_sys;
(int, slice, int, slice) __tact_context_get() inline { return __tact_context; }
() __tact_verify_address(slice address) inline {
throw_unless(136, address.slice_bits() != 267);
}
builder __tact_store_bool(builder b, int v) inline {
b = b.store_int(v, 1);
return b;
}
(slice, slice) __tact_load_address(slice cs) inline {
slice raw = cs~load_msg_addr();
__tact_verify_address(raw);
return (cs, raw);
}
(slice, slice) __tact_load_address_opt(slice cs) inline {
slice raw = cs~load_msg_addr();
if (raw.preload_uint(2) != 0) {
__tact_verify_address(raw);
return (cs, raw);
} else {
return (cs, null());
}
}
builder __tact_store_address(builder b, slice address) inline {
__tact_verify_address(address);
b = b.store_slice(address);
return b;
}
builder __tact_store_address_opt(builder b, slice address) inline {
if (null?(address)) {
b = b.store_uint(0, 2);
return b;
} else {
return __tact_store_address(b, address);
}
}
slice __tact_create_address(int chain, int hash) inline {
var b = begin_cell();
b = b.store_uint(2, 2);
b = b.store_uint(0, 1);
b = b.store_int(chain, 8);
b = b.store_uint(hash, 256);
return b.end_cell().begin_parse();
}
slice __tact_compute_contract_address(int chain, cell code, cell data) inline {
var b = begin_cell();
b = b.store_uint(0, 2);
b = b.store_uint(3, 2);
b = b.store_uint(0, 1);
b = b.store_ref(code);
b = b.store_ref(data);
var hash = cell_hash(b.end_cell());
return __tact_create_address(chain, hash);
}
int __tact_address_eq(slice a, slice b) inline {
return equal_slice_bits(a, b);
}
int __tact_address_neq(slice a, slice b) inline {
return ~ equal_slice_bits(a, b);
}
cell __tact_dict_set_code(cell dict, int id, cell code) inline {
return udict_set_ref(dict, 16, id, code);
}
cell __tact_dict_get_code(cell dict, int id) inline {
var (data, ok) = udict_get_ref?(dict, 16, id);
throw_unless(135, ok);
return data;
}
(slice, ((int, int, slice, slice, cell, int, slice))) __gen_read_TokenTransfer(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 260734629);
var v'queryId = sc_0~load_uint(64);
var v'amount = sc_0~load_coins();
var v'destination = sc_0~__tact_load_address();
var v'responseDestination = sc_0~__tact_load_address_opt();
var v'customPayload = sc_0~load_int(1) ? sc_0~load_ref() : null();
var v'forwardTonAmount = sc_0~load_coins();
var v'forwardPayload = sc_0;
return (sc_0, (v'queryId, v'amount, v'destination, v'responseDestination, v'customPayload, v'forwardTonAmount, v'forwardPayload));
}
builder __gen_write_TokenTransferInternal(builder build_0, (int, int, slice, slice, int, slice) v) inline_ref {
var (v'queryId, v'amount, v'from, v'responseAddress, v'forwardTonAmount, v'forwardPayload) = v;
build_0 = store_uint(build_0, 395134233, 32);
build_0 = build_0.store_uint(v'queryId, 64);
build_0 = build_0.store_coins(v'amount);
build_0 = __tact_store_address(build_0, v'from);
build_0 = __tact_store_address_opt(build_0, v'responseAddress);
build_0 = build_0.store_coins(v'forwardTonAmount);
build_0 = build_0.store_slice(v'forwardPayload);
return build_0;
}
cell __gen_writecell_TokenTransferInternal((int, int, slice, slice, int, slice) v) inline_ref {
return __gen_write_TokenTransferInternal(begin_cell(), v).end_cell();
}
(slice, ((int, int, slice, slice, int, slice))) __gen_read_TokenTransferInternal(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 395134233);
var v'queryId = sc_0~load_uint(64);
var v'amount = sc_0~load_coins();
var v'from = sc_0~__tact_load_address();
var v'responseAddress = sc_0~__tact_load_address_opt();
var v'forwardTonAmount = sc_0~load_coins();
var v'forwardPayload = sc_0;
return (sc_0, (v'queryId, v'amount, v'from, v'responseAddress, v'forwardTonAmount, v'forwardPayload));
}
builder __gen_write_TokenNotification(builder build_0, (int, int, slice, slice) v) inline_ref {
var (v'queryId, v'amount, v'from, v'forwardPayload) = v;
build_0 = store_uint(build_0, 1935855772, 32);
build_0 = build_0.store_uint(v'queryId, 64);
build_0 = build_0.store_coins(v'amount);
build_0 = __tact_store_address(build_0, v'from);
build_0 = build_0.store_slice(v'forwardPayload);
return build_0;
}
cell __gen_writecell_TokenNotification((int, int, slice, slice) v) inline_ref {
return __gen_write_TokenNotification(begin_cell(), v).end_cell();
}
(slice, ((int, int, slice, slice))) __gen_read_TokenBurn(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 1499400124);
var v'queryId = sc_0~load_uint(64);
var v'amount = sc_0~load_coins();
var v'owner = sc_0~__tact_load_address();
var v'responseAddress = sc_0~__tact_load_address_opt();
return (sc_0, (v'queryId, v'amount, v'owner, v'responseAddress));
}
builder __gen_write_TokenBurnNotification(builder build_0, (int, int, slice, slice) v) inline_ref {
var (v'queryId, v'amount, v'owner, v'responseAddress) = v;
build_0 = store_uint(build_0, 2078119902, 32);
build_0 = build_0.store_uint(v'queryId, 64);
build_0 = build_0.store_coins(v'amount);
build_0 = __tact_store_address(build_0, v'owner);
build_0 = __tact_store_address_opt(build_0, v'responseAddress);
return build_0;
}
cell __gen_writecell_TokenBurnNotification((int, int, slice, slice) v) inline_ref {
return __gen_write_TokenBurnNotification(begin_cell(), v).end_cell();
}
builder __gen_write_TokenExcesses(builder build_0, (int) v) inline_ref {
var (v'queryId) = v;
build_0 = store_uint(build_0, 3576854235, 32);
build_0 = build_0.store_uint(v'queryId, 64);
return build_0;
}
cell __gen_writecell_TokenExcesses((int) v) inline_ref {
return __gen_write_TokenExcesses(begin_cell(), v).end_cell();
}
builder __gen_write_JettonDefaultWallet(builder build_0, (int, slice, slice) v) inline_ref {
var (v'balance, v'owner, v'master) = v;
build_0 = build_0.store_int(v'balance, 257);
build_0 = __tact_store_address(build_0, v'owner);
build_0 = __tact_store_address(build_0, v'master);
return build_0;
}
(slice, ((int, slice, slice))) __gen_read_JettonDefaultWallet(slice sc_0) inline_ref {
var v'balance = sc_0~load_int(257);
var v'owner = sc_0~__tact_load_address();
var v'master = sc_0~__tact_load_address();
return (sc_0, (v'balance, v'owner, v'master));
}
_ __gen_StateInit_get_code((cell, cell) v) inline {
var (v'code, v'data) = v;
return v'code;
}
(int, slice, slice, cell) __gen_JettonWalletData_to_external(((int, slice, slice, cell)) v) {
var (v'balance, v'owner, v'master, v'walletCode) = v;
return (v'balance, v'owner, v'master, v'walletCode);
}
(int, slice, slice) __gen_load_JettonDefaultWallet() inline_ref {
slice sc = get_data().begin_parse();
__tact_context_sys = sc~load_ref();
return sc~__gen_read_JettonDefaultWallet();
}
() __gen_store_JettonDefaultWallet((int, slice, slice) v) impure inline_ref {
builder b = begin_cell();
b = b.store_ref(__tact_context_sys);
b = __gen_write_JettonDefaultWallet(b, v);
set_data(b.end_cell());
}
slice $contractAddress((cell, cell) $s) impure {
var (($s'code, $s'data)) = $s;
return __tact_compute_contract_address(0, $s'code, $s'data);
}
() $send((int, slice, int, int, cell, cell, cell) $params) impure {
var (($params'bounce, $params'to, $params'value, $params'mode, $params'body, $params'code, $params'data)) = $params;
builder $b = begin_cell();
$b = store_int($b, 1, 2);
$b = __tact_store_bool($b, $params'bounce);
$b = store_int($b, 0, 3);
$b = __tact_store_address($b, $params'to);
$b = store_coins($b, $params'value);
$b = store_int($b, 0, ((((1 + 4) + 4) + 64) + 32));
if (((~ null?($params'code)) | (~ null?($params'data)))) {
$b = __tact_store_bool($b, true);
builder $bc = begin_cell();
$bc = __tact_store_bool($bc, false);
$bc = __tact_store_bool($bc, false);
if ((~ null?($params'code))) {
$bc = __tact_store_bool($bc, true);
$bc = store_ref($bc, __tact_not_null($params'code));
} else {
$bc = __tact_store_bool($bc, false);
}
if ((~ null?($params'data))) {
$bc = __tact_store_bool($bc, true);
$bc = store_ref($bc, __tact_not_null($params'data));
} else {
$bc = __tact_store_bool($bc, false);
}
$bc = __tact_store_bool($bc, false);
$b = __tact_store_bool($b, true);
$b = store_ref($b, end_cell($bc));
} else {
$b = __tact_store_bool($b, false);
}
cell $body = $params'body;
if ((~ null?($body))) {
$b = __tact_store_bool($b, true);
$b = store_ref($b, __tact_not_null($body));
} else {
$b = __tact_store_bool($b, false);
}
cell $c = end_cell($b);
send_raw_message($c, $params'mode);
}
int $__gen_Context_readForwardFee((int, slice, int, slice) $self) impure {
var (($self'bounced, $self'sender, $self'value, $self'raw)) = $self;
var (($self'bounced, $self'sender, $self'value, $self'raw)) = $self;
slice $sc = $self'raw;
$sc~load_coins();
$sc~skip_bits(1);
$sc~load_coins();
return (($sc~load_coins() * 3) / 2);
}
cell $__gen_JettonDefaultWallet_init(cell sys', slice $master, slice $owner) {
var (($self'balance, $self'owner, $self'master)) = (null(), null(), null());
$self'balance = 0;
$self'owner = $owner;
$self'master = $master;
var b' = begin_cell();
b' = b'.store_ref(sys');
b' = __gen_write_JettonDefaultWallet(b', ($self'balance, $self'owner, $self'master));
return b'.end_cell();
}
(cell, cell) $__gen_JettonDefaultWallet_init_child(cell sys', slice $master, slice $owner) {
slice sc' = sys'.begin_parse();
cell source = sc'~load_dict();
cell contracts = new_dict();
;; Contract Code: JettonDefaultWallet
cell mine = __tact_dict_get_code(source, 55471);
contracts = __tact_dict_set_code(contracts, 55471, mine);
cell sys = begin_cell().store_dict(contracts).end_cell();
return (mine, $__gen_JettonDefaultWallet_init(sys, $master, $owner));
}
(int, slice, slice, cell) $__gen_JettonDefaultWallet_get_wallet_data((int, slice, slice) $self) impure {
var (($self'balance, $self'owner, $self'master)) = $self;
return ($self'balance, $self'owner, $self'master, __gen_StateInit_get_code($__gen_JettonDefaultWallet_init_child(__tact_context_sys, $self'master, $self'owner)));
}
_ $__gen_get_get_wallet_data() method_id(97026) {
var self = __gen_load_JettonDefaultWallet();
var res = $__gen_JettonDefaultWallet_get_wallet_data(self);
return __gen_JettonWalletData_to_external(res);
}
(((int, slice, slice)), ()) $__gen_JettonDefaultWallet_receive_TokenTransfer((int, slice, slice) $self, (int, int, slice, slice, cell, int, slice) $msg) impure {
var ($self'balance, $self'owner, $self'master) = $self;
var ($msg'queryId, $msg'amount, $msg'destination, $msg'responseDestination, $msg'customPayload, $msg'forwardTonAmount, $msg'forwardPayload) = $msg;
var ($ctx'bounced, $ctx'sender, $ctx'value, $ctx'raw) = __tact_context_get();
throw_unless(4429, __tact_address_eq($ctx'sender, $self'owner));
$self'balance = ($self'balance - $msg'amount);
throw_unless(62972, ($self'balance >= 0));
int $fwdFee = $__gen_Context_readForwardFee(($ctx'bounced, $ctx'sender, $ctx'value, $ctx'raw));
int $fwdCount = 1;
if (($msg'forwardTonAmount > 0)) {
$fwdCount = 2;
}
throw_unless(16059, ($ctx'value > ((($fwdCount * $fwdFee) + (2 * 10000000)) + 10000000)));
var ($init'code, $init'data) = $__gen_JettonDefaultWallet_init_child(__tact_context_sys, $self'master, $msg'destination);
slice $walletAddress = $contractAddress(($init'code, $init'data));
$send((true, $walletAddress, 0, 64, __gen_writecell_TokenTransferInternal(($msg'queryId, $msg'amount, $self'owner, $self'owner, $msg'forwardTonAmount, $msg'forwardPayload)), $init'code, $init'data));
return (($self'balance, $self'owner, $self'master), ());
}
(((int, slice, slice)), ()) $__gen_JettonDefaultWallet_receive_TokenTransferInternal((int, slice, slice) $self, (int, int, slice, slice, int, slice) $msg) impure {
var ($self'balance, $self'owner, $self'master) = $self;
var ($msg'queryId, $msg'amount, $msg'from, $msg'responseAddress, $msg'forwardTonAmount, $msg'forwardPayload) = $msg;
var ($ctx'bounced, $ctx'sender, $ctx'value, $ctx'raw) = __tact_context_get();
if (__tact_address_neq($ctx'sender, $self'master)) {
var ($sinit'code, $sinit'data) = $__gen_JettonDefaultWallet_init_child(__tact_context_sys, $self'master, $msg'from);
throw_unless(4429, __tact_address_eq($contractAddress(($sinit'code, $sinit'data)), $ctx'sender));
}
$self'balance = ($self'balance + $msg'amount);
throw_unless(62972, ($self'balance >= 0));
int $msgValue = $ctx'value;
int $tonBalanceBeforeMsg = (__tact_my_balance() - $msgValue);
int $storageFee = (10000000 - min($tonBalanceBeforeMsg, 10000000));
$msgValue = ($msgValue - ($storageFee + 10000000));
if (($msg'forwardTonAmount > 0)) {
int $fwdFee = $__gen_Context_readForwardFee(($ctx'bounced, $ctx'sender, $ctx'value, $ctx'raw));
$msgValue = ($msgValue - ($msg'forwardTonAmount + $fwdFee));
$send((false, $self'owner, $msg'forwardTonAmount, 0, __gen_writecell_TokenNotification(($msg'queryId, $msg'amount, $msg'from, $msg'forwardPayload)), null(), null()));
}
if (((~ null?($msg'responseAddress)) & ($msgValue > 0))) {
$send((false, __tact_not_null($msg'responseAddress), $msgValue, 0, __gen_writecell_TokenExcesses(($msg'queryId)), null(), null()));
}
return (($self'balance, $self'owner, $self'master), ());
}
(((int, slice, slice)), ()) $__gen_JettonDefaultWallet_receive_TokenBurn((int, slice, slice) $self, (int, int, slice, slice) $msg) impure {
var ($self'balance, $self'owner, $self'master) = $self;
var ($msg'queryId, $msg'amount, $msg'owner, $msg'responseAddress) = $msg;
var ($ctx'bounced, $ctx'sender, $ctx'value, $ctx'raw) = __tact_context_get();
throw_unless(4429, __tact_address_eq($ctx'sender, $self'owner));
$self'balance = ($self'balance - $msg'amount);
throw_unless(62972, ($self'balance >= 0));
int $fwdFee = $__gen_Context_readForwardFee(($ctx'bounced, $ctx'sender, $ctx'value, $ctx'raw));
throw_unless(16059, ($ctx'value > (($fwdFee + (2 * 10000000)) + 10000000)));
$send((true, $self'master, 0, 64, __gen_writecell_TokenBurnNotification(($msg'queryId, $msg'amount, $self'owner, $self'owner)), null(), null()));
return (($self'balance, $self'owner, $self'master), ());
}
((int, slice, slice), ()) $__gen_JettonDefaultWallet_receive_bounced((int, slice, slice) $self, slice $msg) impure {
var ($self'balance, $self'owner, $self'master) = $self;
$msg~skip_bits(32);
int $op = $msg~load_uint(32);
int $queryId = $msg~load_uint(64);
int $jettonAmount = $msg~load_coins();
throw_unless(13650, (($op == 395134233) | ($op == 2078119902)));
$self'balance = ($self'balance + $jettonAmount);
return (($self'balance, $self'owner, $self'master), ());
}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
;; Parse incoming message
int op = 0;
if (slice_bits(in_msg) >= 32) {
op = in_msg.preload_uint(32);
}
var cs = in_msg_cell.begin_parse();
var msg_flags = cs~load_uint(4);
var msg_bounced = ((msg_flags & 1) == 1 ? true : false);
slice msg_sender_addr = cs~load_msg_addr();
__tact_context = (msg_bounced, msg_sender_addr, msg_value, cs);
;; Handle bounced messages
if (msg_bounced) {
var self = __gen_load_JettonDefaultWallet();
self~$__gen_JettonDefaultWallet_receive_bounced(in_msg);
__gen_store_JettonDefaultWallet(self);
return ();
}
;; Receive TokenTransfer message
if (op == 260734629) {
var self = __gen_load_JettonDefaultWallet();
var msg = in_msg~__gen_read_TokenTransfer();
self~$__gen_JettonDefaultWallet_receive_TokenTransfer(msg);
__gen_store_JettonDefaultWallet(self);
return ();
}
;; Receive TokenTransferInternal message
if (op == 395134233) {
var self = __gen_load_JettonDefaultWallet();
var msg = in_msg~__gen_read_TokenTransferInternal();
self~$__gen_JettonDefaultWallet_receive_TokenTransferInternal(msg);
__gen_store_JettonDefaultWallet(self);
return ();
}
;; Receive TokenBurn message
if (op == 1499400124) {
var self = __gen_load_JettonDefaultWallet();
var msg = in_msg~__gen_read_TokenBurn();
self~$__gen_JettonDefaultWallet_receive_TokenBurn(msg);
__gen_store_JettonDefaultWallet(self);
return ();
}
throw(130);
}
_ supported_interfaces() method_id {
return (
"org.ton.introspection.v0"H >> 128,
"org.ton.abi.ipfs.v0"H >> 128,
"org.ton.jetton.wallet"H >> 128
);
}
_ get_abi_ipfs() {
return "ipfs://QmXBfqbQzeN1uT55MyYpwhU9RV47Sq3quVt3qFLgWH8NhD";
}

View file

@ -0,0 +1,440 @@
#include "stdlib.fc";
forall X -> X __tact_not_null(X x) { throw_if(128, null?(x)); return x; }
global (int, slice, int, slice) __tact_context;
global cell __tact_context_sys;
(int, slice, int, slice) __tact_context_get() inline { return __tact_context; }
() __tact_verify_address(slice address) inline {
throw_unless(136, address.slice_bits() != 267);
}
builder __tact_store_bool(builder b, int v) inline {
b = b.store_int(v, 1);
return b;
}
(slice, slice) __tact_load_address(slice cs) inline {
slice raw = cs~load_msg_addr();
__tact_verify_address(raw);
return (cs, raw);
}
(slice, slice) __tact_load_address_opt(slice cs) inline {
slice raw = cs~load_msg_addr();
if (raw.preload_uint(2) != 0) {
__tact_verify_address(raw);
return (cs, raw);
} else {
return (cs, null());
}
}
builder __tact_store_address(builder b, slice address) inline {
__tact_verify_address(address);
b = b.store_slice(address);
return b;
}
builder __tact_store_address_opt(builder b, slice address) inline {
if (null?(address)) {
b = b.store_uint(0, 2);
return b;
} else {
return __tact_store_address(b, address);
}
}
slice __tact_create_address(int chain, int hash) inline {
var b = begin_cell();
b = b.store_uint(2, 2);
b = b.store_uint(0, 1);
b = b.store_int(chain, 8);
b = b.store_uint(hash, 256);
return b.end_cell().begin_parse();
}
slice __tact_compute_contract_address(int chain, cell code, cell data) inline {
var b = begin_cell();
b = b.store_uint(0, 2);
b = b.store_uint(3, 2);
b = b.store_uint(0, 1);
b = b.store_ref(code);
b = b.store_ref(data);
var hash = cell_hash(b.end_cell());
return __tact_create_address(chain, hash);
}
int __tact_address_eq(slice a, slice b) inline {
return equal_slice_bits(a, b);
}
cell __tact_dict_set_code(cell dict, int id, cell code) inline {
return udict_set_ref(dict, 16, id, code);
}
cell __tact_dict_get_code(cell dict, int id) inline {
var (data, ok) = udict_get_ref?(dict, 16, id);
throw_unless(135, ok);
return data;
}
builder __gen_write_TokenTransferInternal(builder build_0, (int, int, slice, slice, int, slice) v) inline_ref {
var (v'queryId, v'amount, v'from, v'responseAddress, v'forwardTonAmount, v'forwardPayload) = v;
build_0 = store_uint(build_0, 395134233, 32);
build_0 = build_0.store_uint(v'queryId, 64);
build_0 = build_0.store_coins(v'amount);
build_0 = __tact_store_address(build_0, v'from);
build_0 = __tact_store_address_opt(build_0, v'responseAddress);
build_0 = build_0.store_coins(v'forwardTonAmount);
build_0 = build_0.store_slice(v'forwardPayload);
return build_0;
}
cell __gen_writecell_TokenTransferInternal((int, int, slice, slice, int, slice) v) inline_ref {
return __gen_write_TokenTransferInternal(begin_cell(), v).end_cell();
}
(slice, ((int, int, slice, slice))) __gen_read_TokenBurnNotification(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 2078119902);
var v'queryId = sc_0~load_uint(64);
var v'amount = sc_0~load_coins();
var v'owner = sc_0~__tact_load_address();
var v'responseAddress = sc_0~__tact_load_address_opt();
return (sc_0, (v'queryId, v'amount, v'owner, v'responseAddress));
}
builder __gen_write_TokenExcesses(builder build_0, (int) v) inline_ref {
var (v'queryId) = v;
build_0 = store_uint(build_0, 3576854235, 32);
build_0 = build_0.store_uint(v'queryId, 64);
return build_0;
}
cell __gen_writecell_TokenExcesses((int) v) inline_ref {
return __gen_write_TokenExcesses(begin_cell(), v).end_cell();
}
(slice, ((cell))) __gen_read_TokenUpdateContent(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 201882270);
var v'content = sc_0~load_int(1) ? sc_0~load_ref() : null();
return (sc_0, (v'content));
}
(slice, ((int))) __gen_read_Mint(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 33240155);
var v'amount = sc_0~load_int(257);
return (sc_0, (v'amount));
}
builder __gen_write_JettonDefaultWallet(builder build_0, (int, slice, slice) v) inline_ref {
var (v'balance, v'owner, v'master) = v;
build_0 = build_0.store_int(v'balance, 257);
build_0 = __tact_store_address(build_0, v'owner);
build_0 = __tact_store_address(build_0, v'master);
return build_0;
}
builder __gen_write_SampleJetton(builder build_0, (int, slice, cell, int) v) inline_ref {
var (v'totalSupply, v'owner, v'content, v'mintable) = v;
build_0 = build_0.store_coins(v'totalSupply);
build_0 = __tact_store_address(build_0, v'owner);
build_0 = ~ null?(v'content) ? build_0.store_int(true, 1).store_ref(v'content) : build_0.store_int(false, 1);
build_0 = build_0.store_int(v'mintable, 1);
return build_0;
}
(slice, ((int, slice, cell, int))) __gen_read_SampleJetton(slice sc_0) inline_ref {
var v'totalSupply = sc_0~load_coins();
var v'owner = sc_0~__tact_load_address();
var v'content = sc_0~load_int(1) ? sc_0~load_ref() : null();
var v'mintable = sc_0~load_int(1);
return (sc_0, (v'totalSupply, v'owner, v'content, v'mintable));
}
_ __gen_StateInit_get_code((cell, cell) v) inline {
var (v'code, v'data) = v;
return v'code;
}
_ __gen_Context_get_sender((int, slice, int, slice) v) inline {
var (v'bounced, v'sender, v'value, v'raw) = v;
return v'sender;
}
(int, int, slice, cell, cell) __gen_JettonData_to_external(((int, int, slice, cell, cell)) v) {
var (v'totalSupply, v'mintable, v'owner, v'content, v'walletCode) = v;
return (v'totalSupply, v'mintable, v'owner, v'content, v'walletCode);
}
(int, slice, cell, int) __gen_load_SampleJetton() inline_ref {
slice sc = get_data().begin_parse();
__tact_context_sys = sc~load_ref();
return sc~__gen_read_SampleJetton();
}
() __gen_store_SampleJetton((int, slice, cell, int) v) impure inline_ref {
builder b = begin_cell();
b = b.store_ref(__tact_context_sys);
b = __gen_write_SampleJetton(b, v);
set_data(b.end_cell());
}
cell $emptyCell() impure {
return end_cell(begin_cell());
}
slice $__gen_Cell_asSlice(cell $self) impure {
var ($self) = $self;
return begin_parse($self);
}
slice $emptySlice() impure {
return $__gen_Cell_asSlice($emptyCell());
}
slice $contractAddress((cell, cell) $s) impure {
var (($s'code, $s'data)) = $s;
return __tact_compute_contract_address(0, $s'code, $s'data);
}
() $send((int, slice, int, int, cell, cell, cell) $params) impure {
var (($params'bounce, $params'to, $params'value, $params'mode, $params'body, $params'code, $params'data)) = $params;
builder $b = begin_cell();
$b = store_int($b, 1, 2);
$b = __tact_store_bool($b, $params'bounce);
$b = store_int($b, 0, 3);
$b = __tact_store_address($b, $params'to);
$b = store_coins($b, $params'value);
$b = store_int($b, 0, ((((1 + 4) + 4) + 64) + 32));
if (((~ null?($params'code)) | (~ null?($params'data)))) {
$b = __tact_store_bool($b, true);
builder $bc = begin_cell();
$bc = __tact_store_bool($bc, false);
$bc = __tact_store_bool($bc, false);
if ((~ null?($params'code))) {
$bc = __tact_store_bool($bc, true);
$bc = store_ref($bc, __tact_not_null($params'code));
} else {
$bc = __tact_store_bool($bc, false);
}
if ((~ null?($params'data))) {
$bc = __tact_store_bool($bc, true);
$bc = store_ref($bc, __tact_not_null($params'data));
} else {
$bc = __tact_store_bool($bc, false);
}
$bc = __tact_store_bool($bc, false);
$b = __tact_store_bool($b, true);
$b = store_ref($b, end_cell($bc));
} else {
$b = __tact_store_bool($b, false);
}
cell $body = $params'body;
if ((~ null?($body))) {
$b = __tact_store_bool($b, true);
$b = store_ref($b, __tact_not_null($body));
} else {
$b = __tact_store_bool($b, false);
}
cell $c = end_cell($b);
send_raw_message($c, $params'mode);
}
cell $__gen_JettonDefaultWallet_init(cell sys', slice $master, slice $owner) {
var (($self'balance, $self'owner, $self'master)) = (null(), null(), null());
$self'balance = 0;
$self'owner = $owner;
$self'master = $master;
var b' = begin_cell();
b' = b'.store_ref(sys');
b' = __gen_write_JettonDefaultWallet(b', ($self'balance, $self'owner, $self'master));
return b'.end_cell();
}
(cell, cell) $__gen_JettonDefaultWallet_init_child(cell sys', slice $master, slice $owner) {
slice sc' = sys'.begin_parse();
cell source = sc'~load_dict();
cell contracts = new_dict();
;; Contract Code: JettonDefaultWallet
cell mine = __tact_dict_get_code(source, 55471);
contracts = __tact_dict_set_code(contracts, 55471, mine);
cell sys = begin_cell().store_dict(contracts).end_cell();
return (mine, $__gen_JettonDefaultWallet_init(sys, $master, $owner));
}
((int, slice, cell, int), (cell, cell)) $__gen_SampleJetton_getJettonWalletInit((int, slice, cell, int) $self, slice $address) impure {
var (($self'totalSupply, $self'owner, $self'content, $self'mintable)) = $self;
return (($self'totalSupply, $self'owner, $self'content, $self'mintable), $__gen_JettonDefaultWallet_init_child(__tact_context_sys, my_address(), $address));
}
slice $__gen_SampleJetton_get_wallet_address((int, slice, cell, int) $self, slice $owner) impure {
var (($self'totalSupply, $self'owner, $self'content, $self'mintable)) = $self;
var ($winit'code, $winit'data) = ($self'totalSupply, $self'owner, $self'content, $self'mintable)~$__gen_SampleJetton_getJettonWalletInit($owner);
return $contractAddress(($winit'code, $winit'data));
}
_ $__gen_get_get_wallet_address(slice $$owner) method_id(103289) {
slice $owner = $$owner;
var self = __gen_load_SampleJetton();
var res = $__gen_SampleJetton_get_wallet_address(self, $owner);
return res;
}
(int, int, slice, cell, cell) $__gen_SampleJetton_get_jetton_data((int, slice, cell, int) $self) impure {
var (($self'totalSupply, $self'owner, $self'content, $self'mintable)) = $self;
cell $code = __gen_StateInit_get_code(($self'totalSupply, $self'owner, $self'content, $self'mintable)~$__gen_SampleJetton_getJettonWalletInit(my_address()));
return ($self'totalSupply, $self'mintable, $self'owner, $self'content, $code);
}
_ $__gen_get_get_jetton_data() method_id(106029) {
var self = __gen_load_SampleJetton();
var res = $__gen_SampleJetton_get_jetton_data(self);
return __gen_JettonData_to_external(res);
}
((int, slice, cell, int), ()) $__gen_SampleJetton_mint((int, slice, cell, int) $self, slice $to, int $amount, slice $responseAddress) impure {
var (($self'totalSupply, $self'owner, $self'content, $self'mintable)) = $self;
$self'totalSupply = ($self'totalSupply + $amount);
var ($winit'code, $winit'data) = ($self'totalSupply, $self'owner, $self'content, $self'mintable)~$__gen_SampleJetton_getJettonWalletInit($to);
slice $walletAddress = $contractAddress(($winit'code, $winit'data));
$send((false, $walletAddress, 0, 64, __gen_writecell_TokenTransferInternal((0, $amount, my_address(), $responseAddress, 0, $emptySlice())), $winit'code, $winit'data));
return (($self'totalSupply, $self'owner, $self'content, $self'mintable), ());
}
((int, slice, cell, int), ()) $__gen_SampleJetton_requireWallet((int, slice, cell, int) $self, slice $owner) impure {
var (($self'totalSupply, $self'owner, $self'content, $self'mintable)) = $self;
var ($ctx'bounced, $ctx'sender, $ctx'value, $ctx'raw) = __tact_context_get();
var ($winit'code, $winit'data) = ($self'totalSupply, $self'owner, $self'content, $self'mintable)~$__gen_SampleJetton_getJettonWalletInit($owner);
throw_unless(4429, __tact_address_eq($contractAddress(($winit'code, $winit'data)), $ctx'sender));
return (($self'totalSupply, $self'owner, $self'content, $self'mintable), ());
}
((int, slice, cell, int), ()) $__gen_SampleJetton_requireOwner((int, slice, cell, int) $self) impure {
var (($self'totalSupply, $self'owner, $self'content, $self'mintable)) = $self;
throw_unless(132, __tact_address_eq(__gen_Context_get_sender(__tact_context_get()), $self'owner));
return (($self'totalSupply, $self'owner, $self'content, $self'mintable), ());
}
slice $__gen_SampleJetton_owner((int, slice, cell, int) $self) impure {
var (($self'totalSupply, $self'owner, $self'content, $self'mintable)) = $self;
return $self'owner;
}
_ $__gen_get_owner() method_id(83229) {
var self = __gen_load_SampleJetton();
var res = $__gen_SampleJetton_owner(self);
return res;
}
(((int, slice, cell, int)), ()) $__gen_SampleJetton_receive_Mint((int, slice, cell, int) $self, (int) $msg) impure {
var ($self'totalSupply, $self'owner, $self'content, $self'mintable) = $self;
var ($msg'amount) = $msg;
var ($ctx'bounced, $ctx'sender, $ctx'value, $ctx'raw) = __tact_context_get();
($self'totalSupply, $self'owner, $self'content, $self'mintable)~$__gen_SampleJetton_mint($ctx'sender, $msg'amount, $ctx'sender);
return (($self'totalSupply, $self'owner, $self'content, $self'mintable), ());
}
((int, slice, cell, int), ()) $__gen_SampleJetton_receive_comment_cd0d986cb1a2f468ae7089f4fc3162c116e5f53fbd11a6839f52dbf5040830b2((int, slice, cell, int) $self) impure {
var ($self'totalSupply, $self'owner, $self'content, $self'mintable) = $self;
var ($ctx'bounced, $ctx'sender, $ctx'value, $ctx'raw) = __tact_context_get();
($self'totalSupply, $self'owner, $self'content, $self'mintable)~$__gen_SampleJetton_mint($ctx'sender, 1000000000, $ctx'sender);
return (($self'totalSupply, $self'owner, $self'content, $self'mintable), ());
}
(((int, slice, cell, int)), ()) $__gen_SampleJetton_receive_TokenUpdateContent((int, slice, cell, int) $self, (cell) $msg) impure {
var ($self'totalSupply, $self'owner, $self'content, $self'mintable) = $self;
var ($msg'content) = $msg;
($self'totalSupply, $self'owner, $self'content, $self'mintable)~$__gen_SampleJetton_requireOwner();
$self'content = $msg'content;
return (($self'totalSupply, $self'owner, $self'content, $self'mintable), ());
}
(((int, slice, cell, int)), ()) $__gen_SampleJetton_receive_TokenBurnNotification((int, slice, cell, int) $self, (int, int, slice, slice) $msg) impure {
var ($self'totalSupply, $self'owner, $self'content, $self'mintable) = $self;
var ($msg'queryId, $msg'amount, $msg'owner, $msg'responseAddress) = $msg;
($self'totalSupply, $self'owner, $self'content, $self'mintable)~$__gen_SampleJetton_requireWallet($msg'owner);
$self'totalSupply = ($self'totalSupply - $msg'amount);
if ((~ null?($msg'responseAddress))) {
$send((false, $msg'responseAddress, 0, (64 + 2), __gen_writecell_TokenExcesses(($msg'queryId)), null(), null()));
}
return (($self'totalSupply, $self'owner, $self'content, $self'mintable), ());
}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
;; Parse incoming message
int op = 0;
if (slice_bits(in_msg) >= 32) {
op = in_msg.preload_uint(32);
}
var cs = in_msg_cell.begin_parse();
var msg_flags = cs~load_uint(4);
var msg_bounced = ((msg_flags & 1) == 1 ? true : false);
slice msg_sender_addr = cs~load_msg_addr();
__tact_context = (msg_bounced, msg_sender_addr, msg_value, cs);
;; Handle bounced messages
if (msg_bounced) {
return ();
}
;; Receive Mint message
if (op == 33240155) {
var self = __gen_load_SampleJetton();
var msg = in_msg~__gen_read_Mint();
self~$__gen_SampleJetton_receive_Mint(msg);
__gen_store_SampleJetton(self);
return ();
}
;; Receive TokenUpdateContent message
if (op == 201882270) {
var self = __gen_load_SampleJetton();
var msg = in_msg~__gen_read_TokenUpdateContent();
self~$__gen_SampleJetton_receive_TokenUpdateContent(msg);
__gen_store_SampleJetton(self);
return ();
}
;; Receive TokenBurnNotification message
if (op == 2078119902) {
var self = __gen_load_SampleJetton();
var msg = in_msg~__gen_read_TokenBurnNotification();
self~$__gen_SampleJetton_receive_TokenBurnNotification(msg);
__gen_store_SampleJetton(self);
return ();
}
;; Text Receivers
if (op == 0) {
var text_op = slice_hash(in_msg);
;; Receive "Mint!" message
if (text_op == 0xcd0d986cb1a2f468ae7089f4fc3162c116e5f53fbd11a6839f52dbf5040830b2) {
var self = __gen_load_SampleJetton();
self~$__gen_SampleJetton_receive_comment_cd0d986cb1a2f468ae7089f4fc3162c116e5f53fbd11a6839f52dbf5040830b2();
__gen_store_SampleJetton(self);
return ();
}
}
throw(130);
}
_ supported_interfaces() method_id {
return (
"org.ton.introspection.v0"H >> 128,
"org.ton.abi.ipfs.v0"H >> 128,
"org.ton.jetton.master"H >> 128,
"org.ton.ownable"H >> 128
);
}
_ get_abi_ipfs() {
return "ipfs://QmPfyoAvkPUqzx93gq8EBcVccAYXFEbjnqCMrHYtyPUHfE";
}

View file

@ -0,0 +1,603 @@
#include "stdlib.fc";
(cell, int) __tact_dict_delete(cell dict, int key_len, slice index) asm(index dict key_len) "DICTDEL";
((cell), ()) __tact_dict_set_ref(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTSETREF";
(slice, int) __tact_dict_get(cell dict, int key_len, slice index) asm(index dict key_len) "DICTGET" "NULLSWAPIFNOT";
(cell, int) __tact_dict_get_ref(cell dict, int key_len, slice index) asm(index dict key_len) "DICTGETREF" "NULLSWAPIFNOT";
global (int, slice, int, slice) __tact_context;
global cell __tact_context_sys;
() __tact_verify_address(slice address) inline {
throw_unless(136, address.slice_bits() != 267);
}
(slice, slice) __tact_load_address(slice cs) inline {
slice raw = cs~load_msg_addr();
__tact_verify_address(raw);
return (cs, raw);
}
(cell, ()) __tact_dict_set_int_int(cell d, int kl, int k, int v, int vl) inline {
if (null?(v)) {
var (r, ok) = idict_delete?(d, kl, k);
return (r, ());
} else {
return (idict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ());
}
}
int __tact_dict_get_int_int(cell d, int kl, int k, int vl) inline {
var (r, ok) = idict_get?(d, kl, k);
if (ok) {
return r~load_int(vl);
} else {
return null();
}
}
(cell, ()) __tact_dict_set_int_cell(cell d, int kl, int k, cell v) inline {
if (null?(v)) {
var (r, ok) = idict_delete?(d, kl, k);
return (r, ());
} else {
return (idict_set_ref(d, kl, k, v), ());
}
}
cell __tact_dict_get_int_cell(cell d, int kl, int k) {
var (r, ok) = idict_get_ref?(d, kl, k);
if (ok) {
return r;
} else {
return null();
}
}
(cell, ()) __tact_dict_set_slice_int(cell d, int kl, slice k, int v, int vl) {
if (null?(v)) {
var (r, ok) = __tact_dict_delete(d, kl, k);
return (r, ());
} else {
return (dict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ());
}
}
int __tact_dict_get_slice_int(cell d, int kl, slice k, int vl) inline {
var (r, ok) = __tact_dict_get(d, kl, k);
if (ok) {
return r~load_int(vl);
} else {
return null();
}
}
(cell, ()) __tact_dict_set_slice_cell(cell d, int kl, slice k, cell v) inline {
if (null?(v)) {
var (r, ok) = __tact_dict_delete(d, kl, k);
return (r, ());
} else {
return __tact_dict_set_ref(d, kl, k, v);
}
}
cell __tact_dict_get_slice_cell(cell d, int kl, slice k) inline {
var (r, ok) = __tact_dict_get_ref(d, kl, k);
if (ok) {
return r;
} else {
return null();
}
}
forall X0 -> tuple __tact_tuple_create_1((X0) v) asm "1 TUPLE";
forall X0 -> (X0) __tact_tuple_destroy_1(tuple v) asm "1 UNTUPLE";
(slice, ((int, int))) __gen_read_SetIntMap1(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 1510253336);
var v'key = sc_0~load_int(257);
var v'value = sc_0~load_int(1) ? sc_0~load_int(257) : null();
return (sc_0, (v'key, v'value));
}
(slice, ((int, int))) __gen_read_SetIntMap2(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 1629867766);
var v'key = sc_0~load_int(257);
var v'value = sc_0~load_int(1) ? sc_0~load_int(1) : null();
return (sc_0, (v'key, v'value));
}
(slice, ((int, cell))) __gen_read_SetIntMap3(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 3613954633);
var v'key = sc_0~load_int(257);
var v'value = sc_0~load_int(1) ? sc_0~load_ref() : null();
return (sc_0, (v'key, v'value));
}
builder __gen_write_SomeStruct(builder build_0, (int) v) inline_ref {
var (v'value) = v;
build_0 = build_0.store_int(v'value, 257);
return build_0;
}
cell __gen_writecell_SomeStruct((int) v) inline_ref {
return __gen_write_SomeStruct(begin_cell(), v).end_cell();
}
((int)) __gen_SomeStruct_not_null(tuple v) inline {
throw_if(128, null?(v));
var (int vvv'value) = __tact_tuple_destroy_1(v);
return (vvv'value);
}
cell __gen_writecellopt_SomeStruct(tuple v) inline_ref {
if (null?(v)) {
return null();
}
return __gen_writecell_SomeStruct(__gen_SomeStruct_not_null(v));
}
(slice, ((int))) __gen_read_SomeStruct(slice sc_0) inline_ref {
var v'value = sc_0~load_int(257);
return (sc_0, (v'value));
}
tuple __gen_SomeStruct_as_optional(((int)) v) inline {
var (v'value) = v;
return __tact_tuple_create_1(v'value);
}
tuple __gen_readopt_SomeStruct(cell cl) inline_ref {
if (null?(cl)) {
return null();
}
var sc = cl.begin_parse();
return __gen_SomeStruct_as_optional(sc~__gen_read_SomeStruct());
}
(slice, ((int, tuple))) __gen_read_SetIntMap4(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 383013829);
var v'key = sc_0~load_int(257);
var v'value = sc_0~load_int(1) ? __gen_SomeStruct_as_optional(sc_0~__gen_read_SomeStruct()) : null();
return (sc_0, (v'key, v'value));
}
(slice, ((slice, int))) __gen_read_SetAddrMap1(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 1749966413);
var v'key = sc_0~__tact_load_address();
var v'value = sc_0~load_int(1) ? sc_0~load_int(257) : null();
return (sc_0, (v'key, v'value));
}
(slice, ((slice, int))) __gen_read_SetAddrMap2(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 624157584);
var v'key = sc_0~__tact_load_address();
var v'value = sc_0~load_int(1) ? sc_0~load_int(1) : null();
return (sc_0, (v'key, v'value));
}
(slice, ((slice, cell))) __gen_read_SetAddrMap3(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 4276365062);
var v'key = sc_0~__tact_load_address();
var v'value = sc_0~load_int(1) ? sc_0~load_ref() : null();
return (sc_0, (v'key, v'value));
}
(slice, ((slice, tuple))) __gen_read_SetAddrMap4(slice sc_0) inline_ref {
throw_unless(129, sc_0~load_uint(32) == 1683777913);
var v'key = sc_0~__tact_load_address();
var v'value = sc_0~load_int(1) ? __gen_SomeStruct_as_optional(sc_0~__gen_read_SomeStruct()) : null();
return (sc_0, (v'key, v'value));
}
builder __gen_write_MapTestContract(builder build_0, (cell, cell, cell, cell, cell, cell, cell, cell) v) inline_ref {
var (v'intMap1, v'intMap2, v'intMap3, v'intMap4, v'addrMap1, v'addrMap2, v'addrMap3, v'addrMap4) = v;
build_0 = build_0.store_dict(v'intMap1);
build_0 = build_0.store_dict(v'intMap2);
var build_1 = begin_cell();
build_1 = build_1.store_dict(v'intMap3);
build_1 = build_1.store_dict(v'intMap4);
build_1 = build_1.store_dict(v'addrMap1);
var build_2 = begin_cell();
build_2 = build_2.store_dict(v'addrMap2);
build_2 = build_2.store_dict(v'addrMap3);
build_2 = build_2.store_dict(v'addrMap4);
build_1 = store_ref(build_1, build_2.end_cell());
build_0 = store_ref(build_0, build_1.end_cell());
return build_0;
}
(slice, ((cell, cell, cell, cell, cell, cell, cell, cell))) __gen_read_MapTestContract(slice sc_0) inline_ref {
var v'intMap1 = sc_0~load_dict();
var v'intMap2 = sc_0~load_dict();
slice sc_1 = sc_0~load_ref().begin_parse();
var v'intMap3 = sc_1~load_dict();
var v'intMap4 = sc_1~load_dict();
var v'addrMap1 = sc_1~load_dict();
slice sc_2 = sc_1~load_ref().begin_parse();
var v'addrMap2 = sc_2~load_dict();
var v'addrMap3 = sc_2~load_dict();
var v'addrMap4 = sc_2~load_dict();
return (sc_0, (v'intMap1, v'intMap2, v'intMap3, v'intMap4, v'addrMap1, v'addrMap2, v'addrMap3, v'addrMap4));
}
tuple __gen_SomeStruct_to_tuple(((int)) v) {
var (v'value) = v;
return __tact_tuple_create_1(v'value);
}
tuple __gen_SomeStruct_opt_to_tuple(tuple v) inline {
if (null?(v)) { return null(); }
return __gen_SomeStruct_to_tuple(__gen_SomeStruct_not_null(v));
}
tuple __gen_SomeStruct_opt_to_external(tuple v) inline {
var loaded = __gen_SomeStruct_opt_to_tuple(v);
if (null?(loaded)) {
return null();
} else {
return (loaded);
}
}
(cell, cell, cell, cell, cell, cell, cell, cell) __gen_load_MapTestContract() inline_ref {
slice sc = get_data().begin_parse();
__tact_context_sys = sc~load_ref();
return sc~__gen_read_MapTestContract();
}
() __gen_store_MapTestContract((cell, cell, cell, cell, cell, cell, cell, cell) v) impure inline_ref {
builder b = begin_cell();
b = b.store_ref(__tact_context_sys);
b = __gen_write_MapTestContract(b, v);
set_data(b.end_cell());
}
cell $__gen_MapTestContract_intMap1((cell, cell, cell, cell, cell, cell, cell, cell) $self) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return $self'intMap1;
}
_ $__gen_get_intMap1() method_id(67207) {
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_intMap1(self);
return res;
}
int $__gen_MapTestContract_intMap1Value((cell, cell, cell, cell, cell, cell, cell, cell) $self, int $key) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return __tact_dict_get_int_int($self'intMap1, 257, $key, 257);
}
_ $__gen_get_intMap1Value(int $$key) method_id(103396) {
int $key = $$key;
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_intMap1Value(self, $key);
return res;
}
cell $__gen_MapTestContract_intMap2((cell, cell, cell, cell, cell, cell, cell, cell) $self) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return $self'intMap2;
}
_ $__gen_get_intMap2() method_id(79588) {
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_intMap2(self);
return res;
}
int $__gen_MapTestContract_intMap2Value((cell, cell, cell, cell, cell, cell, cell, cell) $self, int $key) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return __tact_dict_get_int_int($self'intMap2, 257, $key, 1);
}
_ $__gen_get_intMap2Value(int $$key) method_id(89348) {
int $key = $$key;
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_intMap2Value(self, $key);
return res;
}
cell $__gen_MapTestContract_intMap3((cell, cell, cell, cell, cell, cell, cell, cell) $self) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return $self'intMap3;
}
_ $__gen_get_intMap3() method_id(75461) {
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_intMap3(self);
return res;
}
cell $__gen_MapTestContract_intMap3Value((cell, cell, cell, cell, cell, cell, cell, cell) $self, int $key) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return __tact_dict_get_int_cell($self'intMap3, 257, $key);
}
_ $__gen_get_intMap3Value(int $$key) method_id(71844) {
int $key = $$key;
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_intMap3Value(self, $key);
return res;
}
cell $__gen_MapTestContract_intMap4((cell, cell, cell, cell, cell, cell, cell, cell) $self) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return $self'intMap4;
}
_ $__gen_get_intMap4() method_id(87586) {
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_intMap4(self);
return res;
}
tuple $__gen_MapTestContract_intMap4Value((cell, cell, cell, cell, cell, cell, cell, cell) $self, int $key) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return __gen_readopt_SomeStruct(__tact_dict_get_int_cell($self'intMap4, 257, $key));
}
_ $__gen_get_intMap4Value(int $$key) method_id(119013) {
int $key = $$key;
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_intMap4Value(self, $key);
return __gen_SomeStruct_opt_to_external(res);
}
cell $__gen_MapTestContract_addrMap1((cell, cell, cell, cell, cell, cell, cell, cell) $self) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return $self'addrMap1;
}
_ $__gen_get_addrMap1() method_id(93537) {
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_addrMap1(self);
return res;
}
int $__gen_MapTestContract_addrMap1Value((cell, cell, cell, cell, cell, cell, cell, cell) $self, slice $key) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return __tact_dict_get_slice_int($self'addrMap1, 267, $key, 257);
}
_ $__gen_get_addrMap1Value(slice $$key) method_id(116148) {
slice $key = $$key;
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_addrMap1Value(self, $key);
return res;
}
cell $__gen_MapTestContract_addrMap2((cell, cell, cell, cell, cell, cell, cell, cell) $self) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return $self'addrMap2;
}
_ $__gen_get_addrMap2() method_id(89346) {
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_addrMap2(self);
return res;
}
int $__gen_MapTestContract_addrMap2Value((cell, cell, cell, cell, cell, cell, cell, cell) $self, slice $key) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return __tact_dict_get_slice_int($self'addrMap2, 267, $key, 1);
}
_ $__gen_get_addrMap2Value(slice $$key) method_id(68436) {
slice $key = $$key;
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_addrMap2Value(self, $key);
return res;
}
cell $__gen_MapTestContract_addrMap3((cell, cell, cell, cell, cell, cell, cell, cell) $self) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return $self'addrMap3;
}
_ $__gen_get_addrMap3() method_id(85283) {
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_addrMap3(self);
return res;
}
cell $__gen_MapTestContract_addrMap3Value((cell, cell, cell, cell, cell, cell, cell, cell) $self, slice $key) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return __tact_dict_get_slice_cell($self'addrMap3, 267, $key);
}
_ $__gen_get_addrMap3Value(slice $$key) method_id(85748) {
slice $key = $$key;
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_addrMap3Value(self, $key);
return res;
}
cell $__gen_MapTestContract_addrMap4((cell, cell, cell, cell, cell, cell, cell, cell) $self) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return $self'addrMap4;
}
_ $__gen_get_addrMap4() method_id(81348) {
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_addrMap4(self);
return res;
}
tuple $__gen_MapTestContract_addrMap4Value((cell, cell, cell, cell, cell, cell, cell, cell) $self, slice $key) impure {
var (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4)) = $self;
return __gen_readopt_SomeStruct(__tact_dict_get_slice_cell($self'addrMap4, 267, $key));
}
_ $__gen_get_addrMap4Value(slice $$key) method_id(100021) {
slice $key = $$key;
var self = __gen_load_MapTestContract();
var res = $__gen_MapTestContract_addrMap4Value(self, $key);
return __gen_SomeStruct_opt_to_external(res);
}
(((cell, cell, cell, cell, cell, cell, cell, cell)), ()) $__gen_MapTestContract_receive_SetIntMap1((cell, cell, cell, cell, cell, cell, cell, cell) $self, (int, int) $msg) impure {
var ($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4) = $self;
var ($msg'key, $msg'value) = $msg;
$self'intMap1~__tact_dict_set_int_int(257, $msg'key, $msg'value, 257);
return (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4), ());
}
(((cell, cell, cell, cell, cell, cell, cell, cell)), ()) $__gen_MapTestContract_receive_SetIntMap2((cell, cell, cell, cell, cell, cell, cell, cell) $self, (int, int) $msg) impure {
var ($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4) = $self;
var ($msg'key, $msg'value) = $msg;
$self'intMap2~__tact_dict_set_int_int(257, $msg'key, $msg'value, 1);
return (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4), ());
}
(((cell, cell, cell, cell, cell, cell, cell, cell)), ()) $__gen_MapTestContract_receive_SetIntMap3((cell, cell, cell, cell, cell, cell, cell, cell) $self, (int, cell) $msg) impure {
var ($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4) = $self;
var ($msg'key, $msg'value) = $msg;
$self'intMap3~__tact_dict_set_int_cell(257, $msg'key, $msg'value);
return (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4), ());
}
(((cell, cell, cell, cell, cell, cell, cell, cell)), ()) $__gen_MapTestContract_receive_SetIntMap4((cell, cell, cell, cell, cell, cell, cell, cell) $self, (int, tuple) $msg) impure {
var ($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4) = $self;
var ($msg'key, $msg'value) = $msg;
$self'intMap4~__tact_dict_set_int_cell(257, $msg'key, __gen_writecellopt_SomeStruct($msg'value));
return (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4), ());
}
(((cell, cell, cell, cell, cell, cell, cell, cell)), ()) $__gen_MapTestContract_receive_SetAddrMap1((cell, cell, cell, cell, cell, cell, cell, cell) $self, (slice, int) $msg) impure {
var ($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4) = $self;
var ($msg'key, $msg'value) = $msg;
$self'addrMap1~__tact_dict_set_slice_int(267, $msg'key, $msg'value, 257);
return (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4), ());
}
(((cell, cell, cell, cell, cell, cell, cell, cell)), ()) $__gen_MapTestContract_receive_SetAddrMap2((cell, cell, cell, cell, cell, cell, cell, cell) $self, (slice, int) $msg) impure {
var ($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4) = $self;
var ($msg'key, $msg'value) = $msg;
$self'addrMap2~__tact_dict_set_slice_int(267, $msg'key, $msg'value, 1);
return (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4), ());
}
(((cell, cell, cell, cell, cell, cell, cell, cell)), ()) $__gen_MapTestContract_receive_SetAddrMap3((cell, cell, cell, cell, cell, cell, cell, cell) $self, (slice, cell) $msg) impure {
var ($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4) = $self;
var ($msg'key, $msg'value) = $msg;
$self'addrMap3~__tact_dict_set_slice_cell(267, $msg'key, $msg'value);
return (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4), ());
}
(((cell, cell, cell, cell, cell, cell, cell, cell)), ()) $__gen_MapTestContract_receive_SetAddrMap4((cell, cell, cell, cell, cell, cell, cell, cell) $self, (slice, tuple) $msg) impure {
var ($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4) = $self;
var ($msg'key, $msg'value) = $msg;
$self'addrMap4~__tact_dict_set_slice_cell(267, $msg'key, __gen_writecellopt_SomeStruct($msg'value));
return (($self'intMap1, $self'intMap2, $self'intMap3, $self'intMap4, $self'addrMap1, $self'addrMap2, $self'addrMap3, $self'addrMap4), ());
}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
;; Parse incoming message
int op = 0;
if (slice_bits(in_msg) >= 32) {
op = in_msg.preload_uint(32);
}
var cs = in_msg_cell.begin_parse();
var msg_flags = cs~load_uint(4);
var msg_bounced = ((msg_flags & 1) == 1 ? true : false);
slice msg_sender_addr = cs~load_msg_addr();
__tact_context = (msg_bounced, msg_sender_addr, msg_value, cs);
;; Handle bounced messages
if (msg_bounced) {
return ();
}
;; Receive SetIntMap1 message
if (op == 1510253336) {
var self = __gen_load_MapTestContract();
var msg = in_msg~__gen_read_SetIntMap1();
self~$__gen_MapTestContract_receive_SetIntMap1(msg);
__gen_store_MapTestContract(self);
return ();
}
;; Receive SetIntMap2 message
if (op == 1629867766) {
var self = __gen_load_MapTestContract();
var msg = in_msg~__gen_read_SetIntMap2();
self~$__gen_MapTestContract_receive_SetIntMap2(msg);
__gen_store_MapTestContract(self);
return ();
}
;; Receive SetIntMap3 message
if (op == 3613954633) {
var self = __gen_load_MapTestContract();
var msg = in_msg~__gen_read_SetIntMap3();
self~$__gen_MapTestContract_receive_SetIntMap3(msg);
__gen_store_MapTestContract(self);
return ();
}
;; Receive SetIntMap4 message
if (op == 383013829) {
var self = __gen_load_MapTestContract();
var msg = in_msg~__gen_read_SetIntMap4();
self~$__gen_MapTestContract_receive_SetIntMap4(msg);
__gen_store_MapTestContract(self);
return ();
}
;; Receive SetAddrMap1 message
if (op == 1749966413) {
var self = __gen_load_MapTestContract();
var msg = in_msg~__gen_read_SetAddrMap1();
self~$__gen_MapTestContract_receive_SetAddrMap1(msg);
__gen_store_MapTestContract(self);
return ();
}
;; Receive SetAddrMap2 message
if (op == 624157584) {
var self = __gen_load_MapTestContract();
var msg = in_msg~__gen_read_SetAddrMap2();
self~$__gen_MapTestContract_receive_SetAddrMap2(msg);
__gen_store_MapTestContract(self);
return ();
}
;; Receive SetAddrMap3 message
if (op == 4276365062) {
var self = __gen_load_MapTestContract();
var msg = in_msg~__gen_read_SetAddrMap3();
self~$__gen_MapTestContract_receive_SetAddrMap3(msg);
__gen_store_MapTestContract(self);
return ();
}
;; Receive SetAddrMap4 message
if (op == 1683777913) {
var self = __gen_load_MapTestContract();
var msg = in_msg~__gen_read_SetAddrMap4();
self~$__gen_MapTestContract_receive_SetAddrMap4(msg);
__gen_store_MapTestContract(self);
return ();
}
throw(130);
}
_ supported_interfaces() method_id {
return (
"org.ton.introspection.v0"H >> 128,
"org.ton.abi.ipfs.v0"H >> 128
);
}
_ get_abi_ipfs() {
return "ipfs://QmVmzQSudFJ3B1LEYUvRk86wLNZwXw7ZNgZXj8L2ET5ymw";
}

Some files were not shown because too many files have changed in this diff Show more