Michael Toutonghi
6 years ago
156 changed files with 13702 additions and 1129 deletions
@ -1,67 +1,46 @@ |
|||
sudo: required |
|||
os: linux |
|||
dist: xenial |
|||
language: cpp |
|||
compiler: |
|||
- gcc |
|||
env: |
|||
global: |
|||
- CCACHE_SIZE=100M |
|||
- CCACHE_TEMPDIR=/tmp/.ccache-temp |
|||
- CCACHE_COMPRESS=1 |
|||
- STORAGE_DEST=gs://$BUCKET/$PROJECT/$TRAVIS_BRANCH/ |
|||
cache: |
|||
apt: true |
|||
directories: |
|||
- depends/built |
|||
- depends/sdk-sources |
|||
- "$HOME/google-cloud-sdk/" |
|||
- "$HOME/.ccache" |
|||
|
|||
matrix: |
|||
fast_finish: true |
|||
include: |
|||
- compiler: ": Linux" |
|||
env: BUILD_SCRIPT=build.sh |
|||
PACKAGES="build-essential pkg-config libc6-dev m4 g++-multilib |
|||
autoconf libtool ncurses-dev unzip python zlib1g-dev wget bsdmainutils automake |
|||
libssl-dev libprotobuf-dev protobuf-compiler libqrencode-dev libdb++-dev software-properties-common |
|||
libcurl4-openssl-dev curl" |
|||
PACKAGE_NAME=verus-cli-linux.tar.gz |
|||
PACKAGING_MATRIX="cp src/komodod src/komodo-cli kmd/linux/verus-cli && chmod +x kmd/linux/verus-cli/komodod && chmod +x kmd/linux/verus-cli/komodo-cli && cd kmd/linux && tar -czf $PACKAGE_NAME verus-cli && ls" |
|||
- compiler: ": Windows" |
|||
env: RUST_TARGET=x86_64-pc-windows-gnu |
|||
BUILD_SCRIPT=build-win.sh |
|||
PACKAGES="build-essential pkg-config libcurl3-gnutls-dev |
|||
libc6-dev libevent-dev m4 g++-multilib autoconf libtool ncurses-dev unzip git |
|||
python zlib1g-dev wget bsdmainutils automake libssl-dev libprotobuf-dev protobuf-compiler |
|||
libdb++-dev ntp ntpdate mingw-w64 wine bc" |
|||
PACKAGE_NAME=verus-cli-windows.zip |
|||
PACKAGING_MATRIX="cp src/komodod.exe src/komodo-cli.exe src/komodo-tx.exe kmd/windows/verus-cli && |
|||
cd kmd/windows && zip -r9 $PACKAGE_NAME verus-cli && ls" |
|||
exclude: |
|||
- compiler: gcc |
|||
install: |
|||
- sudo rm -f /etc/apt/sources.list.d/travis_ci_zeromq3-source.list |
|||
- travis_retry sudo apt-get -y update && travis_retry sudo apt-get -y install -qq $PACKAGES |
|||
- if [ -n "$RUST_TARGET" ]; then curl -sSf https://build.travis-ci.org/files/rustup-init.sh |
|||
| sh -s -- --default-toolchain stable -y && export PATH=$PATH:$HOME/.cargo/bin:$PATH && rustup target add $RUST_TARGET; fi |
|||
before_script: |
|||
- unset CC; unset CXX |
|||
- os: linux |
|||
dist: xenial |
|||
sudo: required |
|||
- os: osx |
|||
osx_image: xcode8 |
|||
compiler: |
|||
- gcc |
|||
before_install: |
|||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa; fi |
|||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update -qq; fi |
|||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install libgnutls28-dev; fi |
|||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install build-essential pkg-config libcurl3-gnutls-dev libc6-dev libevent-dev m4 g++-multilib autoconf libtool ncurses-dev unzip git python zlib1g-dev wget bsdmainutils automake libssl-dev libprotobuf-dev protobuf-compiler libdb++-dev ntp ntpdate; fi |
|||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then rm '/usr/local/include/c++'; fi |
|||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi |
|||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install gcc@6; fi |
|||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew link --overwrite gcc@6; fi |
|||
script: |
|||
- "./zcutil/fetch-params.sh" |
|||
- "./zcutil/$BUILD_SCRIPT -j2" |
|||
after_script: |
|||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then eval "${PACKAGING_MATRIX}" && |
|||
gsutil cp $PACKAGE_NAME $STORAGE_DEST; fi |
|||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./zcutil/build.sh -j 5; fi |
|||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./zcutil/build-mac.sh -j 5; fi |
|||
notifications: |
|||
slack: |
|||
secure: FiVlFhSw5xnDu1Cx2yAo3J7miFCSRyuzR/2+8LKFjdWl5+fyIGvQ9x5vgUg6dWbv3UP9iIMqQuWfotsg8H+NE8pYRZQ0zDVxZ5h9+PA028qGb3OF4TMFNcltP5DGtAZ6AqrMNRZ4ltatPUm5H9ig1bhzjsx+3pqlqQuVXTXPjaUryB5s/fk2CjrsV6zTLfPHiI30jeMjmQrJJLik1vSWF70sB6HkQhvaT6jymkO4Vuh+cja418W1xIgkkoRsOXiZ/JK4hIypFo/sBkmIOprGqoFUahFqJlsBoSrp9iAzkwbDItIqqvNCHTEeN7lj6kK43ZK72E4etjjNc0CXWeleXBJBCj5Prq2lEkQ4NwuDTos3KLyyr2vI7f54xhb5+wjzY9dByHXGuG5UaNz0+uukuJinAdazGaNmmfesv1wg9p3jGa/TLsfHLMcUti875DzkUHnenivP5cXrc6/uuZyyQNq5+Gn/3DA8k0y7d1e23nm3nDjCNfATAn3yu1jieYY2yYI6CYGEXcD+UbP61uG6no+mm/lkQbQosyDfE0sADqGryqXswRste+R0sSVMBQtTipAZOUoYNbEmhN4+L78SSp3zpmgkrIxAw7le8oj6Evp2ofvE2Kvh+Z0MVoEJx6mtZI6hheIFSS38NeUZr/HBfRSpaElOYTN/ZNf8QwThCWo= |
|||
before_install: |
|||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then openssl aes-256-cbc -K $encrypted_11153c0bb86c_key -iv $encrypted_11153c0bb86c_iv |
|||
-in AUTH_KEY.json.enc -out AUTH_KEY.json -d; fi |
|||
- if [ ! -d "$HOME/google-cloud-sdk/bin" ]; then rm -rf $HOME/google-cloud-sdk; export |
|||
CLOUDSDK_CORE_DISABLE_PROMPTS=1; curl https://sdk.cloud.google.com | bash; fi |
|||
- source /home/travis/google-cloud-sdk/path.bash.inc |
|||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then gcloud auth activate-service-account --key-file AUTH_KEY.json; fi |
|||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then rm AUTH_KEY.json; fi |
|||
- rm AUTH_KEY.json.enc |
|||
irc: |
|||
channels: |
|||
- chat.freenode.net#komodoplatform |
|||
template: |
|||
- "%{repository}/%{branch} (%{commit} - %{author}): %{message}" |
|||
- 'Alt Message : %{repository_slug} - (%{commit} - %{author}): %{message}, Build |
|||
Time: %{duration}' |
|||
- 'Change view : %{compare_url}' |
|||
- 'Build details : %{build_url}' |
|||
before_deploy: |
|||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then git tag "linux-$(date +'%Y%m%d%H%M')-$(git log --format=%h -1)"; fi |
|||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then git tag "osx-$(date +'%Y%m%d%H%M')-$(git log --format=%h -1)"; fi |
|||
deploy: |
|||
provider: releases |
|||
api_key: |
|||
secure: Jms7dz5GMZmuXUCHl5u6iZUtAybv86oW3x36DCJfdzbDiO4B9EWB04z7zA0qvoomefyujHTmQUHxyOKfd4h9/rVMFFv9hgmUxWkrcg7KyLNisOQRaovVOuNtu2lRNXTOSF16Cy+xFGkVh1ObBRhAoMsVKPhMl6PCDiKhNWIekRR9pBtjafKsClQ2ieknUYfqhuvgj7zmqCedeyVaVQyt2W/J65leD0BkfCUESTpANSprHs4bQB65VuQIKKMi+URKx2VgpDdUcWJySt9jAHVPIbI5cT5maAT6RUMnE4oha7Ca1Ox8StBqjQ/hkkMyDbN0keIlN7RjZlwdZQf/qUnT/dPQhsyUCdPXOxmEJ2jekezEK/LGr4Fb+v+vjd9dhLNkD5nVn9zp36biGSCjiMpffQ3fjMeM0YGmVEVRP9kZXLWVRYQoVKrzjyzg5dY8iChbiQEfYpTeBuU+e2rqj4mns+Jvy0zjUbMy6Tyva+iqdZ/PdsBDbiB7c+FIgB1IUTVOD+GgKx6dhCtBZEccn5EyWFwZF9IdQJHZCYV4PA7nuzfm1Ol9SDdZkGHd2OgRCqK/sTwyfTHv8exNqZ1k+epGJp2a0q4IOEknc9aPCAF+m9pHahk7s7VO5gmhO6pvbvuKoeEtEXRZHRzCkGkXfzJlBk+23X5gBexKb6inRdBlj6M= |
|||
file: |
|||
- src/komodod |
|||
- src/komodo-cli |
|||
skip_cleanup: true |
|||
on: |
|||
repo: KomodoPlatform/komodo |
|||
|
@ -0,0 +1,12 @@ |
|||
rpcuser=dontuseweakusernameoryougetrobbed |
|||
rpcpassword=dontuseweakpasswordoryougetrobbed |
|||
txindex=1 |
|||
server=1 |
|||
rpcworkqueue=64 |
|||
addnode=5.9.102.210 |
|||
addnode=78.47.196.146 |
|||
addnode=178.63.69.164 |
|||
addnode=88.198.65.74 |
|||
addnode=5.9.122.241 |
|||
addnode=144.76.94.38 |
|||
addnode=89.248.166.91 |
@ -1,61 +0,0 @@ |
|||
# MigrateCoin protocol |
|||
|
|||
|
|||
|
|||
## ExportCoins tx: |
|||
|
|||
|
|||
|
|||
``` |
|||
|
|||
vin: |
|||
|
|||
[ any ] |
|||
|
|||
vout: |
|||
|
|||
- amount: {burnAmount} |
|||
|
|||
script: OP_RETURN "send to ledger {id} {voutsHash}" |
|||
|
|||
``` |
|||
|
|||
|
|||
|
|||
* ExportCoin is a standard tx which burns coins in an OP_RETURN |
|||
|
|||
|
|||
|
|||
## ImportCoins tx: |
|||
|
|||
|
|||
|
|||
``` |
|||
|
|||
vin: |
|||
|
|||
- txid: 0000000000000000000000000000000000000000000000000000000000000000 |
|||
|
|||
idx: 0 |
|||
|
|||
script: CC_EVAL(EVAL_IMPORTCOINS, {momoProof},{exportCoin}) OP_CHECKCRYPTOCONDITION_UNILATERAL |
|||
|
|||
vout: |
|||
|
|||
- [ vouts matching voutsHash in exportCoin ] |
|||
|
|||
``` |
|||
|
|||
|
|||
|
|||
* ImportCoin transaction has no signature |
|||
|
|||
* ImportCoin is non malleable |
|||
|
|||
* ImportCoin satisfies tx.IsCoinBase() |
|||
|
|||
* ImportCoin uses a new opcode which allows a one sided check (no scriptPubKey) |
|||
|
|||
* ImportCoin must contain CC opcode EVAL_IMPORTCOINS |
|||
|
|||
* ImportCoin fees are equal to the difference between burnAmount in exportCoins and the sum of outputs. |
@ -0,0 +1,43 @@ |
|||
#!/usr/bin/bash |
|||
|
|||
# This script makes the neccesary transactions to migrate |
|||
# coin between 2 assetchains on the same -ac_cc id |
|||
|
|||
set -e |
|||
|
|||
source=TXSCL |
|||
target=TXSCL000 |
|||
address="RFw7byY4xZpZCrtkMk3nFuuG1NTs9rSGgQ" |
|||
amount=1 |
|||
|
|||
# Alias for running cli on source chain |
|||
cli_source="komodo-cli -ac_name=$source" |
|||
|
|||
# Raw tx that we will work with |
|||
txraw=`$cli_source createrawtransaction "[]" "{\"$address\":$amount}"` |
|||
|
|||
# Convert to an export tx |
|||
exportData=`$cli_source migrate_converttoexport $txraw $target $amount` |
|||
exportRaw=`echo $exportData | jq -r .exportTx` |
|||
exportPayouts=`echo $exportData | jq -r .payouts` |
|||
|
|||
# Fund |
|||
exportFundedData=`$cli_source fundrawtransaction $exportRaw` |
|||
exportFundedTx=`echo $exportFundedData | jq -r .hex` |
|||
|
|||
# Sign |
|||
exportSignedData=`$cli_source signrawtransaction $exportFundedTx` |
|||
exportSignedTx=`echo $exportSignedData | jq -r .hex` |
|||
|
|||
# Send |
|||
echo "Sending export tx" |
|||
$cli_source sendrawtransaction $exportSignedTx |
|||
|
|||
read -p "Wait for a notarisation to KMD, and then two more notarisations from the target chain, and then press enter to continue" |
|||
|
|||
# Create import |
|||
importTx=`$cli_source migrate_createimporttransaction $exportSignedTx $payouts` |
|||
importTx=`komodo-cli migrate_completeimporttransaction $importTx` |
|||
|
|||
# Send import |
|||
komodo-cli -ac_name=$target sendrawtransaction $importTx |
@ -0,0 +1,605 @@ |
|||
#!/usr/bin/env python2 |
|||
# Copyright (c) 2018 SuperNET developers |
|||
# Distributed under the MIT software license, see the accompanying |
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|||
|
|||
from test_framework.test_framework import BitcoinTestFramework |
|||
from test_framework.authproxy import JSONRPCException |
|||
from test_framework.util import assert_equal, assert_greater_than, \ |
|||
initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \ |
|||
stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises |
|||
|
|||
import time |
|||
from decimal import Decimal |
|||
|
|||
def assert_success(result): |
|||
assert_equal(result['result'], 'success') |
|||
|
|||
def assert_error(result): |
|||
assert_equal(result['result'], 'error') |
|||
|
|||
class CryptoConditionsTest (BitcoinTestFramework): |
|||
|
|||
def setup_chain(self): |
|||
print("Initializing CC test directory "+self.options.tmpdir) |
|||
self.num_nodes = 1 |
|||
initialize_chain_clean(self.options.tmpdir, self.num_nodes) |
|||
|
|||
def setup_network(self, split = False): |
|||
print("Setting up network...") |
|||
self.addr = "RWPg8B91kfK5UtUN7z6s6TeV9cHSGtVY8D" |
|||
self.pubkey = "02676d00110c2cd14ae24f95969e8598f7ccfaa675498b82654a5b5bd57fc1d8cf" |
|||
self.privkey = "UqMgxk7ySPNQ4r9nKAFPjkXy6r5t898yhuNCjSZJLg3RAM4WW1m9" |
|||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, |
|||
extra_args=[[ |
|||
# always give -ac_name as first extra_arg |
|||
'-ac_name=REGTEST', |
|||
'-conf='+self.options.tmpdir+'/node0/REGTEST.conf', |
|||
'-port=64367', |
|||
'-rpcport=64368', |
|||
'-regtest', |
|||
'-addressindex=1', |
|||
'-spentindex=1', |
|||
'-ac_supply=5555555', |
|||
'-ac_reward=10000000', |
|||
'-pubkey=' + self.pubkey, |
|||
'-ac_cc=2', |
|||
'-whitelist=127.0.0.1', |
|||
'-debug', |
|||
'-daemon', |
|||
'-rpcuser=rt', |
|||
'-rpcpassword=rt' |
|||
]] |
|||
) |
|||
self.is_network_split = split |
|||
self.rpc = self.nodes[0] |
|||
self.sync_all() |
|||
print("Done setting up network") |
|||
|
|||
def send_and_mine(self, xtn): |
|||
txid = self.rpc.sendrawtransaction(xtn) |
|||
assert txid, 'got txid' |
|||
# we need the tx above to be confirmed in the next block |
|||
self.rpc.generate(1) |
|||
return txid |
|||
|
|||
def run_faucet_tests(self): |
|||
rpc = self.rpc |
|||
|
|||
# basic sanity tests |
|||
result = rpc.getwalletinfo() |
|||
assert_greater_than(result['txcount'], 100) |
|||
assert_greater_than(result['balance'], 0.0) |
|||
balance = result['balance'] |
|||
|
|||
faucet = rpc.faucetaddress() |
|||
assert_equal(faucet['result'], 'success') |
|||
# verify all keys look like valid AC addrs, could be better |
|||
for x in ['myCCaddress', 'FaucetCCaddress', 'Faucetmarker', 'myaddress']: |
|||
assert_equal(faucet[x][0], 'R') |
|||
|
|||
result = rpc.faucetaddress(self.pubkey) |
|||
assert_success(result) |
|||
# test that additional CCaddress key is returned |
|||
for x in ['myCCaddress', 'FaucetCCaddress', 'Faucetmarker', 'myaddress', 'CCaddress']: |
|||
assert_equal(result[x][0], 'R') |
|||
|
|||
# no funds in the faucet yet |
|||
result = rpc.faucetget() |
|||
assert_error(result) |
|||
|
|||
result = rpc.faucetinfo() |
|||
assert_success(result) |
|||
|
|||
result = rpc.faucetfund("0") |
|||
assert_error(result) |
|||
|
|||
result = rpc.faucetfund("-1") |
|||
assert_error(result) |
|||
|
|||
# we need at least 1 + txfee to get |
|||
result = rpc.faucetfund("2") |
|||
assert_success(result) |
|||
assert result['hex'], "hex key found" |
|||
|
|||
# broadcast the xtn |
|||
result = rpc.sendrawtransaction(result['hex']) |
|||
txid = result[0] |
|||
assert txid, "found txid" |
|||
|
|||
# we need the tx above to be confirmed in the next block |
|||
rpc.generate(1) |
|||
|
|||
result = rpc.getwalletinfo() |
|||
balance2 = result['balance'] |
|||
# make sure our balance is less now |
|||
assert_greater_than(balance, balance2) |
|||
|
|||
result = rpc.faucetinfo() |
|||
assert_success(result) |
|||
assert_greater_than( result['funding'], 0 ) |
|||
|
|||
result = rpc.faucetget() |
|||
assert_success(result) |
|||
assert result['hex'], "hex key found" |
|||
|
|||
# try to broadcast the xtn, but we will get 'faucet is only for brand new addresses' |
|||
assert_raises(JSONRPCException, rpc.sendrawtransaction, [ result['hex'] ]) |
|||
|
|||
newaddr = rpc.getnewaddress() |
|||
assert newaddr, "got a new address" |
|||
result = rpc.validateaddress(newaddr) |
|||
newpubkey = result['pubkey'] |
|||
assert newpubkey, "got a pubkey for new address" |
|||
|
|||
def run_dice_tests(self): |
|||
rpc = self.nodes[0] |
|||
|
|||
dice = rpc.diceaddress() |
|||
assert_equal(dice['result'], 'success') |
|||
for x in ['myCCaddress', 'DiceCCaddress', 'Dicemarker', 'myaddress']: |
|||
assert_equal(dice[x][0], 'R') |
|||
|
|||
dice = rpc.diceaddress(self.pubkey) |
|||
assert_equal(dice['result'], 'success') |
|||
for x in ['myCCaddress', 'DiceCCaddress', 'Dicemarker', 'myaddress', 'CCaddress']: |
|||
assert_equal(dice[x][0], 'R') |
|||
|
|||
# no dice created yet |
|||
result = rpc.dicelist() |
|||
assert_equal(result, []) |
|||
|
|||
# creating dice plan with too long name (>8 chars) |
|||
result = rpc.dicefund("THISISTOOLONG", "10000", "10", "10000", "10", "5") |
|||
assert_error(result) |
|||
|
|||
# creating dice plan with < 100 funding |
|||
result = rpc.dicefund("LUCKY","10","1","10000","10","5") |
|||
assert_error(result) |
|||
|
|||
# creating dice plan with 0 blocks timeout |
|||
result = rpc.dicefund("LUCKY","10","1","10000","10","0") |
|||
assert_error(result) |
|||
|
|||
# creating dice plan |
|||
dicefundtx = rpc.dicefund("LUCKY","1000","1","800","10","5") |
|||
diceid = self.send_and_mine(dicefundtx['hex']) |
|||
|
|||
# checking if it in plans list now |
|||
result = rpc.dicelist() |
|||
assert_equal(result[0], diceid) |
|||
|
|||
# set dice name for futher usage |
|||
dicename = "LUCKY" |
|||
|
|||
# adding zero funds to plan |
|||
result = rpc.diceaddfunds(dicename,diceid,"0") |
|||
assert_error(result) |
|||
|
|||
# adding negative funds to plan |
|||
result = rpc.diceaddfunds(dicename,diceid,"-1") |
|||
assert_error(result) |
|||
|
|||
# adding funds to plan |
|||
addfundstx = rpc.diceaddfunds(dicename,diceid,"1100") |
|||
result = self.send_and_mine(addfundstx['hex']) |
|||
|
|||
# checking if funds added to plan |
|||
result = rpc.diceinfo(diceid) |
|||
assert_equal(result["funding"], "2100.00000000") |
|||
|
|||
# not valid dice info checking |
|||
result = rpc.diceinfo("invalid") |
|||
assert_error(result) |
|||
|
|||
# placing 0 amount bet |
|||
result = rpc.dicebet(dicename,diceid,"0","1") |
|||
assert_error(result) |
|||
|
|||
# placing negative amount bet |
|||
result = rpc.dicebet(dicename,diceid,"-1","1") |
|||
assert_error(result) |
|||
|
|||
# placing bet more than maxbet |
|||
result = rpc.dicebet(dicename,diceid,"900","1") |
|||
assert_error(result) |
|||
|
|||
# placing bet with amount more than funding |
|||
result = rpc.dicebet(dicename,diceid,"3000","1") |
|||
assert_error(result) |
|||
|
|||
# placing bet with potential won more than funding |
|||
result = rpc.dicebet(dicename,diceid,"750","9") |
|||
assert_error(result) |
|||
|
|||
# placing 0 odds bet |
|||
result = rpc.dicebet(dicename,diceid,"1","0") |
|||
assert_error(result) |
|||
|
|||
# placing negative odds bet |
|||
result = rpc.dicebet(dicename,diceid,"1","-1") |
|||
assert_error(result) |
|||
|
|||
# placing bet with odds more than allowed |
|||
result = rpc.dicebet(dicename,diceid,"1","11") |
|||
assert_error(result) |
|||
|
|||
# placing bet with not correct dice name |
|||
result = rpc.dicebet("nope",diceid,"100","1") |
|||
assert_error(result) |
|||
|
|||
# placing bet with not correct dice id |
|||
result = rpc.dicebet(dicename,self.pubkey,"100","1") |
|||
assert_error(result) |
|||
|
|||
# valid bet placing |
|||
placebet = rpc.dicebet(dicename,diceid,"100","1") |
|||
betid = self.send_and_mine(placebet["hex"]) |
|||
assert result, "bet placed" |
|||
|
|||
# check bet status |
|||
result = rpc.dicestatus(dicename,diceid,betid) |
|||
assert_success(result) |
|||
|
|||
# have to make some entropy for the next test |
|||
entropytx = 0 |
|||
fundingsum = 1 |
|||
while entropytx < 10: |
|||
fundingsuminput = str(fundingsum) |
|||
fundinghex = rpc.diceaddfunds(dicename,diceid,fundingsuminput) |
|||
result = self.send_and_mine(fundinghex['hex']) |
|||
entropytx = entropytx + 1 |
|||
fundingsum = fundingsum + 1 |
|||
|
|||
rpc.generate(2) |
|||
|
|||
# note initial dice funding state at this point. |
|||
# TODO: track player balance somehow (hard to do because of mining and fees) |
|||
diceinfo = rpc.diceinfo(diceid) |
|||
funding = float(diceinfo['funding']) |
|||
|
|||
# placing same amount bets with amount 1 and odds 1:2, checking if balance changed correct |
|||
losscounter = 0 |
|||
wincounter = 0 |
|||
betcounter = 0 |
|||
|
|||
while (betcounter < 10): |
|||
placebet = rpc.dicebet(dicename,diceid,"1","1") |
|||
betid = self.send_and_mine(placebet["hex"]) |
|||
finish = rpc.dicefinish(dicename,diceid,betid) |
|||
self.send_and_mine(finish["hex"]) |
|||
betresult = rpc.dicestatus(dicename,diceid,betid) |
|||
betcounter = betcounter + 1 |
|||
if betresult["status"] == "loss": |
|||
losscounter = losscounter + 1 |
|||
elif betresult["status"] == "win": |
|||
wincounter = wincounter + 1 |
|||
|
|||
# funding balance should increase if player loss, decrease if player won |
|||
fundbalanceguess = funding + losscounter - wincounter |
|||
fundinfoactual = rpc.diceinfo(diceid) |
|||
assert_equal(round(fundbalanceguess),round(float(fundinfoactual['funding']))) |
|||
|
|||
def run_token_tests(self): |
|||
rpc = self.nodes[0] |
|||
result = rpc.tokenaddress() |
|||
assert_success(result) |
|||
for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress']: |
|||
assert_equal(result[x][0], 'R') |
|||
|
|||
result = rpc.tokenaddress(self.pubkey) |
|||
assert_success(result) |
|||
for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress', 'CCaddress']: |
|||
assert_equal(result[x][0], 'R') |
|||
# there are no tokens created yet |
|||
result = rpc.tokenlist() |
|||
assert_equal(result, []) |
|||
|
|||
# trying to create token with negaive supply |
|||
result = rpc.tokencreate("NUKE", "-1987420", "no bueno supply") |
|||
assert_error(result) |
|||
|
|||
# creating token with name more than 32 chars |
|||
result = rpc.tokencreate("NUKE123456789012345678901234567890", "1987420", "name too long") |
|||
assert_error(result) |
|||
|
|||
# creating valid token |
|||
result = rpc.tokencreate("DUKE", "1987.420", "Duke's custom token") |
|||
assert_success(result) |
|||
|
|||
tokenid = self.send_and_mine(result['hex']) |
|||
|
|||
result = rpc.tokenlist() |
|||
assert_equal(result[0], tokenid) |
|||
|
|||
# there are no token orders yet |
|||
result = rpc.tokenorders() |
|||
assert_equal(result, []) |
|||
|
|||
# getting token balance for pubkey |
|||
result = rpc.tokenbalance(self.pubkey) |
|||
assert_success(result) |
|||
assert_equal(result['balance'], 0) |
|||
assert_equal(result['CCaddress'], 'RCRsm3VBXz8kKTsYaXKpy7pSEzrtNNQGJC') |
|||
assert_equal(result['tokenid'], self.pubkey) |
|||
|
|||
# get token balance for token with pubkey |
|||
result = rpc.tokenbalance(tokenid, self.pubkey) |
|||
assert_success(result) |
|||
assert_equal(result['balance'], 198742000000) |
|||
assert_equal(result['tokenid'], tokenid) |
|||
|
|||
# get token balance for token without pubkey |
|||
result = rpc.tokenbalance(tokenid) |
|||
assert_success(result) |
|||
assert_equal(result['balance'], 198742000000) |
|||
assert_equal(result['tokenid'], tokenid) |
|||
|
|||
# this is not a valid assetid |
|||
result = rpc.tokeninfo(self.pubkey) |
|||
assert_error(result) |
|||
|
|||
# check tokeninfo for valid token |
|||
result = rpc.tokeninfo(tokenid) |
|||
assert_success(result) |
|||
assert_equal(result['tokenid'], tokenid) |
|||
assert_equal(result['owner'], self.pubkey) |
|||
assert_equal(result['name'], "DUKE") |
|||
assert_equal(result['supply'], 198742000000) |
|||
assert_equal(result['description'], "Duke's custom token") |
|||
|
|||
# invalid numtokens ask |
|||
result = rpc.tokenask("-1", tokenid, "1") |
|||
assert_error(result) |
|||
|
|||
# invalid numtokens ask |
|||
result = rpc.tokenask("0", tokenid, "1") |
|||
assert_error(result) |
|||
|
|||
# invalid price ask |
|||
result = rpc.tokenask("1", tokenid, "-1") |
|||
assert_error(result) |
|||
|
|||
# invalid price ask |
|||
result = rpc.tokenask("1", tokenid, "0") |
|||
assert_error(result) |
|||
|
|||
# invalid tokenid ask |
|||
result = rpc.tokenask("100", "deadbeef", "1") |
|||
assert_error(result) |
|||
|
|||
# valid ask |
|||
tokenask = rpc.tokenask("100", tokenid, "7.77") |
|||
tokenaskhex = tokenask['hex'] |
|||
tokenaskid = self.send_and_mine(tokenask['hex']) |
|||
result = rpc.tokenorders() |
|||
order = result[0] |
|||
assert order, "found order" |
|||
|
|||
# invalid ask fillunits |
|||
result = rpc.tokenfillask(tokenid, tokenaskid, "0") |
|||
assert_error(result) |
|||
|
|||
# invalid ask fillunits |
|||
result = rpc.tokenfillask(tokenid, tokenaskid, "-777") |
|||
assert_error(result) |
|||
|
|||
# valid ask fillunits |
|||
fillask = rpc.tokenfillask(tokenid, tokenaskid, "777") |
|||
result = self.send_and_mine(fillask['hex']) |
|||
txid = result[0] |
|||
assert txid, "found txid" |
|||
|
|||
# should be no token orders |
|||
result = rpc.tokenorders() |
|||
assert_equal(result, []) |
|||
|
|||
# checking ask cancellation |
|||
testorder = rpc.tokenask("100", tokenid, "7.77") |
|||
testorderid = self.send_and_mine(testorder['hex']) |
|||
cancel = rpc.tokencancelask(tokenid, testorderid) |
|||
self.send_and_mine(cancel["hex"]) |
|||
result = rpc.tokenorders() |
|||
assert_equal(result, []) |
|||
|
|||
# invalid numtokens bid |
|||
result = rpc.tokenbid("-1", tokenid, "1") |
|||
assert_error(result) |
|||
|
|||
# invalid numtokens bid |
|||
result = rpc.tokenbid("0", tokenid, "1") |
|||
assert_error(result) |
|||
|
|||
# invalid price bid |
|||
result = rpc.tokenbid("1", tokenid, "-1") |
|||
assert_error(result) |
|||
|
|||
# invalid price bid |
|||
result = rpc.tokenbid("1", tokenid, "0") |
|||
assert_error(result) |
|||
|
|||
# invalid tokenid bid |
|||
result = rpc.tokenbid("100", "deadbeef", "1") |
|||
assert_error(result) |
|||
|
|||
tokenbid = rpc.tokenbid("100", tokenid, "10") |
|||
tokenbidhex = tokenbid['hex'] |
|||
tokenbidid = self.send_and_mine(tokenbid['hex']) |
|||
result = rpc.tokenorders() |
|||
order = result[0] |
|||
assert order, "found order" |
|||
|
|||
# invalid bid fillunits |
|||
result = rpc.tokenfillbid(tokenid, tokenbidid, "0") |
|||
assert_error(result) |
|||
|
|||
# invalid bid fillunits |
|||
result = rpc.tokenfillbid(tokenid, tokenbidid, "-777") |
|||
assert_error(result) |
|||
|
|||
# valid bid fillunits |
|||
fillbid = rpc.tokenfillbid(tokenid, tokenbidid, "1000") |
|||
result = self.send_and_mine(fillbid['hex']) |
|||
txid = result[0] |
|||
assert txid, "found txid" |
|||
|
|||
# should be no token orders |
|||
result = rpc.tokenorders() |
|||
assert_equal(result, []) |
|||
|
|||
# checking bid cancellation |
|||
testorder = rpc.tokenbid("100", tokenid, "7.77") |
|||
testorderid = self.send_and_mine(testorder['hex']) |
|||
cancel = rpc.tokencancelbid(tokenid, testorderid) |
|||
self.send_and_mine(cancel["hex"]) |
|||
result = rpc.tokenorders() |
|||
assert_equal(result, []) |
|||
|
|||
# invalid token transfer amount (have to add status to CC code!) |
|||
randompubkey = "021a559101e355c907d9c553671044d619769a6e71d624f68bfec7d0afa6bd6a96" |
|||
result = rpc.tokentransfer(tokenid,randompubkey,"0") |
|||
assert_error(result) |
|||
|
|||
# invalid token transfer amount (have to add status to CC code!) |
|||
result = rpc.tokentransfer(tokenid,randompubkey,"-1") |
|||
assert_error(result) |
|||
|
|||
# valid token transfer |
|||
sendtokens = rpc.tokentransfer(tokenid,randompubkey,"1") |
|||
self.send_and_mine(sendtokens["hex"]) |
|||
result = rpc.tokenbalance(tokenid,randompubkey) |
|||
assert_equal(result["balance"], 1) |
|||
|
|||
|
|||
def run_rewards_tests(self): |
|||
rpc = self.nodes[0] |
|||
result = rpc.rewardsaddress() |
|||
for x in ['RewardsCCaddress', 'myCCaddress', 'Rewardsmarker', 'myaddress']: |
|||
assert_equal(result[x][0], 'R') |
|||
|
|||
result = rpc.rewardsaddress(self.pubkey) |
|||
for x in ['RewardsCCaddress', 'myCCaddress', 'Rewardsmarker', 'myaddress', 'CCaddress']: |
|||
assert_equal(result[x][0], 'R') |
|||
|
|||
# no rewards yet |
|||
result = rpc.rewardslist() |
|||
assert_equal(result, []) |
|||
|
|||
# looking up non-existent reward should return error |
|||
result = rpc.rewardsinfo("none") |
|||
assert_error(result) |
|||
|
|||
# creating rewards plan with name > 8 chars, should return error |
|||
result = rpc.rewardscreatefunding("STUFFSTUFF", "7777", "25", "0", "10", "10") |
|||
assert_error(result) |
|||
|
|||
# creating rewards plan with 0 funding |
|||
result = rpc.rewardscreatefunding("STUFF", "0", "25", "0", "10", "10") |
|||
assert_error(result) |
|||
|
|||
# creating rewards plan with 0 maxdays |
|||
result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "0") |
|||
assert_error(result) |
|||
|
|||
# creating rewards plan with > 25% APR |
|||
result = rpc.rewardscreatefunding("STUFF", "7777", "30", "0", "10", "10") |
|||
assert_error(result) |
|||
|
|||
# creating valid rewards plan |
|||
result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10") |
|||
assert result['hex'], 'got raw xtn' |
|||
fundingtxid = rpc.sendrawtransaction(result['hex']) |
|||
assert fundingtxid, 'got txid' |
|||
|
|||
# confirm the above xtn |
|||
rpc.generate(1) |
|||
result = rpc.rewardsinfo(fundingtxid) |
|||
assert_success(result) |
|||
assert_equal(result['name'], 'STUFF') |
|||
assert_equal(result['APR'], "25.00000000") |
|||
assert_equal(result['minseconds'], 0) |
|||
assert_equal(result['maxseconds'], 864000) |
|||
assert_equal(result['funding'], "7777.00000000") |
|||
assert_equal(result['mindeposit'], "10.00000000") |
|||
assert_equal(result['fundingtxid'], fundingtxid) |
|||
|
|||
# checking if new plan in rewardslist |
|||
result = rpc.rewardslist() |
|||
assert_equal(result[0], fundingtxid) |
|||
|
|||
# creating reward plan with already existing name, should return error |
|||
result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10") |
|||
assert_error(result) |
|||
|
|||
# add funding amount must be positive |
|||
result = rpc.rewardsaddfunding("STUFF", fundingtxid, "-1") |
|||
assert_error(result) |
|||
|
|||
# add funding amount must be positive |
|||
result = rpc.rewardsaddfunding("STUFF", fundingtxid, "0") |
|||
assert_error(result) |
|||
|
|||
# adding valid funding |
|||
result = rpc.rewardsaddfunding("STUFF", fundingtxid, "555") |
|||
addfundingtxid = self.send_and_mine(result['hex']) |
|||
assert addfundingtxid, 'got funding txid' |
|||
|
|||
# checking if funding added to rewardsplan |
|||
result = rpc.rewardsinfo(fundingtxid) |
|||
assert_equal(result['funding'], "8332.00000000") |
|||
|
|||
# trying to lock funds, locking funds amount must be positive |
|||
result = rpc.rewardslock("STUFF", fundingtxid, "-5") |
|||
assert_error(result) |
|||
|
|||
# trying to lock funds, locking funds amount must be positive |
|||
result = rpc.rewardslock("STUFF", fundingtxid, "0") |
|||
assert_error(result) |
|||
|
|||
# trying to lock less than the min amount is an error |
|||
result = rpc.rewardslock("STUFF", fundingtxid, "7") |
|||
assert_error(result) |
|||
|
|||
# locking funds in rewards plan |
|||
result = rpc.rewardslock("STUFF", fundingtxid, "10") |
|||
assert_success(result) |
|||
locktxid = result['hex'] |
|||
assert locktxid, "got lock txid" |
|||
|
|||
# locktxid has not been broadcast yet |
|||
result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) |
|||
assert_error(result) |
|||
|
|||
# broadcast xtn |
|||
txid = rpc.sendrawtransaction(locktxid) |
|||
assert txid, 'got txid from sendrawtransaction' |
|||
|
|||
# confirm the xtn above |
|||
rpc.generate(1) |
|||
|
|||
# will not unlock since reward amount is less than tx fee |
|||
result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) |
|||
assert_error(result) |
|||
|
|||
|
|||
def run_test (self): |
|||
print("Mining blocks...") |
|||
rpc = self.nodes[0] |
|||
|
|||
# utxos from block 1 become mature in block 101 |
|||
rpc.generate(101) |
|||
self.sync_all() |
|||
|
|||
# this corresponds to -pubkey above |
|||
print("Importing privkey") |
|||
rpc.importprivkey(self.privkey) |
|||
|
|||
# self.run_faucet_tests() |
|||
self.run_rewards_tests() |
|||
self.run_dice_tests() |
|||
self.run_token_tests() |
|||
self.run_faucet_tests() |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
CryptoConditionsTest ().main () |
@ -0,0 +1 @@ |
|||
curl --url "http://127.0.0.1:7776" --data "{\"conf\":\"VRSC.conf\",\"path\":\"${HOME#"/"}/.komodo/VRSC\",\"unitval\":\"20\",\"zcash\":1,\"RELAY\":-1,\"VALIDATE\":0,\"prefetchlag\":-1,\"poll\":100,\"active\":1,\"agent\":\"iguana\",\"method\":\"addcoin\",\"startpend\":4,\"endpend\":4,\"services\":129,\"maxpeers\":8,\"newcoin\":\"VRSC\",\"name\":\"VRSC\",\"hasheaders\":1,\"useaddmultisig\":0,\"netmagic\":\"ad8a58e2\",\"p2p\":27485,\"rpc\":27486,\"pubval\":60,\"p2shval\":85,\"wifval\":188,\"txfee_satoshis\":\"10000\",\"isPoS\":0,\"minoutput\":10000,\"minconfirms\":2,\"genesishash\":\"027e3758c3a65b12aa1046462b486d0a63bfa1beae327897f56c5cfb7daaae71\",\"protover\":170002,\"genesisblock\":\"0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000000000000000000000000000000000000000000000000000000000000029ab5f490f0f0f200b00000000000000000000000000000000000000000000000000000000000000fd4005000d5ba7cda5d473947263bf194285317179d2b0d307119c2e7cc4bd8ac456f0774bd52b0cd9249be9d40718b6397a4c7bbd8f2b3272fed2823cd2af4bd1632200ba4bf796727d6347b225f670f292343274cc35099466f5fb5f0cd1c105121b28213d15db2ed7bdba490b4cedc69742a57b7c25af24485e523aadbb77a0144fc76f79ef73bd8530d42b9f3b9bed1c135ad1fe152923fafe98f95f76f1615e64c4abb1137f4c31b218ba2782bc15534788dda2cc08a0ee2987c8b27ff41bd4e31cd5fb5643dfe862c9a02ca9f90c8c51a6671d681d04ad47e4b53b1518d4befafefe8cadfb912f3d03051b1efbf1dfe37b56e93a741d8dfd80d576ca250bee55fab1311fc7b3255977558cdda6f7d6f875306e43a14413facdaed2f46093e0ef1e8f8a963e1632dcbeebd8e49fd16b57d49b08f9762de89157c65233f60c8e38a1f503a48c555f8ec45dedecd574a37601323c27be597b956343107f8bd80f3a925afaf30811df83c402116bb9c1e5231c70fff899a7c82f73c902ba54da53cc459b7bf1113db65cc8f6914d3618560ea69abd13658fa7b6af92d374d6eca9529f8bd565166e4fcbf2a8dfb3c9b69539d4d2ee2e9321b85b331925df195915f2757637c2805e1d4131e1ad9ef9bc1bb1c732d8dba4738716d351ab30c996c8657bab39567ee3b29c6d054b711495c0d52e1cd5d8e55b4f0f0325b97369280755b46a02afd54be4ddd9f77c22272b8bbb17ff5118fedbae2564524e797bd28b5f74f7079d532ccc059807989f94d267f47e724b3f1ecfe00ec9e6541c961080d8891251b84b4480bc292f6a180bea089fef5bbda56e1e41390d7c0e85ba0ef530f7177413481a226465a36ef6afe1e2bca69d2078712b3912bba1a99b1fbff0d355d6ffe726d2bb6fbc103c4ac5756e5bee6e47e17424ebcbf1b63d8cb90ce2e40198b4f4198689daea254307e52a25562f4c1455340f0ffeb10f9d8e914775e37d0edca019fb1b9c6ef81255ed86bc51c5391e0591480f66e2d88c5f4fd7277697968656a9b113ab97f874fdd5f2465e5559533e01ba13ef4a8f7a21d02c30c8ded68e8c54603ab9c8084ef6d9eb4e92c75b078539e2ae786ebab6dab73a09e0aa9ac575bcefb29e930ae656e58bcb513f7e3c17e079dce4f05b5dbc18c2a872b22509740ebe6a3903e00ad1abc55076441862643f93606e3dc35e8d9f2caef3ee6be14d513b2e062b21d0061de3bd56881713a1a5c17f5ace05e1ec09da53f99442df175a49bd154aa96e4949decd52fed79ccf7ccbce32941419c314e374e4a396ac553e17b5340336a1a25c22f9e42a243ba5404450b650acfc826a6e432971ace776e15719515e1634ceb9a4a35061b668c74998d3dfb5827f6238ec015377e6f9c94f38108768cf6e5c8b132e0303fb5a200368f845ad9d46343035a6ff94031df8d8309415bb3f6cd5ede9c135fdabcc030599858d803c0f85be7661c88984d88faa3d26fb0e9aac0056a53f1b5d0baed713c853c4a2726869a0a124a8a5bbc0fc0ef80c8ae4cb53636aa02503b86a1eb9836fcc259823e2692d921d88e1ffc1e6cb2bde43939ceb3f32a611686f539f8f7c9f0bf00381f743607d40960f06d347d1cd8ac8a51969c25e37150efdf7aa4c2037a2fd0516fb444525ab157a0ed0a7412b2fa69b217fe397263153782c0f64351fbdf2678fa0dc8569912dcd8e3ccad38f34f23bbbce14c6a26ac24911b308b82c7e43062d180baeac4ba7153858365c72c63dcf5f6a5b08070b730adb017aeae925b7d0439979e2679f45ed2f25a7edcfd2fb77a8794630285ccb0a071f5cce410b46dbf9750b0354aae8b65574501cc69efb5b6a43444074fee116641bb29da56c2b4a7f456991fc92b2\",\"debug\":0,\"seedipaddr\":\"78.47.196.146\"}" |
@ -0,0 +1,2 @@ |
|||
#!/bin/bash |
|||
./komodo-cli -ac_name=CCL $1 $2 $3 $4 $5 $6 |
@ -0,0 +1,589 @@ |
|||
/****************************************************************************** |
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
How to write utxo based CryptoConditions contracts for KMD chains |
|||
by jl777 |
|||
|
|||
This is not the only smart contracts methodology that is possible to build on top of OP_CHECKCRYPTOCONDITION, just the first one. All the credit for getting OP_CHECKCRYPTOCONDITION working in the Komodo codebase goes to @libscott. I am just hooking into the code that he made and tried to make it just a little easier to make new contracts. |
|||
|
|||
There is probably some fancy marketing name to use, but for now, I will just call it "CC contract" for short, knowing that it is not 100% technically accurate as the CryptoConditions aspect is not really the main attribute. However, the KMD contracts were built to make the CryptoConditions codebase that was integrated into it to be more accessible. |
|||
|
|||
Since CC contracts run native C/C++ code, it is turing complete and that means that any contract that is possible to do on any other platform will be possible to create via CC contract. |
|||
|
|||
utxo based contracts are a bit harder to start writing than for balance based contracts. However, they are much more secure as they leverage the existing bitcoin utxo system. That makes it much harder to have bugs that issue a zillion new coins from a bug, since all the CC contract operations needs to also obey the existing bitcoin utxo protocol. |
|||
|
|||
This document will be heavily example based so it will utilize many of the existing reference CC contracts. After understanding this document, you should be in a good position to start creating either a new CC contract to be integrated into komodod or to make rpc based dapps directly. |
|||
|
|||
Chapter 0 - Bitcoin Protocol Basics |
|||
There are many aspects of the bitcoin protocol that isnt needed to understand the CC contracts dependence on it. Such details will not be discussed. The primary aspect is the utxo, unspent transaction output. Just a fancy name for txid/vout, so when you sendtoaddress some coins, it creates a txid and the first output is vout.0, combine it and txid/0 is a specific utxo. |
|||
|
|||
Of course, to understand even this level of detail requires that you understand what a txid is, but there are plenty of reference materials on that. It is basically the 64 char long set of letters and numbers that you get when you send funds. |
|||
|
|||
Implicit with the utxo is that it prevents double spends. Once you spend a utxo, you cant spend it again. This is quite an important characteristic and while advanced readers will point out chain reorgs can allow a double spend, we will not confuse the issue with such details. The important thing is that given a blockchain at a specific height's blockhash, you can know if a txid/vout has been spent or not. |
|||
|
|||
There are also the transactions that are in memory waiting to be mined, the mempool. And it is possible for the utxo to be spent by a tx in the mempool. However since it isnt confirmed yet, it is still unspent at the current height, even if we are pretty sure it will be spent in the next block. |
|||
|
|||
A useful example is to think about a queue of people lined up to get into an event. They need to have a valid ticket and also to get into the queue. After some time passes, they get their ticket stamped and allowed into the event. |
|||
|
|||
In the utxo case, the ticket is the spending transaction and the event is the confirmed blockchain. The queue is the mempool. |
|||
|
|||
|
|||
Chapter 1 - OP_CHECKCRYPTOCONDITION |
|||
In the prior chapter the utxo was explained. However, the specific mechanism used to send a payment was not explained. Contrary to what most people might think, on the blockchain there are not entries that say "pay X amount to address". Instead what exists is a bitcoin script that must be satisfied in order for the funds to be able to be spent. |
|||
|
|||
Originally, there was the pay to pubkey script: |
|||
<pubkey> <checksig> |
|||
|
|||
About as simple of a payment script that you can get. Basically the pubkey's signature is checked and if it is valid, you get to spend it. One problem satoshi realized was that with Quantum Computers such payment scripts are vulnerable! So, he made a way to have a cold address, ie. an address whose pubkey isnt known. At least it isnt known until it is spent, so it is only Quantum resistant prior to the first spend. This line of reasoning is why we have one time use addresses and a new change address for each transaction. Maybe in some ways, this is too forward thinking as it makes things a lot more confusing to use and easier to lose track of all the required private keys. |
|||
|
|||
However, it is here to stay and its script is: |
|||
<hash the pubkey> <pubkey> <verify hash matches> <checksig> |
|||
|
|||
With this, the blockchain has what maps to "pay to address", just that the address is actually a base58 encoded (prefix + pubkeyhash). Hey, if it wasnt complicated, it would be easy! |
|||
|
|||
In order to spend a p2pkh (pay to pubkey hash) utxo, you need to divulge the pubkey in addition to having a valid signature. After the first spend from an address, its security is degraded to p2pk (pay to pubkey) as its pubkey is now known. The net result is that each reused address takes 25 extra bytes on the blockchain, and that is why for addresses that are expected to be reused, I just use the p2pk script. |
|||
|
|||
Originally, bitcoin allowed any type of script opcodes to be used directly. The problem was some of them caused problems and satoshi decided to disable them and only allow standard forms of payments. Thus the p2pk and p2pkh became 99%+ of bitcoin transactions. However, going from having a fully scriptable language that can create countless payment scripts (and bugs!), to having just 2... well it was a "short term" limitation. It did last for some years but eventually a compromise p2sh script was allowed to be standard. This is a pay to script hash, so it can have a standard format as the normal p2pkh, but have infinitely more flexibility. |
|||
|
|||
<hash the script> <script> <verify hash matches> |
|||
|
|||
Wait, something is wrong! If it was just that, then anybody that found out what the required script (called redeemscript) was, they could just spend it. I forgot to say that the redeemscript is then used to determine if the payment can be spent or not. So you can have a normal p2pk or p2pkh redeemscript inside a p2sh script. |
|||
|
|||
OK, I know that just got really confusing. Let us have a more clear example: |
|||
|
|||
redeemscript <- pay to pubkey |
|||
p2sh becomes the hash of the redeem script + the compares |
|||
|
|||
So to spend it, you need to divulge the redeemscript, which in turn requires you to divulge the pubkey. Put it all together and the p2sh mechanism verifies you not only had the correct redeemscript by comparing its hash, but that when the redeemscript is run, it is satisfied. In this case, that the pubkey's signature was valid. |
|||
|
|||
If you are still following, there is some good news! OP_CHECKCRYPTOCONDITION scripts are actually simpler than p2sh scripts in some sense as there isnt this extra level of script inside a scripthash. @libscott implemented the addition of OP_CHECKCRYPTOCONDITION to the set of bitcoin opcodes and what it does is makes sure that a CryptoConditions script is properly signed. |
|||
|
|||
Which gets us to the CryptoConditions specification, which is a monster of a IETF (Internet standards) draft and has hundred(s) of pages of specification. I am sure you are happy to know that you dont really need to know about it much at all! Just know that you can create all sorts of cryptoconditions and its binary encoding can be used in a bitcoin utxo. If the standard CC contracts dont have the power you need, it is always possible to expand on it. So far, most all the CC contracts only need the power of a 1of1 CC script, which is 1 signature combined with custom constraints. The realtime payment channels CC is the only one of the reference CC contracts so far that didnt fit into this model, it needed a 1of2 CC script. |
|||
|
|||
The best part is that all these opcode level things are not needed at all. I just wanted to explain it for those that need to know all the details of everything. |
|||
|
|||
Chapter 2 - CC contract basics |
|||
Each CC contract has an eval code, this is just an arbitrary number that is associated with a specific CC contract. The details about a specific CC contract are all determined by the validation logic, that is ultimately what implements a CC contract. |
|||
|
|||
However, unlike the normal bitcoin payments, where it is validated with only information in the transaction, a CC contract has the power to do pretty much anything. It has full access to the blockchain and even the mempool, though using mempool information is inherently more risky and needs to be done carefully or for exclusions, rather than inclusions. |
|||
|
|||
However, this is the CC contract basics chapter, so let us ignore mempool issues and deal with just the basics. Fundamentally there is no structure for OP_CHECKCRYPTOCONDITION serialized scripts, but if you are like me, you want to avoid having to read and understand a 1000 page IETF standard. What we really want to do is have a logical way to make a new contract and have it be able to be coded and debugged in an efficient way. |
|||
|
|||
That means to just follow a known working template and only changing the things where the existing templates are not sufficient, ie. the core differentiator of your CC contract. |
|||
|
|||
In the ~/komodo/src/cc/eval.h file all the eval codes are defined, currently: |
|||
|
|||
#define FOREACH_EVAL(EVAL) \ |
|||
EVAL(EVAL_IMPORTPAYOUT, 0xe1) \ |
|||
EVAL(EVAL_IMPORTCOIN, 0xe2) \ |
|||
EVAL(EVAL_ASSETS, 0xe3) \ |
|||
EVAL(EVAL_FAUCET, 0xe4) \ |
|||
EVAL(EVAL_REWARDS, 0xe5) \ |
|||
EVAL(EVAL_DICE, 0xe6) \ |
|||
EVAL(EVAL_FSM, 0xe7) \ |
|||
EVAL(EVAL_AUCTION, 0xe8) \ |
|||
EVAL(EVAL_LOTTO, 0xe9) \ |
|||
EVAL(EVAL_MOFN, 0xea) \ |
|||
EVAL(EVAL_CHANNELS, 0xeb) \ |
|||
EVAL(EVAL_ORACLES, 0xec) \ |
|||
EVAL(EVAL_PRICES, 0xed) \ |
|||
EVAL(EVAL_PEGS, 0xee) \ |
|||
EVAL(EVAL_TRIGGERS, 0xef) \ |
|||
EVAL(EVAL_PAYMENTS, 0xf0) \ |
|||
EVAL(EVAL_GATEWAYS, 0xf1) |
|||
|
|||
Ultimately, we will probably end up with all 256 eval codes used, for now there is plenty of room. I imagined that similar to my coins repo, we can end up with a much larger than 256 number of CC contracts and you select the 256 that you want active for your blockchain. That does mean any specific chain will be limited to "only" having 256 contracts. Since there seems to be so few actually useful contracts so far, this limit seems to be sufficient. I am told that the evalcode can be of any length, but the current CC contracts assumes it is one byte. |
|||
|
|||
The simplest CC script would be one that requires a signature from a pubkey along with a CC validation. This is the equivalent of the pay to pubkey bitcoin script and is what most of the initial CC contracts use. Only the channels one needed more than this and it will be explained in its chapter. |
|||
|
|||
We end up with CC scripts of the form (evalcode) + (pubkey) + (other stuff), dont worry about the other stuff, it is automatically handled with some handy internal functions. The important thing to note is that each CC contract of this form needs a single pubkey and eval code and from that we get the CC script. Using the standard bitcoin's "hash and make an address from it" method, this means that the same pubkey will generate a different address for each different CC contract! |
|||
|
|||
This is an important point, so I will say it in a different way. In bitcoin there used to be uncompressed pubkeys which had both the right and left half combined, into a giant 64 byte pubkey. But since you can derive one from the other, compressed pubkeys became the standard, that is why you have bitcoin pubkeys of 33 bytes instead of 65 bytes. There is a 02, 03 or 04 prefix, to mean odd or even or big pubkey. This means there are two different pubkeys for each privkey, the compressed and uncompressed. And in fact you can have two different bitcoin protocol addresses that are spendable by the same privkey. If you use some paper wallet generators, you might have noticed this. |
|||
|
|||
CC contracts are like that, where each pubkey gets a different address for each evalcode. It is the same pubkey, just different address due to the actual script having a different evalcode, it ends up with a different hash and thus a different address. Now funds send to a specific CC address is only accessible by that CC contract and must follow the rules of that contract. |
|||
|
|||
I also added another very useful feature where the convention is for each CC contract to have a special address that is known to all, including its private key. Before you panic about publishing the private key, remember that to spend a CC output, you need to properly sign it AND satisfy all the rules. By everyone having the privkey for the CC contract, everybody can do the "properly sign" part, but they still need to follow the rest of the rules. |
|||
|
|||
From a user's perspective, there is the global CC address for a CC contract and some contracts also use the user pubkey's CC address. Having a pair of new addresses for each contract can get a bit confusing at first, but eventually we will get easy to use GUI that will make it all easy to use. |
|||
|
|||
|
|||
Chapter 3 - CC vins and vouts |
|||
You might want to review the bitcoin basics and other materials to refresh about how bitcoin outputs become inputs. It is a bit complicated, but ultimately it is about one specific amount of coins that are spent, once spent it is combined with the other coins that are also spent in that transaction and then various outputs are created. |
|||
|
|||
vin0 + vin1 + vin2 -> vout0 + vout1 |
|||
|
|||
That is a 3 input, 2 output transaction. The value from the three inputs are combined and then split into vout0 and vout1, each of the vouts gets a spend script that must be satisfied to be able to be spent. Which means for all three of out vins, all the requirements (as specified in the output that created them) are satisfied. |
|||
|
|||
Yes, I know this is a bit too complicated without a nice chart, so we will hope that a nice chart is added here: |
|||
|
|||
[nice chart goes here] |
|||
|
|||
Out of all the aspects of the CC contracts, the flexibility that different vins and vouts created was the biggest surprise. When I started writing the first of these a month ago, I had no idea the power inherent in the smart utxo contracts. I was just happy to have a way to lock funds and release them upon some specific conditions. |
|||
|
|||
After the assets/tokens CC contract, I realized that it was just a tip of the iceberg. I knew it was Turing complete, but after all these years of restricted bitcoin script, to have the full power of any arbitrary algorithm, it was eye opening. Years of writing blockchain code and having really bad consequences with every bug naturally makes you gun shy about doing aggressive things at the consensus level. And that is the way it should be, if not very careful, some really bad things can and do happen. The foundation of building on top of the existing (well tested and reliable) utxo system is what makes the CC contracts less likely for the monster bugs. That being said, lack of validation can easily allow an improperly coded CC contract to have its funds drained. |
|||
|
|||
The CC contract breaks out of the standard limitations of a bitcoin transaction. Already, what I wrote explains the reason, but it was not obvious even to me at first, so likely you might have missed it too. If you are wondering what on earth I am talking about, THAT is what I am talking about! |
|||
|
|||
To recap, we have now a new standard bitcoin output type called a CC output. Further, there can be up to 256 different types of CC outputs active on any given blockchain. We also know that to spend any output, you need to satisfy its spending script, which in our case is the signature and whatever constraints the CC validation imposes. We also have the convention of a globally shared keypair, which gives us a general CC address that can have funds sent to it, along with a user pubkey specific CC address. |
|||
|
|||
Let us go back to the 3+2 transaction example: |
|||
|
|||
vin0 + vin1 + vin2 -> vout0 + vout1 |
|||
|
|||
Given the prior paragraph, try to imagine the possibilities the simple 3+2 transaction can be. Each vin could be a normal vin, from the global contract address, the user's CC address and the vouts can also have this range. Theoretically, there can be 257 * 257 * 257 * 257 * 257 forms of a 3+2 transaction! |
|||
|
|||
In reality, we really dont want that much degrees of freedom as it will ensure a large degree of bugs! So we need to reduce things to a more manageable level where there are at most 3 types for each, and preferably just 1 type. That will make the job of validating it much simpler and simple is better as long as we dont sacrifice the power. We dont. |
|||
|
|||
Ultimately the CC contract is all about how it constrains its inputs, but before it can constrain them, they need to be created as outputs. More about this in the CC validation chapter. |
|||
|
|||
Chapter 4 - CC rpc extensions |
|||
Currently, CC contracts need to be integrated at the source level. This limits who is able to create and add new CC contracts, which at first is good, but eventually will be a too strict limitation. The runtime bindings chapter will touch on how to break out of the source based limitation, but there is another key interface level, the RPC. |
|||
|
|||
By convention, each CC contract adds an associated set of rpc calls to the komodo-cli. This not only simplifies the creation of the CC contract transactions, it further will allow dapps to be created just via rpc calls. That will require there being enough foundational CC contracts already in place. As we find new usecases that cannot be implemented via rpc, then a new CC contract is made that can handle that (and more) and the power of the rpc level increases. This is a long term process. |
|||
|
|||
The typical rpc calls that are added <CC>address, <CClist>, <CCinfo> return the various special CC addresses, the list of CC contract instances and info about each CC contract instance. Along with an rpc that creates a CC instance and of course the calls to invoke a CC instance. |
|||
|
|||
The role of the rpc calls are to create properly signed rawtransactions that are ready for broadcasting. This then allows using only the rpc calls to not only invoke but to create a specific instance of a CC. The faucet contract is special in that it only has a single instance, so some of these rpc calls are skipped. |
|||
|
|||
So, there is no MUSTHAVE rpc calls, just a sane convention to follow so it fits into the general pattern. |
|||
|
|||
One thing that I forgot to describe was how to create a special CC address and even though this is not really an rpc issue, it is kind of separate from the core CC functions, so I will show how to do it here: |
|||
|
|||
const char *FaucetCCaddr = "R9zHrofhRbub7ER77B7NrVch3A63R39GuC"; |
|||
const char *FaucetNormaladdr = "RKQV4oYs4rvxAWx1J43VnT73rSTVtUeckk"; |
|||
char FaucetCChexstr[67] = { "03682b255c40d0cde8faee381a1a50bbb89980ff24539cb8518e294d3a63cefe12" }; |
|||
uint8_t FaucetCCpriv[32] = { 0xd4, 0x4f, 0xf2, 0x31, 0x71, 0x7d, 0x28, 0x02, 0x4b, 0xc7, 0xdd, 0x71, 0xa0, 0x39, 0xc4, 0xbe, 0x1a, 0xfe, 0xeb, 0xc2, 0x46, 0xda, 0x76, 0xf8, 0x07, 0x53, 0x3d, 0x96, 0xb4, 0xca, 0xa0, 0xe9 }; |
|||
|
|||
Above are the specifics for the faucet CC, but each one has the equivalent in CCcustom.cpp. At the bottom of the file is a big switch statement where these values are copied into an in memory data structure for each CC type. This allows all the CC codebase to access these special addresses in a standard way. |
|||
|
|||
In order to get the above values, follow these steps: |
|||
A. use getnewaddress to get a new address and put that in the <CC>Normaladdr = ""; line |
|||
B. use validateaddress <newaddress from A> to get the pubkey, which is put into the <CC>hexstr[67] = ""; line |
|||
C. stop the daemon and start with -pubkey=<pubkey from B> and do a <CC>address rpc call. In the console you will get a printout of the hex for the privkey, assuming the if ( 0 ) in Myprivkey() is enabled (CCutils.cpp) |
|||
D. update the CCaddress and privkey and dont forget to change the -pubkey= parameter |
|||
|
|||
The first rpc command to add is <CC>address and to do that, add a line to rpcserver.h and update the commands array in rpcserver.cpp |
|||
|
|||
In the rpcwallet.cpp file you will find the actual rpc functions, find one of the <CC>address ones, copy paste, change the eval code to your eval code and customize the function. Oh, and dont forget to add an entry into eval.h |
|||
|
|||
Now you have made your own CC contract, but it wont link as you still need to implement the actual functions of it. This will be covered in the following chapters. |
|||
|
|||
|
|||
Chapter 5 - CC validation |
|||
CC validation is what its all about, not the "hokey pokey"! |
|||
|
|||
Each CC must have its own validation function and when the blockchain is validating a transaction, it will call the CC validation code. It is totally up to the CC validation whether to validate it or not. |
|||
|
|||
Any set of rules that you can think of and implement can be part of the validation. Make sure that there is no ambiguity! Make sure that all transactions that should be rejected are in fact rejected. |
|||
|
|||
Also, make sure any rpc calls that create a CC transaction dont create anything that doesnt validate. |
|||
|
|||
Really, that is all that needs to be said about validation that is generic, as it is just a concept and gets a dedicated function to determine if a transaction is valid or not. |
|||
|
|||
For most of the initial CC contracts, I made a function code for various functions of the CC contract and add that along with the creation txid. That enables the validation of the transactions much easier, as the required data is right there in the opreturn. |
|||
|
|||
You do need to be careful not to cause a deadlock as the CC validation code is called while already locked in the main loop of the bitcoin protocol. As long as the provided CC contracts are used as models, you should keep out of deadlock troubles. |
|||
|
|||
|
|||
Chapter 6 - faucet example |
|||
Finally, we are ready for the first actual example of a CC contract. The faucet. This is a very simple contract and it ran into some interesting bugs in the first incarnation. |
|||
|
|||
The code in ~/komodo/src/cc/faucet.cpp is the ultimate documentation for it with all the details, so I will just address the conceptual issues here. |
|||
|
|||
The idea is that people send funds to the faucet by locking it in faucet's global CC address and anybody is allowed to create a faucetget transaction that spends it. |
|||
|
|||
There are only 7 functions in faucet.cpp, a bit over 200 lines including comments. The first three are for validation, the last four for the rpc calls to use. |
|||
|
|||
int64_t IsFaucetvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) |
|||
|
|||
bool FaucetExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) |
|||
|
|||
bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) |
|||
|
|||
int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) |
|||
|
|||
std::string FaucetGet(uint64_t txfee) |
|||
|
|||
std::string FaucetFund(uint64_t txfee,int64_t funds) |
|||
|
|||
UniValue FaucetInfo() |
|||
|
|||
Functions in rpcwallet implement: |
|||
|
|||
faucetaddress fully implemented in rpcwallet.cpp |
|||
faucetfund calls FaucetFund |
|||
faucetget calls FaucetGet |
|||
faucetinfo calls FaucetInfo |
|||
|
|||
Now you might not be a programmer, but I hope you are able to understand the above sequence. user types in a cli call, komodo-cli processes it by calling the rpc function, which in turn calls the function inside faucet.cpp |
|||
|
|||
No magic, just simple conversion of a user command line call that runs code inside the komodod. Both the faucetfund and faucetget create properly signed rawtransaction that is ready to be broadcast to the network using the standard sendrawtransaction rpc. It doesnt automatically do this to allow the GUI to have a confirmation step with all the details before doing an irrevocable CC contract transaction. |
|||
|
|||
faucetfund allows anybody to add funds to the faucet |
|||
faucetget allows anybody to get 0.1 coins from the faucet as long as they dont violate the rules. |
|||
|
|||
And we come to what it is all about. The rules of the faucet. Initially it was much less strict and that allowed it to be drained slowly, but automatically and it prevented most from being able to use the faucet. |
|||
|
|||
To make it much harder to leech, it was made so each faucetget returned only 0.1 coins (down from 1.0) so it was worth 90% less. It was also made so that it had to be to a fresh address with less than 3 transactions. Finally each txid was constrained to start and end with 00! This is a cool trick to force usage of precious CPU time (20 to 60 seconds depending on system) to generate a valid txid. Like PoW mining for the txid and I expect other CC contracts to use a similar mechanism if they want to rate limit usage. |
|||
|
|||
Combined, it became such a pain to get 0.1 coins, the faucet leeching problem was solved. It might not seem like too much trouble to change an address to get another 0.1 coins, but the way things are setup you need to launch the komodod -pubkey=<your pubkey> to change the pubkey that is active for a node. That means to change the pubkey being used, the komodod needs to be restarted and this creates a lot of issues for any automation trying to do this. Combined with the PoW required, only when 0.1 coins becomes worth a significant effort will faucet leeching return. In that case, the PoW requirement can be increased and coin amount decreased, likely with a faucet2 CC contract as I dont expect many such variations to be needed. |
|||
|
|||
Chapter 7 - rewards example |
|||
The next CC contract in complexity is the rewards CC contract. This is designed to capture what most people like about masternodes, without anything else, ie. the rewards! |
|||
|
|||
The idea is to allow people to lock funds for some amount of time and get an extra reward. We also want to support having more than one rewards plan at a time and to allow customization of plan details. One twist that makes it a bit unexpected is that anybody should be able to unlock the funds that were locked, as long as it ends up in the locking address. The reason for this is that SPV servers want to be supported and while locking can be done via normal sendrawtransaction, it requires a native node to do the unlocking. By allowing anybody to be able to unlock, then there can be a special node that unlocks all locked funds when they are ready. This way, from the user's point of view, they lock the funds and after it is matured, it reappears in their wallet. |
|||
|
|||
The above requirements leads us to using the global CC address for the rewards contract to lock the funds in. That allows anybody to properly sign the unlock, but of course that is not enough, we need to make sure they are following all the unlock requirements. Primarily that the funds go back to the locking address. |
|||
|
|||
The four aspects of the rewards plan that are customizable are: |
|||
APR, minseconds, maxseconds, mindeposit |
|||
|
|||
This allows each plan to set a different APR (up to 25%, anything above is becoming silly), the minimum time funds must be locked, the maximum time they are earning rewards and the minimum that can be deposited. |
|||
|
|||
So the tx that creates the rewards plan will have these attributes and it is put into the OP_RETURN data. All the other calls will reference the plan creation txid and inherit these parameters from the creation tx. This means it is an important validation to do, to make sure the funding txid is a valid funding txid. |
|||
|
|||
Since it is possible that the initial funding will be used up, there needs to be a way for more funding to be added to the rewards plan. |
|||
|
|||
Having multiple possible rewards plans means it is useful to have rpc calls to get information about them. Hence: rewardslist returns the list of rewards creation txids and rewardsinfo <txid> returns the details about a specific rewards plan. |
|||
|
|||
A locking transaction sends funds to the rewards CC address, along with a normal (small) tx to the address that the unlock should go to. This allows the validation of the proper unlocking. Also, it is important to make sure only locking transactions are able to be unlocked. Additionally, the minimum time needs to elapse before unlocking is allowed. |
|||
|
|||
All of these things are done in rewards.cpp, with the validation code being about 200 lines and a total of 700 lines or so. Bigger than faucet, but most of the code is the non-consensus code to create the proper transactions. In order to simplify the validation, specific vin and vout positions are designated to have specific required values: |
|||
|
|||
createfunding |
|||
vins.*: normal inputs |
|||
vout.0: CC vout for funding |
|||
vout.1: normal marker vout for easy searching |
|||
vout.2: normal change |
|||
vout.n-1: opreturn 'F' sbits APR minseconds maxseconds mindeposit |
|||
|
|||
addfunding |
|||
vins.*: normal inputs |
|||
vout.0: CC vout for funding |
|||
vout.1: normal change |
|||
vout.n-1: opreturn 'A' sbits fundingtxid |
|||
|
|||
lock |
|||
vins.*: normal inputs |
|||
vout.0: CC vout for locked funds |
|||
vout.1: normal output to unlock address |
|||
vout.2: change |
|||
vout.n-1: opreturn 'L' sbits fundingtxid |
|||
|
|||
unlock |
|||
vin.0: locked funds CC vout.0 from lock |
|||
vin.1+: funding CC vout.0 from 'F' and 'A' and 'U' |
|||
vout.0: funding CC change |
|||
vout.1: normal output to unlock address |
|||
vout.n-1: opreturn 'U' sbits fundingtxid |
|||
|
|||
It is recommended to create such a vin/vout allocation for each CC contract to make sure that the rpc calls that create the transaction and the validation code have a specific set of constraints that can be checked for. |
|||
|
|||
Chapter 8 - assets example |
|||
In some respects the assets CC is the most complex, it was actually the first one that I coded. It is however using a simple model, even for the DEX functions, so while it is quite involved, it does not have the challenge/response complexity of dice. |
|||
|
|||
There are two major aspects to creating tokens. First is to create and track it, down to every specific satoshi. The second is solving how to implement DEX functions of trading assets. |
|||
|
|||
The model used is "colored coins". This means that the token creating txid issues the assets as denoted by all the satoshis, so locking 1 COIN issues 100 million tokens. This multiplication will allow creation of plenty of assets. We want to preserve all the tokens created across all allowed operations. The way this is achieved is that all operations attaches the token creation txid in its OP_RETURN, along with the specified operation. |
|||
|
|||
Ownership of tokens are represented by the colored satoshis in the CC address for the user's pubkey. This allows using the standard utxo system to automatically track ownership of the tokens. This automatic inheritance is one of the big advantages of utxo CC contracts that compensates for the slightly more work needed to implement a CC contract. |
|||
|
|||
So now we have the standard CC addresss, list and info commands that provide the CC addresses, list of all tokens and info on specific tokens and the ability to create and transfer tokens. Any amount of tokens can be created from 1 to very large numbers and using standard addressbalance, addressutxo type of commands, the details of all assets owned can be determined for a specific pubkey. |
|||
|
|||
Now we can solve the DEX part of the tokenization, which turns out to be much simpler than initially imagined. We start with bidding for a specific token. Funds for the bid are locked into the global CC address, along with the desired token and price. This creates a bid utxo that is able to be listed via an orderbook rpc call. To fill the bid, a specific bid utxo is spent with the appropriate number of assets and change and updated price for the unfilled amount. if the entire amount is filled, then it wont appear in the orderbook anymore. |
|||
|
|||
asks work by locking assets along with the required price. Partial fills can be supported and the rpc calls can mask the utxo-ness of the funds/assets needed by automatically gathering the required amount of funds to fill the specific amount. |
|||
|
|||
With calls to cancel the pending bid or ask, we get a complete set of rpc calls that can support a COIN-centric DEX. |
|||
|
|||
In the future, it is expected that a token swap rpc can be supported to allow directly swapping one token for another, but at first it is expected that there wont be sufficient volumes for such token to token swaps, so it was left out of the initial implementation. |
|||
|
|||
With just these rpc calls and associated validation, we get the ability to issue tokens and trade them on a DEX! |
|||
|
|||
create |
|||
vin.0: normal input |
|||
vout.0: issuance assetoshis to CC |
|||
vout.1: tag sent to normal address of AssetsCCaddress |
|||
vout.2: normal output for change (if any) |
|||
vout.n-1: opreturn [EVAL_ASSETS] ['c'] [origpubkey] "<assetname>" "<description>" |
|||
|
|||
transfer |
|||
vin.0: normal input |
|||
vin.1 .. vin.n-1: valid CC outputs |
|||
vout.0 to n-2: assetoshis output to CC |
|||
vout.n-2: normal output for change (if any) |
|||
vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid] |
|||
|
|||
buyoffer: |
|||
vins.*: normal inputs (bid + change) |
|||
vout.0: amount of bid to unspendable |
|||
vout.1: normal output for change (if any) |
|||
vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey] |
|||
|
|||
cancelbuy: |
|||
vin.0: normal input |
|||
vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] |
|||
vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey] |
|||
vout.1: normal output for change (if any) |
|||
vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid] |
|||
|
|||
fillbuy: |
|||
vin.0: normal input |
|||
vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0] |
|||
vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue |
|||
vout.0: remaining amount of bid to unspendable |
|||
vout.1: vin.1 value to signer of vin.2 |
|||
vout.2: vin.2 assetoshis to original pubkey |
|||
vout.3: CC output for assetoshis change (if any) |
|||
vout.4: normal output for change (if any) |
|||
vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey] |
|||
|
|||
selloffer: |
|||
vin.0: normal input |
|||
vin.1+: valid CC output for sale |
|||
vout.0: vin.1 assetoshis output to CC to unspendable |
|||
vout.1: CC output for change (if any) |
|||
vout.2: normal output for change (if any) |
|||
vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey] |
|||
|
|||
cancel: |
|||
vin.0: normal input |
|||
vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx |
|||
vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey] |
|||
vout.1: normal output for change (if any) |
|||
vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid] |
|||
|
|||
fillsell: |
|||
vin.0: normal input |
|||
vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0] |
|||
vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue |
|||
vout.0: remaining assetoshis -> unspendable |
|||
vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any |
|||
vout.2: vin.2 value to original pubkey [origpubkey] |
|||
vout.3: CC asset for change (if any) |
|||
vout.4: CC asset2 for change (if any) 'E' only |
|||
vout.5: normal output for change (if any) |
|||
vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] |
|||
|
|||
Chapter 9 - dice example |
|||
The dice CC contract is actually more complex in the sequences required than the assets/tokens CC. The reason is the need for realtime response by the dealer node, but also having a way to resolve bets if the dealer node is not online. The dice CC contract shows how to build in such a challenge/response mechanism, which likely will be very useful for many other realtime interactive CC contracts. |
|||
|
|||
First, let us describe the issues that the dice CC contract needs to solve. Foremost is that it needs to be random and fair. It should also have realtime response and a fallback timeout in case the realtime response doesnt happen. As with the rewards CC contract, multiple dice plans are supported. Each plan can be customized as to the following: minbet, maxbet, maxodds, timeoutblocks |
|||
|
|||
This allows each plan to control the risk exposure and also advertises to everyone when dicebets expire and a timeout win can be claimed. In event the dealer node does not process a dicebet in time, in order to prevent dealer nodes from simply not responding to dicebets that they lose, a timeout must go to the dicebet player. A short timeframe means that the dealer would need to be running multiple redundant nodes to make sure they can respond in time. If the timeout is set to long, then many players would prefer to use a different dice plan with a shorter timeout. |
|||
|
|||
Now to describe how to ensure a proper random number that is fair. The method chosen was for the dealer node to create transactions with hash of their entropy in the OP_RETURN. Then the dicebet player would select a specific entropy tx and include their (unhashed) entropy to their OP_RETURN. This allows the dealer node to immediately determine if the dicebet won or lost. If the dicebet included the hash of the bettor entropy, then another step would be needed. However, doing so would allow some timeouts to end with a refund, rather than an automatic win for the dicebet player. |
|||
|
|||
One additional technique used to keep all required data on the blockchain is the dealer entropy value calculation. The vin0 txid is used as one of the privkeys to calculate a shared secret and then hashed to remove links to the original privkey. This method allows recreating the dealer's entropy value (by the dealer node) given the blockchain itself, which means there is no need for any local storage. |
|||
|
|||
This allows the dealer node to recreate the unhashed entropy value used and so when the dicebet transaction is seen (in the mempool!), the dealer node can immediately determine if it is a winner or a loser. This is done by creating a dealer hash vs. a bettor hash via: |
|||
|
|||
dealer hash: SHA256(dealer entropy + bettor entropy) |
|||
bettor hash: SHA256(bettor entropy + dealer entropy) |
|||
|
|||
The same values are used, but in different order. The resulting hashes are compared arithmetically for 1:1 bets and the standard industry use is used for the higher odds: https://dicesites.com/provably-fair |
|||
|
|||
The dealer creates a dice plan and then also needs to create entropy transactions. Each win or loss that creates change also creates entropy transactions by the dealer, but timeout transactions wont as it needs to be created by the dealer node to prevent cheating. The dealer tx are locked into the global dice CC address, as is the dicebet transaction, which selects a specific entropy tx to "roll" against. Then the dicefinish process by the dealer will spend the dicebet outputs either all to itself for a loss, or the winning amount to th dice bettor's address. For dicebets that are not dicefinish'ed by the dealer, anybody is able to do a timeout completion. |
|||
|
|||
createfunding: |
|||
vins.*: normal inputs |
|||
vout.0: CC vout for funding |
|||
vout.1: owner vout |
|||
vout.2: dice marker address vout for easy searching |
|||
vout.3: normal change |
|||
vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks |
|||
|
|||
addfunding (entropy): |
|||
vins.*: normal inputs |
|||
vout.0: CC vout for locked entropy funds |
|||
vout.1: tag to owner address for entropy funds |
|||
vout.2: normal change |
|||
vout.n-1: opreturn 'E' sbits fundingtxid hentropy |
|||
|
|||
bet: |
|||
vin.0: entropy txid from house (must validate vin0 of 'E') |
|||
vins.1+: normal inputs |
|||
vout.0: CC vout for locked entropy |
|||
vout.1: CC vout for locked bet |
|||
vout.2: tag for bettor's address (txfee + odds) |
|||
vout.3: change |
|||
vout.n-1: opreturn 'B' sbits fundingtxid entropy |
|||
|
|||
loser: |
|||
vin.0: normal input |
|||
vin.1: betTx CC vout.0 entropy from bet |
|||
vin.2: betTx CC vout.1 bet amount from bet |
|||
vin.3+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T' |
|||
vout.0: funding CC to entropy owner |
|||
vout.1: tag to owner address for entropy funds |
|||
vout.2: change to fundingpk |
|||
vout.n-1: opreturn 'L' sbits fundingtxid hentropy proof |
|||
|
|||
winner: |
|||
same as loser, but vout.2 is winnings |
|||
vout.3: change to fundingpk |
|||
vout.n-1: opreturn 'W' sbits fundingtxid hentropy proof |
|||
|
|||
timeout: |
|||
same as winner, just without hentropy or proof |
|||
|
|||
WARNING: there is an attack vector that precludes betting any large amounts, it goes as follows: |
|||
1. do dicebet to get the house entropy revealed |
|||
2. calculate bettor entropy that would win against the house entropy |
|||
3. reorg the chain and make a big bet using the winning entropy calculated in 2. |
|||
|
|||
In order to mitigate this, the disclosure of the house entropy needs to be delayed beyond a reasonable reorg depth (notarization). It is recommended for production dice game with significant amounts of money to use such a delayed disclosure method. |
|||
|
|||
|
|||
Chapter 10 - lotto example |
|||
|
|||
Chapter 11 - oracles example |
|||
Oracles CC is an example where it ended up being simpler than I first expected, but at the same time a lot more powerful. It is one of the smaller CC, but it enables creation of an arbitrary number of data markets, in a performant way. |
|||
|
|||
In order to gain the performance, some clever usage of special addresses was needed. It was a bit tricky to generate a special address to keep track of the latest data. |
|||
|
|||
Let's back up to the beginning. Just what is an oracle? In this context it is something that puts data that is not on the blockchain, onto the blockchain. Since everything other than the transactions and blocks are not in the blockchain, there is a very large universe of data that can be oracle-ized. It can be literally anything, from the obvious like prices to specific results relative to an arbitrary description. |
|||
|
|||
The most difficult issue about oracles is that they need to be trusted to various degree to provide accurate and timely data. The danger is that if a trusted node is used to write data to the blockchain, it creates a trust point and a single point of attack. Ultimately there is nothing that can ensure only valid data is written to the blockchain, so what is done is to reinforce good behavior via pay per datapoint. However, for critical data, higher level processing is needed that combines multiple data providers into a validated signal. |
|||
|
|||
At the oracles CC level, it is enough that there is financial incentive to provide good data. Also it is needed to allow multiple vendors for each data that is required and to enable efficient ways to update and query the data. |
|||
|
|||
The following are the rpc calls: |
|||
oraclescreate name description format |
|||
oracleslist |
|||
oraclesinfo oracletxid |
|||
oraclesregister oracletxid datafee |
|||
oraclessubscribe oracletxid publisher amount |
|||
oraclesdata oracletxid hexstr |
|||
oraclessamples oracletxid batonutxo num |
|||
|
|||
The first step is to create a specific data description with oraclescreate, which also defines the format of the binary data. This creates an oracletxid, which is used in the other rpc calls. name and description are just arbitrary strings, with name preferably being a short name used to access the data. The format is a string comprised of a single character per data element: |
|||
|
|||
's' -> <256 char string |
|||
'S' -> <65536 char string |
|||
'd' -> <256 binary data |
|||
'D' -> <65536 binary data |
|||
'c' -> 1 byte signed little endian number, 'C' unsigned |
|||
't' -> 2 byte signed little endian number, 'T' unsigned |
|||
'i' -> 4 byte signed little endian number, 'I' unsigned |
|||
'l' -> 8 byte signed little endian number, 'L' unsigned |
|||
'h' -> 32 byte hash |
|||
|
|||
For example, if the datapoint is comprised of a 4byte timestamp and an 8byte number the format string would be: "IL" |
|||
|
|||
oracleslist displays a list of all the oraclestxid and oraclesinfo displays information about the specific oracletxid. Each oracletxid deterministically generates a marker address and a small amount is sent to that address to mark a transaction's relation to the oracltxid. |
|||
|
|||
{ |
|||
"result": "success", |
|||
"txid": "4895f631316a649e216153aee7a574bd281686265dc4e8d37597f72353facac3", |
|||
"name": "BTCUSD", |
|||
"description": "coindeskpricedata", |
|||
"format": "L", |
|||
"marker": "RVqJCSrdBm1gYJZS1h7dgtHioA5TEYzNRk", |
|||
"registered": [ |
|||
{ |
|||
"publisher": "02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92", |
|||
"baton": "RKY4zmHJZ5mNtf6tfKE5VMsKoV71Euej3i", |
|||
"batontxid": "4de10b01242ce1a5e29d5fbb03098b4519976879e05ad0458ef7174ed9127f18", |
|||
"lifetime": "1.50000000", |
|||
"funds": "0.01000000", |
|||
"datafee": "0.01000000" |
|||
} |
|||
] |
|||
} |
|||
|
|||
A data publisher needs to register a datafee and their pubkey for a specific oracletxid. datafee needs to be at least as big as a txfee. Using oraclesregister the current datafee can be updated so a publisher can adapt to market conditions. Once registered, subscribers can prepay for some number of datapoints to a specific publisher using the oraclessubscribe rpc. At first, it is likely that the publisher would pay themselves to enable the posting of initial data points so the potential subscribers can evaluate the quality and consistency of the data. |
|||
|
|||
The one final rpc is oraclessamples, which returns the most recent samples of data from a specific publisher. In order to have a performant solution to track all the potential data streams from all the publishers for all the oracletxid, a baton utxo is used. This is an output sent to a specific address and expected to have just a single utxo at any given time to allow for direct lookup. oraclessamples requires a starting txid to use and with each datapoint having the prior batontxid, there is a reverse linked list to traverse the most recent data. |
|||
|
|||
In order to implement this, the following vin/vout contraints are used: |
|||
|
|||
create: |
|||
vins.*: normal inputs |
|||
vout.0: txfee tag to oracle normal address |
|||
vout.1: change, if any |
|||
vout.n-1: opreturn with name and description and format for data |
|||
|
|||
register: |
|||
vins.*: normal inputs |
|||
vout.0: txfee tag to normal marker address |
|||
vout.1: baton CC utxo |
|||
vout.2: change, if any |
|||
vout.n-1: opreturn with oracletxid, pubkey and price per data point |
|||
|
|||
subscribe: |
|||
vins.*: normal inputs |
|||
vout.0: subscription fee to publishers CC address |
|||
vout.1: change, if any |
|||
vout.n-1: opreturn with oracletxid, registered provider's pubkey, amount |
|||
|
|||
data: |
|||
vin.0: normal input |
|||
vin.1: baton CC utxo (most of the time) |
|||
vin.2+: subscription or data vout.0 |
|||
vout.0: change to publishers CC address |
|||
vout.1: baton CC utxo |
|||
vout.2: payment for dataprovider |
|||
vout.3: change, if any |
|||
vout.n-1: opreturn with oracletxid, prevbatontxid and data in proper format |
|||
|
|||
The oraclesdata transaction is the most complex as it needs to find and spend the baton utxo, use the correct datafee and spend funds from the locked subscription funds. With the above, the oracles CC is complete and allows the creations of massively parallel data streams from multiple vendors that uses free market feedback via payments, ie. poorly performing providers wont get renewals. |
|||
|
|||
I expect that at first, the data providers will just be dapp developers deploying a working system including the required data, but its structure allows open market competition. Of course, specific dapps could restrict themselves to using only publishers from a whitelist of pubkeys. The potential usecases for oracles CC is quite varied and limited only by the imagination. |
|||
|
|||
Chapter 12 - limitless possibilities |
|||
As can be seen, CC contracts can do a wide range of things and since they are Turing complete, we know that this is true. However, what is more important is the added security gained from using a utxo based system. While in some ways it is more complex to have to deal with utxo, as can be seen by the above examples, it is either solved and made invisible at the rpc level, or actually used as part of the solution. |
|||
|
|||
Being utxo based, automatically builds in a rate limit to how many tx per block a specific CC contract can do. The state advancing by one transaction at a time is another means that rate limits. Since more utxo can be made available to increase capacity, it actually offers a way for managing load. |
|||
|
|||
I believe I have made one of the first operational utxo smart contracts, CC or otherwise and hope that there will be many more developers joining forces to create more foundational CC contracts. Feel free to contact me for feedback on the type of CC contract you want to make. I have not documented all my notes and it could well be I already sort of know how to implement what your want your CC contract to do. Just only so many I can actually make time to code and debug. |
|||
|
|||
Our testing cycle went a lot faster than expected as the bugs found were few and far between. Considering the scope of the assets CC and the realtime response aspects of dice CC, this was quite unexpected. I can only attribute it to the fact that CC validation is just the final validation on top of all the standard bitcoin protocol validations. Not having to worry about double spends is sure a nice luxury, though dont get too complacent about chain rewrites! It is possible to wait for information to be divulged and then reorg the chain to take advantage of this knowledge in a chain which is rewound. |
|||
|
|||
Yes, blockchains are complicated. |
|||
|
|||
Chapter 13 - different languages |
|||
The current codebase is integrated into the komodod codebase, which is C/C++. However, it is possible to use different languages and integrate into the C/C++ as zcash has shown by using the rust language for some parts of the zcashd. |
|||
|
|||
I think any language that is compiled and can create a linkable library while being able to call and be called by C/C++ functions can be used. If you are able to make such a language binding for a simple CC contract like faucet, this will be good for a 777 KMD bounty. Of course, you need to be the first to submit a properly working pull request. |
|||
|
|||
|
|||
Chapter 14 - runtime bindings |
|||
Once build time linking works, then it is one step away from being able to do runtime linking, ie. dynamically linked libraries. There will be some work required to prevent duplication of eval codes and making sure it is a valid version of the CC contract plugin, but these are issues that have been solved before and I dont see any reason they cant be solved for CC contracts. |
|||
|
|||
This would open up the door for quite an interesting ecosystem of CC plugins that blockchains can subscribe to. |
|||
|
|||
Chapter 15 - rpc based dapps |
|||
Ultimately, I expect there to be so many new rpc calls (one set from each CC contract), that virtually any dapp can be made with rpc calls. We are just at the beginning now, but it is just a matter of time when we get there. |
|||
|
|||
For now, we just need to keep listening to what the market wants as far as dapps go. Then make a new CC contract that enables doing as many of those as possible. |
|||
|
|||
Repeat... |
|||
|
|||
Imagine the scope that will exist after a year or two of continuous new CC contracts being created, along with all the rpc based dapps. I have seen some automatic GUI generators and it could be that for most cases, there can be a special GUI that not only create the dapp's GUI, but also all the rpc calls that are needed to make it work the way it is customized. |
|||
|
|||
This codebase and tools in between the GUI and the rpc level will be a very good area for new initiatives. |
|||
|
|||
########## |
|||
|
|||
Conclusion |
|||
I hope this document has helped you understand what a Komodo utxo based CC contract is and how it is different from the other smart contracts. If you are now able to dive into the cc directory and start making your own CC contract, then I am very happy! |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
@ -0,0 +1,35 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
|
|||
#ifndef CC_GATEWAYS_H |
|||
#define CC_GATEWAYS_H |
|||
|
|||
#include "CCinclude.h" |
|||
#include "../merkleblock.h" |
|||
|
|||
bool GatewaysValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); |
|||
std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector<CPubKey> pubkeys); |
|||
std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vector<uint8_t>proof,CPubKey destpub,int64_t amount); |
|||
std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount); |
|||
std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,std::vector<uint8_t> withdrawpub,int64_t amount); |
|||
UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin); |
|||
std::string GatewaysMarkdone(uint64_t txfee,uint256 withdrawtxid); |
|||
|
|||
// CCcustom
|
|||
UniValue GatewaysInfo(uint256 bindtxid); |
|||
UniValue GatewaysList(); |
|||
|
|||
#endif |
@ -0,0 +1,33 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
|
|||
#ifndef CC_ORACLES_H |
|||
#define CC_ORACLES_H |
|||
|
|||
#include "CCinclude.h" |
|||
|
|||
bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); |
|||
std::string OracleCreate(int64_t txfee,std::string name,std::string description,std::string format); |
|||
std::string OracleRegister(int64_t txfee,uint256 oracletxid,int64_t datafee); |
|||
std::string OracleSubscribe(int64_t txfee,uint256 oracletxid,CPubKey publisher,int64_t amount); |
|||
std::string OracleData(int64_t txfee,uint256 oracletxid,std::vector <uint8_t> data); |
|||
|
|||
// CCcustom
|
|||
UniValue OracleDataSamples(uint256 reforacletxid,uint256 batontxid,int32_t num); |
|||
UniValue OracleInfo(uint256 origtxid); |
|||
UniValue OraclesList(); |
|||
|
|||
#endif |
@ -0,0 +1,27 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
|
|||
#ifndef CC_PAYMENTS_H |
|||
#define CC_PAYMENTS_H |
|||
|
|||
#include "CCinclude.h" |
|||
|
|||
bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); |
|||
|
|||
// CCcustom
|
|||
UniValue PaymentsInfo(); |
|||
|
|||
#endif |
@ -0,0 +1,27 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
|
|||
#ifndef CC_PEGS_H |
|||
#define CC_PEGS_H |
|||
|
|||
#include "CCinclude.h" |
|||
|
|||
bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); |
|||
|
|||
// CCcustom
|
|||
UniValue PegsInfo(); |
|||
|
|||
#endif |
@ -0,0 +1,27 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
|
|||
#ifndef CC_PRICES_H |
|||
#define CC_PRICES_H |
|||
|
|||
#include "CCinclude.h" |
|||
|
|||
bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); |
|||
|
|||
// CCcustom
|
|||
UniValue PricesInfo(); |
|||
|
|||
#endif |
@ -0,0 +1,27 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
|
|||
#ifndef CC_TRIGGERS_H |
|||
#define CC_TRIGGERS_H |
|||
|
|||
#include "CCinclude.h" |
|||
|
|||
bool TriggersValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); |
|||
|
|||
// CCcustom
|
|||
UniValue TriggersInfo(); |
|||
|
|||
#endif |
@ -0,0 +1,33 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
|
|||
#ifndef CC_CHANNELS_H |
|||
#define CC_CHANNELS_H |
|||
|
|||
#include "CCinclude.h" |
|||
#define CHANNELS_MAXPAYMENTS 1000 |
|||
|
|||
bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); |
|||
std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment); |
|||
std::string ChannelStop(uint64_t txfee,CPubKey destpub,uint256 origtxid); |
|||
std::string ChannelPayment(uint64_t txfee,uint256 prevtxid,uint256 origtxid,int32_t n,int64_t amount); |
|||
std::string ChannelCollect(uint64_t txfee,uint256 paytxid,uint256 origtxid,int32_t n,int64_t amount); |
|||
std::string ChannelRefund(uint64_t txfee,uint256 stoptxid,uint256 origtxid); |
|||
|
|||
// CCcustom
|
|||
UniValue ChannelsInfo(); |
|||
|
|||
#endif |
@ -0,0 +1,30 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
|
|||
#ifndef CC_FSM_H |
|||
#define CC_FSM_H |
|||
|
|||
#include "CCinclude.h" |
|||
|
|||
#define EVAL_FSM 0xe7 |
|||
|
|||
bool FSMValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); |
|||
|
|||
std::string FSMList(); |
|||
std::string FSMInfo(uint256 fsmtxid); |
|||
std::string FSMCreate(uint64_t txfee,std::string name,std::string states); |
|||
|
|||
#endif |
@ -0,0 +1,226 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#include "CCMofN.h" |
|||
|
|||
/*
|
|||
The idea of MofN CC is to allow non-interactive multisig, preferably in a cross chain compatible way, ie. for actual bitcoin multisig. |
|||
|
|||
full redeemscript in an initial tx with opreturn |
|||
ability to post partial signatures and construct a full transaction from M such partial signatures |
|||
a new transaction would refer to the initialtx and other partial would refer to both |
|||
|
|||
There is no need for a CC contract to use it for normal multisig as normal multisig transactions are already supported. |
|||
|
|||
In order to take advantage of CC powers, we can create a more powerful multisig using shamir's secret MofN (up to 255) algo to allow spends. Using the same non-interactive partial signing is possible. also, in addition to spending, data payload can have additional data that is also revealed when the funds are spent. |
|||
|
|||
rpc calls needed: |
|||
1) create msig address (normal or shamir) |
|||
2) post payment with partial sig |
|||
3) add partial sig to 2) |
|||
4) combine and submit M partial sigs |
|||
|
|||
*/ |
|||
|
|||
// start of consensus code
|
|||
|
|||
int64_t IsMofNvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) |
|||
{ |
|||
char destaddr[64]; |
|||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) |
|||
{ |
|||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) |
|||
return(tx.vout[v].nValue); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
bool MofNExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) |
|||
{ |
|||
static uint256 zerohash; |
|||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
//fprintf(stderr,"vini.%d\n",i);
|
|||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
|||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) |
|||
return eval->Invalid("cant find vinTx"); |
|||
else |
|||
{ |
|||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
|||
if ( hashBlock == zerohash ) |
|||
return eval->Invalid("cant MofN from mempool"); |
|||
if ( (assetoshis= IsMofNvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) |
|||
inputs += assetoshis; |
|||
} |
|||
} |
|||
} |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
|||
if ( (assetoshis= IsMofNvout(cp,tx,i)) != 0 ) |
|||
outputs += assetoshis; |
|||
} |
|||
if ( inputs != outputs+txfee ) |
|||
{ |
|||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); |
|||
return eval->Invalid("mismatched inputs != outputs + txfee"); |
|||
} |
|||
else return(true); |
|||
} |
|||
|
|||
bool MofNValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) |
|||
{ |
|||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; |
|||
return(false); |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
preventCCvins = preventCCvouts = -1; |
|||
if ( numvouts < 1 ) |
|||
return eval->Invalid("no vouts"); |
|||
else |
|||
{ |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) |
|||
{ |
|||
return eval->Invalid("illegal normal vini"); |
|||
} |
|||
} |
|||
//fprintf(stderr,"check amounts\n");
|
|||
if ( MofNExactAmounts(cp,eval,tx,1,10000) == false ) |
|||
{ |
|||
fprintf(stderr,"mofnget invalid amount\n"); |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
txid = tx.GetHash(); |
|||
memcpy(hash,&txid,sizeof(hash)); |
|||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); |
|||
if ( retval != 0 ) |
|||
fprintf(stderr,"mofnget validated\n"); |
|||
else fprintf(stderr,"mofnget invalid\n"); |
|||
return(retval); |
|||
} |
|||
} |
|||
} |
|||
// end of consensus code
|
|||
|
|||
// helper functions for rpc calls in rpcwallet.cpp
|
|||
|
|||
int64_t AddMofNInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) |
|||
{ |
|||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
GetCCaddress(cp,coinaddr,pk); |
|||
SetCCunspents(unspentOutputs,coinaddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
// no need to prevent dup
|
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( (nValue= IsMofNvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) |
|||
{ |
|||
if ( total != 0 && maxinputs != 0 ) |
|||
mtx.vin.push_back(CTxIn(txid,vout,CScript())); |
|||
nValue = it->second.satoshis; |
|||
totalinputs += nValue; |
|||
n++; |
|||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return(totalinputs); |
|||
} |
|||
|
|||
std::string MofNGet(uint64_t txfee,int64_t nValue) |
|||
{ |
|||
CMutableTransaction mtx,tmpmtx; CPubKey mypk,mofnpk; int64_t inputs,CCchange=0; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; |
|||
cp = CCinit(&C,EVAL_MOFN); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mofnpk = GetUnspendable(cp,0); |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if ( (inputs= AddMofNInputs(cp,mtx,mofnpk,nValue+txfee,60)) > 0 ) |
|||
{ |
|||
if ( inputs > nValue ) |
|||
CCchange = (inputs - nValue - txfee); |
|||
if ( CCchange != 0 ) |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_MOFN,CCchange,mofnpk)); |
|||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
|||
fprintf(stderr,"start at %u\n",(uint32_t)time(NULL)); |
|||
j = rand() & 0xfffffff; |
|||
for (i=0; i<1000000; i++,j++) |
|||
{ |
|||
tmpmtx = mtx; |
|||
rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_MOFN << (uint8_t)'G' << j)); |
|||
if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) |
|||
{ |
|||
len >>= 1; |
|||
decode_hex(buf,len,(char *)rawhex.c_str()); |
|||
hash = bits256_doublesha256(0,buf,len); |
|||
if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 ) |
|||
{ |
|||
fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL)); |
|||
return(rawhex); |
|||
} |
|||
//fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]);
|
|||
} |
|||
} |
|||
fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); |
|||
return(""); |
|||
} else fprintf(stderr,"cant find mofn inputs\n"); |
|||
return(""); |
|||
} |
|||
|
|||
std::string MofNFund(uint64_t txfee,int64_t funds) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk,mofnpk; CScript opret; struct CCcontract_info *cp,C; |
|||
cp = CCinit(&C,EVAL_MOFN); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
mofnpk = GetUnspendable(cp,0); |
|||
if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_MOFN,funds,mofnpk)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
UniValue MofNInfo() |
|||
{ |
|||
UniValue result(UniValue::VOBJ); char numstr[64]; |
|||
CMutableTransaction mtx; CPubKey mofnpk; struct CCcontract_info *cp,C; int64_t funding; |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("name","MofN")); |
|||
cp = CCinit(&C,EVAL_MOFN); |
|||
mofnpk = GetUnspendable(cp,0); |
|||
funding = AddMofNInputs(cp,mtx,mofnpk,0,0); |
|||
sprintf(numstr,"%.8f",(double)funding/COIN); |
|||
result.push_back(Pair("funding",numstr)); |
|||
return(result); |
|||
} |
|||
|
@ -0,0 +1,337 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#include "CCchannels.h" |
|||
|
|||
/*
|
|||
The idea here is to allow instant (mempool) payments that are secured by dPoW. In order to simplify things, channels CC will require creating reserves for each payee locked in the destination user's CC address. This will look like the payment is already made, but it is locked until further released. The dPoW protection comes from the cancel channel having a delayed effect until the next notarization. This way, if a payment release is made and the chain reorged, the same payment release will still be valid when it is re-broadcast into the mempool. |
|||
|
|||
In order to achieve this effect, the payment release needs to be in a special form where its input cannot be spent only by the sender. |
|||
|
|||
Given sender's payment to dest CC address, only the destination is able to spend, so we need to constrain that spending with a release mechanism. One idea is a 2of2 multisig, but that has the issue of needing confirmation and since a sender utxo is involved, subject to doublespend and we lose the speed. Another idea is release on secrets! since once revealed, the secret remains valid, this method is immune from double spend. Also, there is no worry about an MITM attack as the funds are only spendable by the destination pubkey and only with the secret. The secrets can be sent via any means, including OP_RETURN of normal transaction in the mempool. |
|||
|
|||
Now the only remaining issue for sending is how to allocate funds to the secrets. This needs to be sent as hashes of secrets when the channel is created. A bruteforce method would be one secret per COIN, but for large amount channels this is cumbersome. A more practical approach is to have a set of secrets for each order of magnitude: |
|||
|
|||
123.45 channel funds -> 1x secret100, 2x secret10, 3x secret1, 4x secret.1, 5x secret.01 |
|||
15 secrets achieves the 123.45 channel funding. |
|||
|
|||
In order to avoid networking issues, the convention can be to send tx to normal address of destination with just an OP_RETURN, for the cost of txfee. For micropayments, a separate method of secret release needs to be established, but that is beyond the scope of this CC. |
|||
|
|||
There is now the dPoW security that needs to be ensured. In order to close the channel, a tx needs to be confirmed that cancels the channel. As soon as this tx is seen, the destination will know that the channel will be closing soon, if the node is online. If not, the payments cant be credited anyway, so it seems no harm. Even after the channel is closed, it is possible for secrets to be releasing funds, but depending on when the notarization happens, it could invalidate the spends, so it is safest that as soon as the channel cancel tx is confirmed to invalidate any further payments released. |
|||
|
|||
Given a channelclose and notarization confirmation (or enough blocks), the remaining funds needs to be able to come back to the sender. this means the funds need to be in a 1of2 CC multisig to allow either party to spend. Cheating is prevented by the additional rules of spending, ie. denomination secrets, or channelclose. |
|||
|
|||
For efficiency we want to allow batch spend with multiple secrets to claim a single total |
|||
|
|||
Second iteration: |
|||
As implementing it, some efficieny gains to be made with a slightly different approach. |
|||
Instead of separate secrets for each amount, a hashchain will be used, each releasing the same amount |
|||
|
|||
To spend, the prior value in the hash chain is published, or can publish N deep. validation takes N hashes. |
|||
|
|||
Also, in order to be able to track open channels, a tag is needed to be sent and better to send to a normal CC address for a pubkey to isolate the transactions for channel opens. |
|||
|
|||
Possible third iteration: |
|||
Let us try to setup a single "hot wallet" address to have all the channel funds and use it for payments to any destination. If there are no problems with reorgs and double spends, this would allow everyone to be "connected" to everyone else via the single special address. |
|||
|
|||
So funds -> user's CC address along with hashchain, but likely best to have several utxo to span order of magnitudes. |
|||
|
|||
a micropayment would then spend a utxo and attach a shared secret encoded unhashed link from the hashchain. That makes the receiver the only one that can decode the actual hashchain's prior value. |
|||
|
|||
however, since this spend is only spendable by the sender, it is subject to a double spend attack. It seems it is a dead end. Alternative is to use the global CC address, but that commingles all funds from all users and any accounting error puts all funds at risk. |
|||
|
|||
So, back to the second iteration, which is the only one so far that is immune from doublespend attack as the funds are already in the destination's CC address. One complication is that due to CC sorting of pubkeys, the address for sending and receiving is the same, so the destination pubkey needs to be attached to each opreturn. |
|||
|
|||
Now when the prior hashchain value is sent via payment, it allows the receiver to spend the utxo, so the only protection needed is to prevent channel close from invalidating already made payments. |
|||
|
|||
In order to allow multiple payments included in a single transaction, presentation of the N prior hashchain value can be used to get N payments and all the spends create a spending chain in sequential order of the hashchain. |
|||
|
|||
*/ |
|||
|
|||
// start of consensus code
|
|||
|
|||
int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) |
|||
{ |
|||
char destaddr[64]; |
|||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) |
|||
{ |
|||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) |
|||
return(tx.vout[v].nValue); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) |
|||
{ |
|||
static uint256 zerohash; |
|||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
//fprintf(stderr,"vini.%d\n",i);
|
|||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
|||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) |
|||
return eval->Invalid("cant find vinTx"); |
|||
else |
|||
{ |
|||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
|||
if ( hashBlock == zerohash ) |
|||
return eval->Invalid("cant Channels from mempool"); |
|||
if ( (assetoshis= IsChannelsvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) |
|||
inputs += assetoshis; |
|||
} |
|||
} |
|||
} |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
|||
if ( (assetoshis= IsChannelsvout(cp,tx,i)) != 0 ) |
|||
outputs += assetoshis; |
|||
} |
|||
if ( inputs != outputs+txfee ) |
|||
{ |
|||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); |
|||
return eval->Invalid("mismatched inputs != outputs + txfee"); |
|||
} |
|||
else return(true); |
|||
} |
|||
|
|||
bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) |
|||
{ |
|||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; |
|||
return(false); |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
preventCCvins = preventCCvouts = -1; |
|||
if ( numvouts < 1 ) |
|||
return eval->Invalid("no vouts"); |
|||
else |
|||
{ |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) |
|||
{ |
|||
return eval->Invalid("illegal normal vini"); |
|||
} |
|||
} |
|||
//fprintf(stderr,"check amounts\n");
|
|||
if ( ChannelsExactAmounts(cp,eval,tx,1,10000) == false ) |
|||
{ |
|||
fprintf(stderr,"Channelsget invalid amount\n"); |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
txid = tx.GetHash(); |
|||
memcpy(hash,&txid,sizeof(hash)); |
|||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); |
|||
if ( retval != 0 ) |
|||
fprintf(stderr,"Channelsget validated\n"); |
|||
else fprintf(stderr,"Channelsget invalid\n"); |
|||
return(retval); |
|||
} |
|||
} |
|||
} |
|||
// end of consensus code
|
|||
|
|||
// helper functions for rpc calls in rpcwallet.cpp
|
|||
|
|||
CScript EncodeChannelsOpRet(uint8_t funcid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain) |
|||
{ |
|||
CScript opret; uint8_t evalcode = EVAL_CHANNELS; |
|||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << srcpub << destpub << numpayments << payment << hashchain); |
|||
return(opret); |
|||
} |
|||
|
|||
uint8_t DecodeChannelsOpRet(uint256 txid,const CScript &scriptPubKey,CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain) |
|||
{ |
|||
std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; |
|||
GetOpReturnData(scriptPubKey, vopret); |
|||
if ( vopret.size() > 2 ) |
|||
{ |
|||
script = (uint8_t *)vopret.data(); |
|||
if ( script[0] == EVAL_CHANNELS ) |
|||
{ |
|||
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> srcpub; ss >> destpub; ss >> numpayments; ss >> payment; ss >> hashchain) != 0 ) |
|||
{ |
|||
return(f); |
|||
} |
|||
} else fprintf(stderr,"script[0] %02x != EVAL_CHANNELS\n",script[0]); |
|||
} else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size()); |
|||
return(0); |
|||
} |
|||
|
|||
int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) |
|||
{ |
|||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
GetCCaddress(cp,coinaddr,pk); |
|||
SetCCunspents(unspentOutputs,coinaddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
// no need to prevent dup
|
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( (nValue= IsChannelsvout(cp,vintx,vout)) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) |
|||
{ |
|||
if ( total != 0 && maxinputs != 0 ) |
|||
mtx.vin.push_back(CTxIn(txid,vout,CScript())); |
|||
nValue = it->second.satoshis; |
|||
totalinputs += nValue; |
|||
n++; |
|||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return(totalinputs); |
|||
} |
|||
|
|||
std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment) |
|||
{ |
|||
CMutableTransaction mtx; uint8_t hash[32],hashdest[32]; uint64_t funds; int32_t i; uint256 hashchain,entropy,hentropy; CPubKey mypk; struct CCcontract_info *cp,C; |
|||
if ( numpayments <= 0 || payment <= 0 || numpayments > CHANNELS_MAXPAYMENTS ) |
|||
{ |
|||
CCerror = strprintf("invalid ChannelsFund param numpayments.%d max.%d payment.%lld\n",numpayments,CHANNELS_MAXPAYMENTS,(long long)payment); |
|||
fprintf(stderr,"%s\n",CCerror.c_str()); |
|||
return(""); |
|||
} |
|||
cp = CCinit(&C,EVAL_CHANNELS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
funds = numpayments * payment; |
|||
if ( AddNormalinputs(mtx,mypk,funds+3*txfee,64) > 0 ) |
|||
{ |
|||
hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash); |
|||
endiancpy(hash,(uint8_t *)&hentropy,32); |
|||
for (i=0; i<numpayments; i++) |
|||
{ |
|||
vcalc_sha256(0,hashdest,hash,32); |
|||
memcpy(hash,hashdest,32); |
|||
} |
|||
endiancpy((uint8_t *)&hashchain,hashdest,32); |
|||
mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub)); |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',mypk,destpub,numpayments,payment,hashchain))); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
std::string ChannelStop(uint64_t txfee,CPubKey destpub,uint256 origtxid) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk; struct CCcontract_info *cp,C; |
|||
// verify this is one of our outbound channels
|
|||
cp = CCinit(&C,EVAL_CHANNELS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('S',mypk,destpub,0,0,zeroid))); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
std::string ChannelPayment(uint64_t txfee,uint256 prevtxid,uint256 origtxid,int32_t n,int64_t amount) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk,destpub; uint256 secret; struct CCcontract_info *cp,C; int32_t prevdepth; |
|||
// verify lasttxid and origtxid match and src is me
|
|||
// also verify hashchain depth and amount, set prevdepth
|
|||
cp = CCinit(&C,EVAL_CHANNELS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) |
|||
{ |
|||
// add locked funds inputs
|
|||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('P',mypk,destpub,prevdepth-n,amount,secret))); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
std::string ChannelCollect(uint64_t txfee,uint256 paytxid,uint256 origtxid,int32_t n,int64_t amount) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk,senderpub; struct CCcontract_info *cp,C; int32_t prevdepth; |
|||
// verify paytxid and origtxid match and dest is me
|
|||
// also verify hashchain depth and amount
|
|||
cp = CCinit(&C,EVAL_CHANNELS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) |
|||
{ |
|||
// add locked funds inputs
|
|||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); |
|||
mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',senderpub,mypk,prevdepth-n,amount,paytxid))); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
std::string ChannelRefund(uint64_t txfee,uint256 stoptxid,uint256 origtxid) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk; struct CCcontract_info *cp,C; int64_t amount; |
|||
// verify stoptxid and origtxid match and are mine
|
|||
cp = CCinit(&C,EVAL_CHANNELS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); |
|||
mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',mypk,mypk,0,0,stoptxid))); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
UniValue ChannelsInfo() |
|||
{ |
|||
UniValue result(UniValue::VOBJ); CTransaction tx; uint256 txid,hashBlock,hashchain; struct CCcontract_info *cp,C; uint8_t funcid; char myCCaddr[64]; int32_t vout,numvouts,numpayments; int64_t nValue,payment; CPubKey srcpub,destpub,mypk; |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids; |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("name","Channels")); |
|||
cp = CCinit(&C,EVAL_CHANNELS); |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
GetCCaddress(cp,myCCaddr,mypk); |
|||
SetCCtxids(txids,myCCaddr); |
|||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++) |
|||
{ |
|||
//int height = it->first.blockHeight;
|
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
nValue = (int64_t)it->second; |
|||
if ( (vout == 1 || vout == 2) && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) |
|||
{ |
|||
if ( DecodeChannelsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,srcpub,destpub,numpayments,payment,hashchain) == 'O' || funcid == 'P' ) |
|||
{ |
|||
char str[67],str2[67]; |
|||
fprintf(stderr,"%s func.%c %s -> %s %.8f num.%d of %.8f\n",mypk == srcpub ? "send" : "recv",funcid,pubkey33_str(str,(uint8_t *)&srcpub),pubkey33_str(str2,(uint8_t *)&destpub),(double)tx.vout[0].nValue/COIN,numpayments,(double)payment/COIN); |
|||
} |
|||
} |
|||
} |
|||
return(result); |
|||
} |
|||
|
@ -0,0 +1,436 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
#ifndef OS_PORTABLEH |
|||
#define OS_PORTABLEH |
|||
|
|||
// iguana_OS has functions that invoke system calls. Whenever possible stdio and similar functions are use and most functions are fully portable and in this file. For things that require OS specific, the call is routed to iguana_OS_portable_* Usually, all but one OS can be handled with the same code, so iguana_OS_portable.c has most of this shared logic and an #ifdef iguana_OS_nonportable.c
|
|||
|
|||
#ifdef __APPLE__ |
|||
//#define LIQUIDITY_PROVIDER 1
|
|||
#endif |
|||
|
|||
#ifdef NATIVE_WINDOWS |
|||
//#define uint64_t unsigned __int64
|
|||
#define PACKED |
|||
#else |
|||
#define PACKED __attribute__((packed)) |
|||
#endif |
|||
|
|||
#include <stdio.h> |
|||
#include <string.h> |
|||
#include <stdlib.h> |
|||
#include <stdint.h> |
|||
#define HAVE_STRUCT_TIMESPEC |
|||
#include <ctype.h> |
|||
#include <fcntl.h> |
|||
#include <math.h> |
|||
#include <errno.h> |
|||
#include <sys/stat.h> |
|||
#include <sys/types.h> |
|||
#include <time.h> |
|||
|
|||
#ifdef _WIN32 |
|||
#define sleep(x) Sleep(1000*(x)) |
|||
#include "../OSlibs/win/mingw.h" |
|||
#include "../OSlibs/win/mman.h" |
|||
#define PTW32_STATIC_LIB |
|||
#include "../OSlibs/win/pthread.h" |
|||
|
|||
#ifndef NATIVE_WINDOWS |
|||
#define EADDRINUSE WSAEADDRINUSE |
|||
#endif |
|||
|
|||
#else |
|||
#include <sys/time.h> |
|||
#include <time.h> |
|||
#include <poll.h> |
|||
#include <netdb.h> |
|||
#define HAVE_STRUCT_TIMESPEC |
|||
#include <pthread.h> |
|||
#include <sys/mman.h> |
|||
#include <sys/socket.h> |
|||
#include <unistd.h> |
|||
#define closesocket close |
|||
#endif |
|||
|
|||
#ifndef MIN |
|||
#define MIN(x, y) ( ((x)<(y))?(x):(y) ) |
|||
#endif |
|||
|
|||
#include "../includes/libgfshare.h" |
|||
#include "../includes/utlist.h" |
|||
#include "../includes/uthash.h" |
|||
#include "../includes/curve25519.h" |
|||
#include "../includes/cJSON.h" |
|||
#include "../includes/tweetnacl.h" |
|||
|
|||
#ifndef MAP_FILE |
|||
#define MAP_FILE 0 |
|||
#endif |
|||
|
|||
//#define fopen myfopen
|
|||
//#define fclose myfclose
|
|||
//FILE *myfopen(char *fname,char *mode);
|
|||
//int32_t myfclose(FILE *fp);
|
|||
|
|||
struct huffstream { uint8_t *ptr,*buf; uint32_t bitoffset,maski,endpos; uint32_t allocsize:31,allocated:1; }; |
|||
typedef struct huffstream HUFF; |
|||
|
|||
struct ramcoder |
|||
{ |
|||
uint32_t cumulativeProb; |
|||
uint16_t lower,upper,code,underflowBits,lastsymbol,upper_lastsymbol,counter; |
|||
uint64_t *histo; |
|||
uint16_t ranges[]; |
|||
}; |
|||
|
|||
#define hrewind(hp) hseek(hp,0,SEEK_SET) |
|||
int32_t ramcoder_decoder(struct ramcoder *coder,int32_t updateprobs,uint8_t *buf,int32_t maxlen,HUFF *hp,bits256 *seed); |
|||
int32_t ramcoder_encoder(struct ramcoder *coder,int32_t updateprobs,uint8_t *buf,int32_t len,HUFF *hp,uint64_t *histo,bits256 *seed); |
|||
//int32_t init_ramcoder(struct ramcoder *coder,HUFF *hp,bits256 *seed);
|
|||
int32_t ramcoder_decompress(uint8_t *data,int32_t maxlen,uint8_t *bits,uint32_t numbits,bits256 seed); |
|||
int32_t ramcoder_compress(uint8_t *bits,int32_t maxlen,uint8_t *data,int32_t datalen,bits256 seed); |
|||
uint64_t hconv_bitlen(uint64_t bitlen); |
|||
void _init_HUFF(HUFF *hp,int32_t allocsize,void *buf); |
|||
int32_t hgetbit(HUFF *hp); |
|||
int32_t hputbit(HUFF *hp,int32_t bit); |
|||
uint64_t hconv_bitlen(uint64_t bitlen); |
|||
int32_t hseek(HUFF *hp,int32_t offset,int32_t mode); |
|||
|
|||
#define SCRIPT_OPRETURN 0x6a |
|||
#define GENESIS_ACCT "1739068987193023818" // NXT-MRCC-2YLS-8M54-3CMAJ
|
|||
#define GENESIS_PUBKEYSTR "1259ec21d31a30898d7cd1609f80d9668b4778e3d97e941044b39f0c44d2e51b" |
|||
#define GENESIS_PRIVKEYSTR "88a71671a6edd987ad9e9097428fc3f169decba3ac8f10da7b24e0ca16803b70" |
|||
#define GENESIS_SECRET "It was a bright cold day in April, and the clocks were striking thirteen." |
|||
|
|||
#define SATOSHIDEN ((uint64_t)100000000L) |
|||
#define dstr(x) ((double)(x) / SATOSHIDEN) |
|||
|
|||
#define SMALLVAL 0.000000000000001 |
|||
|
|||
#define SETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] |= (1 << ((bitoffset) & 7))) |
|||
#define GETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] & (1 << ((bitoffset) & 7))) |
|||
#define CLEARBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] &= ~(1 << ((bitoffset) & 7))) |
|||
|
|||
#define portable_mutex_t pthread_mutex_t |
|||
#define portable_mutex_init(ptr) pthread_mutex_init(ptr,NULL) |
|||
#define portable_mutex_lock pthread_mutex_lock |
|||
#define portable_mutex_unlock pthread_mutex_unlock |
|||
#define OS_thread_create pthread_create |
|||
|
|||
#define issue_curl(cmdstr) bitcoind_RPC(0,"curl",cmdstr,0,0,0,0) |
|||
#define issue_curlt(cmdstr,timeout) bitcoind_RPC(0,"curl",cmdstr,0,0,0,timeout) |
|||
|
|||
struct allocitem { uint32_t allocsize,type; } PACKED; |
|||
struct queueitem { struct queueitem *next,*prev; uint32_t allocsize,type; } PACKED; |
|||
struct stritem { struct queueitem DL; void **retptrp; uint32_t expiration; char str[]; }; |
|||
|
|||
typedef struct queue |
|||
{ |
|||
struct queueitem *list; |
|||
portable_mutex_t mutex; |
|||
char name[64],initflag; |
|||
} queue_t; |
|||
|
|||
struct rpcrequest_info |
|||
{ |
|||
struct rpcrequest_info *next,*prev; |
|||
pthread_t T; |
|||
int32_t sock; |
|||
uint32_t ipbits; |
|||
uint16_t port,pad; |
|||
}; |
|||
|
|||
struct OS_mappedptr |
|||
{ |
|||
char fname[512]; |
|||
void *fileptr,*pending; |
|||
long allocsize,changedsize; |
|||
int32_t rwflag,dirty,actually_allocated; |
|||
uint32_t closetime,opentime; |
|||
}; |
|||
|
|||
struct OS_memspace |
|||
{ |
|||
portable_mutex_t mutex; long used,totalsize; struct OS_mappedptr M; char name[64]; void *ptr; |
|||
int32_t alignflag,counter,maxheight,openfiles,lastcounter,threadsafe,allocated:1,mapped:1,external:1; |
|||
#ifdef IGUANA_PEERALLOC |
|||
int32_t outofptrs,numptrs,availptrs; |
|||
void *ptrs[4096]; int32_t allocsizes[4096],maxsizes[4096]; |
|||
#endif |
|||
}; |
|||
|
|||
struct tai { uint64_t x; double millis; }; |
|||
struct taidate { int32_t year,month,day; }; |
|||
struct taitime { struct taidate date; int32_t hour,minute,second; uint32_t offset; double millis; }; |
|||
int32_t leapsecs_sub(struct tai *); |
|||
|
|||
struct tai tai_now(void); |
|||
uint32_t tai2utc(struct tai t); |
|||
struct taidate taidate_frommjd(int32_t day,int32_t *pwday,int32_t *pyday); |
|||
struct taitime tai2time(struct tai t,int32_t *pwday,int32_t *pyday); |
|||
struct taidate tai2date(struct tai t); |
|||
int32_t taidate_str(char *s,struct taidate cd); |
|||
char *taitime_str(char *s,struct taitime ct); |
|||
int32_t taidate_mjd(struct taidate cd); |
|||
uint64_t tai2utime(struct tai t); |
|||
struct tai taitime2tai(struct taitime ct); |
|||
char *tai_str(char *str,struct tai t); |
|||
char *utc_str(char *str,uint32_t utc); |
|||
double tai_diff(struct tai reftai,struct tai cmptai); |
|||
uint32_t OS_conv_utime(char *utime); |
|||
|
|||
//int32_t msync(void *addr,size_t len,int32_t flags);
|
|||
|
|||
#ifdef __PNACL |
|||
int32_t OS_nonportable_syncmap(struct OS_mappedptr *mp,long len); |
|||
void *OS_nonportable_tmpalloc(char *dirname,char *name,struct OS_memspace *mem,long origsize); |
|||
|
|||
#elif _WIN32 |
|||
char *OS_portable_path(char *str); |
|||
int32_t OS_nonportable_renamefile(char *fname,char *newfname); |
|||
int32_t OS_nonportable_launch(char *args[]); |
|||
void OS_nonportable_randombytes(uint8_t *x,long xlen); |
|||
int32_t OS_nonportable_init(); |
|||
#endif |
|||
|
|||
void OS_portable_init(); |
|||
void OS_init(); |
|||
int32_t sortds(double *buf,uint32_t num,int32_t size); |
|||
int32_t revsortds(double *buf,uint32_t num,int32_t size); |
|||
|
|||
double OS_portable_milliseconds(); |
|||
void OS_portable_randombytes(uint8_t *x,long xlen); |
|||
int32_t OS_portable_truncate(char *fname,long filesize); |
|||
char *OS_portable_path(char *str); |
|||
void OS_remove_directory(char *dirname); |
|||
int32_t OS_portable_renamefile(char *fname,char *newfname); |
|||
int32_t OS_portable_removefile(char *fname); |
|||
void *OS_portable_mapfile(char *fname,long *filesizep,int32_t enablewrite); |
|||
//int32_t OS_portable_syncmap(struct OS_mappedptr *mp,long len);
|
|||
//void *OS_portable_tmpalloc(char *dirname,char *name,struct OS_memspace *mem,long origsize);
|
|||
|
|||
int32_t is_DST(int32_t datenum); |
|||
int32_t extract_datenum(int32_t *yearp,int32_t *monthp,int32_t *dayp,int32_t datenum); |
|||
int32_t expand_datenum(char *date,int32_t datenum); |
|||
int32_t calc_datenum(int32_t year,int32_t month,int32_t day); |
|||
int32_t ecb_decrdate(int32_t *yearp,int32_t *monthp,int32_t *dayp,char *date,int32_t datenum); |
|||
int32_t conv_date(int32_t *secondsp,char *buf); |
|||
uint32_t OS_conv_datenum(int32_t datenum,int32_t hour,int32_t minute,int32_t second); |
|||
int32_t OS_conv_unixtime(struct tai *t,int32_t *secondsp,time_t timestamp); |
|||
|
|||
char *OS_compatible_path(char *str); |
|||
FILE *OS_appendfile(char *origfname); |
|||
|
|||
int32_t OS_compare_files(char *fname,char *fname2); |
|||
int64_t OS_copyfile(char *src,char *dest,int32_t cmpflag); |
|||
void _OS_closemap(struct OS_mappedptr *mp); |
|||
void *OS_loadfile(char *fname,char **bufp,long *lenp,long *allocsizep); |
|||
void *OS_filestr(long *allocsizep,char *fname); |
|||
void OS_closemap(struct OS_mappedptr *mp); |
|||
int32_t OS_openmap(struct OS_mappedptr *mp); |
|||
void *OS_mappedptr(void **ptrp,struct OS_mappedptr *mp,unsigned long allocsize,int32_t rwflag,char *fname); |
|||
void *OS_filealloc(struct OS_mappedptr *M,char *fname,struct OS_memspace *mem,long size); |
|||
void *OS_nonportable_mapfile(char *fname,long *filesizep,int32_t enablewrite); |
|||
int32_t OS_nonportable_removefile(char *fname); |
|||
|
|||
unsigned long OS_filesize(char *fname); |
|||
void OS_ensure_directory(char *dirname); |
|||
long OS_ensurefilesize(char *fname,long filesize,int32_t truncateflag); |
|||
int32_t OS_truncate(char *fname,long filesize); |
|||
int32_t OS_renamefile(char *fname,char *newfname); |
|||
int32_t OS_removefile(char *fname,int32_t scrubflag); |
|||
|
|||
void *OS_mapfile(char *fname,long *filesizep,int32_t enablewrite); |
|||
int32_t OS_releasemap(void *ptr,unsigned long filesize); |
|||
|
|||
double OS_milliseconds(); |
|||
void OS_randombytes(uint8_t *x,long xlen); |
|||
|
|||
//int32_t OS_syncmap(struct OS_mappedptr *mp,long len);
|
|||
//void *OS_tmpalloc(char *dirname,char *name,struct OS_memspace *mem,long origsize);
|
|||
|
|||
long myallocated(uint8_t type,long change); |
|||
void *mycalloc(uint8_t type,int32_t n,long itemsize); |
|||
void myfree(void *_ptr,long allocsize); |
|||
//void free_queueitem(void *itemdata);
|
|||
void *myrealloc(uint8_t type,void *oldptr,long oldsize,long newsize); |
|||
void *myaligned_alloc(uint64_t allocsize); |
|||
int32_t myaligned_free(void *ptr,long size); |
|||
|
|||
struct queueitem *queueitem(char *str); |
|||
void queue_enqueue(char *name,queue_t *queue,struct queueitem *origitem);//,int32_t offsetflag);
|
|||
void *queue_dequeue(queue_t *queue);//,int32_t offsetflag);
|
|||
void *queue_delete(queue_t *queue,struct queueitem *copy,int32_t copysize,int32_t freeitem); |
|||
void *queue_free(queue_t *queue); |
|||
void *queue_clone(queue_t *clone,queue_t *queue,int32_t size); |
|||
int32_t queue_size(queue_t *queue); |
|||
char *mbstr(char *str,double n); |
|||
|
|||
void iguana_memreset(struct OS_memspace *mem); |
|||
void iguana_mempurge(struct OS_memspace *mem); |
|||
void *iguana_meminit(struct OS_memspace *mem,char *name,void *ptr,int64_t totalsize,int32_t threadsafe); |
|||
void *iguana_memalloc(struct OS_memspace *mem,long size,int32_t clearflag); |
|||
int64_t iguana_memfree(struct OS_memspace *mem,void *ptr,int32_t size); |
|||
|
|||
// generic functions
|
|||
bits256 iguana_merkle(char *symbol,bits256 *tree,int32_t txn_count); |
|||
bits256 bits256_calctxid(char *symbol,uint8_t *serialized,int32_t len); |
|||
int32_t unhex(char c); |
|||
void touppercase(char *str); |
|||
uint32_t is_ipaddr(char *str); |
|||
void iguana_bitmap(char *space,int32_t max,char *name); |
|||
double _pairaved(double valA,double valB); |
|||
int32_t unstringbits(char *buf,uint64_t bits); |
|||
uint64_t stringbits(char *str); |
|||
int32_t is_decimalstr(char *str); |
|||
void tolowercase(char *str); |
|||
char *clonestr(char *str); |
|||
int32_t is_hexstr(char *str,int32_t n); |
|||
int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); |
|||
void reverse_hexstr(char *str); |
|||
int32_t init_hexbytes_noT(char *hexbytes,uint8_t *message,long len); |
|||
uint16_t parse_ipaddr(char *ipaddr,char *ip_port); |
|||
int32_t bitweight(uint64_t x); |
|||
uint8_t _decode_hex(char *hex); |
|||
char *uppercase_str(char *buf,char *str); |
|||
char *lowercase_str(char *buf,char *str); |
|||
int32_t strsearch(char *strs[],int32_t num,char *name); |
|||
int32_t OS_getline(int32_t waitflag,char *line,int32_t max,char *dispstr); |
|||
int32_t sort64s(uint64_t *buf,uint32_t num,int32_t size); |
|||
int32_t revsort64s(uint64_t *buf,uint32_t num,int32_t size); |
|||
int decode_base32(uint8_t *token,uint8_t *tokenstr,int32_t len); |
|||
int init_base32(char *tokenstr,uint8_t *token,int32_t len); |
|||
char *OS_mvstr(); |
|||
|
|||
long _stripwhite(char *buf,int accept); |
|||
int32_t is_DST(int32_t datenum); |
|||
int32_t extract_datenum(int32_t *yearp,int32_t *monthp,int32_t *dayp,int32_t datenum); |
|||
int32_t expand_datenum(char *date,int32_t datenum); |
|||
int32_t calc_datenum(int32_t year,int32_t month,int32_t day); |
|||
int32_t ecb_decrdate(int32_t *yearp,int32_t *monthp,int32_t *dayp,char *date,int32_t datenum); |
|||
int32_t conv_date(int32_t *secondsp,char *buf); |
|||
uint32_t OS_conv_datenum(int32_t datenum,int32_t hour,int32_t minute,int32_t second); |
|||
int32_t OS_conv_unixtime(struct tai *t,int32_t *secondsp,time_t timestamp); |
|||
int32_t btc_coinaddr(char *coinaddr,uint8_t addrtype,char *pubkeystr); |
|||
int32_t btc_convaddr(char *hexaddr,char *addr58); |
|||
|
|||
uint64_t RS_decode(char *rs); |
|||
int32_t RS_encode(char *rsaddr,uint64_t id); |
|||
char *cmc_ticker(char *base); |
|||
|
|||
void calc_sha1(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_md2(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_md4(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_md4str(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_md2str(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_md5str(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_sha224(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_sha384(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_sha512(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_sha224(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_rmd160(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_rmd128(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_rmd256(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_rmd320(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_tiger(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_whirlpool(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
|
|||
char *hmac_sha1_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_md2_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_md4_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_md5_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_sha224_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_sha256_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_sha384_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_sha512_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_rmd128_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_rmd160_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_rmd256_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_rmd320_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_tiger_str(char *dest,char *key,int32_t key_size,char *message); |
|||
char *hmac_whirlpool_str(char *dest,char *key,int32_t key_size,char *message); |
|||
int nn_base64_encode(const uint8_t *in,size_t in_len,char *out,size_t out_len); |
|||
int nn_base64_decode(const char *in,size_t in_len,uint8_t *out,size_t out_len); |
|||
void calc_rmd160_sha256(uint8_t rmd160[20],uint8_t *data,int32_t datalen); |
|||
void sha256_sha256(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void rmd160ofsha256(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_md5str(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_crc32str(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_NXTaddr(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_curve25519_str(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_base64_encodestr(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_base64_decodestr(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_hexstr(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
void calc_unhexstr(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); |
|||
int32_t safecopy(char *dest,char *src,long len); |
|||
double dxblend(double *destp,double val,double decay); |
|||
|
|||
uint64_t calc_ipbits(char *ip_port); |
|||
void expand_ipbits(char *ipaddr,uint64_t ipbits); |
|||
void escape_code(char *escaped,char *str); |
|||
void SaM_PrepareIndices(); |
|||
|
|||
// iguana_serdes.c
|
|||
#ifndef IGUANA_LOG2PACKETSIZE |
|||
#define IGUANA_LOG2PACKETSIZE 22 |
|||
#endif |
|||
#ifndef IGUANA_MAXPACKETSIZE |
|||
#define IGUANA_MAXPACKETSIZE (1 << IGUANA_LOG2PACKETSIZE) |
|||
#endif |
|||
struct iguana_msghdr { uint8_t netmagic[4]; char command[12]; uint8_t serdatalen[4],hash[4]; } PACKED; |
|||
|
|||
int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); |
|||
int32_t iguana_validatehdr(char *symbol,struct iguana_msghdr *H); |
|||
int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp); |
|||
int32_t iguana_sethdr(struct iguana_msghdr *H,const uint8_t netmagic[4],char *command,uint8_t *data,int32_t datalen); |
|||
uint8_t *iguana_varint16(int32_t rwflag,uint8_t *serialized,uint16_t *varint16p); |
|||
uint8_t *iguana_varint32(int32_t rwflag,uint8_t *serialized,uint16_t *varint16p); |
|||
uint8_t *iguana_varint64(int32_t rwflag,uint8_t *serialized,uint32_t *varint32p); |
|||
int32_t iguana_rwvarint(int32_t rwflag,uint8_t *serialized,uint64_t *varint64p); |
|||
int32_t iguana_rwvarint32(int32_t rwflag,uint8_t *serialized,uint32_t *int32p); |
|||
int32_t iguana_rwvarstr(int32_t rwflag,uint8_t *serialized,int32_t maxlen,char *endianedp); |
|||
int32_t iguana_rwmem(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); |
|||
#define bits256_nonz(a) (((a).ulongs[0] | (a).ulongs[1] | (a).ulongs[2] | (a).ulongs[3]) != 0) |
|||
|
|||
bits256 bits256_ave(bits256 a,bits256 b); |
|||
bits256 bits256_doublesha256(char *hashstr,uint8_t *data,int32_t datalen); |
|||
char *bits256_str(char hexstr[65],bits256 x); |
|||
char *bits256_lstr(char hexstr[65],bits256 x); |
|||
bits256 bits256_add(bits256 a,bits256 b); |
|||
int32_t bits256_cmp(bits256 a,bits256 b); |
|||
bits256 bits256_lshift(bits256 x); |
|||
bits256 bits256_rshift(bits256 x); |
|||
bits256 bits256_from_compact(uint32_t c); |
|||
uint32_t bits256_to_compact(bits256 x); |
|||
bits256 bits256_conv(char *hexstr); |
|||
int32_t btc_priv2pub(uint8_t pubkey[33],uint8_t privkey[32]); |
|||
int32_t OS_portable_rmdir(char *dirname,int32_t diralso); |
|||
void calc_hmac_sha256(uint8_t *mac,int32_t maclen,uint8_t *key,int32_t key_size,uint8_t *message,int32_t len); |
|||
int32_t revsort32(uint32_t *buf,uint32_t num,int32_t size); |
|||
|
|||
bits256 bits256_sha256(bits256 data); |
|||
void bits256_rmd160(uint8_t rmd160[20],bits256 data); |
|||
void bits256_rmd160_sha256(uint8_t rmd160[20],bits256 data); |
|||
double get_theoretical(double *avebidp,double *aveaskp,double *highbidp,double *lowaskp,double *CMC_averagep,double changes[3],char *name,char *base,char *rel,double *USD_averagep); |
|||
char *bitcoind_RPCnew(void *curl_handle,char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *params,int32_t timeout); |
|||
|
|||
extern char *Iguana_validcommands[]; |
|||
extern bits256 GENESIS_PUBKEY,GENESIS_PRIVKEY; |
|||
extern char NXTAPIURL[]; |
|||
extern int32_t smallprimes[168],Debuglevel; |
|||
|
|||
#endif |
|||
|
File diff suppressed because it is too large
@ -0,0 +1,24 @@ |
|||
#!/bin/bash |
|||
|
|||
# SET AC |
|||
read -p "Enter AC name you use : " acname |
|||
sed -i "/#define ACNAME */c\#define ACNAME \"$acname\"" oraclefeed.c |
|||
# Set ORACLETXID |
|||
read -p "Enter your oracle TXID (Oracle should have L data type) : " oracletxid |
|||
sed -i "/#define ORACLETXID */c\#define ORACLETXID \"$oracletxid\"" oraclefeed.c |
|||
# SET PUBKEY |
|||
read -p "Enter your pubkey : " pubkey |
|||
sed -i "/#define MYPUBKEY */c\#define MYPUBKEY \"$pubkey\"" oraclefeed.c |
|||
# COMPILATION |
|||
echo "Great, compiling !" |
|||
gcc oraclefeed.c -lm -o oracle_dapp |
|||
mv oracle_dapp ../../oracle_dapp |
|||
echo "Oracle is ready to use !" |
|||
while true; do |
|||
read -p "Would you like to run BTCUSD oracle app? [Y/N]" yn |
|||
case $yn in |
|||
[Yy]* ) cd ../..; ./oracle_dapp; break;; |
|||
[Nn]* ) exit;; |
|||
* ) echo "Please answer yes or no.";; |
|||
esac |
|||
done |
@ -0,0 +1,779 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <unistd.h> |
|||
#include <memory.h> |
|||
#include "cJSON.c" |
|||
|
|||
char hexbyte(int32_t c) |
|||
{ |
|||
c &= 0xf; |
|||
if ( c < 10 ) |
|||
return('0'+c); |
|||
else if ( c < 16 ) |
|||
return('a'+c-10); |
|||
else return(0); |
|||
} |
|||
|
|||
int32_t _unhex(char c) |
|||
{ |
|||
if ( c >= '0' && c <= '9' ) |
|||
return(c - '0'); |
|||
else if ( c >= 'a' && c <= 'f' ) |
|||
return(c - 'a' + 10); |
|||
else if ( c >= 'A' && c <= 'F' ) |
|||
return(c - 'A' + 10); |
|||
return(-1); |
|||
} |
|||
|
|||
int32_t is_hexstr(char *str,int32_t n) |
|||
{ |
|||
int32_t i; |
|||
if ( str == 0 || str[0] == 0 ) |
|||
return(0); |
|||
for (i=0; str[i]!=0; i++) |
|||
{ |
|||
if ( n > 0 && i >= n ) |
|||
break; |
|||
if ( _unhex(str[i]) < 0 ) |
|||
break; |
|||
} |
|||
if ( n == 0 ) |
|||
return(i); |
|||
return(i == n); |
|||
} |
|||
|
|||
int32_t unhex(char c) |
|||
{ |
|||
int32_t hex; |
|||
if ( (hex= _unhex(c)) < 0 ) |
|||
{ |
|||
//printf("unhex: illegal hexchar.(%c)\n",c);
|
|||
} |
|||
return(hex); |
|||
} |
|||
|
|||
unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } |
|||
|
|||
int32_t decode_hex(unsigned char *bytes,int32_t n,char *hex) |
|||
{ |
|||
int32_t adjust,i = 0; |
|||
//printf("decode.(%s)\n",hex);
|
|||
if ( is_hexstr(hex,n) <= 0 ) |
|||
{ |
|||
memset(bytes,0,n); |
|||
return(n); |
|||
} |
|||
if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) |
|||
hex[--n] = 0; |
|||
if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) |
|||
hex[--n] = 0; |
|||
if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) |
|||
{ |
|||
if ( n > 0 ) |
|||
{ |
|||
bytes[0] = unhex(hex[0]); |
|||
printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); |
|||
} |
|||
bytes++; |
|||
hex++; |
|||
adjust = 1; |
|||
} else adjust = 0; |
|||
if ( n > 0 ) |
|||
{ |
|||
for (i=0; i<n; i++) |
|||
bytes[i] = _decode_hex(&hex[i*2]); |
|||
} |
|||
//bytes[i] = 0;
|
|||
return(n + adjust); |
|||
} |
|||
|
|||
int32_t init_hexbytes_noT(char *hexbytes,unsigned char *message,long len) |
|||
{ |
|||
int32_t i; |
|||
if ( len <= 0 ) |
|||
{ |
|||
hexbytes[0] = 0; |
|||
return(1); |
|||
} |
|||
for (i=0; i<len; i++) |
|||
{ |
|||
hexbytes[i*2] = hexbyte((message[i]>>4) & 0xf); |
|||
hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); |
|||
//printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]);
|
|||
} |
|||
hexbytes[len*2] = 0; |
|||
//printf("len.%ld\n",len*2+1);
|
|||
return((int32_t)len*2+1); |
|||
} |
|||
|
|||
long _stripwhite(char *buf,int accept) |
|||
{ |
|||
int32_t i,j,c; |
|||
if ( buf == 0 || buf[0] == 0 ) |
|||
return(0); |
|||
for (i=j=0; buf[i]!=0; i++) |
|||
{ |
|||
buf[j] = c = buf[i]; |
|||
if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) |
|||
j++; |
|||
} |
|||
buf[j] = 0; |
|||
return(j); |
|||
} |
|||
|
|||
char *clonestr(char *str) |
|||
{ |
|||
char *clone; |
|||
if ( str == 0 || str[0] == 0 ) |
|||
{ |
|||
printf("warning cloning nullstr.%p\n",str); |
|||
//#ifdef __APPLE__
|
|||
// while ( 1 ) sleep(1);
|
|||
//#endif
|
|||
str = (char *)"<nullstr>"; |
|||
} |
|||
clone = (char *)malloc(strlen(str)+16); |
|||
strcpy(clone,str); |
|||
return(clone); |
|||
} |
|||
|
|||
int32_t safecopy(char *dest,char *src,long len) |
|||
{ |
|||
int32_t i = -1; |
|||
if ( src != 0 && dest != 0 && src != dest ) |
|||
{ |
|||
if ( dest != 0 ) |
|||
memset(dest,0,len); |
|||
for (i=0; i<len&&src[i]!=0; i++) |
|||
dest[i] = src[i]; |
|||
if ( i == len ) |
|||
{ |
|||
printf("safecopy: %s too long %ld\n",src,len); |
|||
//printf("divide by zero! %d\n",1/zeroval());
|
|||
#ifdef __APPLE__ |
|||
//getchar();
|
|||
#endif |
|||
return(-1); |
|||
} |
|||
dest[i] = 0; |
|||
} |
|||
return(i); |
|||
} |
|||
|
|||
char *bits256_str(char hexstr[65],bits256 x) |
|||
{ |
|||
init_hexbytes_noT(hexstr,x.bytes,sizeof(x)); |
|||
return(hexstr); |
|||
} |
|||
|
|||
int64_t conv_floatstr(char *numstr) |
|||
{ |
|||
double val,corr; |
|||
val = atof(numstr); |
|||
corr = (val < 0.) ? -0.50000000001 : 0.50000000001; |
|||
return((int64_t)(val * SATOSHIDEN + corr)); |
|||
} |
|||
|
|||
char *nonportable_path(char *str) |
|||
{ |
|||
int32_t i; |
|||
for (i=0; str[i]!=0; i++) |
|||
if ( str[i] == '/' ) |
|||
str[i] = '\\'; |
|||
return(str); |
|||
} |
|||
|
|||
char *portable_path(char *str) |
|||
{ |
|||
#ifdef _WIN32 |
|||
return(nonportable_path(str)); |
|||
#else |
|||
#ifdef __PNACL |
|||
/*int32_t i,n;
|
|||
if ( str[0] == '/' ) |
|||
return(str); |
|||
else |
|||
{ |
|||
n = (int32_t)strlen(str); |
|||
for (i=n; i>0; i--) |
|||
str[i] = str[i-1]; |
|||
str[0] = '/'; |
|||
str[n+1] = 0; |
|||
}*/ |
|||
#endif |
|||
return(str); |
|||
#endif |
|||
} |
|||
|
|||
void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) |
|||
{ |
|||
FILE *fp; |
|||
long filesize,buflen = *allocsizep; |
|||
uint8_t *buf = *bufp; |
|||
*lenp = 0; |
|||
if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) |
|||
{ |
|||
fseek(fp,0,SEEK_END); |
|||
filesize = ftell(fp); |
|||
if ( filesize == 0 ) |
|||
{ |
|||
fclose(fp); |
|||
*lenp = 0; |
|||
printf("loadfile null size.(%s)\n",fname); |
|||
return(0); |
|||
} |
|||
if ( filesize > buflen ) |
|||
{ |
|||
*allocsizep = filesize; |
|||
*bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); |
|||
} |
|||
rewind(fp); |
|||
if ( buf == 0 ) |
|||
printf("Null buf ???\n"); |
|||
else |
|||
{ |
|||
if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) |
|||
printf("error reading filesize.%ld\n",(long)filesize); |
|||
buf[filesize] = 0; |
|||
} |
|||
fclose(fp); |
|||
*lenp = filesize; |
|||
//printf("loaded.(%s)\n",buf);
|
|||
} //else printf("OS_loadfile couldnt load.(%s)\n",fname);
|
|||
return(buf); |
|||
} |
|||
|
|||
void *filestr(long *allocsizep,char *_fname) |
|||
{ |
|||
long filesize = 0; char *fname,*buf = 0; void *retptr; |
|||
*allocsizep = 0; |
|||
fname = malloc(strlen(_fname)+1); |
|||
strcpy(fname,_fname); |
|||
retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); |
|||
free(fname); |
|||
return(retptr); |
|||
} |
|||
|
|||
char *send_curl(char *url,char *fname) |
|||
{ |
|||
long fsize; char curlstr[1024]; |
|||
sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); |
|||
system(curlstr); |
|||
return(filestr(&fsize,fname)); |
|||
} |
|||
|
|||
cJSON *get_urljson(char *url,char *fname) |
|||
{ |
|||
char *jsonstr; cJSON *json = 0; |
|||
if ( (jsonstr= send_curl(url,fname)) != 0 ) |
|||
{ |
|||
//printf("(%s) -> (%s)\n",url,jsonstr);
|
|||
json = cJSON_Parse(jsonstr); |
|||
free(jsonstr); |
|||
} |
|||
return(json); |
|||
} |
|||
|
|||
//////////////////////////////////////////////
|
|||
// start of dapp
|
|||
//////////////////////////////////////////////
|
|||
|
|||
uint64_t get_btcusd() |
|||
{ |
|||
cJSON *pjson,*bpi,*usd; uint64_t btcusd = 0; |
|||
if ( (pjson= get_urljson("http://api.coindesk.com/v1/bpi/currentprice.json","/tmp/oraclefeed.json")) != 0 ) |
|||
{ |
|||
if ( (bpi= jobj(pjson,"bpi")) != 0 && (usd= jobj(bpi,"USD")) != 0 ) |
|||
{ |
|||
btcusd = jdouble(usd,"rate_float") * SATOSHIDEN; |
|||
printf("BTC/USD %.4f\n",dstr(btcusd)); |
|||
} |
|||
free_json(pjson); |
|||
} |
|||
return(btcusd); |
|||
} |
|||
|
|||
cJSON *get_komodocli(char **retstrp,char *acname,char *method,char *arg0,char *arg1,char *arg2) |
|||
{ |
|||
long fsize; cJSON *retjson = 0; char cmdstr[32768],*jsonstr,fname[256]; |
|||
sprintf(fname,"/tmp/oraclefeed.%s",method); |
|||
if ( acname[0] != 0 ) |
|||
sprintf(cmdstr,"./komodo-cli -ac_name=%s %s %s %s %s > %s\n",acname,method,arg0,arg1,arg2,fname); |
|||
else sprintf(cmdstr,"./komodo-cli %s %s %s %s > %s\n",method,arg0,arg1,arg2,fname); |
|||
system(cmdstr); |
|||
*retstrp = 0; |
|||
if ( (jsonstr= filestr(&fsize,fname)) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"%s -> jsonstr.(%s)\n",cmdstr,jsonstr);
|
|||
if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0 ) |
|||
*retstrp = jsonstr; |
|||
else free(jsonstr); |
|||
} |
|||
return(retjson); |
|||
} |
|||
|
|||
bits256 komodobroadcast(char *acname,cJSON *hexjson) |
|||
{ |
|||
char *hexstr,*retstr,str[65]; cJSON *retjson; bits256 txid; |
|||
memset(txid.bytes,0,sizeof(txid)); |
|||
if ( (hexstr= jstr(hexjson,"hex")) != 0 ) |
|||
{ |
|||
if ( (retjson= get_komodocli(&retstr,acname,"sendrawtransaction",hexstr,"","")) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"broadcast.(%s)\n",jprint(retjson,0));
|
|||
free_json(retjson); |
|||
} |
|||
else if ( retstr != 0 ) |
|||
{ |
|||
if ( strlen(retstr) >= 64 ) |
|||
{ |
|||
retstr[64] = 0; |
|||
decode_hex(txid.bytes,32,retstr); |
|||
} |
|||
fprintf(stderr,"broadcast %s txid.(%s)\n",acname,bits256_str(str,txid)); |
|||
free(retstr); |
|||
} |
|||
} |
|||
return(txid); |
|||
} |
|||
|
|||
bits256 sendtoaddress(char *acname,char *destaddr,int64_t satoshis) |
|||
{ |
|||
char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; |
|||
memset(txid.bytes,0,sizeof(txid)); |
|||
sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN); |
|||
if ( (retjson= get_komodocli(&retstr,acname,"sendtoaddress",destaddr,numstr,"")) != 0 ) |
|||
{ |
|||
fprintf(stderr,"unexpected sendrawtransaction json.(%s)\n",jprint(retjson,0)); |
|||
free_json(retjson); |
|||
} |
|||
else if ( retstr != 0 ) |
|||
{ |
|||
if ( strlen(retstr) >= 64 ) |
|||
{ |
|||
retstr[64] = 0; |
|||
decode_hex(txid.bytes,32,retstr); |
|||
} |
|||
fprintf(stderr,"sendtoaddress %s %.8f txid.(%s)\n",destaddr,(double)satoshis/SATOSHIDEN,bits256_str(str,txid)); |
|||
free(retstr); |
|||
} |
|||
return(txid); |
|||
} |
|||
|
|||
int32_t get_KMDheight(char *acname) |
|||
{ |
|||
cJSON *retjson; char *retstr; int32_t height=0; |
|||
if ( (retjson= get_komodocli(&retstr,acname,"getinfo","","","")) != 0 ) |
|||
{ |
|||
height = jint(retjson,"blocks"); |
|||
//fprintf(stderr,"%s height.%d\n",acname[0]!=0?acname:"KMD",height);
|
|||
free_json(retjson); |
|||
} |
|||
else if ( retstr != 0 ) |
|||
{ |
|||
fprintf(stderr,"get_KMDheight.(%s) error.(%s)\n",acname,retstr); |
|||
free(retstr); |
|||
} |
|||
return(height); |
|||
} |
|||
|
|||
bits256 get_KMDblockhash(int32_t height) |
|||
{ |
|||
cJSON *retjson; char *retstr,heightstr[32]; bits256 hash; |
|||
memset(hash.bytes,0,sizeof(hash)); |
|||
sprintf(heightstr,"%d",height); |
|||
if ( (retjson= get_komodocli(&retstr,"","getblockhash",heightstr,"","")) != 0 ) |
|||
{ |
|||
fprintf(stderr,"unexpected blockhash json.(%s)\n",jprint(retjson,0)); |
|||
free_json(retjson); |
|||
} |
|||
else if ( retstr != 0 ) |
|||
{ |
|||
//fprintf(stderr,"get_KMDblockhash.(%s) %d\n",retstr,(int32_t)strlen(retstr));
|
|||
if ( strlen(retstr) >= 64 ) |
|||
{ |
|||
retstr[64] = 0; |
|||
decode_hex(hash.bytes,32,retstr); |
|||
} |
|||
free(retstr); |
|||
} |
|||
return(hash); |
|||
} |
|||
|
|||
bits256 get_KMDmerkleroot(bits256 blockhash) |
|||
{ |
|||
cJSON *retjson; char *retstr,str[65]; bits256 merkleroot; |
|||
memset(merkleroot.bytes,0,sizeof(merkleroot)); |
|||
if ( (retjson= get_komodocli(&retstr,"","getblockheader",bits256_str(str,blockhash),"","")) != 0 ) |
|||
{ |
|||
merkleroot = jbits256(retjson,"merkleroot"); |
|||
//fprintf(stderr,"got merkleroot.(%s)\n",bits256_str(str,merkleroot));
|
|||
free_json(retjson); |
|||
} |
|||
else if ( retstr != 0 ) |
|||
{ |
|||
fprintf(stderr,"get_KMDmerkleroot error.(%s)\n",retstr); |
|||
free(retstr); |
|||
} |
|||
return(merkleroot); |
|||
} |
|||
|
|||
int32_t get_KMDheader(bits256 *blockhashp,bits256 *merklerootp,int32_t prevheight) |
|||
{ |
|||
int32_t height = 0; char str[65]; |
|||
if ( prevheight == 0 ) |
|||
height = get_KMDheight("") - 20; |
|||
else height = prevheight + 1; |
|||
if ( height > 0 ) |
|||
{ |
|||
*blockhashp = get_KMDblockhash(height); |
|||
if ( bits256_nonz(*blockhashp) != 0 ) |
|||
{ |
|||
*merklerootp = get_KMDmerkleroot(*blockhashp); |
|||
if ( bits256_nonz(*merklerootp) != 0 ) |
|||
return(height); |
|||
} |
|||
} |
|||
memset(blockhashp,0,sizeof(*blockhashp)); |
|||
memset(merklerootp,0,sizeof(*merklerootp)); |
|||
return(0); |
|||
} |
|||
|
|||
cJSON *get_gatewayspending(char *acname,char *oraclestxidstr,char *coin) |
|||
{ |
|||
cJSON *retjson; char *retstr; |
|||
if ( (retjson= get_komodocli(&retstr,acname,"gatewayspending",oraclestxidstr,coin,"")) != 0 ) |
|||
{ |
|||
//printf("pending.(%s)\n",jprint(retjson,0));
|
|||
return(retjson); |
|||
} |
|||
else if ( retstr != 0 ) |
|||
{ |
|||
fprintf(stderr,"get_gatewayspending.(%s) error.(%s)\n",acname,retstr); |
|||
free(retstr); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
cJSON *get_rawmempool(char *acname) |
|||
{ |
|||
cJSON *retjson; char *retstr; |
|||
if ( (retjson= get_komodocli(&retstr,acname,"getrawmempool","","","")) != 0 ) |
|||
{ |
|||
//printf("mempool.(%s)\n",jprint(retjson,0));
|
|||
return(retjson); |
|||
} |
|||
else if ( retstr != 0 ) |
|||
{ |
|||
fprintf(stderr,"get_rawmempool.(%s) error.(%s)\n",acname,retstr); |
|||
free(retstr); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
cJSON *get_addressutxos(char *acname,char *coinaddr) |
|||
{ |
|||
cJSON *retjson; char *retstr,jsonbuf[256]; |
|||
sprintf(jsonbuf,"{\\\"addresses\\\":[\\\"%s\\\"]}",coinaddr); |
|||
if ( (retjson= get_komodocli(&retstr,acname,"getaddressutxos",jsonbuf,"","")) != 0 ) |
|||
{ |
|||
//printf("addressutxos.(%s)\n",jprint(retjson,0));
|
|||
return(retjson); |
|||
} |
|||
else if ( retstr != 0 ) |
|||
{ |
|||
fprintf(stderr,"get_addressutxos.(%s) error.(%s)\n",acname,retstr); |
|||
free(retstr); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
cJSON *get_rawtransaction(char *acname,bits256 txid) |
|||
{ |
|||
cJSON *retjson; char *retstr,str[65]; |
|||
if ( (retjson= get_komodocli(&retstr,acname,"getrawtransaction",bits256_str(str,txid),"1","")) != 0 ) |
|||
{ |
|||
return(retjson); |
|||
} |
|||
else if ( retstr != 0 ) |
|||
{ |
|||
fprintf(stderr,"get_rawtransaction.(%s) error.(%s)\n",acname,retstr); |
|||
free(retstr); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
void gatewaysmarkdone(char *acname,bits256 txid) |
|||
{ |
|||
char str[65],*retstr; cJSON *retjson; |
|||
printf("spend %s %s/v2 as marker\n",acname,bits256_str(str,txid)); |
|||
if ( (retjson= get_komodocli(&retstr,acname,"gatewaysmarkdone",bits256_str(str,txid),"","")) != 0 ) |
|||
{ |
|||
komodobroadcast(acname,retjson); |
|||
free_json(retjson); |
|||
} |
|||
else if ( retstr != 0 ) |
|||
{ |
|||
printf("error parsing gatewaysmarkdone.(%s)\n",retstr); |
|||
free(retstr); |
|||
} |
|||
} |
|||
|
|||
int32_t tx_has_voutaddress(char *acname,bits256 txid,char *coinaddr) |
|||
{ |
|||
cJSON *txobj,*vouts,*vout,*sobj,*addresses; char *addr,str[65]; int32_t i,j,n,numvouts,retval = 0; |
|||
if ( (txobj= get_rawtransaction(acname,txid)) != 0 ) |
|||
{ |
|||
if ( (vouts= jarray(&numvouts,txobj,"vout")) != 0 ) |
|||
{ |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
vout = jitem(vouts,i); |
|||
if ( (sobj= jobj(vout,"scriptPubKey")) != 0 ) |
|||
{ |
|||
if ( (addresses= jarray(&n,sobj,"addresses")) != 0 ) |
|||
{ |
|||
for (j=0; j<n; j++) |
|||
{ |
|||
addr = jstri(addresses,j); |
|||
if ( strcmp(addr,coinaddr) == 0 ) |
|||
{ |
|||
//fprintf(stderr,"found %s in %s v%d\n",coinaddr,bits256_str(str,txid),i);
|
|||
retval = 1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
free_json(txobj); |
|||
} |
|||
return(retval); |
|||
} |
|||
|
|||
int32_t coinaddrexists(char *acname,char *coinaddr) |
|||
{ |
|||
cJSON *array; bits256 txid; int32_t i,n,num=0; |
|||
if ( (array= get_addressutxos(acname,coinaddr)) != 0 ) |
|||
{ |
|||
num = cJSON_GetArraySize(array); |
|||
free_json(array); |
|||
} else return(-1); |
|||
if ( num == 0 ) |
|||
{ |
|||
if ( (array= get_rawmempool(acname)) != 0 ) |
|||
{ |
|||
if ( (n= cJSON_GetArraySize(array)) != 0 ) |
|||
{ |
|||
for (i=0; i<n; i++) |
|||
{ |
|||
txid = jbits256i(array,i); |
|||
if ( tx_has_voutaddress(acname,txid,coinaddr) > 0 ) |
|||
{ |
|||
num = 1; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
free_json(array); |
|||
} else return(-1); |
|||
} |
|||
return(num); |
|||
} |
|||
|
|||
void update_gatewayspending(char *acname,char *oraclestxidstr,char *coin) |
|||
{ |
|||
// check queue to prevent duplicate
|
|||
// check KMD chain and mempool for txidaddr
|
|||
// if txidaddr exists properly, spend the marker (txid.2)
|
|||
// create withdraw tx and sign it
|
|||
/// if enough sigs, sendrawtransaction and when it confirms spend marker (txid.2)
|
|||
/// if not enough sigs, post partially signed to acname with marker2
|
|||
// monitor marker2, for the partially signed withdraws
|
|||
cJSON *retjson,*pending,*item; char str[65],*coinstr,*txidaddr,*signeraddr,*withdrawaddr; int32_t i,n,retval,processed = 0; bits256 txid,withtxid,origtxid; int64_t satoshis; |
|||
if ( (retjson= get_gatewayspending(acname,oraclestxidstr,coin)) != 0 ) |
|||
{ |
|||
if ( jint(retjson,"queueflag") != 0 && (coinstr= jstr(retjson,"coin")) != 0 && strcmp(coinstr,coin) == 0 ) |
|||
{ |
|||
if ( (pending= jarray(&n,retjson,"pending")) != 0 ) |
|||
{ |
|||
for (i=0; i<n; i++) |
|||
{ |
|||
if ( processed != 0 ) // avoid out of utxo conditions
|
|||
break; |
|||
item = jitem(pending,i); |
|||
origtxid = jbits256(item,"txid"); |
|||
//process item.0 {"txid":"10ec8f4dad6903df6b249b361b879ac77b0617caad7629b97e10f29fa7e99a9b","txidaddr":"RMbite4TGugVmkGmu76ytPHDEQZQGSUjxz","withdrawaddr":"RNJmgYaFF5DbnrNUX6pMYz9rcnDKC2tuAc","amount":"1.00000000","depositaddr":"RHV2As4rox97BuE3LK96vMeNY8VsGRTmBj","signeraddr":"RHV2As4rox97BuE3LK96vMeNY8VsGRTmBj"}
|
|||
if ( (txidaddr= jstr(item,"txidaddr")) != 0 && (withdrawaddr= jstr(item,"withdrawaddr")) != 0 && (signeraddr= jstr(item,"signeraddr")) != 0 ) |
|||
{ |
|||
if ( (satoshis= jdouble(item,"amount")*SATOSHIDEN) != 0 && (retval= coinaddrexists(acname,txidaddr)) == 0 ) |
|||
{ |
|||
// this is less errors but more expensive: ./komodo-cli z_sendmany "signeraddr" '[{"address":"<txidaddr>","amount":0.0001},{"address":"<withdrawaddr>","amount":<withamount>}]'
|
|||
txid = sendtoaddress(acname,txidaddr,10000); |
|||
if ( bits256_nonz(txid) != 0 && coinaddrexists(acname,txidaddr) > 0 ) |
|||
{ |
|||
// the actual withdraw
|
|||
withtxid = sendtoaddress(strcmp("KMD",coin)==0?"":coin,withdrawaddr,satoshis); |
|||
if ( bits256_nonz(withtxid) != 0 ) |
|||
{ |
|||
fprintf(stderr,"withdraw %s %s %s %.8f processed\n",coin,bits256_str(str,withtxid),withdrawaddr,(double)satoshis/SATOSHIDEN); |
|||
gatewaysmarkdone(acname,origtxid); |
|||
processed++; |
|||
} |
|||
else |
|||
{ |
|||
fprintf(stderr,"ERROR withdraw %s %s %s %.8f processed\n",coin,bits256_str(str,withtxid),withdrawaddr,(double)satoshis/SATOSHIDEN); |
|||
} |
|||
} else fprintf(stderr,"error sending %s txidaddr.%s -> %s exists.%d\n",acname,txidaddr,bits256_str(str,txid),coinaddrexists(acname,txidaddr)); |
|||
} |
|||
else if ( retval > 0 ) |
|||
{ |
|||
fprintf(stderr,"already did withdraw %s %s %.8f processed\n",coin,withdrawaddr,(double)satoshis/SATOSHIDEN); |
|||
gatewaysmarkdone(acname,origtxid); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
free_json(retjson); |
|||
} |
|||
} |
|||
|
|||
int32_t get_oracledata(int32_t prevheight,char *hexstr,int32_t maxsize,char *format) |
|||
{ |
|||
int32_t i; uint32_t height; uint64_t price; bits256 blockhash,merkleroot; |
|||
hexstr[0] = 0; |
|||
if ( format[0] == 'L' || format[0] == 'l' ) |
|||
{ |
|||
if ( (price= get_btcusd()) != 0 ) |
|||
{ |
|||
for (i=0; i<8; i++) |
|||
sprintf(&hexstr[i*2],"%02x",(uint8_t)((price >> (i*8)) & 0xff)); |
|||
hexstr[16] = 0; |
|||
return(16); |
|||
} |
|||
} |
|||
else if ( strcmp(format,"Ihh") == 0 ) |
|||
{ |
|||
if ( (height= get_KMDheader(&blockhash,&merkleroot,prevheight)) > prevheight ) |
|||
{ |
|||
for (i=0; i<4; i++) |
|||
sprintf(&hexstr[i*2],"%02x",(uint8_t)((height >> (i*8)) & 0xff)); |
|||
for (i=31; i>=0; i--) |
|||
sprintf(&hexstr[8 + (31-i)*2],"%02x",blockhash.bytes[i]); |
|||
for (i=31; i>=0; i--) |
|||
sprintf(&hexstr[8 + 64 + (31-i)*2],"%02x",merkleroot.bytes[i]); |
|||
hexstr[8 + 64*2] = 0; |
|||
return(height); |
|||
} |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
/*
|
|||
oraclescreate "BTCUSD" "coindeskpricedata" "L" -> 4895f631316a649e216153aee7a574bd281686265dc4e8d37597f72353facac3 |
|||
oraclesregister 4895f631316a649e216153aee7a574bd281686265dc4e8d37597f72353facac3 1000000 -> 11c54d4ab17293217276396e27d86f714576ff55a3300dac34417047825edf93 |
|||
oraclessubscribe 4895f631316a649e216153aee7a574bd281686265dc4e8d37597f72353facac3 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 1.5 -> ce4e4afa53765b11a74543dacbd3174a93f33f12bb94cdc080c2c023726b5838 |
|||
oraclesdata 4895f631316a649e216153aee7a574bd281686265dc4e8d37597f72353facac3 000000ff00000000 -> e8a8c897e97389dcac31d81b617ab73a829110bd5c6f99f9f533b9c0e22700d0 |
|||
oraclessamples 4895f631316a649e216153aee7a574bd281686265dc4e8d37597f72353facac3 90ff8813a93b5b2615ec43974ff4fc91e4373dfd672d995676c43ff2dcda1010 10 -> |
|||
{ |
|||
"result": "success", |
|||
"samples": [ |
|||
[ |
|||
"4278190080" |
|||
] |
|||
] |
|||
} |
|||
|
|||
oraclescreate test testsformat s -> 17a841a919c284cea8a676f34e793da002e606f19a9258a3190bed12d5aaa3ff |
|||
oraclesregister -> 7825ad75ba854ab12868f7d2e06b4061903687fe93f41a2a99202a6b9ca3c029 |
|||
oraclessubscribe 17a841a919c284cea8a676f34e793da002e606f19a9258a3190bed12d5aaa3ff 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 1.5 -> faf8a6676f6389abad9e7f397015d200395c9f8a24c4ded291d83e6265b2f4d1 |
|||
oraclesdata 17a841a919c284cea8a676f34e793da002e606f19a9258a3190bed12d5aaa3ff 03404040 -> e8a8c897e97389dcac31d81b617ab73a829110bd5c6f99f9f533b9c0e22700d0 |
|||
|
|||
*/ |
|||
|
|||
// ./a.out AT5 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 Ihh e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e
|
|||
|
|||
int32_t main(int32_t argc,char **argv) |
|||
{ |
|||
cJSON *clijson,*clijson2,*regjson,*item; int32_t acheight,i,retval,n,height,prevheight = 0; char *format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,hexstr[4096]; uint64_t price; bits256 txid; |
|||
if ( argc != 6 ) |
|||
{ |
|||
printf("usage: oraclefeed $ACNAME $ORACLETXID $MYPUBKEY $FORMAT $BINDTXID\nPowered by CoinDesk (%s) %.8f\n","https://www.coindesk.com/price/",dstr(get_btcusd())); |
|||
return(-1); |
|||
} |
|||
acname = argv[1]; |
|||
oraclestr = argv[2]; |
|||
pkstr = argv[3]; |
|||
format = argv[4]; |
|||
bindtxidstr = argv[5]; |
|||
if ( strncmp(format,"Ihh",3) != 0 && format[0] != 'L' ) |
|||
{ |
|||
printf("only formats of L and Ihh are supported now\n"); |
|||
return(-1); |
|||
} |
|||
acheight = 0; |
|||
while ( 1 ) |
|||
{ |
|||
retstr = 0; |
|||
if ( prevheight < (get_KMDheight("") - 10) && (clijson= get_komodocli(&retstr,acname,"oraclesinfo",oraclestr,"","")) != 0 ) |
|||
{ |
|||
if ( (regjson= jarray(&n,clijson,"registered")) != 0 ) |
|||
{ |
|||
for (i=0; i<n; i++) |
|||
{ |
|||
item = jitem(regjson,i); |
|||
if ( (pubstr= jstr(item,"publisher")) != 0 && strcmp(pkstr,pubstr) == 0 ) |
|||
{ |
|||
if ( (height= get_oracledata(prevheight,hexstr,sizeof(hexstr),"Ihh")) != 0 ) |
|||
{ |
|||
if ( (clijson2= get_komodocli(&retstr2,acname,"oraclesdata",oraclestr,hexstr,"")) != 0 ) |
|||
{ |
|||
//printf("data.(%s)\n",jprint(clijson2,0));
|
|||
txid = komodobroadcast(acname,clijson2); |
|||
if ( bits256_nonz(txid) != 0 ) |
|||
{ |
|||
prevheight = height; |
|||
acheight = get_KMDheight(acname); |
|||
printf("ht.%d <- %s\n",height,hexstr); |
|||
update_gatewayspending(acname,bindtxidstr,"KMD"); |
|||
} |
|||
free_json(clijson2); |
|||
} |
|||
else if ( retstr2 != 0 ) |
|||
{ |
|||
printf("error parsing oraclesdata.(%s)\n",retstr2); |
|||
free(retstr2); |
|||
} |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
free_json(clijson); |
|||
} |
|||
if ( retstr != 0 ) |
|||
{ |
|||
printf("got json parse error.(%s)\n",retstr); |
|||
free(retstr); |
|||
} |
|||
sleep(10); |
|||
// best check is for txid to not be in mempool, ie confirmed
|
|||
} |
|||
return(0); |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,84 @@ |
|||
#include <cryptoconditions.h> |
|||
|
|||
#include "hash.h" |
|||
#include "chain.h" |
|||
#include "version.h" |
|||
#include "script/cc.h" |
|||
#include "cc/eval.h" |
|||
#include "cc/betprotocol.h" |
|||
#include "primitives/transaction.h" |
|||
|
|||
|
|||
/*
|
|||
* Crypto-Condition EVAL method that resolves a dispute of a session |
|||
* |
|||
* IN: vm - AppVM virtual machine to verify states |
|||
* IN: params - condition params |
|||
* IN: disputeTx - transaction attempting to resolve dispute |
|||
* IN: nIn - index of input of dispute tx |
|||
* |
|||
* disputeTx: attempt to resolve a dispute |
|||
* |
|||
* in 0: Spends Session TX first output, reveals DisputeHeader |
|||
* out 0: OP_RETURN hash of payouts |
|||
*/ |
|||
bool Eval::DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn) |
|||
{ |
|||
if (disputeTx.vout.size() == 0) return Invalid("no-vouts"); |
|||
|
|||
// get payouts hash
|
|||
uint256 payoutHash; |
|||
if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash)) |
|||
return Invalid("invalid-payout-hash"); |
|||
|
|||
// load params
|
|||
uint16_t waitBlocks; |
|||
std::vector<uint8_t> vmParams; |
|||
if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams)) |
|||
return Invalid("malformed-params"); |
|||
|
|||
// ensure that enough time has passed
|
|||
{ |
|||
CTransaction sessionTx; |
|||
CBlockIndex sessionBlock; |
|||
|
|||
// if unconformed its too soon
|
|||
if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock)) |
|||
return Error("couldnt-get-parent"); |
|||
|
|||
if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks) |
|||
return Invalid("dispute-too-soon"); // Not yet
|
|||
} |
|||
|
|||
// get spends
|
|||
std::vector<CTransaction> spends; |
|||
if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends)) |
|||
return Error("couldnt-get-spends"); |
|||
|
|||
// verify result from VM
|
|||
int maxLength = -1; |
|||
uint256 bestPayout; |
|||
for (int i=1; i<spends.size(); i++) |
|||
{ |
|||
std::vector<unsigned char> vmState; |
|||
if (spends[i].vout.size() == 0) continue; |
|||
if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue; |
|||
auto out = vm.evaluate(vmParams, vmState); |
|||
uint256 resultHash = SerializeHash(out.second); |
|||
if (out.first > maxLength) { |
|||
maxLength = out.first; |
|||
bestPayout = resultHash; |
|||
} |
|||
// The below means that if for any reason there is a draw, the first dispute wins
|
|||
else if (out.first == maxLength) { |
|||
if (bestPayout != payoutHash) { |
|||
fprintf(stderr, "WARNING: VM has multiple solutions of same length\n"); |
|||
bestPayout = resultHash; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (maxLength == -1) return Invalid("no-evidence"); |
|||
|
|||
return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout"); |
|||
} |
@ -0,0 +1,838 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#include "CCGateways.h" |
|||
|
|||
/*
|
|||
prevent duplicate bindtxid via mempool scan |
|||
wait for notarization for oraclefeed and validation of gatewaysdeposit |
|||
|
|||
validation |
|||
|
|||
string oracles |
|||
*/ |
|||
|
|||
/*
|
|||
Uses MofN CC's normal msig handling to create automated deposits -> token issuing. And partial signing by the selected pubkeys for releasing the funds. A user would be able to select which pubkeys to use to construct the automated deposit/redeem multisigs. |
|||
|
|||
the potential pubkeys to be used would be based on active oracle data providers with recent activity. |
|||
|
|||
bind asset <-> KMD gateway deposit address |
|||
KMD deposit -> globally spendable marker utxo |
|||
spend marker utxo and spend linked/locked asset to user's CC address |
|||
|
|||
redeem -> asset to global CC address with withdraw address -> gateway spendable marker utxo |
|||
spend market utxo and withdraw from gateway deposit address |
|||
|
|||
rpc calls: |
|||
GatewayList |
|||
GatewayInfo bindtxid |
|||
GatewayBind coin tokenid M N pubkey(s) |
|||
external: deposit to depositaddr with claimpubkey |
|||
GatewayDeposit coin tokenid external.deposittxid -> markertxid |
|||
GatewayClaim coin tokenid external.deposittxid markertxid -> spend marker and deposit asset |
|||
|
|||
GatewayWithdraw coin tokenid withdrawaddr |
|||
external: do withdraw to withdrawaddr and spend marker, support for partial signatures and autocomplete |
|||
|
|||
deposit addr can be 1 to MofN pubkeys |
|||
1:1 gateway with native coin |
|||
|
|||
In order to create a new gateway it is necessary to follow some strict steps. |
|||
1. create a token with the max possible supply that will be issued |
|||
2. transfer 100% of them to the gateways CC's global pubkey's asset CC address. (yes it is a bit confusing) |
|||
3. create an oracle with the identical name, ie. KMD and format must start with Ihh (height, blockhash, merkleroot) |
|||
4. register a publisher and fund it with a subscribe. there will be a special client app that will automatically publish the merkleroots. |
|||
5. Now a gatewaysbind can bind an external coin to an asset, along with the oracle for the merkleroots. the txid from the bind is used in most of the other gateways CC calls |
|||
|
|||
usage: |
|||
./c tokencreate KMD 1000000 KMD_equivalent_token_for_gatewaysCC |
|||
a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a |
|||
|
|||
transfer to gateways pubkey: 03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40 RDMqGyREkP1Gwub1Nr5Ye8a325LGZsWBCb |
|||
./c tokentransfer a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a 03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40 100000000000000 |
|||
2206fc39c0f384ca79819eb491ddbf889642cbfe4d0796bb6a8010ed53064a56 |
|||
|
|||
./c oraclescreate KMD blockheaders Ihh |
|||
1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 |
|||
|
|||
./c oraclesregister 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 1000000 |
|||
83b59eac238cbe54616ee13b2fdde85a48ec869295eb04051671a1727c9eb402 |
|||
|
|||
./c oraclessubscribe 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 1000 |
|||
f9499d8bb04ffb511fcec4838d72e642ec832558824a2ce5aed87f1f686f8102 |
|||
|
|||
./c gatewaysbind a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 KMD 100000000000000 1 1 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 |
|||
e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e |
|||
|
|||
./c gatewaysinfo e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e |
|||
{ |
|||
"result": "success", |
|||
"name": "Gateways", |
|||
"pubkey": "02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92", |
|||
"coin": "KMD", |
|||
"oracletxid": "1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808", |
|||
"taddr": 0, |
|||
"prefix": 60, |
|||
"prefix2": 85, |
|||
"deposit": "", |
|||
"tokenid": "a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a", |
|||
"totalsupply": "1000000.00000000", |
|||
"remaining": "1000000.00000000", |
|||
"issued": "0.00000000" |
|||
} |
|||
|
|||
To make a gateway deposit, send the funds to the "deposit" address, along with any amount to the same pubkey address you want to get the assetized KMD to appear in. |
|||
|
|||
0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 pubkey for RFpxgqff7FDHFuHa3jSX5NzqqWCcELz8ha |
|||
./komodo-cli z_sendmany "<funding addr>" '[{"address":"RFpxgqff7FDHFuHa3jSX5NzqqWCcELz8ha","amount":0.0001},{"address":"RHV2As4rox97BuE3LK96vMeNY8VsGRTmBj","amount":7.6999}]' |
|||
bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 height.1003776 merkle.90aedc2f19200afc9aca2e351438d011ebae8264a58469bf225883045f61917f |
|||
|
|||
./komodo-cli gettxoutproof '["bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009"]' |
|||
04000000232524ea04b54489eb222f8b3f566ed35504e3050488a63f0ab7b1c2030000007f91615f04835822bf6984a56482aeeb11d03814352eca9afc0a20192fdcae900000000000000000000000000000000000000000000000000000000000000000268e965ba608071d0800038e16762900000000000000000000000000000000000000000042008dd6fd4005016b4292b05df6dfd316c458c53e28fb2befb96e4870a40c6c04e733d75d8a7a18cce34fe326005efdc403bfa4644e30eeafdaeff34419edc591299e6cc5933cb2eeecbab5c4dfe97cd413b75215999a3dd02b540373581e81d8512bff1640590a6b4da4aaa9b8adc0102c38ca0022daed997b53ed192ba326e212fba5e505ce29e3ad149cef7f48d0e00948a1acd81731d84008760759211eb4abbc7b037939a7964182edb59cf9065357e864188ee5fc7316e8796963036bb99eeb9f06c95d64f78749ecec7181c12eb5d83a3b9b1c1e8a0aae9a20ce04a250b28216620bfc99bb81a6de4db80b93a5aea916de97c1a272e26644abdd683f19c5e3174a2e4513ed767d8f11a4c3074295f697839c5d9139676a813451cc7da38f68cbae5d990a79075f98903233ca04fe1b4b099e433585e5adcc45d41d54a9c648179297359c75950a5e574f13f70b728bbbf552770256315cd0a00139d6ab6934cb5ed70a4fc01a92611b096dd0028f17f4cc687b75f37dca530aa47a18321c50528dbd9272eabb3e13a87021a05918a6d2627e2caba6d7cf1a9f0b831ea3337b9a6af92746d83140078d60c72b6beacf91c9e68a34cee209e08670be1d17ff8d80b7a2285b1325461a2e33f2ee675593f1900e066a5d212615cd8da18749b0e684eee73edcc9031709be715b889c6d015cf4bd4ad5ab7e21bd3492c208930a54d353ef36a437f507ead38855633c1b88d060d9e4221ca8ce2f698e8a6ae0d41e9ace3cbd401f1e0f07650e9c126d4ef20278c8be6e85c7637513643f8d02d7ad64c09da11c16429d60e5160c345844b8158ece62794e8ad280d4e4664150e74978609ece431e51a9f9e1ce8aa49c16f36c7fd12b71acc42d893e18476b8b1e144a8175519612efc93e0aecc61f3b21212c958b0e2331d76aaa62faf11a58fe2bd91ab9ab01b906406c9bbc02df2a106e67182aae0a20b538bf19f09c57f9de5e198ba254580fb1b11e22ad526550093420cb7c68628d4c3ad329c8acc6e219093d277810ed016b6099b7e3781de412a22dacedaa2acf29e8062debcd85c7b9529a20b2782a2470763ac27cf89611a527d43ac89b8063ffb93b6ed993425194f8ee821a8493a563072c896f9584f95db28e3f2fc5fb4a6f3c39d615cd563641717cd50afb73ed3989cbf504b2043882993ce9575f56402534173b1396fbc13df80920b46788ae340ad5a91f25177cc74aa69024d76f56166199d2e4d50a053555256c4e3137ea1cee1130e916a88b6ee5cf2c85652fb8824d5dacfa485e3ef6190591ac0c2fcacc4fc7deb65aca4b0b89b76e35a46b0627e2e967cc63a5d606a984c8e63eabb98fde3e69114340ae524c974cb936e57690e98a7a74533f6f7d1d0496976496b54d14a8163efb32b70dfbb79d80a3022c4f53571c08bf044270565716b435084376714b224ab23e9817c05af8223723afc0577af5c8fc28f71036ca82528aaa4ca9bcd18a50e25d2a528f183d3a2074d968d170876d8dce434c5937261b55173ab87e03d5632ca0834fdc5387c15ab3a17d75c0f274004f289ff1bf7d14e97fdf4172eb49adfb418cc2f4794806ae7c0111c97df4d65d38679ec93fea3ef738ed565e8906a8fe1861cafe3938c772fedcfab40159938e06ef414fd299f2355c6d3369bc1bd3c4db64ce205f0a1b70a40030f505b736e28230de82e97776b5ee7b10708bb3020d28cec7a8e124549ec80c547ac4e7b52bf397c72bcfce30820554ab8fb4d1f73b209bc32a0e7e878843cdbf5f01222728ccea7e6ab7cb5e3fee3234f5b85d1985f91492f6ceaa6454a658dab5074f163ce26ed753137fa61c940679de13bd7b212cd3cf2b334f5201cecbc7473342bd7a239e09169bccd56d03000000037a9068df0625e548e71263c8361b4e904c998378f6b9e32729c3f19b10ad752e093013788222f5c26bfc5da4eeb7d32f01486a54179f19c341b79d420ea041bc8878d22fad4692b2d609c3cf190903874d3682a714c7483518c9392e07c25035010b |
|||
|
|||
./komodo-cli getrawtransaction bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 |
|||
010000000149964cdcd17fe9b1cae4d0f3b5f5db301d9b4f54099fdf4d34498df281757094010000006a4730440220594f3a630dd73c123f44621aa8bb9968ab86734833453dd479af6d79ae6f584202207bb5e35f13b337ccc8a88d9a006c8c5ddb016c0a6f4f2dc44357a8128623d85d01210223154bf53cd3a75e64d86697070d6437c8f0010a09c1df35b659e31ce3d79b5dffffffff0310270000000000001976a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac701de52d000000001976a91459fdba29ea85c65ad90f6d38f7a6646476b26b1688acb0a86a00000000001976a914f9a9daf5519dae38b8b61d945f075da895df441d88ace18d965b |
|||
|
|||
gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof destpub amount |
|||
./komodo-cli -ac_name=AT5 gatewaysdeposit e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e 1003776 KMD bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 0 010000000149964cdcd17fe9b1cae4d0f3b5f5db301d9b4f54099fdf4d34498df281757094010000006a4730440220594f3a630dd73c123f44621aa8bb9968ab86734833453dd479af6d79ae6f584202207bb5e35f13b337ccc8a88d9a006c8c5ddb016c0a6f4f2dc44357a8128623d85d01210223154bf53cd3a75e64d86697070d6437c8f0010a09c1df35b659e31ce3d79b5dffffffff0310270000000000001976a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac701de52d000000001976a91459fdba29ea85c65ad90f6d38f7a6646476b26b1688acb0a86a00000000001976a914f9a9daf5519dae38b8b61d945f075da895df441d88ace18d965b 04000000232524ea04b54489eb222f8b3f566ed35504e3050488a63f0ab7b1c2030000007f91615f04835822bf6984a56482aeeb11d03814352eca9afc0a20192fdcae900000000000000000000000000000000000000000000000000000000000000000268e965ba608071d0800038e16762900000000000000000000000000000000000000000042008dd6fd4005016b4292b05df6dfd316c458c53e28fb2befb96e4870a40c6c04e733d75d8a7a18cce34fe326005efdc403bfa4644e30eeafdaeff34419edc591299e6cc5933cb2eeecbab5c4dfe97cd413b75215999a3dd02b540373581e81d8512bff1640590a6b4da4aaa9b8adc0102c38ca0022daed997b53ed192ba326e212fba5e505ce29e3ad149cef7f48d0e00948a1acd81731d84008760759211eb4abbc7b037939a7964182edb59cf9065357e864188ee5fc7316e8796963036bb99eeb9f06c95d64f78749ecec7181c12eb5d83a3b9b1c1e8a0aae9a20ce04a250b28216620bfc99bb81a6de4db80b93a5aea916de97c1a272e26644abdd683f19c5e3174a2e4513ed767d8f11a4c3074295f697839c5d9139676a813451cc7da38f68cbae5d990a79075f98903233ca04fe1b4b099e433585e5adcc45d41d54a9c648179297359c75950a5e574f13f70b728bbbf552770256315cd0a00139d6ab6934cb5ed70a4fc01a92611b096dd0028f17f4cc687b75f37dca530aa47a18321c50528dbd9272eabb3e13a87021a05918a6d2627e2caba6d7cf1a9f0b831ea3337b9a6af92746d83140078d60c72b6beacf91c9e68a34cee209e08670be1d17ff8d80b7a2285b1325461a2e33f2ee675593f1900e066a5d212615cd8da18749b0e684eee73edcc9031709be715b889c6d015cf4bd4ad5ab7e21bd3492c208930a54d353ef36a437f507ead38855633c1b88d060d9e4221ca8ce2f698e8a6ae0d41e9ace3cbd401f1e0f07650e9c126d4ef20278c8be6e85c7637513643f8d02d7ad64c09da11c16429d60e5160c345844b8158ece62794e8ad280d4e4664150e74978609ece431e51a9f9e1ce8aa49c16f36c7fd12b71acc42d893e18476b8b1e144a8175519612efc93e0aecc61f3b21212c958b0e2331d76aaa62faf11a58fe2bd91ab9ab01b906406c9bbc02df2a106e67182aae0a20b538bf19f09c57f9de5e198ba254580fb1b11e22ad526550093420cb7c68628d4c3ad329c8acc6e219093d277810ed016b6099b7e3781de412a22dacedaa2acf29e8062debcd85c7b9529a20b2782a2470763ac27cf89611a527d43ac89b8063ffb93b6ed993425194f8ee821a8493a563072c896f9584f95db28e3f2fc5fb4a6f3c39d615cd563641717cd50afb73ed3989cbf504b2043882993ce9575f56402534173b1396fbc13df80920b46788ae340ad5a91f25177cc74aa69024d76f56166199d2e4d50a053555256c4e3137ea1cee1130e916a88b6ee5cf2c85652fb8824d5dacfa485e3ef6190591ac0c2fcacc4fc7deb65aca4b0b89b76e35a46b0627e2e967cc63a5d606a984c8e63eabb98fde3e69114340ae524c974cb936e57690e98a7a74533f6f7d1d0496976496b54d14a8163efb32b70dfbb79d80a3022c4f53571c08bf044270565716b435084376714b224ab23e9817c05af8223723afc0577af5c8fc28f71036ca82528aaa4ca9bcd18a50e25d2a528f183d3a2074d968d170876d8dce434c5937261b55173ab87e03d5632ca0834fdc5387c15ab3a17d75c0f274004f289ff1bf7d14e97fdf4172eb49adfb418cc2f4794806ae7c0111c97df4d65d38679ec93fea3ef738ed565e8906a8fe1861cafe3938c772fedcfab40159938e06ef414fd299f2355c6d3369bc1bd3c4db64ce205f0a1b70a40030f505b736e28230de82e97776b5ee7b10708bb3020d28cec7a8e124549ec80c547ac4e7b52bf397c72bcfce30820554ab8fb4d1f73b209bc32a0e7e878843cdbf5f01222728ccea7e6ab7cb5e3fee3234f5b85d1985f91492f6ceaa6454a658dab5074f163ce26ed753137fa61c940679de13bd7b212cd3cf2b334f5201cecbc7473342bd7a239e09169bccd56d03000000037a9068df0625e548e71263c8361b4e904c998378f6b9e32729c3f19b10ad752e093013788222f5c26bfc5da4eeb7d32f01486a54179f19c341b79d420ea041bc8878d22fad4692b2d609c3cf190903874d3682a714c7483518c9392e07c25035010b 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 7.6999 |
|||
-> 9d80ea79a65aaa0d464f8b762356fa01047e16e9793505a22ca04559f81a6eb6 |
|||
|
|||
to get the merkleroots onchain, from the multisig signers nodes run the oraclefeed program with acname oracletxid pubkey Ihh |
|||
./oraclefeed AT5 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 Ihh |
|||
|
|||
gatewaysclaim bindtxid coin deposittxid destpub amount |
|||
./c gatewaysclaim e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e KMD 9d80ea79a65aaa0d464f8b762356fa01047e16e9793505a22ca04559f81a6eb6 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 7.6999 |
|||
|
|||
now the asset is in the pubkey's asset address! |
|||
it can be used, traded freely and any node who has the asset can do a gatewayswithdraw |
|||
|
|||
gatewayswithdraw bindtxid coin withdrawpub amount |
|||
./c gatewayswithdraw e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e KMD 03b7621b44118017a16043f19b30cc8a4cfe068ac4e42417bae16ba460c80f3828 1 |
|||
ef3cc452da006eb2edda6b6ed3d3347664be51260f3e91f59ec44ec9701367f0 |
|||
|
|||
Now there is a withdraw pending, so it needs to be processed by the signing nodes on the KMD side |
|||
|
|||
gatewayspending bindtxid coin |
|||
gatewayspending will display all pending withdraws and if it is done on one of the msigpubkeys, then it will queue it for processing |
|||
./c gatewayspending e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e KMD |
|||
|
|||
*/ |
|||
|
|||
|
|||
int32_t GatewaysAddQueue(std::string coin,uint256 txid,CScript scriptPubKey,int64_t nValue) |
|||
{ |
|||
char destaddr[64],str[65]; |
|||
Getscriptaddress(destaddr,scriptPubKey); |
|||
fprintf(stderr,"GatewaysAddQueue: %s %s %s %.8f\n",coin.c_str(),uint256_str(str,txid),destaddr,(double)nValue/COIN); |
|||
} |
|||
|
|||
// start of consensus code
|
|||
|
|||
CScript EncodeGatewaysBindOpRet(uint8_t funcid,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector<CPubKey> pubkeys,uint8_t taddr,uint8_t prefix,uint8_t prefix2) |
|||
{ |
|||
CScript opret; uint8_t evalcode = EVAL_GATEWAYS; |
|||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << coin << prefix << prefix2 << taddr << tokenid << totalsupply << M << N << pubkeys << oracletxid); |
|||
return(opret); |
|||
} |
|||
|
|||
CScript EncodeGatewaysOpRet(uint8_t funcid,std::string coin,uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256>txids,int32_t height,uint256 cointxid,std::string deposithex,std::vector<uint8_t>proof,CPubKey destpub,int64_t amount) |
|||
{ |
|||
CScript opret; uint8_t evalcode = EVAL_GATEWAYS; |
|||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << coin << bindtxid << publishers << txids << height << cointxid << deposithex << proof << destpub << amount); |
|||
return(opret); |
|||
} |
|||
|
|||
uint8_t DecodeGatewaysOpRet(const CScript &scriptPubKey,std::string &coin,uint256 &bindtxid,std::vector<CPubKey>&publishers,std::vector<uint256>&txids,int32_t &height,uint256 &cointxid,std::string &deposithex,std::vector<uint8_t> &proof,CPubKey &destpub,int64_t &amount) |
|||
{ |
|||
std::vector<uint8_t> vopret; uint8_t *script,e,f; |
|||
GetOpReturnData(scriptPubKey, vopret); |
|||
script = (uint8_t *)vopret.data(); |
|||
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> bindtxid; ss >> publishers; ss >> txids; ss >> height; ss >> cointxid; ss >> deposithex; ss >> proof; ss >> destpub; ss >> amount) != 0 ) |
|||
{ |
|||
return(f); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,std::string &coin,uint256 &tokenid,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector<CPubKey> &pubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2) |
|||
{ |
|||
std::vector<uint8_t> vopret; uint8_t *script,e,f; |
|||
GetOpReturnData(scriptPubKey, vopret); |
|||
script = (uint8_t *)vopret.data(); |
|||
depositaddr[0] = 0; |
|||
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> prefix; ss >> prefix2; ss >> taddr; ss >> tokenid; ss >> totalsupply; ss >> M; ss >> N; ss >> pubkeys; ss >> oracletxid) != 0 ) |
|||
{ |
|||
if ( prefix == 60 ) |
|||
{ |
|||
if ( N > 1 ) |
|||
Getscriptaddress(depositaddr,GetScriptForMultisig(M,pubkeys)); |
|||
else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(pubkeys[0])) << OP_CHECKSIG); |
|||
} |
|||
else |
|||
{ |
|||
fprintf(stderr,"need to generate non-KMD addresses prefix.%d\n",prefix); |
|||
} |
|||
return(f); |
|||
} else fprintf(stderr,"error decoding bind opret\n"); |
|||
return(0); |
|||
} |
|||
|
|||
int64_t IsGatewaysvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) |
|||
{ |
|||
char destaddr[64]; |
|||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) |
|||
{ |
|||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) |
|||
return(tx.vout[v].nValue); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
bool GatewaysExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) |
|||
{ |
|||
static uint256 zerohash; |
|||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
//fprintf(stderr,"vini.%d\n",i);
|
|||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
|||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) |
|||
return eval->Invalid("cant find vinTx"); |
|||
else |
|||
{ |
|||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
|||
if ( hashBlock == zerohash ) |
|||
return eval->Invalid("cant Gateways from mempool"); |
|||
if ( (assetoshis= IsGatewaysvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) |
|||
inputs += assetoshis; |
|||
} |
|||
} |
|||
} |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
|||
if ( (assetoshis= IsGatewaysvout(cp,tx,i)) != 0 ) |
|||
outputs += assetoshis; |
|||
} |
|||
if ( inputs != outputs+txfee ) |
|||
{ |
|||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); |
|||
return eval->Invalid("mismatched inputs != outputs + txfee"); |
|||
} |
|||
else return(true); |
|||
} |
|||
|
|||
bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx) |
|||
{ |
|||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids; |
|||
fprintf(stderr,"return true without gateways validation\n"); |
|||
return(true); |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
preventCCvins = preventCCvouts = -1; |
|||
if ( numvouts < 1 ) |
|||
return eval->Invalid("no vouts"); |
|||
else |
|||
{ |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) |
|||
{ |
|||
return eval->Invalid("illegal normal vini"); |
|||
} |
|||
} |
|||
//fprintf(stderr,"check amounts\n");
|
|||
if ( GatewaysExactAmounts(cp,eval,tx,1,10000) == false ) |
|||
{ |
|||
fprintf(stderr,"Gatewaysget invalid amount\n"); |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
txid = tx.GetHash(); |
|||
memcpy(hash,&txid,sizeof(hash)); |
|||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); |
|||
if ( retval != 0 ) |
|||
fprintf(stderr,"Gatewaysget validated\n"); |
|||
else fprintf(stderr,"Gatewaysget invalid\n"); |
|||
return(retval); |
|||
} |
|||
} |
|||
} |
|||
// end of consensus code
|
|||
|
|||
// helper functions for rpc calls in rpcwallet.cpp
|
|||
|
|||
int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) |
|||
{ |
|||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
GetCCaddress(cp,coinaddr,pk); |
|||
SetCCunspents(unspentOutputs,coinaddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
// no need to prevent dup
|
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( (nValue= IsGatewaysvout(cp,vintx,vout)) > 10000 && myIsutxo_spentinmempool(txid,vout) == 0 ) |
|||
{ |
|||
if ( total != 0 && maxinputs != 0 ) |
|||
mtx.vin.push_back(CTxIn(txid,vout,CScript())); |
|||
nValue = it->second.satoshis; |
|||
totalinputs += nValue; |
|||
n++; |
|||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return(totalinputs); |
|||
} |
|||
|
|||
int32_t GatewaysBindExists(struct CCcontract_info *cp,CPubKey gatewayspk,uint256 reftokenid) // dont forget to check mempool!
|
|||
{ |
|||
char markeraddr[64],depositaddr[64]; std::string coin; int32_t numvouts; int64_t totalsupply; uint256 tokenid,oracletxid,hashBlock; uint8_t M,N,taddr,prefix,prefix2; std::vector<CPubKey> pubkeys; CTransaction tx; |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; |
|||
_GetCCaddress(markeraddr,EVAL_GATEWAYS,gatewayspk); |
|||
fprintf(stderr,"bind markeraddr.(%s) need to scan mempool also\n",markeraddr); |
|||
SetCCtxids(addressIndex,markeraddr); |
|||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) |
|||
{ |
|||
if ( GetTransaction(it->first.txhash,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) |
|||
{ |
|||
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) == 'B' ) |
|||
{ |
|||
if ( tokenid == reftokenid ) |
|||
{ |
|||
fprintf(stderr,"trying to bind an existing tokenid\n"); |
|||
return(1); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
static int32_t myIs_coinaddr_inmempoolvout(char *coinaddr) |
|||
{ |
|||
int32_t i,n; char destaddr[64]; |
|||
BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) |
|||
{ |
|||
const CTransaction &tx = e.GetTx(); |
|||
if ( (n= tx.vout.size()) > 0 ) |
|||
{ |
|||
const uint256 &txid = tx.GetHash(); |
|||
for (i=0; i<n; i++) |
|||
{ |
|||
Getscriptaddress(destaddr,tx.vout[i].scriptPubKey); |
|||
if ( strcmp(destaddr,coinaddr) == 0 ) |
|||
{ |
|||
fprintf(stderr,"found (%s) vout in mempool\n",coinaddr); |
|||
return(1); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
int32_t GatewaysCointxidExists(struct CCcontract_info *cp,uint256 cointxid) // dont forget to check mempool!
|
|||
{ |
|||
char txidaddr[64]; std::string coin; int32_t numvouts; uint256 hashBlock; |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; |
|||
CCtxidaddr(txidaddr,cointxid); |
|||
SetCCtxids(addressIndex,txidaddr); |
|||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) |
|||
{ |
|||
return(-1); |
|||
} |
|||
return(myIs_coinaddr_inmempoolvout(txidaddr)); |
|||
} |
|||
|
|||
UniValue GatewaysInfo(uint256 bindtxid) |
|||
{ |
|||
UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::string coin; char str[67],numstr[65],depositaddr[64],gatewaysassets[64]; uint8_t M,N; std::vector<CPubKey> pubkeys; uint8_t taddr,prefix,prefix2; uint256 tokenid,oracletxid,hashBlock; CTransaction tx; CMutableTransaction mtx; CPubKey Gatewayspk; struct CCcontract_info *cp,C; int32_t i; int64_t totalsupply,remaining; |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("name","Gateways")); |
|||
cp = CCinit(&C,EVAL_GATEWAYS); |
|||
Gatewayspk = GetUnspendable(cp,0); |
|||
_GetCCaddress(gatewaysassets,EVAL_ASSETS,Gatewayspk); |
|||
if ( GetTransaction(bindtxid,tx,hashBlock,false) != 0 ) |
|||
{ |
|||
depositaddr[0] = 0; |
|||
if ( tx.vout.size() > 0 && DecodeGatewaysBindOpRet(depositaddr,tx.vout[tx.vout.size()-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 0 && M <= N && N > 0 ) |
|||
{ |
|||
if ( N > 1 ) |
|||
{ |
|||
result.push_back(Pair("M",M)); |
|||
result.push_back(Pair("N",N)); |
|||
for (i=0; i<N; i++) |
|||
a.push_back(pubkey33_str(str,(uint8_t *)&pubkeys[i])); |
|||
result.push_back(Pair("pubkeys",a)); |
|||
} else result.push_back(Pair("pubkey",pubkey33_str(str,(uint8_t *)&pubkeys[0]))); |
|||
result.push_back(Pair("coin",coin)); |
|||
result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); |
|||
result.push_back(Pair("taddr",taddr)); |
|||
result.push_back(Pair("prefix",prefix)); |
|||
result.push_back(Pair("prefix2",prefix2)); |
|||
result.push_back(Pair("deposit",depositaddr)); |
|||
result.push_back(Pair("tokenid",uint256_str(str,tokenid))); |
|||
sprintf(numstr,"%.8f",(double)totalsupply/COIN); |
|||
result.push_back(Pair("totalsupply",numstr)); |
|||
remaining = CCaddress_balance(gatewaysassets); |
|||
sprintf(numstr,"%.8f",(double)remaining/COIN); |
|||
result.push_back(Pair("remaining",numstr)); |
|||
sprintf(numstr,"%.8f",(double)(totalsupply - remaining)/COIN); |
|||
result.push_back(Pair("issued",numstr)); |
|||
} |
|||
} |
|||
return(result); |
|||
} |
|||
|
|||
UniValue GatewaysList() |
|||
{ |
|||
UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid,tokenid; CTransaction vintx; std::string coin; int64_t totalsupply; char str[65],depositaddr[64]; uint8_t M,N,taddr,prefix,prefix2; std::vector<CPubKey> pubkeys; |
|||
cp = CCinit(&C,EVAL_GATEWAYS); |
|||
SetCCtxids(addressIndex,cp->unspendableCCaddr); |
|||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( vintx.vout.size() > 0 && DecodeGatewaysBindOpRet(depositaddr,vintx.vout[vintx.vout.size()-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 0 ) |
|||
{ |
|||
result.push_back(uint256_str(str,txid)); |
|||
} |
|||
} |
|||
} |
|||
return(result); |
|||
} |
|||
|
|||
std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector<CPubKey> pubkeys) |
|||
{ |
|||
CMutableTransaction mtx; CTransaction oracletx; uint8_t taddr,prefix,prefix2; CPubKey mypk,gatewayspk; CScript opret; uint256 hashBlock; struct CCcontract_info *cp,C; std::string name,description,format; int32_t i,numvouts; int64_t fullsupply; char destaddr[64],coinaddr[64],str[65],*fstr; |
|||
cp = CCinit(&C,EVAL_GATEWAYS); |
|||
if ( N == 0 || N > 15 || M > N ) |
|||
{ |
|||
fprintf(stderr,"illegal M.%d or N.%d\n",M,N); |
|||
return(""); |
|||
} |
|||
if ( strcmp((char *)"KMD",coin.c_str()) != 0 ) |
|||
{ |
|||
fprintf(stderr,"only KMD supported for now\n"); |
|||
return(""); |
|||
} |
|||
taddr = 0; |
|||
prefix = 60; |
|||
prefix2 = 85; |
|||
if ( pubkeys.size() != N ) |
|||
{ |
|||
fprintf(stderr,"M.%d N.%d but pubkeys[%d]\n",M,N,(int32_t)pubkeys.size()); |
|||
return(""); |
|||
} |
|||
for (i=0; i<N; i++) |
|||
{ |
|||
Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(pubkeys[i])) << OP_CHECKSIG); |
|||
if ( CCaddress_balance(coinaddr) == 0 ) |
|||
{ |
|||
fprintf(stderr,"M.%d N.%d but pubkeys[%d] has no balance\n",M,N,i); |
|||
return(""); |
|||
} |
|||
} |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
gatewayspk = GetUnspendable(cp,0); |
|||
if ( _GetCCaddress(destaddr,EVAL_ASSETS,gatewayspk) == 0 ) |
|||
{ |
|||
fprintf(stderr,"Gateway bind.%s (%s) cant create globaladdr\n",coin.c_str(),uint256_str(str,tokenid)); |
|||
return(""); |
|||
} |
|||
if ( (fullsupply= CCfullsupply(tokenid)) != totalsupply ) |
|||
{ |
|||
fprintf(stderr,"Gateway bind.%s (%s) globaladdr.%s totalsupply %.8f != fullsupply %.8f\n",coin.c_str(),uint256_str(str,tokenid),cp->unspendableCCaddr,(double)totalsupply/COIN,(double)fullsupply/COIN); |
|||
return(""); |
|||
} |
|||
if ( CCtoken_balance(destaddr,tokenid) != totalsupply ) |
|||
{ |
|||
fprintf(stderr,"Gateway bind.%s (%s) globaladdr.%s token balance %.8f != %.8f\n",coin.c_str(),uint256_str(str,tokenid),cp->unspendableCCaddr,(double)CCtoken_balance(destaddr,tokenid)/COIN,(double)totalsupply/COIN); |
|||
return(""); |
|||
} |
|||
if ( GetTransaction(oracletxid,oracletx,hashBlock,false) == 0 || (numvouts= oracletx.vout.size()) <= 0 ) |
|||
{ |
|||
fprintf(stderr,"cant find oracletxid %s\n",uint256_str(str,oracletxid)); |
|||
return(""); |
|||
} |
|||
if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' ) |
|||
{ |
|||
fprintf(stderr,"mismatched oracle name %s != %s\n",name.c_str(),coin.c_str()); |
|||
return(""); |
|||
} |
|||
if ( (fstr= (char *)format.c_str()) == 0 || strncmp(fstr,"Ihh",3) != 0 ) |
|||
{ |
|||
fprintf(stderr,"illegal format (%s) != (%s)\n",fstr,(char *)"Ihh"); |
|||
return(""); |
|||
} |
|||
if ( GatewaysBindExists(cp,gatewayspk,tokenid) != 0 ) // dont forget to check mempool!
|
|||
{ |
|||
fprintf(stderr,"Gateway bind.%s (%s) already exists\n",coin.c_str(),uint256_str(str,tokenid)); |
|||
return(""); |
|||
} |
|||
if ( AddNormalinputs(mtx,mypk,2*txfee,60) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,gatewayspk)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysBindOpRet('B',coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2))); |
|||
} |
|||
fprintf(stderr,"cant find enough inputs\n"); |
|||
return(""); |
|||
} |
|||
|
|||
uint256 GatewaysReverseScan(uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid) |
|||
{ |
|||
CTransaction tx; uint256 hash,mhash,hashBlock,oracletxid; int64_t val; int32_t numvouts; int64_t merkleht; CPubKey pk; std::vector<uint8_t>data; |
|||
txid = zeroid; |
|||
char str[65]; fprintf(stderr,"reverse scan %s\n",uint256_str(str,batontxid)); |
|||
while ( GetTransaction(batontxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) |
|||
{ |
|||
fprintf(stderr,"reverse scan %s\n",uint256_str(str,batontxid)); |
|||
if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,hash,pk,data) == 'D' && oracletxid == reforacletxid ) |
|||
{ |
|||
if ( oracle_format(&hash,&merkleht,0,'I',(uint8_t *)data.data(),0,(int32_t)data.size()) == sizeof(int32_t) && merkleht == height ) |
|||
{ |
|||
if ( oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size()) == sizeof(hash) && |
|||
oracle_format(&mhash,&val,0,'h',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)),(int32_t)data.size()) == sizeof(hash) && mhash != zeroid ) |
|||
{ |
|||
txid = batontxid; |
|||
return(mhash); |
|||
} else return(zeroid); |
|||
} |
|||
batontxid = hash; |
|||
} else break; |
|||
} |
|||
return(zeroid); |
|||
} |
|||
|
|||
/* Get the block merkle root for a proof
|
|||
* IN: proofData |
|||
* OUT: merkle root |
|||
* OUT: transaction IDS |
|||
*/ |
|||
uint256 BitcoinGetProofMerkleRoot(const std::vector<uint8_t> &proofData, std::vector<uint256> &txids) |
|||
{ |
|||
CMerkleBlock merkleBlock; |
|||
if (!E_UNMARSHAL(proofData, ss >> merkleBlock)) |
|||
return uint256(); |
|||
return merkleBlock.txn.ExtractMatches(txids); |
|||
} |
|||
|
|||
int64_t GatewaysVerify(char *refdepositaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 cointxid,const std::string deposithex,std::vector<uint8_t>proof,uint256 merkleroot,CPubKey destpub) |
|||
{ |
|||
std::vector<uint256> txids; uint256 proofroot,hashBlock,txid = zeroid; CTransaction tx; std::string name,description,format; char destaddr[64],destpubaddr[64],claimaddr[64],str[65],str2[65]; int32_t i,numvouts; int64_t nValue = 0; |
|||
if ( GetTransaction(oracletxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) |
|||
{ |
|||
fprintf(stderr,"GatewaysVerify cant find oracletxid %s\n",uint256_str(str,oracletxid)); |
|||
return(0); |
|||
} |
|||
if ( DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' || name != refcoin ) |
|||
{ |
|||
fprintf(stderr,"GatewaysVerify mismatched oracle name %s != %s\n",name.c_str(),refcoin.c_str()); |
|||
return(0); |
|||
} |
|||
proofroot = BitcoinGetProofMerkleRoot(proof,txids); |
|||
if ( proofroot != merkleroot ) |
|||
{ |
|||
fprintf(stderr,"GatewaysVerify mismatched merkleroot %s != %s\n",uint256_str(str,proofroot),uint256_str(str2,merkleroot)); |
|||
return(0); |
|||
} |
|||
if ( DecodeHexTx(tx,deposithex) != 0 ) |
|||
{ |
|||
Getscriptaddress(claimaddr,tx.vout[claimvout].scriptPubKey); |
|||
Getscriptaddress(destpubaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); |
|||
if ( strcmp(claimaddr,destpubaddr) == 0 ) |
|||
{ |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
Getscriptaddress(destaddr,tx.vout[i].scriptPubKey); |
|||
if ( strcmp(refdepositaddr,destaddr) == 0 ) |
|||
{ |
|||
txid = tx.GetHash(); |
|||
nValue = tx.vout[i].nValue; |
|||
break; |
|||
} |
|||
} |
|||
} else fprintf(stderr,"claimaddr.(%s) != destpubaddr.(%s)\n",claimaddr,destpubaddr); |
|||
} |
|||
if ( txid == cointxid ) |
|||
{ |
|||
fprintf(stderr,"verify proof for cointxid in merkleroot\n"); |
|||
return(nValue); |
|||
} else fprintf(stderr,"(%s) != (%s) or txid mismatch.%d or script mismatch\n",refdepositaddr,destaddr,txid != cointxid); |
|||
return(0); |
|||
} |
|||
|
|||
int64_t GatewaysDepositval(CTransaction tx) |
|||
{ |
|||
int32_t numvouts,height; int64_t amount; std::string coin,deposithex; std::vector<CPubKey> publishers; std::vector<uint256>txids; uint256 bindtxid,cointxid; std::vector<uint8_t> proof; CPubKey claimpubkey; |
|||
if ( (numvouts= tx.vout.size()) > 0 ) |
|||
{ |
|||
if ( DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey,coin,bindtxid,publishers,txids,height,cointxid,deposithex,proof,claimpubkey,amount) == 'D' ) |
|||
{ |
|||
// coin, bindtxid, publishers
|
|||
fprintf(stderr,"need to validate deposittxid more\n"); |
|||
return(amount); |
|||
} |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vector<uint8_t>proof,CPubKey destpub,int64_t amount) |
|||
{ |
|||
CMutableTransaction mtx; CTransaction bindtx; CPubKey mypk,gatewayspk; uint256 oracletxid,merkleroot,mhash,hashBlock,tokenid,txid; int64_t totalsupply; int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2; std::string coin; struct CCcontract_info *cp,C; std::vector<CPubKey> pubkeys,publishers; std::vector<uint256>txids; char str[67],depositaddr[64],txidaddr[64]; |
|||
cp = CCinit(&C,EVAL_GATEWAYS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
gatewayspk = GetUnspendable(cp,0); |
|||
//fprintf(stderr,"GatewaysDeposit ht.%d %s %.8f numpks.%d\n",height,refcoin.c_str(),(double)amount/COIN,(int32_t)pubkeys.size());
|
|||
if ( GetTransaction(bindtxid,bindtx,hashBlock,false) == 0 || (numvouts= bindtx.vout.size()) <= 0 ) |
|||
{ |
|||
fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); |
|||
return(""); |
|||
} |
|||
if ( DecodeGatewaysBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 'B' || refcoin != coin ) |
|||
{ |
|||
fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str()); |
|||
return(""); |
|||
} |
|||
n = (int32_t)pubkeys.size(); |
|||
merkleroot = zeroid; |
|||
for (i=m=0; i<n; i++) |
|||
{ |
|||
fprintf(stderr,"pubkeys[%d] %s\n",i,pubkey33_str(str,(uint8_t *)&pubkeys[i])); |
|||
if ( (mhash= GatewaysReverseScan(txid,height,oracletxid,OraclesBatontxid(oracletxid,pubkeys[i]))) != zeroid ) |
|||
{ |
|||
if ( merkleroot == zeroid ) |
|||
merkleroot = mhash, m = 1; |
|||
else if ( mhash == merkleroot ) |
|||
m++; |
|||
publishers.push_back(pubkeys[i]); |
|||
txids.push_back(txid); |
|||
} |
|||
} |
|||
fprintf(stderr,"m.%d of n.%d\n",m,n); |
|||
if ( merkleroot == zeroid || m < n/2 ) |
|||
{ |
|||
//uint256 tmp;
|
|||
//decode_hex((uint8_t *)&tmp,32,(char *)"90aedc2f19200afc9aca2e351438d011ebae8264a58469bf225883045f61917f");
|
|||
//merkleroot = revuint256(tmp);
|
|||
fprintf(stderr,"couldnt find merkleroot for ht.%d %s oracle.%s m.%d vs n.%d\n",height,coin.c_str(),uint256_str(str,oracletxid),m,n); |
|||
return(""); |
|||
} |
|||
if ( GatewaysCointxidExists(cp,cointxid) != 0 ) |
|||
{ |
|||
fprintf(stderr,"cointxid.%s already exists\n",uint256_str(str,cointxid)); |
|||
return(""); |
|||
} |
|||
if ( GatewaysVerify(depositaddr,oracletxid,claimvout,coin,cointxid,deposithex,proof,merkleroot,destpub) != amount ) |
|||
{ |
|||
fprintf(stderr,"deposittxid didnt validate\n"); |
|||
return(""); |
|||
} |
|||
if ( AddNormalinputs(mtx,mypk,3*txfee,60) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); |
|||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysOpRet('D',coin,bindtxid,publishers,txids,height,cointxid,deposithex,proof,destpub,amount))); |
|||
} |
|||
fprintf(stderr,"cant find enough inputs\n"); |
|||
return(""); |
|||
} |
|||
|
|||
std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount) |
|||
{ |
|||
CMutableTransaction mtx; CTransaction tx; CPubKey mypk,gatewayspk; struct CCcontract_info *cp,C,*assetscp,C2; uint8_t M,N,taddr,prefix,prefix2,mypriv[32]; std::string coin; std::vector<CPubKey> msigpubkeys; int64_t totalsupply,depositamount,inputs,CCchange=0; int32_t numvouts; uint256 hashBlock,assetid,oracletxid; char str[65],depositaddr[64],coinaddr[64]; |
|||
cp = CCinit(&C,EVAL_GATEWAYS); |
|||
assetscp = CCinit(&C2,EVAL_ASSETS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
gatewayspk = GetUnspendable(cp,0); |
|||
_GetCCaddress(coinaddr,EVAL_ASSETS,gatewayspk); |
|||
CCaddr2set(assetscp,EVAL_ASSETS,gatewayspk,cp->CCpriv,coinaddr); |
|||
Myprivkey(mypriv); |
|||
_GetCCaddress(coinaddr,EVAL_GATEWAYS,mypk); |
|||
CCaddr3set(assetscp,EVAL_GATEWAYS,mypk,mypriv,coinaddr); |
|||
if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) |
|||
{ |
|||
fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); |
|||
return(""); |
|||
} |
|||
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin ) |
|||
{ |
|||
fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str()); |
|||
return(""); |
|||
} |
|||
if ( GetTransaction(deposittxid,tx,hashBlock,false) == 0 ) |
|||
{ |
|||
fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); |
|||
return(""); |
|||
} |
|||
if ( (depositamount= GatewaysDepositval(tx)) != amount ) |
|||
{ |
|||
fprintf(stderr,"invalid Gateways deposittxid %s %.8f != %.8f\n",uint256_str(str,deposittxid),(double)depositamount/COIN,(double)amount/COIN); |
|||
return(""); |
|||
} |
|||
//fprintf(stderr,"depositaddr.(%s) vs %s\n",depositaddr,cp->unspendableaddr2);
|
|||
if ( AddNormalinputs(mtx,mypk,txfee,1) > 0 ) |
|||
{ |
|||
if ( (inputs= AddAssetInputs(assetscp,mtx,gatewayspk,assetid,amount,60)) > 0 ) |
|||
{ |
|||
if ( inputs > amount ) |
|||
CCchange = (inputs - amount); |
|||
mtx.vin.push_back(CTxIn(deposittxid,0,CScript())); |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,amount,mypk)); |
|||
if ( CCchange != 0 ) |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,gatewayspk)); |
|||
return(FinalizeCCTx(0,assetscp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey()))); |
|||
} |
|||
} |
|||
fprintf(stderr,"cant find enough inputs or mismatched total\n"); |
|||
return(""); |
|||
} |
|||
|
|||
std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,std::vector<uint8_t> withdrawpub,int64_t amount) |
|||
{ |
|||
CMutableTransaction mtx; CTransaction tx; CPubKey mypk,gatewayspk; struct CCcontract_info *cp,C,*assetscp,C2; uint256 assetid,hashBlock,oracletxid; int32_t numvouts; int64_t totalsupply,inputs,CCchange=0; uint8_t M,N,taddr,prefix,prefix2,mypriv[32]; std::string coin; std::vector<CPubKey> msigpubkeys; char depositaddr[64],str[65],coinaddr[64]; |
|||
cp = CCinit(&C,EVAL_GATEWAYS); |
|||
assetscp = CCinit(&C2,EVAL_ASSETS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
gatewayspk = GetUnspendable(cp,0); |
|||
_GetCCaddress(coinaddr,EVAL_ASSETS,gatewayspk); |
|||
CCaddr2set(assetscp,EVAL_ASSETS,gatewayspk,cp->CCpriv,coinaddr); |
|||
Myprivkey(mypriv); |
|||
_GetCCaddress(coinaddr,EVAL_GATEWAYS,mypk); |
|||
CCaddr3set(assetscp,EVAL_GATEWAYS,mypk,mypriv,coinaddr); |
|||
if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) |
|||
{ |
|||
fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); |
|||
return(""); |
|||
} |
|||
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin ) |
|||
{ |
|||
fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str()); |
|||
return(""); |
|||
} |
|||
if ( AddNormalinputs(mtx,mypk,3*txfee,3) > 0 ) |
|||
{ |
|||
if ( (inputs= AddAssetInputs(assetscp,mtx,mypk,assetid,amount,60)) > 0 ) |
|||
{ |
|||
if ( inputs > amount ) |
|||
CCchange = (inputs - amount); |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,amount,gatewayspk)); |
|||
mtx.vout.push_back(CTxOut(txfee,CScript() << withdrawpub << OP_CHECKSIG)); |
|||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(gatewayspk)) << OP_CHECKSIG)); |
|||
if ( CCchange != 0 ) |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk)); |
|||
return(FinalizeCCTx(0,assetscp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey()))); |
|||
} |
|||
} |
|||
fprintf(stderr,"cant find enough inputs or mismatched total\n"); |
|||
return(""); |
|||
} |
|||
|
|||
std::string GatewaysMarkdone(uint64_t txfee,uint256 withdrawtxid) |
|||
{ |
|||
CMutableTransaction mtx; CScript opret; CPubKey mypk; struct CCcontract_info *cp,C; |
|||
cp = CCinit(&C,EVAL_GATEWAYS); |
|||
if ( txfee == 0 ) |
|||
txfee = 5000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
mtx.vin.push_back(CTxIn(withdrawtxid,2,CScript())); |
|||
mtx.vout.push_back(CTxOut(5000,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); |
|||
} |
|||
|
|||
UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin) |
|||
{ |
|||
UniValue result(UniValue::VOBJ),pending(UniValue::VARR),obj(UniValue::VOBJ); CTransaction tx; std::string coin; CPubKey mypk,gatewayspk; std::vector<CPubKey> msigpubkeys; uint256 hashBlock,assetid,txid,oracletxid; uint8_t M,N,taddr,prefix,prefix2; char depositaddr[64],withmarker[64],coinaddr[64],destaddr[64],str[65],withaddr[64],numstr[32],txidaddr[64],signeraddr[64]; int32_t i,n,numvouts,vout,numqueued,queueflag; int64_t totalsupply; struct CCcontract_info *cp,C; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
cp = CCinit(&C,EVAL_GATEWAYS); |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
gatewayspk = GetUnspendable(cp,0); |
|||
_GetCCaddress(coinaddr,EVAL_ASSETS,gatewayspk); |
|||
if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) |
|||
{ |
|||
fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); |
|||
return(result); |
|||
} |
|||
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin ) |
|||
{ |
|||
fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str()); |
|||
return(result); |
|||
} |
|||
n = msigpubkeys.size(); |
|||
queueflag = 0; |
|||
for (i=0; i<n; i++) |
|||
if ( msigpubkeys[i] == mypk ) |
|||
{ |
|||
queueflag = 1; |
|||
break; |
|||
} |
|||
Getscriptaddress(withmarker,CScript() << ParseHex(HexStr(gatewayspk)) << OP_CHECKSIG); |
|||
SetCCunspents(unspentOutputs,withmarker); |
|||
numqueued = 0; |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
if ( GetTransaction(txid,tx,hashBlock,false) != 0 ) |
|||
{ |
|||
Getscriptaddress(destaddr,tx.vout[0].scriptPubKey); |
|||
Getscriptaddress(withaddr,tx.vout[1].scriptPubKey); |
|||
if ( strcmp(destaddr,coinaddr) == 0 ) |
|||
{ |
|||
obj.push_back(Pair("txid",uint256_str(str,txid))); |
|||
CCtxidaddr(txidaddr,txid); |
|||
obj.push_back(Pair("txidaddr",txidaddr)); |
|||
obj.push_back(Pair("withdrawaddr",withaddr)); |
|||
sprintf(numstr,"%.8f",(double)tx.vout[0].nValue/COIN); |
|||
obj.push_back(Pair("amount",numstr)); |
|||
if ( queueflag != 0 ) |
|||
{ |
|||
obj.push_back(Pair("depositaddr",depositaddr)); |
|||
Getscriptaddress(signeraddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG); |
|||
obj.push_back(Pair("signeraddr",signeraddr)); |
|||
// numqueued += GatewaysAddQueue(refcoin,txid,tx.vout[1].scriptPubKey,tx.vout[0].nValue);
|
|||
} |
|||
pending.push_back(obj); |
|||
} |
|||
} |
|||
} |
|||
result.push_back(Pair("coin",refcoin)); |
|||
result.push_back(Pair("pending",pending)); |
|||
result.push_back(Pair("queueflag",queueflag)); |
|||
return(result); |
|||
} |
|||
|
@ -0,0 +1,76 @@ |
|||
#include <cryptoconditions.h> |
|||
|
|||
#include "main.h" |
|||
#include "chain.h" |
|||
#include "streams.h" |
|||
#include "cc/eval.h" |
|||
#include "cc/betprotocol.h" |
|||
#include "primitives/transaction.h" |
|||
|
|||
|
|||
/*
|
|||
* Crypto-Condition EVAL method that verifies a payout against a transaction |
|||
* notarised on another chain. |
|||
* |
|||
* IN: params - condition params |
|||
* IN: importTx - Payout transaction on value chain (KMD) |
|||
* IN: nIn - index of input of stake |
|||
* |
|||
* importTx: Spends stakeTx with payouts from asset chain |
|||
* |
|||
* in 0: Spends Stake TX and contains ImportPayout CC |
|||
* out 0: OP_RETURN MomProof, disputeTx |
|||
* out 1-: arbitrary payouts |
|||
* |
|||
* disputeTx: Spends sessionTx.0 (opener on asset chain) |
|||
* |
|||
* in 0: spends sessionTx.0 |
|||
* in 1-: anything |
|||
* out 0: OP_RETURN hash of payouts |
|||
* out 1-: anything |
|||
*/ |
|||
bool Eval::ImportPayout(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn) |
|||
{ |
|||
if (importTx.vout.size() == 0) return Invalid("no-vouts"); |
|||
|
|||
// load data from vout[0]
|
|||
MoMProof proof; |
|||
CTransaction disputeTx; |
|||
{ |
|||
std::vector<unsigned char> vopret; |
|||
GetOpReturnData(importTx.vout[0].scriptPubKey, vopret); |
|||
if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx)) |
|||
return Invalid("invalid-payload"); |
|||
} |
|||
|
|||
// Check disputeTx.0 shows correct payouts
|
|||
{ |
|||
uint256 givenPayoutsHash; |
|||
GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash); |
|||
std::vector<CTxOut> payouts(importTx.vout.begin() + 1, importTx.vout.end()); |
|||
if (givenPayoutsHash != SerializeHash(payouts)) |
|||
return Invalid("wrong-payouts"); |
|||
} |
|||
|
|||
// Check disputeTx spends sessionTx.0
|
|||
// condition ImportPayout params is session ID from other chain
|
|||
{ |
|||
uint256 sessionHash; |
|||
if (!E_UNMARSHAL(params, ss >> sessionHash)) |
|||
return Invalid("malformed-params"); |
|||
if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0)) |
|||
return Invalid("wrong-session"); |
|||
} |
|||
|
|||
// Check disputeTx solves momproof from vout[0]
|
|||
{ |
|||
NotarisationData data; |
|||
if (!GetNotarisationData(proof.notarisationHash, data)) |
|||
return Invalid("coudnt-load-mom"); |
|||
|
|||
if (data.MoM != proof.Exec(disputeTx.GetHash())) |
|||
return Invalid("mom-check-fail"); |
|||
} |
|||
|
|||
return Valid(); |
|||
} |
@ -0,0 +1,237 @@ |
|||
/*
|
|||
Copyright (c) 2009 Dave Gamble |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
|||
*/ |
|||
|
|||
#ifndef cJSON__h |
|||
#define cJSON__h |
|||
|
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <stdint.h> |
|||
#include <math.h> |
|||
#include <ctype.h> |
|||
#include <float.h> |
|||
#include <memory.h> |
|||
|
|||
#include "../crypto777/OS_portable.h" |
|||
|
|||
#define SATOSHIDEN ((uint64_t)100000000L) |
|||
#define dstr(x) ((double)(x) / SATOSHIDEN) |
|||
#define MAX_JSON_FIELD 4096 // on the big side
|
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" |
|||
{ |
|||
#endif |
|||
|
|||
/* cJSON Types: */ |
|||
#define cJSON_False 0 |
|||
#define cJSON_True 1 |
|||
#define cJSON_NULL 2 |
|||
#define cJSON_Number 3 |
|||
#define cJSON_String 4 |
|||
#define cJSON_Array 5 |
|||
#define cJSON_Object 6 |
|||
|
|||
#define is_cJSON_Null(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_NULL) |
|||
#define is_cJSON_Array(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_Array) |
|||
#define is_cJSON_String(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_String) |
|||
#define is_cJSON_Number(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_Number) |
|||
#define is_cJSON_Object(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_Object) |
|||
#define is_cJSON_True(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_True) |
|||
#define is_cJSON_False(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_False) |
|||
|
|||
#define cJSON_IsReference 256 |
|||
|
|||
/* The cJSON structure: */ |
|||
typedef struct cJSON { |
|||
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ |
|||
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ |
|||
|
|||
int32_t type; /* The type of the item, as above. */ |
|||
|
|||
char *valuestring; /* The item's string, if type==cJSON_String */ |
|||
int64_t valueint; /* The item's number, if type==cJSON_Number */ |
|||
double valuedouble; /* The item's number, if type==cJSON_Number */ |
|||
|
|||
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ |
|||
uint32_t cjsonid; |
|||
} cJSON; |
|||
|
|||
typedef struct cJSON_Hooks { |
|||
void *(*malloc_fn)(size_t sz); |
|||
void (*free_fn)(void *ptr); |
|||
} cJSON_Hooks; |
|||
|
|||
/* Supply malloc, realloc and free functions to cJSON */ |
|||
extern void cJSON_InitHooks(cJSON_Hooks* hooks); |
|||
|
|||
|
|||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ |
|||
extern cJSON *cJSON_Parse(const char *value); |
|||
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ |
|||
extern char *cJSON_Print(cJSON *item); |
|||
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ |
|||
extern char *cJSON_PrintUnformatted(cJSON *item); |
|||
/* Delete a cJSON entity and all subentities. */ |
|||
extern void cJSON_Delete(cJSON *c); |
|||
|
|||
/* Returns the number of items in an array (or object). */ |
|||
extern int cJSON_GetArraySize(cJSON *array); |
|||
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ |
|||
extern cJSON *cJSON_GetArrayItem(cJSON *array,int32_t item); |
|||
/* Get item "string" from object. Case insensitive. */ |
|||
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); |
|||
|
|||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ |
|||
extern const char *cJSON_GetErrorPtr(void); |
|||
|
|||
/* These calls create a cJSON item of the appropriate type. */ |
|||
extern cJSON *cJSON_CreateNull(void); |
|||
extern cJSON *cJSON_CreateTrue(void); |
|||
extern cJSON *cJSON_CreateFalse(void); |
|||
extern cJSON *cJSON_CreateBool(int32_t b); |
|||
extern cJSON *cJSON_CreateNumber(double num); |
|||
extern cJSON *cJSON_CreateString(const char *string); |
|||
extern cJSON *cJSON_CreateArray(void); |
|||
extern cJSON *cJSON_CreateObject(void); |
|||
|
|||
/* These utilities create an Array of count items. */ |
|||
extern cJSON *cJSON_CreateIntArray(int64_t *numbers,int32_t count); |
|||
extern cJSON *cJSON_CreateFloatArray(float *numbers,int32_t count); |
|||
extern cJSON *cJSON_CreateDoubleArray(double *numbers,int32_t count); |
|||
extern cJSON *cJSON_CreateStringArray(char **strings,int32_t count); |
|||
|
|||
/* Append item to the specified array/object. */ |
|||
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); |
|||
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); |
|||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ |
|||
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); |
|||
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); |
|||
|
|||
/* Remove/Detatch items from Arrays/Objects. */ |
|||
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int32_t which); |
|||
extern void cJSON_DeleteItemFromArray(cJSON *array,int32_t which); |
|||
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); |
|||
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); |
|||
|
|||
/* Update array items. */ |
|||
extern void cJSON_ReplaceItemInArray(cJSON *array,int32_t which,cJSON *newitem); |
|||
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); |
|||
|
|||
/* Duplicate a cJSON item */ |
|||
extern cJSON *cJSON_Duplicate(cJSON *item,int32_t recurse); |
|||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
|||
need to be released. With recurse!=0, it will duplicate any children connected to the item. |
|||
The item->next and ->prev pointers are always zero on return from Duplicate. */ |
|||
|
|||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ |
|||
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int32_t require_null_terminated); |
|||
|
|||
extern void cJSON_Minify(char *json); |
|||
|
|||
/* Macros for creating things quickly. */ |
|||
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) |
|||
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) |
|||
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) |
|||
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) |
|||
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) |
|||
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) |
|||
|
|||
struct destbuf { char buf[MAX_JSON_FIELD]; }; |
|||
|
|||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */ |
|||
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) |
|||
#define jfieldstr get_cJSON_fieldname |
|||
|
|||
char *cJSON_str(cJSON *json); |
|||
char *jstr(cJSON *json,char *field); |
|||
char *jprint(cJSON *json,int32_t freeflag); |
|||
int32_t jint(cJSON *json,char *field); |
|||
uint32_t juint(cJSON *json,char *field); |
|||
char *jstri(cJSON *json,int32_t i); |
|||
int32_t jinti(cJSON *json,int32_t i); |
|||
uint32_t juinti(cJSON *json,int32_t i); |
|||
uint64_t j64bitsi(cJSON *json,int32_t i); |
|||
double jdoublei(cJSON *json,int32_t i); |
|||
double jdouble(cJSON *json,char *field); |
|||
cJSON *jobj(cJSON *json,char *field); |
|||
cJSON *jarray(int32_t *nump,cJSON *json,char *field); |
|||
cJSON *jitem(cJSON *array,int32_t i); |
|||
uint64_t j64bits(cJSON *json,char *field); |
|||
void jadd(cJSON *json,char *field,cJSON *item); |
|||
void jaddstr(cJSON *json,char *field,char *str); |
|||
void jaddnum(cJSON *json,char *field,double num); |
|||
void jadd64bits(cJSON *json,char *field,uint64_t nxt64bits); |
|||
void jaddi(cJSON *json,cJSON *item); |
|||
void jaddistr(cJSON *json,char *str); |
|||
void jaddinum(cJSON *json,double num); |
|||
void jaddi64bits(cJSON *json,uint64_t nxt64bits); |
|||
void jdelete(cJSON *object,char *string); |
|||
cJSON *jduplicate(cJSON *json); |
|||
int32_t jnum(cJSON *obj,char *field); |
|||
|
|||
bits256 jbits256(cJSON *json,char *field); |
|||
bits256 jbits256i(cJSON *json,int32_t i); |
|||
void jaddbits256(cJSON *json,char *field,bits256 hash); |
|||
void jaddibits256(cJSON *json,bits256 hash); |
|||
void copy_cJSON(struct destbuf *dest,cJSON *obj); |
|||
void copy_cJSON2(char *dest,int32_t maxlen,cJSON *obj); |
|||
cJSON *gen_list_json(char **list); |
|||
int32_t extract_cJSON_str(char *dest,int32_t max,cJSON *json,char *field); |
|||
|
|||
void free_json(cJSON *json); |
|||
int64_t _conv_cJSON_float(cJSON *json); |
|||
int64_t conv_cJSON_float(cJSON *json,char *field); |
|||
int64_t get_cJSON_int(cJSON *json,char *field); |
|||
void add_satoshis_json(cJSON *json,char *field,uint64_t satoshis); |
|||
uint64_t get_satoshi_obj(cJSON *json,char *field); |
|||
|
|||
int32_t get_API_int(cJSON *obj,int32_t val); |
|||
uint32_t get_API_uint(cJSON *obj,uint32_t val); |
|||
uint64_t get_API_nxt64bits(cJSON *obj); |
|||
double get_API_float(cJSON *obj); |
|||
char *get_cJSON_fieldname(cJSON *obj); |
|||
void ensure_jsonitem(cJSON *json,char *field,char *value); |
|||
int32_t in_jsonarray(cJSON *array,char *value); |
|||
char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *params,int32_t timeout); |
|||
uint64_t calc_nxt64bits(const char *str); |
|||
int32_t expand_nxt64bits(char *str,uint64_t nxt64bits); |
|||
char *nxt64str(uint64_t nxt64bits); |
|||
char *nxt64str2(uint64_t nxt64bits); |
|||
cJSON *addrs_jsonarray(uint64_t *addrs,int32_t num); |
|||
int32_t myatoi(char *str,int32_t range); |
|||
void cJSON_register(cJSON *item); |
|||
void cJSON_unregister(cJSON *item); |
|||
|
|||
char *stringifyM(char *str); |
|||
#define replace_backslashquotes unstringify |
|||
char *unstringify(char *str); |
|||
#define jtrue cJSON_CreateTrue |
|||
#define jfalse cJSON_CreateFalse |
|||
|
|||
#define jfieldname get_cJSON_fieldname |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
|||
|
|||
#endif |
@ -0,0 +1,87 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2015 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
// derived from curve25519_donna
|
|||
|
|||
#ifndef dcnet_curve25519_h |
|||
#define dcnet_curve25519_h |
|||
#include <stdint.h> |
|||
#include <memory.h> |
|||
#include <string.h> |
|||
|
|||
union _bits128 { uint8_t bytes[16]; uint16_t ushorts[8]; uint32_t uints[4]; uint64_t ulongs[2]; uint64_t txid; }; |
|||
typedef union _bits128 bits128; |
|||
union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; |
|||
typedef union _bits256 bits256; |
|||
|
|||
union _bits320 { uint8_t bytes[40]; uint16_t ushorts[20]; uint32_t uints[10]; uint64_t ulongs[5]; uint64_t txid; }; |
|||
typedef union _bits320 bits320; |
|||
|
|||
union _bits384 { bits256 sig; uint8_t bytes[48]; uint16_t ushorts[24]; uint32_t uints[12]; uint64_t ulongs[6]; uint64_t txid; }; |
|||
typedef union _bits384 bits384; |
|||
|
|||
struct sha256_vstate { uint64_t length; uint32_t state[8],curlen; uint8_t buf[64]; }; |
|||
struct rmd160_vstate { uint64_t length; uint8_t buf[64]; uint32_t curlen, state[5]; }; |
|||
|
|||
struct acct777_sig { bits256 sigbits,pubkey; uint64_t signer64bits; uint32_t timestamp,allocsize; }; |
|||
|
|||
//#undef force_inline
|
|||
//#define force_inline __attribute__((always_inline))
|
|||
|
|||
|
|||
bits320 fmul(const bits320 in2,const bits320 in); |
|||
bits320 fexpand(bits256 basepoint); |
|||
bits256 fcontract(const bits320 input); |
|||
void cmult(bits320 *resultx,bits320 *resultz,bits256 secret,const bits320 q); |
|||
bits320 crecip(const bits320 z); |
|||
bits256 curve25519(bits256 mysecret,bits256 basepoint); |
|||
void OS_randombytes(unsigned char *x,long xlen); |
|||
bits256 rand256(int32_t privkeyflag); |
|||
bits256 curve25519_basepoint9(); |
|||
bits256 curve25519_keypair(bits256 *pubkeyp); |
|||
|
|||
void vcalc_sha256(char hashstr[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len); |
|||
void vcalc_sha256cat(uint8_t hash[256 >> 3],uint8_t *src,int32_t len,uint8_t *src2,int32_t len2); |
|||
void vupdate_sha256(uint8_t hash[256 >> 3],struct sha256_vstate *state,uint8_t *src,int32_t len); |
|||
bits256 curve25519_shared(bits256 privkey,bits256 otherpub); |
|||
int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); |
|||
int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp); |
|||
|
|||
uint32_t calc_crc32(uint32_t crc,const void *buf,size_t size); |
|||
uint64_t conv_NXTpassword(unsigned char *mysecret,unsigned char *mypublic,uint8_t *pass,int32_t passlen); |
|||
bits128 calc_md5(char digeststr[33],void *buf,int32_t len); |
|||
|
|||
bits256 acct777_msgprivkey(uint8_t *data,int32_t datalen); |
|||
bits256 acct777_msgpubkey(uint8_t *data,int32_t datalen); |
|||
void acct777_rwsig(int32_t rwflag,uint8_t *serialized,struct acct777_sig *sig); |
|||
int32_t acct777_sigcheck(struct acct777_sig *sig); |
|||
|
|||
bits256 acct777_pubkey(bits256 privkey); |
|||
uint64_t acct777_nxt64bits(bits256 pubkey); |
|||
bits256 acct777_hashiter(bits256 privkey,bits256 pubkey,int32_t lockdays,uint8_t chainlen); |
|||
bits256 acct777_lockhash(bits256 pubkey,int32_t lockdays,uint8_t chainlen); |
|||
bits256 acct777_invoicehash(bits256 *invoicehash,uint16_t lockdays,uint8_t chainlen); |
|||
uint64_t acct777_sign(struct acct777_sig *sig,bits256 privkey,bits256 otherpubkey,uint32_t timestamp,uint8_t *serialized,int32_t datalen); |
|||
uint64_t acct777_validate(struct acct777_sig *sig,bits256 privkey,bits256 pubkey); |
|||
uint64_t acct777_signtx(struct acct777_sig *sig,bits256 privkey,uint32_t timestamp,uint8_t *data,int32_t datalen); |
|||
uint64_t acct777_swaptx(bits256 privkey,struct acct777_sig *sig,uint32_t timestamp,uint8_t *data,int32_t datalen); |
|||
void calc_hmac_sha256(uint8_t *mac,int32_t maclen,uint8_t *key,int32_t key_size,uint8_t *message,int32_t len); |
|||
|
|||
#include "../includes/tweetnacl.h" |
|||
int32_t _SuperNET_cipher(uint8_t nonce[crypto_box_NONCEBYTES],uint8_t *cipher,uint8_t *message,int32_t len,bits256 destpub,bits256 srcpriv,uint8_t *buf); |
|||
uint8_t *_SuperNET_decipher(uint8_t nonce[crypto_box_NONCEBYTES],uint8_t *cipher,uint8_t *message,int32_t len,bits256 srcpub,bits256 mypriv); |
|||
void *SuperNET_deciphercalc(void **ptrp,int32_t *msglenp,bits256 privkey,bits256 srcpubkey,uint8_t *cipher,int32_t cipherlen,uint8_t *buf,int32_t bufsize); |
|||
uint8_t *SuperNET_ciphercalc(void **ptrp,int32_t *cipherlenp,bits256 *privkeyp,bits256 *destpubkeyp,uint8_t *data,int32_t datalen,uint8_t *space2,int32_t space2size); |
|||
|
|||
#endif |
@ -0,0 +1,91 @@ |
|||
/*
|
|||
* This file is Copyright Daniel Silverstone <dsilvers@digital-scurf.org> 2006 |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person |
|||
* obtaining a copy of this software and associated documentation |
|||
* files (the "Software"), to deal in the Software without |
|||
* restriction, including without limitation the rights to use, copy, |
|||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
|||
* of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be |
|||
* included in all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
|||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
|||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|||
* DEALINGS IN THE SOFTWARE. |
|||
* |
|||
*/ |
|||
|
|||
#ifndef LIBGFSHARE_H |
|||
#define LIBGFSHARE_H |
|||
#include <stdint.h> |
|||
|
|||
typedef struct _gfshare_ctx gfshare_ctx; |
|||
|
|||
typedef void (*gfshare_rand_func_t)(unsigned char*,long); |
|||
|
|||
/* This will, by default, use random(). It's not very good so you should
|
|||
* replace it (perhaps with a function which reads from /dev/urandom). |
|||
* If you can't be bothered, be sure to srandom() before you use any |
|||
* of the gfshare_ctx_enc_* functions |
|||
*/ |
|||
extern gfshare_rand_func_t gfshare_fill_rand; |
|||
|
|||
/* ------------------------------------------------------[ Preparation ]---- */ |
|||
|
|||
/* Initialise a gfshare context for producing shares */ |
|||
gfshare_ctx* gfshare_ctx_init_enc(unsigned char* /* sharenrs */, |
|||
uint32_t /* sharecount */, |
|||
unsigned char /* threshold */, |
|||
uint32_t /* size */); |
|||
|
|||
/* Initialise a gfshare context for recombining shares */ |
|||
gfshare_ctx* gfshare_ctx_init_dec(unsigned char* /* sharenrs */, |
|||
uint32_t /* sharecount */, |
|||
uint32_t /* size */); |
|||
|
|||
/* Free a share context's memory. */ |
|||
void gfshare_ctx_free(gfshare_ctx* /* ctx */); |
|||
|
|||
/* --------------------------------------------------------[ Splitting ]---- */ |
|||
|
|||
/* Provide a secret to the encoder. (this re-scrambles the coefficients) */ |
|||
void gfshare_ctx_enc_setsecret(gfshare_ctx* /* ctx */, |
|||
unsigned char* /* secret */); |
|||
|
|||
/* Extract a share from the context.
|
|||
* 'share' must be preallocated and at least 'size' bytes long. |
|||
* 'sharenr' is the index into the 'sharenrs' array of the share you want. |
|||
*/ |
|||
void gfshare_ctx_encgetshare(uint8_t *logs,uint8_t *exps,gfshare_ctx* /* ctx */, unsigned char /* sharenr */, unsigned char* /* share */); |
|||
void gfshare_ctx_enc_getshare(gfshare_ctx* /* ctx */, unsigned char /* sharenr */, unsigned char* /* share */); |
|||
|
|||
/* ----------------------------------------------------[ Recombination ]---- */ |
|||
|
|||
/* Inform a recombination context of a change in share indexes */ |
|||
void gfshare_ctx_dec_newshares(gfshare_ctx* /* ctx */, |
|||
unsigned char* /* sharenrs */); |
|||
|
|||
/* Provide a share context with one of the shares.
|
|||
* The 'sharenr' is the index into the 'sharenrs' array |
|||
*/ |
|||
void gfshare_ctx_dec_giveshare(gfshare_ctx* /* ctx */, |
|||
unsigned char /* sharenr */, |
|||
unsigned char* /* share */); |
|||
|
|||
/* Extract the secret by interpolation of the shares.
|
|||
* secretbuf must be allocated and at least 'size' bytes long |
|||
*/ |
|||
|
|||
void gfshare_ctx_decextract(uint8_t *logs,uint8_t *exps,gfshare_ctx* /* ctx */, unsigned char* /* secretbuf */); |
|||
void gfshare_ctx_dec_extract(gfshare_ctx* /* ctx */, unsigned char* /* secretbuf */); |
|||
|
|||
#endif /* LIBGFSHARE_H */ |
|||
|
@ -0,0 +1,275 @@ |
|||
#ifndef TWEETNACL_H |
|||
#define TWEETNACL_H |
|||
#define crypto_auth_PRIMITIVE "hmacsha512256" |
|||
#define crypto_auth crypto_auth_hmacsha512256 |
|||
#define crypto_auth_verify crypto_auth_hmacsha512256_verify |
|||
#define crypto_auth_BYTES crypto_auth_hmacsha512256_BYTES |
|||
#define crypto_auth_KEYBYTES crypto_auth_hmacsha512256_KEYBYTES |
|||
#define crypto_auth_IMPLEMENTATION crypto_auth_hmacsha512256_IMPLEMENTATION |
|||
#define crypto_auth_VERSION crypto_auth_hmacsha512256_VERSION |
|||
#define crypto_auth_hmacsha512256_tweet_BYTES 32 |
|||
#define crypto_auth_hmacsha512256_tweet_KEYBYTES 32 |
|||
extern int crypto_auth_hmacsha512256_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); |
|||
extern int crypto_auth_hmacsha512256_tweet_verify(const unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); |
|||
#define crypto_auth_hmacsha512256_tweet_VERSION "-" |
|||
#define crypto_auth_hmacsha512256 crypto_auth_hmacsha512256_tweet |
|||
#define crypto_auth_hmacsha512256_verify crypto_auth_hmacsha512256_tweet_verify |
|||
#define crypto_auth_hmacsha512256_BYTES crypto_auth_hmacsha512256_tweet_BYTES |
|||
#define crypto_auth_hmacsha512256_KEYBYTES crypto_auth_hmacsha512256_tweet_KEYBYTES |
|||
#define crypto_auth_hmacsha512256_VERSION crypto_auth_hmacsha512256_tweet_VERSION |
|||
#define crypto_auth_hmacsha512256_IMPLEMENTATION "crypto_auth/hmacsha512256/tweet" |
|||
#define crypto_box_PRIMITIVE "curve25519xsalsa20poly1305" |
|||
#define crypto_box crypto_box_curve25519xsalsa20poly1305 |
|||
#define crypto_box_open crypto_box_curve25519xsalsa20poly1305_open |
|||
#define crypto_box_keypair crypto_box_curve25519xsalsa20poly1305_keypair |
|||
#define crypto_box_priv2pub crypto_box_curve25519xsalsa20poly1305_priv2pub |
|||
#define crypto_box_beforenm crypto_box_curve25519xsalsa20poly1305_beforenm |
|||
#define crypto_box_afternm crypto_box_curve25519xsalsa20poly1305_afternm |
|||
#define crypto_box_open_afternm crypto_box_curve25519xsalsa20poly1305_open_afternm |
|||
#define crypto_box_PUBLICKEYBYTES crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES |
|||
#define crypto_box_SECRETKEYBYTES crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES |
|||
#define crypto_box_BEFORENMBYTES crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES |
|||
#define crypto_box_NONCEBYTES crypto_box_curve25519xsalsa20poly1305_NONCEBYTES |
|||
#define crypto_box_ZEROBYTES crypto_box_curve25519xsalsa20poly1305_ZEROBYTES |
|||
#define crypto_box_BOXZEROBYTES crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES |
|||
#define crypto_box_IMPLEMENTATION crypto_box_curve25519xsalsa20poly1305_IMPLEMENTATION |
|||
#define crypto_box_VERSION crypto_box_curve25519xsalsa20poly1305_VERSION |
|||
#define crypto_box_curve25519xsalsa20poly1305_tweet_PUBLICKEYBYTES 32 |
|||
#define crypto_box_curve25519xsalsa20poly1305_tweet_SECRETKEYBYTES 32 |
|||
#define crypto_box_curve25519xsalsa20poly1305_tweet_BEFORENMBYTES 32 |
|||
#define crypto_box_curve25519xsalsa20poly1305_tweet_NONCEBYTES 24 |
|||
#define crypto_box_curve25519xsalsa20poly1305_tweet_ZEROBYTES 32 |
|||
#define crypto_box_curve25519xsalsa20poly1305_tweet_BOXZEROBYTES 16 |
|||
extern int crypto_box_curve25519xsalsa20poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *,const unsigned char *); |
|||
extern int crypto_box_curve25519xsalsa20poly1305_tweet_open(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *,const unsigned char *); |
|||
extern int crypto_box_curve25519xsalsa20poly1305_tweet_keypair(unsigned char *,unsigned char *); |
|||
extern int crypto_box_curve25519xsalsa20poly1305_tweet_priv2pub(unsigned char *,unsigned char *); |
|||
extern int crypto_box_curve25519xsalsa20poly1305_tweet_beforenm(unsigned char *,const unsigned char *,const unsigned char *); |
|||
extern int crypto_box_curve25519xsalsa20poly1305_tweet_afternm(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); |
|||
extern int crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); |
|||
#define crypto_box_curve25519xsalsa20poly1305_tweet_VERSION "-" |
|||
#define crypto_box_curve25519xsalsa20poly1305 crypto_box_curve25519xsalsa20poly1305_tweet |
|||
#define crypto_box_curve25519xsalsa20poly1305_open crypto_box_curve25519xsalsa20poly1305_tweet_open |
|||
#define crypto_box_curve25519xsalsa20poly1305_keypair crypto_box_curve25519xsalsa20poly1305_tweet_keypair |
|||
#define crypto_box_curve25519xsalsa20poly1305_priv2pub crypto_box_curve25519xsalsa20poly1305_tweet_priv2pub |
|||
#define crypto_box_curve25519xsalsa20poly1305_beforenm crypto_box_curve25519xsalsa20poly1305_tweet_beforenm |
|||
#define crypto_box_curve25519xsalsa20poly1305_afternm crypto_box_curve25519xsalsa20poly1305_tweet_afternm |
|||
#define crypto_box_curve25519xsalsa20poly1305_open_afternm crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm |
|||
#define crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES crypto_box_curve25519xsalsa20poly1305_tweet_PUBLICKEYBYTES |
|||
#define crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES crypto_box_curve25519xsalsa20poly1305_tweet_SECRETKEYBYTES |
|||
#define crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES crypto_box_curve25519xsalsa20poly1305_tweet_BEFORENMBYTES |
|||
#define crypto_box_curve25519xsalsa20poly1305_NONCEBYTES crypto_box_curve25519xsalsa20poly1305_tweet_NONCEBYTES |
|||
#define crypto_box_curve25519xsalsa20poly1305_ZEROBYTES crypto_box_curve25519xsalsa20poly1305_tweet_ZEROBYTES |
|||
#define crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES crypto_box_curve25519xsalsa20poly1305_tweet_BOXZEROBYTES |
|||
#define crypto_box_curve25519xsalsa20poly1305_VERSION crypto_box_curve25519xsalsa20poly1305_tweet_VERSION |
|||
#define crypto_box_curve25519xsalsa20poly1305_IMPLEMENTATION "crypto_box/curve25519xsalsa20poly1305/tweet" |
|||
#define crypto_core_PRIMITIVE "salsa20" |
|||
#define crypto_core crypto_core_salsa20 |
|||
#define crypto_core_OUTPUTBYTES crypto_core_salsa20_OUTPUTBYTES |
|||
#define crypto_core_INPUTBYTES crypto_core_salsa20_INPUTBYTES |
|||
#define crypto_core_KEYBYTES crypto_core_salsa20_KEYBYTES |
|||
#define crypto_core_CONSTBYTES crypto_core_salsa20_CONSTBYTES |
|||
#define crypto_core_IMPLEMENTATION crypto_core_salsa20_IMPLEMENTATION |
|||
#define crypto_core_VERSION crypto_core_salsa20_VERSION |
|||
#define crypto_core_salsa20_tweet_OUTPUTBYTES 64 |
|||
#define crypto_core_salsa20_tweet_INPUTBYTES 16 |
|||
#define crypto_core_salsa20_tweet_KEYBYTES 32 |
|||
#define crypto_core_salsa20_tweet_CONSTBYTES 16 |
|||
extern int crypto_core_salsa20_tweet(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *); |
|||
#define crypto_core_salsa20_tweet_VERSION "-" |
|||
#define crypto_core_salsa20 crypto_core_salsa20_tweet |
|||
#define crypto_core_salsa20_OUTPUTBYTES crypto_core_salsa20_tweet_OUTPUTBYTES |
|||
#define crypto_core_salsa20_INPUTBYTES crypto_core_salsa20_tweet_INPUTBYTES |
|||
#define crypto_core_salsa20_KEYBYTES crypto_core_salsa20_tweet_KEYBYTES |
|||
#define crypto_core_salsa20_CONSTBYTES crypto_core_salsa20_tweet_CONSTBYTES |
|||
#define crypto_core_salsa20_VERSION crypto_core_salsa20_tweet_VERSION |
|||
#define crypto_core_salsa20_IMPLEMENTATION "crypto_core/salsa20/tweet" |
|||
#define crypto_core_hsalsa20_tweet_OUTPUTBYTES 32 |
|||
#define crypto_core_hsalsa20_tweet_INPUTBYTES 16 |
|||
#define crypto_core_hsalsa20_tweet_KEYBYTES 32 |
|||
#define crypto_core_hsalsa20_tweet_CONSTBYTES 16 |
|||
extern int crypto_core_hsalsa20_tweet(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *); |
|||
#define crypto_core_hsalsa20_tweet_VERSION "-" |
|||
#define crypto_core_hsalsa20 crypto_core_hsalsa20_tweet |
|||
#define crypto_core_hsalsa20_OUTPUTBYTES crypto_core_hsalsa20_tweet_OUTPUTBYTES |
|||
#define crypto_core_hsalsa20_INPUTBYTES crypto_core_hsalsa20_tweet_INPUTBYTES |
|||
#define crypto_core_hsalsa20_KEYBYTES crypto_core_hsalsa20_tweet_KEYBYTES |
|||
#define crypto_core_hsalsa20_CONSTBYTES crypto_core_hsalsa20_tweet_CONSTBYTES |
|||
#define crypto_core_hsalsa20_VERSION crypto_core_hsalsa20_tweet_VERSION |
|||
#define crypto_core_hsalsa20_IMPLEMENTATION "crypto_core/hsalsa20/tweet" |
|||
#define crypto_hashblocks_PRIMITIVE "sha512" |
|||
#define crypto_hashblocks crypto_hashblocks_sha512 |
|||
#define crypto_hashblocks_STATEBYTES crypto_hashblocks_sha512_STATEBYTES |
|||
#define crypto_hashblocks_BLOCKBYTES crypto_hashblocks_sha512_BLOCKBYTES |
|||
#define crypto_hashblocks_IMPLEMENTATION crypto_hashblocks_sha512_IMPLEMENTATION |
|||
#define crypto_hashblocks_VERSION crypto_hashblocks_sha512_VERSION |
|||
#define crypto_hashblocks_sha512_tweet_STATEBYTES 64 |
|||
#define crypto_hashblocks_sha512_tweet_BLOCKBYTES 128 |
|||
extern int crypto_hashblocks_sha512_tweet(unsigned char *,const unsigned char *,unsigned long long); |
|||
#define crypto_hashblocks_sha512_tweet_VERSION "-" |
|||
#define crypto_hashblocks_sha512 crypto_hashblocks_sha512_tweet |
|||
#define crypto_hashblocks_sha512_STATEBYTES crypto_hashblocks_sha512_tweet_STATEBYTES |
|||
#define crypto_hashblocks_sha512_BLOCKBYTES crypto_hashblocks_sha512_tweet_BLOCKBYTES |
|||
#define crypto_hashblocks_sha512_VERSION crypto_hashblocks_sha512_tweet_VERSION |
|||
#define crypto_hashblocks_sha512_IMPLEMENTATION "crypto_hashblocks/sha512/tweet" |
|||
#define crypto_hashblocks_sha256_tweet_STATEBYTES 32 |
|||
#define crypto_hashblocks_sha256_tweet_BLOCKBYTES 64 |
|||
extern int crypto_hashblocks_sha256_tweet(unsigned char *,const unsigned char *,unsigned long long); |
|||
#define crypto_hashblocks_sha256_tweet_VERSION "-" |
|||
#define crypto_hashblocks_sha256 crypto_hashblocks_sha256_tweet |
|||
#define crypto_hashblocks_sha256_STATEBYTES crypto_hashblocks_sha256_tweet_STATEBYTES |
|||
#define crypto_hashblocks_sha256_BLOCKBYTES crypto_hashblocks_sha256_tweet_BLOCKBYTES |
|||
#define crypto_hashblocks_sha256_VERSION crypto_hashblocks_sha256_tweet_VERSION |
|||
#define crypto_hashblocks_sha256_IMPLEMENTATION "crypto_hashblocks/sha256/tweet" |
|||
#define crypto_hash_PRIMITIVE "sha512" |
|||
#define crypto_hash crypto_hash_sha512 |
|||
#define crypto_hash_BYTES crypto_hash_sha512_BYTES |
|||
#define crypto_hash_IMPLEMENTATION crypto_hash_sha512_IMPLEMENTATION |
|||
#define crypto_hash_VERSION crypto_hash_sha512_VERSION |
|||
#define crypto_hash_sha512_tweet_BYTES 64 |
|||
extern int crypto_hash_sha512_tweet(unsigned char *,const unsigned char *,unsigned long long); |
|||
#define crypto_hash_sha512_tweet_VERSION "-" |
|||
#define crypto_hash_sha512 crypto_hash_sha512_tweet |
|||
#define crypto_hash_sha512_BYTES crypto_hash_sha512_tweet_BYTES |
|||
#define crypto_hash_sha512_VERSION crypto_hash_sha512_tweet_VERSION |
|||
#define crypto_hash_sha512_IMPLEMENTATION "crypto_hash/sha512/tweet" |
|||
#define crypto_hash_sha256_tweet_BYTES 32 |
|||
extern int crypto_hash_sha256_tweet(unsigned char *,const unsigned char *,unsigned long long); |
|||
#define crypto_hash_sha256_tweet_VERSION "-" |
|||
#define crypto_hash_sha256 crypto_hash_sha256_tweet |
|||
#define crypto_hash_sha256_BYTES crypto_hash_sha256_tweet_BYTES |
|||
#define crypto_hash_sha256_VERSION crypto_hash_sha256_tweet_VERSION |
|||
#define crypto_hash_sha256_IMPLEMENTATION "crypto_hash/sha256/tweet" |
|||
#define crypto_onetimeauth_PRIMITIVE "poly1305" |
|||
#define crypto_onetimeauth crypto_onetimeauth_poly1305 |
|||
#define crypto_onetimeauth_verify crypto_onetimeauth_poly1305_verify |
|||
#define crypto_onetimeauth_BYTES crypto_onetimeauth_poly1305_BYTES |
|||
#define crypto_onetimeauth_KEYBYTES crypto_onetimeauth_poly1305_KEYBYTES |
|||
#define crypto_onetimeauth_IMPLEMENTATION crypto_onetimeauth_poly1305_IMPLEMENTATION |
|||
#define crypto_onetimeauth_VERSION crypto_onetimeauth_poly1305_VERSION |
|||
#define crypto_onetimeauth_poly1305_tweet_BYTES 16 |
|||
#define crypto_onetimeauth_poly1305_tweet_KEYBYTES 32 |
|||
extern int crypto_onetimeauth_poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); |
|||
extern int crypto_onetimeauth_poly1305_tweet_verify(const unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); |
|||
#define crypto_onetimeauth_poly1305_tweet_VERSION "-" |
|||
#define crypto_onetimeauth_poly1305 crypto_onetimeauth_poly1305_tweet |
|||
#define crypto_onetimeauth_poly1305_verify crypto_onetimeauth_poly1305_tweet_verify |
|||
#define crypto_onetimeauth_poly1305_BYTES crypto_onetimeauth_poly1305_tweet_BYTES |
|||
#define crypto_onetimeauth_poly1305_KEYBYTES crypto_onetimeauth_poly1305_tweet_KEYBYTES |
|||
#define crypto_onetimeauth_poly1305_VERSION crypto_onetimeauth_poly1305_tweet_VERSION |
|||
#define crypto_onetimeauth_poly1305_IMPLEMENTATION "crypto_onetimeauth/poly1305/tweet" |
|||
#define crypto_scalarmult_PRIMITIVE "curve25519" |
|||
#define crypto_scalarmult crypto_scalarmult_curve25519 |
|||
#define crypto_scalarmult_base crypto_scalarmult_curve25519_base |
|||
#define crypto_scalarmult_BYTES crypto_scalarmult_curve25519_BYTES |
|||
#define crypto_scalarmult_SCALARBYTES crypto_scalarmult_curve25519_SCALARBYTES |
|||
#define crypto_scalarmult_IMPLEMENTATION crypto_scalarmult_curve25519_IMPLEMENTATION |
|||
#define crypto_scalarmult_VERSION crypto_scalarmult_curve25519_VERSION |
|||
#define crypto_scalarmult_curve25519_tweet_BYTES 32 |
|||
#define crypto_scalarmult_curve25519_tweet_SCALARBYTES 32 |
|||
extern int crypto_scalarmult_curve25519_tweet(unsigned char *,const unsigned char *,const unsigned char *); |
|||
extern int crypto_scalarmult_curve25519_tweet_base(unsigned char *,const unsigned char *); |
|||
#define crypto_scalarmult_curve25519_tweet_VERSION "-" |
|||
#define crypto_scalarmult_curve25519 crypto_scalarmult_curve25519_tweet |
|||
#define crypto_scalarmult_curve25519_base crypto_scalarmult_curve25519_tweet_base |
|||
#define crypto_scalarmult_curve25519_BYTES crypto_scalarmult_curve25519_tweet_BYTES |
|||
#define crypto_scalarmult_curve25519_SCALARBYTES crypto_scalarmult_curve25519_tweet_SCALARBYTES |
|||
#define crypto_scalarmult_curve25519_VERSION crypto_scalarmult_curve25519_tweet_VERSION |
|||
#define crypto_scalarmult_curve25519_IMPLEMENTATION "crypto_scalarmult/curve25519/tweet" |
|||
#define crypto_secretbox_PRIMITIVE "xsalsa20poly1305" |
|||
#define crypto_secretbox crypto_secretbox_xsalsa20poly1305 |
|||
#define crypto_secretbox_open crypto_secretbox_xsalsa20poly1305_open |
|||
#define crypto_secretbox_KEYBYTES crypto_secretbox_xsalsa20poly1305_KEYBYTES |
|||
#define crypto_secretbox_NONCEBYTES crypto_secretbox_xsalsa20poly1305_NONCEBYTES |
|||
#define crypto_secretbox_ZEROBYTES crypto_secretbox_xsalsa20poly1305_ZEROBYTES |
|||
#define crypto_secretbox_BOXZEROBYTES crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES |
|||
#define crypto_secretbox_IMPLEMENTATION crypto_secretbox_xsalsa20poly1305_IMPLEMENTATION |
|||
#define crypto_secretbox_VERSION crypto_secretbox_xsalsa20poly1305_VERSION |
|||
#define crypto_secretbox_xsalsa20poly1305_tweet_KEYBYTES 32 |
|||
#define crypto_secretbox_xsalsa20poly1305_tweet_NONCEBYTES 24 |
|||
#define crypto_secretbox_xsalsa20poly1305_tweet_ZEROBYTES 32 |
|||
#define crypto_secretbox_xsalsa20poly1305_tweet_BOXZEROBYTES 16 |
|||
extern int crypto_secretbox_xsalsa20poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); |
|||
extern int crypto_secretbox_xsalsa20poly1305_tweet_open(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); |
|||
#define crypto_secretbox_xsalsa20poly1305_tweet_VERSION "-" |
|||
#define crypto_secretbox_xsalsa20poly1305 crypto_secretbox_xsalsa20poly1305_tweet |
|||
#define crypto_secretbox_xsalsa20poly1305_open crypto_secretbox_xsalsa20poly1305_tweet_open |
|||
#define crypto_secretbox_xsalsa20poly1305_KEYBYTES crypto_secretbox_xsalsa20poly1305_tweet_KEYBYTES |
|||
#define crypto_secretbox_xsalsa20poly1305_NONCEBYTES crypto_secretbox_xsalsa20poly1305_tweet_NONCEBYTES |
|||
#define crypto_secretbox_xsalsa20poly1305_ZEROBYTES crypto_secretbox_xsalsa20poly1305_tweet_ZEROBYTES |
|||
#define crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES crypto_secretbox_xsalsa20poly1305_tweet_BOXZEROBYTES |
|||
#define crypto_secretbox_xsalsa20poly1305_VERSION crypto_secretbox_xsalsa20poly1305_tweet_VERSION |
|||
#define crypto_secretbox_xsalsa20poly1305_IMPLEMENTATION "crypto_secretbox/xsalsa20poly1305/tweet" |
|||
#define crypto_sign_PRIMITIVE "ed25519" |
|||
#define crypto_sign crypto_sign_ed25519 |
|||
#define crypto_sign_open crypto_sign_ed25519_open |
|||
#define crypto_sign_keypair crypto_sign_ed25519_keypair |
|||
#define crypto_sign_BYTES crypto_sign_ed25519_BYTES |
|||
#define crypto_sign_PUBLICKEYBYTES crypto_sign_ed25519_PUBLICKEYBYTES |
|||
#define crypto_sign_SECRETKEYBYTES crypto_sign_ed25519_SECRETKEYBYTES |
|||
#define crypto_sign_IMPLEMENTATION crypto_sign_ed25519_IMPLEMENTATION |
|||
#define crypto_sign_VERSION crypto_sign_ed25519_VERSION |
|||
#define crypto_sign_ed25519_tweet_BYTES 64 |
|||
#define crypto_sign_ed25519_tweet_PUBLICKEYBYTES 32 |
|||
#define crypto_sign_ed25519_tweet_SECRETKEYBYTES 64 |
|||
extern int crypto_sign_ed25519_tweet(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); |
|||
extern int crypto_sign_ed25519_tweet_open(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); |
|||
extern int crypto_sign_ed25519_tweet_keypair(unsigned char *,unsigned char *); |
|||
#define crypto_sign_ed25519_tweet_VERSION "-" |
|||
#define crypto_sign_ed25519 crypto_sign_ed25519_tweet |
|||
#define crypto_sign_ed25519_open crypto_sign_ed25519_tweet_open |
|||
#define crypto_sign_ed25519_keypair crypto_sign_ed25519_tweet_keypair |
|||
#define crypto_sign_ed25519_BYTES crypto_sign_ed25519_tweet_BYTES |
|||
#define crypto_sign_ed25519_PUBLICKEYBYTES crypto_sign_ed25519_tweet_PUBLICKEYBYTES |
|||
#define crypto_sign_ed25519_SECRETKEYBYTES crypto_sign_ed25519_tweet_SECRETKEYBYTES |
|||
#define crypto_sign_ed25519_VERSION crypto_sign_ed25519_tweet_VERSION |
|||
#define crypto_sign_ed25519_IMPLEMENTATION "crypto_sign/ed25519/tweet" |
|||
#define crypto_stream_PRIMITIVE "xsalsa20" |
|||
#define crypto_stream crypto_stream_xsalsa20 |
|||
#define crypto_stream_xor crypto_stream_xsalsa20_xor |
|||
#define crypto_stream_KEYBYTES crypto_stream_xsalsa20_KEYBYTES |
|||
#define crypto_stream_NONCEBYTES crypto_stream_xsalsa20_NONCEBYTES |
|||
#define crypto_stream_IMPLEMENTATION crypto_stream_xsalsa20_IMPLEMENTATION |
|||
#define crypto_stream_VERSION crypto_stream_xsalsa20_VERSION |
|||
#define crypto_stream_xsalsa20_tweet_KEYBYTES 32 |
|||
#define crypto_stream_xsalsa20_tweet_NONCEBYTES 24 |
|||
extern int crypto_stream_xsalsa20_tweet(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); |
|||
extern int crypto_stream_xsalsa20_tweet_xor(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); |
|||
#define crypto_stream_xsalsa20_tweet_VERSION "-" |
|||
#define crypto_stream_xsalsa20 crypto_stream_xsalsa20_tweet |
|||
#define crypto_stream_xsalsa20_xor crypto_stream_xsalsa20_tweet_xor |
|||
#define crypto_stream_xsalsa20_KEYBYTES crypto_stream_xsalsa20_tweet_KEYBYTES |
|||
#define crypto_stream_xsalsa20_NONCEBYTES crypto_stream_xsalsa20_tweet_NONCEBYTES |
|||
#define crypto_stream_xsalsa20_VERSION crypto_stream_xsalsa20_tweet_VERSION |
|||
#define crypto_stream_xsalsa20_IMPLEMENTATION "crypto_stream/xsalsa20/tweet" |
|||
#define crypto_stream_salsa20_tweet_KEYBYTES 32 |
|||
#define crypto_stream_salsa20_tweet_NONCEBYTES 8 |
|||
extern int crypto_stream_salsa20_tweet(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); |
|||
extern int crypto_stream_salsa20_tweet_xor(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); |
|||
#define crypto_stream_salsa20_tweet_VERSION "-" |
|||
#define crypto_stream_salsa20 crypto_stream_salsa20_tweet |
|||
#define crypto_stream_salsa20_xor crypto_stream_salsa20_tweet_xor |
|||
#define crypto_stream_salsa20_KEYBYTES crypto_stream_salsa20_tweet_KEYBYTES |
|||
#define crypto_stream_salsa20_NONCEBYTES crypto_stream_salsa20_tweet_NONCEBYTES |
|||
#define crypto_stream_salsa20_VERSION crypto_stream_salsa20_tweet_VERSION |
|||
#define crypto_stream_salsa20_IMPLEMENTATION "crypto_stream/salsa20/tweet" |
|||
#define crypto_verify_PRIMITIVE "16" |
|||
#define crypto_verify crypto_verify_16 |
|||
#define crypto_verify_BYTES crypto_verify_16_BYTES |
|||
#define crypto_verify_IMPLEMENTATION crypto_verify_16_IMPLEMENTATION |
|||
#define crypto_verify_VERSION crypto_verify_16_VERSION |
|||
#define crypto_verify_16_tweet_BYTES 16 |
|||
extern int crypto_verify_16_tweet(const unsigned char *,const unsigned char *); |
|||
#define crypto_verify_16_tweet_VERSION "-" |
|||
#define crypto_verify_16 crypto_verify_16_tweet |
|||
#define crypto_verify_16_BYTES crypto_verify_16_tweet_BYTES |
|||
#define crypto_verify_16_VERSION crypto_verify_16_tweet_VERSION |
|||
#define crypto_verify_16_IMPLEMENTATION "crypto_verify/16/tweet" |
|||
#define crypto_verify_32_tweet_BYTES 32 |
|||
extern int crypto_verify_32_tweet(const unsigned char *,const unsigned char *); |
|||
#define crypto_verify_32_tweet_VERSION "-" |
|||
#define crypto_verify_32 crypto_verify_32_tweet |
|||
#define crypto_verify_32_BYTES crypto_verify_32_tweet_BYTES |
|||
#define crypto_verify_32_VERSION crypto_verify_32_tweet_VERSION |
|||
#define crypto_verify_32_IMPLEMENTATION "crypto_verify/32/tweet" |
|||
#endif |
@ -0,0 +1,963 @@ |
|||
|
|||
/*
|
|||
Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
|
|||
All rights reserved. |
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions are met: |
|||
* Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
|||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
|||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
|||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
|||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#ifndef UTHASH_H |
|||
#define UTHASH_H |
|||
|
|||
//#define HASH_BLOOM 16
|
|||
|
|||
#include <string.h> /* memcmp,strlen */ |
|||
#include <stddef.h> /* ptrdiff_t */ |
|||
#include <stdlib.h> /* exit() */ |
|||
|
|||
/* These macros use decltype or the earlier __typeof GNU extension.
|
|||
As decltype is only available in newer compilers (VS2010 or gcc 4.3+ |
|||
when compiling c++ source) this code uses whatever method is needed |
|||
or, for VS2008 where neither is available, uses casting workarounds. */ |
|||
#if defined(_MSC_VER) /* MS compiler */ |
|||
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ |
|||
#define DECLTYPE(x) (decltype(x)) |
|||
#else /* VS2008 or older (or VS2010 in C mode) */ |
|||
#define NO_DECLTYPE |
|||
#define DECLTYPE(x) |
|||
#endif |
|||
#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) |
|||
#define NO_DECLTYPE |
|||
#define DECLTYPE(x) |
|||
#else /* GNU, Sun and other compilers */ |
|||
#define DECLTYPE(x) (__typeof(x)) |
|||
#endif |
|||
|
|||
#ifdef NO_DECLTYPE |
|||
#define DECLTYPE_ASSIGN(dst,src) \ |
|||
do { \ |
|||
char **_da_dst = (char**)(&(dst)); \ |
|||
*_da_dst = (char*)(src); \ |
|||
} while(0) |
|||
#else |
|||
#define DECLTYPE_ASSIGN(dst,src) \ |
|||
do { \ |
|||
(dst) = DECLTYPE(dst)(src); \ |
|||
} while(0) |
|||
#endif |
|||
|
|||
/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ |
|||
#if defined (_WIN32) |
|||
#if defined(_MSC_VER) && _MSC_VER >= 1600 |
|||
#include <stdint.h> |
|||
#elif defined(__WATCOMC__) |
|||
#include <stdint.h> |
|||
#else |
|||
//typedef unsigned int uint32_t;
|
|||
//typedef unsigned char uint8_t;
|
|||
#endif |
|||
#else |
|||
#include <stdint.h> |
|||
#endif |
|||
|
|||
#define UTHASH_VERSION 1.9.9 |
|||
|
|||
#ifndef uthash_fatal |
|||
#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ |
|||
#endif |
|||
#ifndef uthash_malloc |
|||
#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ |
|||
#endif |
|||
#ifndef uthash_free |
|||
#define uthash_free(ptr,sz) free(ptr) /* free fcn */ |
|||
#endif |
|||
|
|||
#ifndef uthash_noexpand_fyi |
|||
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ |
|||
#endif |
|||
#ifndef uthash_expand_fyi |
|||
#define uthash_expand_fyi(tbl) /* can be defined to log expands */ |
|||
#endif |
|||
|
|||
/* initial number of buckets */ |
|||
#ifndef HASH_INITIAL_NUM_BUCKETS_LOG2 |
|||
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ |
|||
#endif |
|||
#define HASH_INITIAL_NUM_BUCKETS (1<<HASH_INITIAL_NUM_BUCKETS_LOG2) /* initial number of buckets */ |
|||
#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ |
|||
|
|||
/* calculate the element whose hash handle address is hhe */ |
|||
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) |
|||
|
|||
#define HASH_FIND(hh,head,keyptr,keylen,out) \ |
|||
do { \ |
|||
out=NULL; \ |
|||
if (head) { \ |
|||
uint32_t _hf_bkt,_hf_hashv; \ |
|||
HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ |
|||
if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ |
|||
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ |
|||
keyptr,keylen,out); \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#ifdef HASH_BLOOM |
|||
#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) |
|||
#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) |
|||
#define HASH_BLOOM_MAKE(tbl) \ |
|||
do { \ |
|||
(tbl)->bloom_nbits = HASH_BLOOM; \ |
|||
(tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ |
|||
if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ |
|||
memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ |
|||
(tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ |
|||
} while (0) |
|||
|
|||
#define HASH_BLOOM_FREE(tbl) \ |
|||
do { \ |
|||
uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ |
|||
} while (0) |
|||
|
|||
#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) |
|||
#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) |
|||
|
|||
#define HASH_BLOOM_ADD(tbl,hashv) \ |
|||
HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) |
|||
|
|||
#define HASH_BLOOM_TEST(tbl,hashv) \ |
|||
HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) |
|||
|
|||
#else |
|||
#define HASH_BLOOM_MAKE(tbl) |
|||
#define HASH_BLOOM_FREE(tbl) |
|||
#define HASH_BLOOM_ADD(tbl,hashv) |
|||
#define HASH_BLOOM_TEST(tbl,hashv) (1) |
|||
#define HASH_BLOOM_BYTELEN 0 |
|||
#endif |
|||
|
|||
#define HASH_MAKE_TABLE(hh,head) \ |
|||
do { \ |
|||
(head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ |
|||
sizeof(UT_hash_table)); \ |
|||
if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ |
|||
memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ |
|||
(head)->hh.tbl->tail = &((head)->hh); \ |
|||
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ |
|||
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ |
|||
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ |
|||
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ |
|||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ |
|||
if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ |
|||
memset((head)->hh.tbl->buckets, 0, \ |
|||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ |
|||
HASH_BLOOM_MAKE((head)->hh.tbl); \ |
|||
(head)->hh.tbl->signature = HASH_SIGNATURE; \ |
|||
} while(0) |
|||
|
|||
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ |
|||
HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) |
|||
|
|||
#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ |
|||
do { \ |
|||
replaced=NULL; \ |
|||
HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ |
|||
if (replaced!=NULL) { \ |
|||
HASH_DELETE(hh,head,replaced); \ |
|||
}; \ |
|||
HASH_ADD(hh,head,fieldname,keylen_in,add); \ |
|||
} while(0) |
|||
|
|||
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ |
|||
do { \ |
|||
uint32_t _ha_bkt; \ |
|||
(add)->hh.next = NULL; \ |
|||
(add)->hh.key = (char*)(keyptr); \ |
|||
(add)->hh.keylen = (uint32_t)(keylen_in); \ |
|||
if (!(head)) { \ |
|||
head = (add); \ |
|||
(head)->hh.prev = NULL; \ |
|||
HASH_MAKE_TABLE(hh,head); \ |
|||
} else { \ |
|||
(head)->hh.tbl->tail->next = (add); \ |
|||
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ |
|||
(head)->hh.tbl->tail = &((add)->hh); \ |
|||
} \ |
|||
(head)->hh.tbl->num_items++; \ |
|||
(add)->hh.tbl = (head)->hh.tbl; \ |
|||
HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ |
|||
(add)->hh.hashv, _ha_bkt); \ |
|||
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ |
|||
HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ |
|||
HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ |
|||
HASH_FSCK(hh,head); \ |
|||
} while(0) |
|||
|
|||
#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ |
|||
do { \ |
|||
bkt = ((hashv) & ((num_bkts) - 1)); \ |
|||
} while(0) |
|||
|
|||
/* delete "delptr" from the hash table.
|
|||
* "the usual" patch-up process for the app-order doubly-linked-list. |
|||
* The use of _hd_hh_del below deserves special explanation. |
|||
* These used to be expressed using (delptr) but that led to a bug |
|||
* if someone used the same symbol for the head and deletee, like |
|||
* HASH_DELETE(hh,users,users); |
|||
* We want that to work, but by changing the head (users) below |
|||
* we were forfeiting our ability to further refer to the deletee (users) |
|||
* in the patch-up process. Solution: use scratch space to |
|||
* copy the deletee pointer, then the latter references are via that |
|||
* scratch pointer rather than through the repointed (users) symbol. |
|||
*/ |
|||
#define HASH_DELETE(hh,head,delptr) \ |
|||
do { \ |
|||
struct UT_hash_handle *_hd_hh_del; \ |
|||
if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ |
|||
uthash_free((head)->hh.tbl->buckets, \ |
|||
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ |
|||
HASH_BLOOM_FREE((head)->hh.tbl); \ |
|||
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ |
|||
head = NULL; \ |
|||
} else { \ |
|||
uint32_t _hd_bkt; \ |
|||
_hd_hh_del = &((delptr)->hh); \ |
|||
if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ |
|||
(head)->hh.tbl->tail = \ |
|||
(UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ |
|||
(head)->hh.tbl->hho); \ |
|||
} \ |
|||
if ((delptr)->hh.prev) { \ |
|||
((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ |
|||
(head)->hh.tbl->hho))->next = (delptr)->hh.next; \ |
|||
} else { \ |
|||
DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ |
|||
} \ |
|||
if (_hd_hh_del->next) { \ |
|||
((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ |
|||
(head)->hh.tbl->hho))->prev = \ |
|||
_hd_hh_del->prev; \ |
|||
} \ |
|||
HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ |
|||
HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ |
|||
(head)->hh.tbl->num_items--; \ |
|||
} \ |
|||
HASH_FSCK(hh,head); \ |
|||
} while (0) |
|||
|
|||
|
|||
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ |
|||
#define HASH_FIND_STR(head,findstr,out) \ |
|||
HASH_FIND(hh,head,findstr,(uint32_t)strlen(findstr),out) |
|||
#define HASH_ADD_STR(head,strfield,add) \ |
|||
HASH_ADD(hh,head,strfield[0],strlen(add->strfield),add) |
|||
#define HASH_REPLACE_STR(head,strfield,add,replaced) \ |
|||
HASH_REPLACE(hh,head,strfield[0],(uint32_t)strlen(add->strfield),add,replaced) |
|||
#define HASH_FIND_INT(head,findint,out) \ |
|||
HASH_FIND(hh,head,findint,sizeof(int),out) |
|||
#define HASH_ADD_INT(head,intfield,add) \ |
|||
HASH_ADD(hh,head,intfield,sizeof(int),add) |
|||
#define HASH_REPLACE_INT(head,intfield,add,replaced) \ |
|||
HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) |
|||
#define HASH_FIND_PTR(head,findptr,out) \ |
|||
HASH_FIND(hh,head,findptr,sizeof(void *),out) |
|||
#define HASH_ADD_PTR(head,ptrfield,add) \ |
|||
HASH_ADD(hh,head,ptrfield,sizeof(void *),add) |
|||
#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ |
|||
HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) |
|||
#define HASH_DEL(head,delptr) \ |
|||
HASH_DELETE(hh,head,delptr) |
|||
|
|||
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
|
|||
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. |
|||
*/ |
|||
#ifdef HASH_DEBUG |
|||
#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) |
|||
#define HASH_FSCK(hh,head) \ |
|||
do { \ |
|||
struct UT_hash_handle *_thh; \ |
|||
if (head) { \ |
|||
uint32_t _bkt_i; \ |
|||
uint32_t _count; \ |
|||
char *_prev; \ |
|||
_count = 0; \ |
|||
for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ |
|||
uint32_t _bkt_count = 0; \ |
|||
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ |
|||
_prev = NULL; \ |
|||
while (_thh) { \ |
|||
if (_prev != (char*)(_thh->hh_prev)) { \ |
|||
HASH_OOPS("invalid hh_prev %p, actual %p\n", \ |
|||
_thh->hh_prev, _prev ); \ |
|||
} \ |
|||
_bkt_count++; \ |
|||
_prev = (char*)(_thh); \ |
|||
_thh = _thh->hh_next; \ |
|||
} \ |
|||
_count += _bkt_count; \ |
|||
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ |
|||
HASH_OOPS("invalid bucket count %u, actual %u\n", \ |
|||
(head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ |
|||
} \ |
|||
} \ |
|||
if (_count != (head)->hh.tbl->num_items) { \ |
|||
HASH_OOPS("invalid hh item count %u, actual %u\n", \ |
|||
(head)->hh.tbl->num_items, _count ); \ |
|||
} \ |
|||
/* traverse hh in app order; check next/prev integrity, count */ \ |
|||
_count = 0; \ |
|||
_prev = NULL; \ |
|||
_thh = &(head)->hh; \ |
|||
while (_thh) { \ |
|||
_count++; \ |
|||
if (_prev !=(char*)(_thh->prev)) { \ |
|||
HASH_OOPS("invalid prev %p, actual %p\n", \ |
|||
_thh->prev, _prev ); \ |
|||
} \ |
|||
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ |
|||
_thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ |
|||
(head)->hh.tbl->hho) : NULL ); \ |
|||
} \ |
|||
if (_count != (head)->hh.tbl->num_items) { \ |
|||
HASH_OOPS("invalid app item count %u, actual %u\n", \ |
|||
(head)->hh.tbl->num_items, _count ); \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
#else |
|||
#define HASH_FSCK(hh,head) |
|||
#endif |
|||
|
|||
/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
|
|||
* the descriptor to which this macro is defined for tuning the hash function. |
|||
* The app can #include <unistd.h> to get the prototype for write(2). */ |
|||
#ifdef HASH_EMIT_KEYS |
|||
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ |
|||
do { \ |
|||
uint32_t _klen = fieldlen; \ |
|||
write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ |
|||
write(HASH_EMIT_KEYS, keyptr, fieldlen); \ |
|||
} while (0) |
|||
#else |
|||
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) |
|||
#endif |
|||
|
|||
/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ |
|||
#ifdef HASH_FUNCTION |
|||
#define HASH_FCN HASH_FUNCTION |
|||
#else |
|||
#define HASH_FCN HASH_JEN |
|||
#endif |
|||
|
|||
/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ |
|||
#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ |
|||
do { \ |
|||
uint32_t _hb_keylen=keylen; \ |
|||
char *_hb_key=(char*)(key); \ |
|||
(hashv) = 0; \ |
|||
while (_hb_keylen--) { (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; } \ |
|||
bkt = (hashv) & (num_bkts-1); \ |
|||
} while (0) |
|||
|
|||
|
|||
/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
|
|||
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
|
|||
#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ |
|||
do { \ |
|||
uint32_t _sx_i; \ |
|||
char *_hs_key=(char*)(key); \ |
|||
hashv = 0; \ |
|||
for(_sx_i=0; _sx_i < keylen; _sx_i++) \ |
|||
hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ |
|||
bkt = hashv & (num_bkts-1); \ |
|||
} while (0) |
|||
/* FNV-1a variation */ |
|||
#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ |
|||
do { \ |
|||
uint32_t _fn_i; \ |
|||
char *_hf_key=(char*)(key); \ |
|||
hashv = 2166136261UL; \ |
|||
for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ |
|||
hashv = hashv ^ _hf_key[_fn_i]; \ |
|||
hashv = hashv * 16777619; \ |
|||
} \ |
|||
bkt = hashv & (num_bkts-1); \ |
|||
} while(0) |
|||
|
|||
#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ |
|||
do { \ |
|||
uint32_t _ho_i; \ |
|||
char *_ho_key=(char*)(key); \ |
|||
hashv = 0; \ |
|||
for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ |
|||
hashv += _ho_key[_ho_i]; \ |
|||
hashv += (hashv << 10); \ |
|||
hashv ^= (hashv >> 6); \ |
|||
} \ |
|||
hashv += (hashv << 3); \ |
|||
hashv ^= (hashv >> 11); \ |
|||
hashv += (hashv << 15); \ |
|||
bkt = hashv & (num_bkts-1); \ |
|||
} while(0) |
|||
|
|||
#define HASH_JEN_MIX(a,b,c) \ |
|||
do { \ |
|||
a -= b; a -= c; a ^= ( c >> 13 ); \ |
|||
b -= c; b -= a; b ^= ( a << 8 ); \ |
|||
c -= a; c -= b; c ^= ( b >> 13 ); \ |
|||
a -= b; a -= c; a ^= ( c >> 12 ); \ |
|||
b -= c; b -= a; b ^= ( a << 16 ); \ |
|||
c -= a; c -= b; c ^= ( b >> 5 ); \ |
|||
a -= b; a -= c; a ^= ( c >> 3 ); \ |
|||
b -= c; b -= a; b ^= ( a << 10 ); \ |
|||
c -= a; c -= b; c ^= ( b >> 15 ); \ |
|||
} while (0) |
|||
|
|||
#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ |
|||
do { \ |
|||
uint32_t _hj_i,_hj_j,_hj_k; \ |
|||
unsigned char *_hj_key=(unsigned char*)(key); \ |
|||
hashv = 0xfeedbeef; \ |
|||
_hj_i = _hj_j = 0x9e3779b9; \ |
|||
_hj_k = (uint32_t)(keylen); \ |
|||
while (_hj_k >= 12) { \ |
|||
_hj_i += (_hj_key[0] + ( (uint32_t)_hj_key[1] << 8 ) \ |
|||
+ ( (uint32_t)_hj_key[2] << 16 ) \ |
|||
+ ( (uint32_t)_hj_key[3] << 24 ) ); \ |
|||
_hj_j += (_hj_key[4] + ( (uint32_t)_hj_key[5] << 8 ) \ |
|||
+ ( (uint32_t)_hj_key[6] << 16 ) \ |
|||
+ ( (uint32_t)_hj_key[7] << 24 ) ); \ |
|||
hashv += (_hj_key[8] + ( (uint32_t)_hj_key[9] << 8 ) \ |
|||
+ ( (uint32_t)_hj_key[10] << 16 ) \ |
|||
+ ( (uint32_t)_hj_key[11] << 24 ) ); \ |
|||
\ |
|||
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ |
|||
\ |
|||
_hj_key += 12; \ |
|||
_hj_k -= 12; \ |
|||
} \ |
|||
hashv += keylen; \ |
|||
switch ( _hj_k ) { \ |
|||
case 11: hashv += ( (uint32_t)_hj_key[10] << 24 ); \ |
|||
case 10: hashv += ( (uint32_t)_hj_key[9] << 16 ); \ |
|||
case 9: hashv += ( (uint32_t)_hj_key[8] << 8 ); \ |
|||
case 8: _hj_j += ( (uint32_t)_hj_key[7] << 24 ); \ |
|||
case 7: _hj_j += ( (uint32_t)_hj_key[6] << 16 ); \ |
|||
case 6: _hj_j += ( (uint32_t)_hj_key[5] << 8 ); \ |
|||
case 5: _hj_j += _hj_key[4]; \ |
|||
case 4: _hj_i += ( (uint32_t)_hj_key[3] << 24 ); \ |
|||
case 3: _hj_i += ( (uint32_t)_hj_key[2] << 16 ); \ |
|||
case 2: _hj_i += ( (uint32_t)_hj_key[1] << 8 ); \ |
|||
case 1: _hj_i += _hj_key[0]; \ |
|||
} \ |
|||
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ |
|||
bkt = hashv & (num_bkts-1); \ |
|||
} while(0) |
|||
|
|||
/* The Paul Hsieh hash function */ |
|||
#undef get16bits |
|||
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ |
|||
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) |
|||
#define get16bits(d) (*((const uint16_t *) (d))) |
|||
#endif |
|||
|
|||
#if !defined (get16bits) |
|||
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ |
|||
+(uint32_t)(((const uint8_t *)(d))[0]) ) |
|||
#endif |
|||
#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ |
|||
do { \ |
|||
unsigned char *_sfh_key=(unsigned char*)(key); \ |
|||
uint32_t _sfh_tmp, _sfh_len = keylen; \ |
|||
\ |
|||
int _sfh_rem = _sfh_len & 3; \ |
|||
_sfh_len >>= 2; \ |
|||
hashv = 0xcafebabe; \ |
|||
\ |
|||
/* Main loop */ \ |
|||
for (;_sfh_len > 0; _sfh_len--) { \ |
|||
hashv += get16bits (_sfh_key); \ |
|||
_sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \ |
|||
hashv = (hashv << 16) ^ _sfh_tmp; \ |
|||
_sfh_key += 2*sizeof (uint16_t); \ |
|||
hashv += hashv >> 11; \ |
|||
} \ |
|||
\ |
|||
/* Handle end cases */ \ |
|||
switch (_sfh_rem) { \ |
|||
case 3: hashv += get16bits (_sfh_key); \ |
|||
hashv ^= hashv << 16; \ |
|||
hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \ |
|||
hashv += hashv >> 11; \ |
|||
break; \ |
|||
case 2: hashv += get16bits (_sfh_key); \ |
|||
hashv ^= hashv << 11; \ |
|||
hashv += hashv >> 17; \ |
|||
break; \ |
|||
case 1: hashv += *_sfh_key; \ |
|||
hashv ^= hashv << 10; \ |
|||
hashv += hashv >> 1; \ |
|||
} \ |
|||
\ |
|||
/* Force "avalanching" of final 127 bits */ \ |
|||
hashv ^= hashv << 3; \ |
|||
hashv += hashv >> 5; \ |
|||
hashv ^= hashv << 4; \ |
|||
hashv += hashv >> 17; \ |
|||
hashv ^= hashv << 25; \ |
|||
hashv += hashv >> 6; \ |
|||
bkt = hashv & (num_bkts-1); \ |
|||
} while(0) |
|||
|
|||
#ifdef HASH_USING_NO_STRICT_ALIASING |
|||
/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
|
|||
* For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. |
|||
* MurmurHash uses the faster approach only on CPU's where we know it's safe. |
|||
* |
|||
* Note the preprocessor built-in defines can be emitted using: |
|||
* |
|||
* gcc -m64 -dM -E - < /dev/null (on gcc) |
|||
* cc -## a.c (where a.c is a simple test file) (Sun Studio) |
|||
*/ |
|||
#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) |
|||
#define MUR_GETBLOCK(p,i) p[i] |
|||
#else /* non intel */ |
|||
#define MUR_PLUS0_ALIGNED(p) (((uint64_t)p & 0x3) == 0) |
|||
#define MUR_PLUS1_ALIGNED(p) (((uint64_t)p & 0x3) == 1) |
|||
#define MUR_PLUS2_ALIGNED(p) (((uint64_t)p & 0x3) == 2) |
|||
#define MUR_PLUS3_ALIGNED(p) (((uint64_t)p & 0x3) == 3) |
|||
#define WP(p) ((uint32_t*)((uint64_t)(p) & ~3UL)) |
|||
#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) |
|||
#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) |
|||
#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) |
|||
#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) |
|||
#else /* assume little endian non-intel */ |
|||
#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) |
|||
#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) |
|||
#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) |
|||
#endif |
|||
#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ |
|||
(MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ |
|||
(MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ |
|||
MUR_ONE_THREE(p)))) |
|||
#endif |
|||
#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) |
|||
#define MUR_FMIX(_h) \ |
|||
do { \ |
|||
_h ^= _h >> 16; \ |
|||
_h *= 0x85ebca6b; \ |
|||
_h ^= _h >> 13; \ |
|||
_h *= 0xc2b2ae35l; \ |
|||
_h ^= _h >> 16; \ |
|||
} while(0) |
|||
|
|||
#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ |
|||
do { \ |
|||
const uint8_t *_mur_data = (const uint8_t*)(key); \ |
|||
const int _mur_nblocks = (keylen) / 4; \ |
|||
uint32_t _mur_h1 = 0xf88D5353; \ |
|||
uint32_t _mur_c1 = 0xcc9e2d51; \ |
|||
uint32_t _mur_c2 = 0x1b873593; \ |
|||
uint32_t _mur_k1 = 0; \ |
|||
const uint8_t *_mur_tail; \ |
|||
const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ |
|||
int _mur_i; \ |
|||
for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ |
|||
_mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ |
|||
_mur_k1 *= _mur_c1; \ |
|||
_mur_k1 = MUR_ROTL32(_mur_k1,15); \ |
|||
_mur_k1 *= _mur_c2; \ |
|||
\ |
|||
_mur_h1 ^= _mur_k1; \ |
|||
_mur_h1 = MUR_ROTL32(_mur_h1,13); \ |
|||
_mur_h1 = _mur_h1*5+0xe6546b64; \ |
|||
} \ |
|||
_mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ |
|||
_mur_k1=0; \ |
|||
switch((keylen) & 3) { \ |
|||
case 3: _mur_k1 ^= _mur_tail[2] << 16; \ |
|||
case 2: _mur_k1 ^= _mur_tail[1] << 8; \ |
|||
case 1: _mur_k1 ^= _mur_tail[0]; \ |
|||
_mur_k1 *= _mur_c1; \ |
|||
_mur_k1 = MUR_ROTL32(_mur_k1,15); \ |
|||
_mur_k1 *= _mur_c2; \ |
|||
_mur_h1 ^= _mur_k1; \ |
|||
} \ |
|||
_mur_h1 ^= (keylen); \ |
|||
MUR_FMIX(_mur_h1); \ |
|||
hashv = _mur_h1; \ |
|||
bkt = hashv & (num_bkts-1); \ |
|||
} while(0) |
|||
#endif /* HASH_USING_NO_STRICT_ALIASING */ |
|||
|
|||
/* key comparison function; return 0 if keys equal */ |
|||
#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) |
|||
|
|||
/* iterate over items in a known bucket to find desired item */ |
|||
#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ |
|||
do { \ |
|||
if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ |
|||
else out=NULL; \ |
|||
while (out) { \ |
|||
if ((out)->hh.keylen == keylen_in) { \ |
|||
if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ |
|||
} \ |
|||
if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ |
|||
else out = NULL; \ |
|||
} \ |
|||
} while(0) |
|||
|
|||
/* add an item to a bucket */ |
|||
#define HASH_ADD_TO_BKT(head,addhh) \ |
|||
do { \ |
|||
head.count++; \ |
|||
(addhh)->hh_next = head.hh_head; \ |
|||
(addhh)->hh_prev = NULL; \ |
|||
if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ |
|||
(head).hh_head=addhh; \ |
|||
if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ |
|||
&& (addhh)->tbl->noexpand != 1) { \ |
|||
HASH_EXPAND_BUCKETS((addhh)->tbl); \ |
|||
} \ |
|||
} while(0) |
|||
|
|||
/* remove an item from a given bucket */ |
|||
#define HASH_DEL_IN_BKT(hh,head,hh_del) \ |
|||
(head).count--; \ |
|||
if ((head).hh_head == hh_del) { \ |
|||
(head).hh_head = hh_del->hh_next; \ |
|||
} \ |
|||
if (hh_del->hh_prev) { \ |
|||
hh_del->hh_prev->hh_next = hh_del->hh_next; \ |
|||
} \ |
|||
if (hh_del->hh_next) { \ |
|||
hh_del->hh_next->hh_prev = hh_del->hh_prev; \ |
|||
} |
|||
|
|||
/* Bucket expansion has the effect of doubling the number of buckets
|
|||
* and redistributing the items into the new buckets. Ideally the |
|||
* items will distribute more or less evenly into the new buckets |
|||
* (the extent to which this is true is a measure of the quality of |
|||
* the hash function as it applies to the key domain). |
|||
* |
|||
* With the items distributed into more buckets, the chain length |
|||
* (item count) in each bucket is reduced. Thus by expanding buckets |
|||
* the hash keeps a bound on the chain length. This bounded chain |
|||
* length is the essence of how a hash provides constant time lookup. |
|||
* |
|||
* The calculation of tbl->ideal_chain_maxlen below deserves some |
|||
* explanation. First, keep in mind that we're calculating the ideal |
|||
* maximum chain length based on the *new* (doubled) bucket count. |
|||
* In fractions this is just n/b (n=number of items,b=new num buckets). |
|||
* Since the ideal chain length is an integer, we want to calculate |
|||
* ceil(n/b). We don't depend on floating point arithmetic in this |
|||
* hash, so to calculate ceil(n/b) with integers we could write |
|||
* |
|||
* ceil(n/b) = (n/b) + ((n%b)?1:0) |
|||
* |
|||
* and in fact a previous version of this hash did just that. |
|||
* But now we have improved things a bit by recognizing that b is |
|||
* always a power of two. We keep its base 2 log handy (call it lb), |
|||
* so now we can write this with a bit shift and logical AND: |
|||
* |
|||
* ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) |
|||
* |
|||
*/ |
|||
#define HASH_EXPAND_BUCKETS(tbl) \ |
|||
do { \ |
|||
uint32_t _he_bkt; \ |
|||
uint32_t _he_bkt_i; \ |
|||
struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ |
|||
UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ |
|||
_he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ |
|||
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ |
|||
if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ |
|||
memset(_he_new_buckets, 0, \ |
|||
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ |
|||
tbl->ideal_chain_maxlen = \ |
|||
(tbl->num_items >> (tbl->log2_num_buckets+1)) + \ |
|||
((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ |
|||
tbl->nonideal_items = 0; \ |
|||
for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ |
|||
{ \ |
|||
_he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ |
|||
while (_he_thh) { \ |
|||
_he_hh_nxt = _he_thh->hh_next; \ |
|||
HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ |
|||
_he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ |
|||
if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ |
|||
tbl->nonideal_items++; \ |
|||
_he_newbkt->expand_mult = _he_newbkt->count / \ |
|||
tbl->ideal_chain_maxlen; \ |
|||
} \ |
|||
_he_thh->hh_prev = NULL; \ |
|||
_he_thh->hh_next = _he_newbkt->hh_head; \ |
|||
if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ |
|||
_he_thh; \ |
|||
_he_newbkt->hh_head = _he_thh; \ |
|||
_he_thh = _he_hh_nxt; \ |
|||
} \ |
|||
} \ |
|||
uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ |
|||
tbl->num_buckets *= 2; \ |
|||
tbl->log2_num_buckets++; \ |
|||
tbl->buckets = _he_new_buckets; \ |
|||
tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ |
|||
(tbl->ineff_expands+1) : 0; \ |
|||
if (tbl->ineff_expands > 1) { \ |
|||
tbl->noexpand=1; \ |
|||
uthash_noexpand_fyi(tbl); \ |
|||
} \ |
|||
uthash_expand_fyi(tbl); \ |
|||
} while(0) |
|||
|
|||
|
|||
/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ |
|||
/* Note that HASH_SORT assumes the hash handle name to be hh.
|
|||
* HASH_SRT was added to allow the hash handle name to be passed in. */ |
|||
#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) |
|||
#define HASH_SRT(hh,head,cmpfcn) \ |
|||
do { \ |
|||
uint32_t _hs_i; \ |
|||
uint32_t _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ |
|||
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ |
|||
if (head) { \ |
|||
_hs_insize = 1; \ |
|||
_hs_looping = 1; \ |
|||
_hs_list = &((head)->hh); \ |
|||
while (_hs_looping) { \ |
|||
_hs_p = _hs_list; \ |
|||
_hs_list = NULL; \ |
|||
_hs_tail = NULL; \ |
|||
_hs_nmerges = 0; \ |
|||
while (_hs_p) { \ |
|||
_hs_nmerges++; \ |
|||
_hs_q = _hs_p; \ |
|||
_hs_psize = 0; \ |
|||
for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ |
|||
_hs_psize++; \ |
|||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \ |
|||
((void*)((char*)(_hs_q->next) + \ |
|||
(head)->hh.tbl->hho)) : NULL); \ |
|||
if (! (_hs_q) ) break; \ |
|||
} \ |
|||
_hs_qsize = _hs_insize; \ |
|||
while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ |
|||
if (_hs_psize == 0) { \ |
|||
_hs_e = _hs_q; \ |
|||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \ |
|||
((void*)((char*)(_hs_q->next) + \ |
|||
(head)->hh.tbl->hho)) : NULL); \ |
|||
_hs_qsize--; \ |
|||
} else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ |
|||
_hs_e = _hs_p; \ |
|||
if (_hs_p){ \ |
|||
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \ |
|||
((void*)((char*)(_hs_p->next) + \ |
|||
(head)->hh.tbl->hho)) : NULL); \ |
|||
} \ |
|||
_hs_psize--; \ |
|||
} else if (( \ |
|||
cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ |
|||
DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ |
|||
) <= 0) { \ |
|||
_hs_e = _hs_p; \ |
|||
if (_hs_p){ \ |
|||
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \ |
|||
((void*)((char*)(_hs_p->next) + \ |
|||
(head)->hh.tbl->hho)) : NULL); \ |
|||
} \ |
|||
_hs_psize--; \ |
|||
} else { \ |
|||
_hs_e = _hs_q; \ |
|||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \ |
|||
((void*)((char*)(_hs_q->next) + \ |
|||
(head)->hh.tbl->hho)) : NULL); \ |
|||
_hs_qsize--; \ |
|||
} \ |
|||
if ( _hs_tail ) { \ |
|||
_hs_tail->next = ((_hs_e) ? \ |
|||
ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ |
|||
} else { \ |
|||
_hs_list = _hs_e; \ |
|||
} \ |
|||
if (_hs_e) { \ |
|||
_hs_e->prev = ((_hs_tail) ? \ |
|||
ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ |
|||
} \ |
|||
_hs_tail = _hs_e; \ |
|||
} \ |
|||
_hs_p = _hs_q; \ |
|||
} \ |
|||
if (_hs_tail){ \ |
|||
_hs_tail->next = NULL; \ |
|||
} \ |
|||
if ( _hs_nmerges <= 1 ) { \ |
|||
_hs_looping=0; \ |
|||
(head)->hh.tbl->tail = _hs_tail; \ |
|||
DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ |
|||
} \ |
|||
_hs_insize *= 2; \ |
|||
} \ |
|||
HASH_FSCK(hh,head); \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
/* This function selects items from one hash into another hash.
|
|||
* The end result is that the selected items have dual presence |
|||
* in both hashes. There is no copy of the items made; rather |
|||
* they are added into the new hash through a secondary hash |
|||
* hash handle that must be present in the structure. */ |
|||
#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ |
|||
do { \ |
|||
uint32_t _src_bkt, _dst_bkt; \ |
|||
void *_last_elt=NULL, *_elt; \ |
|||
UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ |
|||
ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ |
|||
if (src) { \ |
|||
for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ |
|||
for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ |
|||
_src_hh; \ |
|||
_src_hh = _src_hh->hh_next) { \ |
|||
_elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ |
|||
if (cond(_elt)) { \ |
|||
_dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ |
|||
_dst_hh->key = _src_hh->key; \ |
|||
_dst_hh->keylen = _src_hh->keylen; \ |
|||
_dst_hh->hashv = _src_hh->hashv; \ |
|||
_dst_hh->prev = _last_elt; \ |
|||
_dst_hh->next = NULL; \ |
|||
if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ |
|||
if (!dst) { \ |
|||
DECLTYPE_ASSIGN(dst,_elt); \ |
|||
HASH_MAKE_TABLE(hh_dst,dst); \ |
|||
} else { \ |
|||
_dst_hh->tbl = (dst)->hh_dst.tbl; \ |
|||
} \ |
|||
HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ |
|||
HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ |
|||
(dst)->hh_dst.tbl->num_items++; \ |
|||
_last_elt = _elt; \ |
|||
_last_elt_hh = _dst_hh; \ |
|||
} \ |
|||
} \ |
|||
} \ |
|||
} \ |
|||
HASH_FSCK(hh_dst,dst); \ |
|||
} while (0) |
|||
|
|||
#define HASH_CLEAR(hh,head) \ |
|||
do { \ |
|||
if (head) { \ |
|||
uthash_free((head)->hh.tbl->buckets, \ |
|||
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ |
|||
HASH_BLOOM_FREE((head)->hh.tbl); \ |
|||
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ |
|||
(head)=NULL; \ |
|||
} \ |
|||
} while(0) |
|||
|
|||
#define HASH_OVERHEAD(hh,head) \ |
|||
(size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ |
|||
((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ |
|||
(sizeof(UT_hash_table)) + \ |
|||
(HASH_BLOOM_BYTELEN))) |
|||
|
|||
#ifdef NO_DECLTYPE |
|||
#define HASH_ITER(hh,head,el,tmp) \ |
|||
for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ |
|||
el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) |
|||
#else |
|||
#define HASH_ITER(hh,head,el,tmp) \ |
|||
for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ |
|||
el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) |
|||
#endif |
|||
|
|||
/* obtain a count of items in the hash */ |
|||
#define HASH_COUNT(head) HASH_CNT(hh,head) |
|||
#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) |
|||
|
|||
typedef struct UT_hash_bucket { |
|||
struct UT_hash_handle *hh_head; |
|||
uint32_t count; |
|||
|
|||
/* expand_mult is normally set to 0. In this situation, the max chain length
|
|||
* threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If |
|||
* the bucket's chain exceeds this length, bucket expansion is triggered). |
|||
* However, setting expand_mult to a non-zero value delays bucket expansion |
|||
* (that would be triggered by additions to this particular bucket) |
|||
* until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. |
|||
* (The multiplier is simply expand_mult+1). The whole idea of this |
|||
* multiplier is to reduce bucket expansions, since they are expensive, in |
|||
* situations where we know that a particular bucket tends to be overused. |
|||
* It is better to let its chain length grow to a longer yet-still-bounded |
|||
* value, than to do an O(n) bucket expansion too often. |
|||
*/ |
|||
uint32_t expand_mult; |
|||
|
|||
} UT_hash_bucket; |
|||
|
|||
/* random signature used only to find hash tables in external analysis */ |
|||
#define HASH_SIGNATURE 0xa0111fe1 |
|||
#define HASH_BLOOM_SIGNATURE 0xb12220f2 |
|||
|
|||
typedef struct UT_hash_table { |
|||
UT_hash_bucket *buckets; |
|||
uint32_t num_buckets, log2_num_buckets; |
|||
uint32_t num_items; |
|||
struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ |
|||
ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ |
|||
|
|||
/* in an ideal situation (all buckets used equally), no bucket would have
|
|||
* more than ceil(#items/#buckets) items. that's the ideal chain length. */ |
|||
uint32_t ideal_chain_maxlen; |
|||
|
|||
/* nonideal_items is the number of items in the hash whose chain position
|
|||
* exceeds the ideal chain maxlen. these items pay the penalty for an uneven |
|||
* hash distribution; reaching them in a chain traversal takes >ideal steps */ |
|||
uint32_t nonideal_items; |
|||
|
|||
/* ineffective expands occur when a bucket doubling was performed, but
|
|||
* afterward, more than half the items in the hash had nonideal chain |
|||
* positions. If this happens on two consecutive expansions we inhibit any |
|||
* further expansion, as it's not helping; this happens when the hash |
|||
* function isn't a good fit for the key domain. When expansion is inhibited |
|||
* the hash will still work, albeit no longer in constant time. */ |
|||
uint32_t ineff_expands, noexpand; |
|||
|
|||
uint32_t signature; /* used only to find hash tables in external analysis */ |
|||
#ifdef HASH_BLOOM |
|||
uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ |
|||
uint8_t *bloom_bv; |
|||
char bloom_nbits; |
|||
#endif |
|||
|
|||
} UT_hash_table; |
|||
|
|||
typedef struct UT_hash_handle { |
|||
struct UT_hash_table *tbl; |
|||
void *prev; /* prev element in app order */ |
|||
void *next; /* next element in app order */ |
|||
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ |
|||
struct UT_hash_handle *hh_next; /* next hh in bucket order */ |
|||
void *key; /* ptr to enclosing struct's key */ |
|||
uint32_t hashv; /* result of hash-fcn(key) */ |
|||
uint8_t keylen; /* enclosing struct's key len */ |
|||
uint32_t itemind; |
|||
} UT_hash_handle; |
|||
|
|||
#endif /* UTHASH_H */ |
|||
|
@ -0,0 +1,753 @@ |
|||
/*
|
|||
Copyright (c) 2007-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
|
|||
All rights reserved. |
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions are met: |
|||
* Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
|||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
|||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
|||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
|||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#ifndef UTLIST_H |
|||
#define UTLIST_H |
|||
|
|||
#define UTLIST_VERSION 1.9.9 |
|||
|
|||
#include <assert.h> |
|||
|
|||
/*
|
|||
* This file contains macros to manipulate singly and doubly-linked lists. |
|||
* |
|||
* 1. LL_ macros: singly-linked lists. |
|||
* 2. DL_ macros: doubly-linked lists. |
|||
* 3. CDL_ macros: circular doubly-linked lists. |
|||
* |
|||
* To use singly-linked lists, your structure must have a "next" pointer. |
|||
* To use doubly-linked lists, your structure must "prev" and "next" pointers. |
|||
* Either way, the pointer to the head of the list must be initialized to NULL. |
|||
* |
|||
* ----------------.EXAMPLE ------------------------- |
|||
* struct item { |
|||
* int id; |
|||
* struct item *prev, *next; |
|||
* } |
|||
* |
|||
* struct item *list = NULL: |
|||
* |
|||
* int main() { |
|||
* struct item *item; |
|||
* ... allocate and populate item ... |
|||
* DL_APPEND(list, item); |
|||
* } |
|||
* -------------------------------------------------- |
|||
* |
|||
* For doubly-linked lists, the append and delete macros are O(1) |
|||
* For singly-linked lists, append and delete are O(n) but prepend is O(1) |
|||
* The sort macro is O(n log(n)) for all types of single/double/circular lists. |
|||
*/ |
|||
|
|||
/* These macros use decltype or the earlier __typeof GNU extension.
|
|||
As decltype is only available in newer compilers (VS2010 or gcc 4.3+ |
|||
when compiling c++ code), this code uses whatever method is needed |
|||
or, for VS2008 where neither is available, uses casting workarounds. */ |
|||
#ifdef _MSC_VER /* MS compiler */ |
|||
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ |
|||
#define LDECLTYPE(x) decltype(x) |
|||
#else /* VS2008 or older (or VS2010 in C mode) */ |
|||
#define NO_DECLTYPE |
|||
#define LDECLTYPE(x) char* |
|||
#endif |
|||
#elif defined(__ICCARM__) |
|||
#define NO_DECLTYPE |
|||
#define LDECLTYPE(x) char* |
|||
#else /* GNU, Sun and other compilers */ |
|||
#define LDECLTYPE(x) __typeof(x) |
|||
#endif |
|||
|
|||
/* for VS2008 we use some workarounds to get around the lack of decltype,
|
|||
* namely, we always reassign our tmp variable to the list head if we need |
|||
* to dereference its prev/next pointers, and save/restore the real head.*/ |
|||
#ifdef NO_DECLTYPE |
|||
#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } |
|||
#define _NEXT(elt,list,next) ((char*)((list)->next)) |
|||
#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } |
|||
/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */ |
|||
#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } |
|||
#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } |
|||
#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } |
|||
#else |
|||
#define _SV(elt,list) |
|||
#define _NEXT(elt,list,next) ((elt)->next) |
|||
#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to) |
|||
/* #define _PREV(elt,list,prev) ((elt)->prev) */ |
|||
#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) |
|||
#define _RS(list) |
|||
#define _CASTASGN(a,b) (a)=(b) |
|||
#endif |
|||
|
|||
/******************************************************************************
|
|||
* The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * |
|||
* Unwieldy variable names used here to avoid shadowing passed-in variables. * |
|||
*****************************************************************************/ |
|||
#define LL_SORT(list, cmp) \ |
|||
LL_SORT2(list, cmp, next) |
|||
|
|||
#define LL_SORT2(list, cmp, next) \ |
|||
do { \ |
|||
LDECLTYPE(list) _ls_p; \ |
|||
LDECLTYPE(list) _ls_q; \ |
|||
LDECLTYPE(list) _ls_e; \ |
|||
LDECLTYPE(list) _ls_tail; \ |
|||
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ |
|||
if (list) { \ |
|||
_ls_insize = 1; \ |
|||
_ls_looping = 1; \ |
|||
while (_ls_looping) { \ |
|||
_CASTASGN(_ls_p,list); \ |
|||
list = NULL; \ |
|||
_ls_tail = NULL; \ |
|||
_ls_nmerges = 0; \ |
|||
while (_ls_p) { \ |
|||
_ls_nmerges++; \ |
|||
_ls_q = _ls_p; \ |
|||
_ls_psize = 0; \ |
|||
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ |
|||
_ls_psize++; \ |
|||
_SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ |
|||
if (!_ls_q) break; \ |
|||
} \ |
|||
_ls_qsize = _ls_insize; \ |
|||
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ |
|||
if (_ls_psize == 0) { \ |
|||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ |
|||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ |
|||
} else if (_ls_qsize == 0 || !_ls_q) { \ |
|||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ |
|||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ |
|||
} else if (cmp(_ls_p,_ls_q) <= 0) { \ |
|||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ |
|||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ |
|||
} else { \ |
|||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ |
|||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ |
|||
} \ |
|||
if (_ls_tail) { \ |
|||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ |
|||
} else { \ |
|||
_CASTASGN(list,_ls_e); \ |
|||
} \ |
|||
_ls_tail = _ls_e; \ |
|||
} \ |
|||
_ls_p = _ls_q; \ |
|||
} \ |
|||
if (_ls_tail) { \ |
|||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ |
|||
} \ |
|||
if (_ls_nmerges <= 1) { \ |
|||
_ls_looping=0; \ |
|||
} \ |
|||
_ls_insize *= 2; \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
|
|||
#define DL_SORT(list, cmp) \ |
|||
DL_SORT2(list, cmp, prev, next) |
|||
|
|||
#define DL_SORT2(list, cmp, prev, next) \ |
|||
do { \ |
|||
LDECLTYPE(list) _ls_p; \ |
|||
LDECLTYPE(list) _ls_q; \ |
|||
LDECLTYPE(list) _ls_e; \ |
|||
LDECLTYPE(list) _ls_tail; \ |
|||
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ |
|||
if (list) { \ |
|||
_ls_insize = 1; \ |
|||
_ls_looping = 1; \ |
|||
while (_ls_looping) { \ |
|||
_CASTASGN(_ls_p,list); \ |
|||
list = NULL; \ |
|||
_ls_tail = NULL; \ |
|||
_ls_nmerges = 0; \ |
|||
while (_ls_p) { \ |
|||
_ls_nmerges++; \ |
|||
_ls_q = _ls_p; \ |
|||
_ls_psize = 0; \ |
|||
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ |
|||
_ls_psize++; \ |
|||
_SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ |
|||
if (!_ls_q) break; \ |
|||
} \ |
|||
_ls_qsize = _ls_insize; \ |
|||
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ |
|||
if (_ls_psize == 0) { \ |
|||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ |
|||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ |
|||
} else if (_ls_qsize == 0 || !_ls_q) { \ |
|||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ |
|||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ |
|||
} else if (cmp(_ls_p,_ls_q) <= 0) { \ |
|||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ |
|||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ |
|||
} else { \ |
|||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ |
|||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ |
|||
} \ |
|||
if (_ls_tail) { \ |
|||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ |
|||
} else { \ |
|||
_CASTASGN(list,_ls_e); \ |
|||
} \ |
|||
_SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ |
|||
_ls_tail = _ls_e; \ |
|||
} \ |
|||
_ls_p = _ls_q; \ |
|||
} \ |
|||
_CASTASGN(list->prev, _ls_tail); \ |
|||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ |
|||
if (_ls_nmerges <= 1) { \ |
|||
_ls_looping=0; \ |
|||
} \ |
|||
_ls_insize *= 2; \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define CDL_SORT(list, cmp) \ |
|||
CDL_SORT2(list, cmp, prev, next) |
|||
|
|||
#define CDL_SORT2(list, cmp, prev, next) \ |
|||
do { \ |
|||
LDECLTYPE(list) _ls_p; \ |
|||
LDECLTYPE(list) _ls_q; \ |
|||
LDECLTYPE(list) _ls_e; \ |
|||
LDECLTYPE(list) _ls_tail; \ |
|||
LDECLTYPE(list) _ls_oldhead; \ |
|||
LDECLTYPE(list) _tmp; \ |
|||
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ |
|||
if (list) { \ |
|||
_ls_insize = 1; \ |
|||
_ls_looping = 1; \ |
|||
while (_ls_looping) { \ |
|||
_CASTASGN(_ls_p,list); \ |
|||
_CASTASGN(_ls_oldhead,list); \ |
|||
list = NULL; \ |
|||
_ls_tail = NULL; \ |
|||
_ls_nmerges = 0; \ |
|||
while (_ls_p) { \ |
|||
_ls_nmerges++; \ |
|||
_ls_q = _ls_p; \ |
|||
_ls_psize = 0; \ |
|||
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ |
|||
_ls_psize++; \ |
|||
_SV(_ls_q,list); \ |
|||
if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \ |
|||
_ls_q = NULL; \ |
|||
} else { \ |
|||
_ls_q = _NEXT(_ls_q,list,next); \ |
|||
} \ |
|||
_RS(list); \ |
|||
if (!_ls_q) break; \ |
|||
} \ |
|||
_ls_qsize = _ls_insize; \ |
|||
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ |
|||
if (_ls_psize == 0) { \ |
|||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ |
|||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ |
|||
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ |
|||
} else if (_ls_qsize == 0 || !_ls_q) { \ |
|||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ |
|||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ |
|||
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ |
|||
} else if (cmp(_ls_p,_ls_q) <= 0) { \ |
|||
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ |
|||
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ |
|||
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ |
|||
} else { \ |
|||
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ |
|||
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ |
|||
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ |
|||
} \ |
|||
if (_ls_tail) { \ |
|||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ |
|||
} else { \ |
|||
_CASTASGN(list,_ls_e); \ |
|||
} \ |
|||
_SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ |
|||
_ls_tail = _ls_e; \ |
|||
} \ |
|||
_ls_p = _ls_q; \ |
|||
} \ |
|||
_CASTASGN(list->prev,_ls_tail); \ |
|||
_CASTASGN(_tmp,list); \ |
|||
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \ |
|||
if (_ls_nmerges <= 1) { \ |
|||
_ls_looping=0; \ |
|||
} \ |
|||
_ls_insize *= 2; \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
/******************************************************************************
|
|||
* singly linked list macros (non-circular) * |
|||
*****************************************************************************/ |
|||
#define LL_PREPEND(head,add) \ |
|||
LL_PREPEND2(head,add,next) |
|||
|
|||
#define LL_PREPEND2(head,add,next) \ |
|||
do { \ |
|||
(add)->next = head; \ |
|||
head = add; \ |
|||
} while (0) |
|||
|
|||
#define LL_CONCAT(head1,head2) \ |
|||
LL_CONCAT2(head1,head2,next) |
|||
|
|||
#define LL_CONCAT2(head1,head2,next) \ |
|||
do { \ |
|||
LDECLTYPE(head1) _tmp; \ |
|||
if (head1) { \ |
|||
_tmp = head1; \ |
|||
while (_tmp->next) { _tmp = _tmp->next; } \ |
|||
_tmp->next=(head2); \ |
|||
} else { \ |
|||
(head1)=(head2); \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define LL_APPEND(head,add) \ |
|||
LL_APPEND2(head,add,next) |
|||
|
|||
#define LL_APPEND2(head,add,next) \ |
|||
do { \ |
|||
LDECLTYPE(head) _tmp; \ |
|||
(add)->next=NULL; \ |
|||
if (head) { \ |
|||
_tmp = head; \ |
|||
while (_tmp->next) { _tmp = _tmp->next; } \ |
|||
_tmp->next=(add); \ |
|||
} else { \ |
|||
(head)=(add); \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define LL_DELETE(head,del) \ |
|||
LL_DELETE2(head,del,next) |
|||
|
|||
#define LL_DELETE2(head,del,next) \ |
|||
do { \ |
|||
LDECLTYPE(head) _tmp; \ |
|||
if ((head) == (del)) { \ |
|||
(head)=(head)->next; \ |
|||
} else { \ |
|||
_tmp = head; \ |
|||
while (_tmp->next && (_tmp->next != (del))) { \ |
|||
_tmp = _tmp->next; \ |
|||
} \ |
|||
if (_tmp->next) { \ |
|||
_tmp->next = ((del)->next); \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ |
|||
#define LL_APPEND_VS2008(head,add) \ |
|||
LL_APPEND2_VS2008(head,add,next) |
|||
|
|||
#define LL_APPEND2_VS2008(head,add,next) \ |
|||
do { \ |
|||
if (head) { \ |
|||
(add)->next = head; /* use add->next as a temp variable */ \ |
|||
while ((add)->next->next) { (add)->next = (add)->next->next; } \ |
|||
(add)->next->next=(add); \ |
|||
} else { \ |
|||
(head)=(add); \ |
|||
} \ |
|||
(add)->next=NULL; \ |
|||
} while (0) |
|||
|
|||
#define LL_DELETE_VS2008(head,del) \ |
|||
LL_DELETE2_VS2008(head,del,next) |
|||
|
|||
#define LL_DELETE2_VS2008(head,del,next) \ |
|||
do { \ |
|||
if ((head) == (del)) { \ |
|||
(head)=(head)->next; \ |
|||
} else { \ |
|||
char *_tmp = (char*)(head); \ |
|||
while ((head)->next && ((head)->next != (del))) { \ |
|||
head = (head)->next; \ |
|||
} \ |
|||
if ((head)->next) { \ |
|||
(head)->next = ((del)->next); \ |
|||
} \ |
|||
{ \ |
|||
char **_head_alias = (char**)&(head); \ |
|||
*_head_alias = _tmp; \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
#ifdef NO_DECLTYPE |
|||
#undef LL_APPEND |
|||
#define LL_APPEND LL_APPEND_VS2008 |
|||
#undef LL_DELETE |
|||
#define LL_DELETE LL_DELETE_VS2008 |
|||
#undef LL_DELETE2 |
|||
#define LL_DELETE2 LL_DELETE2_VS2008 |
|||
#undef LL_APPEND2 |
|||
#define LL_APPEND2 LL_APPEND2_VS2008 |
|||
#undef LL_CONCAT /* no LL_CONCAT_VS2008 */ |
|||
#undef DL_CONCAT /* no DL_CONCAT_VS2008 */ |
|||
#endif |
|||
/* end VS2008 replacements */ |
|||
|
|||
#define LL_COUNT(head,el,counter) \ |
|||
LL_COUNT2(head,el,counter,next) \ |
|||
|
|||
#define LL_COUNT2(head,el,counter,next) \ |
|||
{ \ |
|||
counter = 0; \ |
|||
LL_FOREACH2(head,el,next){ ++counter; } \ |
|||
} |
|||
|
|||
#define LL_FOREACH(head,el) \ |
|||
LL_FOREACH2(head,el,next) |
|||
|
|||
#define LL_FOREACH2(head,el,next) \ |
|||
for(el=head;el;el=(el)->next) |
|||
|
|||
#define LL_FOREACH_SAFE(head,el,tmp) \ |
|||
LL_FOREACH_SAFE2(head,el,tmp,next) |
|||
|
|||
#define LL_FOREACH_SAFE2(head,el,tmp,next) \ |
|||
for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) |
|||
|
|||
#define LL_SEARCH_SCALAR(head,out,field,val) \ |
|||
LL_SEARCH_SCALAR2(head,out,field,val,next) |
|||
|
|||
#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ |
|||
do { \ |
|||
LL_FOREACH2(head,out,next) { \ |
|||
if ((out)->field == (val)) break; \ |
|||
} \ |
|||
} while(0) |
|||
|
|||
#define LL_SEARCH(head,out,elt,cmp) \ |
|||
LL_SEARCH2(head,out,elt,cmp,next) |
|||
|
|||
#define LL_SEARCH2(head,out,elt,cmp,next) \ |
|||
do { \ |
|||
LL_FOREACH2(head,out,next) { \ |
|||
if ((cmp(out,elt))==0) break; \ |
|||
} \ |
|||
} while(0) |
|||
|
|||
#define LL_REPLACE_ELEM(head, el, add) \ |
|||
do { \ |
|||
LDECLTYPE(head) _tmp; \ |
|||
assert(head != NULL); \ |
|||
assert(el != NULL); \ |
|||
assert(add != NULL); \ |
|||
(add)->next = (el)->next; \ |
|||
if ((head) == (el)) { \ |
|||
(head) = (add); \ |
|||
} else { \ |
|||
_tmp = head; \ |
|||
while (_tmp->next && (_tmp->next != (el))) { \ |
|||
_tmp = _tmp->next; \ |
|||
} \ |
|||
if (_tmp->next) { \ |
|||
_tmp->next = (add); \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define LL_PREPEND_ELEM(head, el, add) \ |
|||
do { \ |
|||
LDECLTYPE(head) _tmp; \ |
|||
assert(head != NULL); \ |
|||
assert(el != NULL); \ |
|||
assert(add != NULL); \ |
|||
(add)->next = (el); \ |
|||
if ((head) == (el)) { \ |
|||
(head) = (add); \ |
|||
} else { \ |
|||
_tmp = head; \ |
|||
while (_tmp->next && (_tmp->next != (el))) { \ |
|||
_tmp = _tmp->next; \ |
|||
} \ |
|||
if (_tmp->next) { \ |
|||
_tmp->next = (add); \ |
|||
} \ |
|||
} \ |
|||
} while (0) \ |
|||
|
|||
|
|||
/******************************************************************************
|
|||
* doubly linked list macros (non-circular) * |
|||
*****************************************************************************/ |
|||
#define DL_PREPEND(head,add) \ |
|||
DL_PREPEND2(head,add,prev,next) |
|||
|
|||
#define DL_PREPEND2(head,add,prev,next) \ |
|||
do { \ |
|||
(add)->next = head; \ |
|||
if (head) { \ |
|||
(add)->prev = (head)->prev; \ |
|||
(head)->prev = (add); \ |
|||
} else { \ |
|||
(add)->prev = (add); \ |
|||
} \ |
|||
(head) = (add); \ |
|||
} while (0) |
|||
|
|||
#define DL_APPEND(head,add) \ |
|||
DL_APPEND2(head,add,prev,next) |
|||
|
|||
#define DL_APPEND2(head,add,prev,next) \ |
|||
do { \ |
|||
if (head) { \ |
|||
(add)->prev = (head)->prev; \ |
|||
(head)->prev->next = (add); \ |
|||
(head)->prev = (add); \ |
|||
(add)->next = NULL; \ |
|||
} else { \ |
|||
(head)=(add); \ |
|||
(head)->prev = (head); \ |
|||
(head)->next = NULL; \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define DL_CONCAT(head1,head2) \ |
|||
DL_CONCAT2(head1,head2,prev,next) |
|||
|
|||
#define DL_CONCAT2(head1,head2,prev,next) \ |
|||
do { \ |
|||
LDECLTYPE(head1) _tmp; \ |
|||
if (head2) { \ |
|||
if (head1) { \ |
|||
_tmp = (head2)->prev; \ |
|||
(head2)->prev = (head1)->prev; \ |
|||
(head1)->prev->next = (head2); \ |
|||
(head1)->prev = _tmp; \ |
|||
} else { \ |
|||
(head1)=(head2); \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define DL_DELETE(head,del) \ |
|||
DL_DELETE2(head,del,prev,next) |
|||
|
|||
#define DL_DELETE2(head,del,prev,next) \ |
|||
do { \ |
|||
assert((del)->prev != NULL); \ |
|||
if ((del)->prev == (del)) { \ |
|||
(head)=NULL; \ |
|||
} else if ((del)==(head)) { \ |
|||
(del)->next->prev = (del)->prev; \ |
|||
(head) = (del)->next; \ |
|||
} else { \ |
|||
(del)->prev->next = (del)->next; \ |
|||
if ((del)->next) { \ |
|||
(del)->next->prev = (del)->prev; \ |
|||
} else { \ |
|||
(head)->prev = (del)->prev; \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define DL_COUNT(head,el,counter) \ |
|||
DL_COUNT2(head,el,counter,next) \ |
|||
|
|||
#define DL_COUNT2(head,el,counter,next) \ |
|||
{ \ |
|||
counter = 0; \ |
|||
DL_FOREACH2(head,el,next){ ++counter; } \ |
|||
} |
|||
|
|||
#define DL_FOREACH(head,el) \ |
|||
DL_FOREACH2(head,el,next) |
|||
|
|||
#define DL_FOREACH2(head,el,next) \ |
|||
for(el=head;el;el=(el)->next) |
|||
|
|||
/* this version is safe for deleting the elements during iteration */ |
|||
#define DL_FOREACH_SAFE(head,el,tmp) \ |
|||
DL_FOREACH_SAFE2(head,el,tmp,next) |
|||
|
|||
#define DL_FOREACH_SAFE2(head,el,tmp,next) \ |
|||
for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) |
|||
|
|||
/* these are identical to their singly-linked list counterparts */ |
|||
#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR |
|||
#define DL_SEARCH LL_SEARCH |
|||
#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 |
|||
#define DL_SEARCH2 LL_SEARCH2 |
|||
|
|||
#define DL_REPLACE_ELEM(head, el, add) \ |
|||
do { \ |
|||
assert(head != NULL); \ |
|||
assert(el != NULL); \ |
|||
assert(add != NULL); \ |
|||
if ((head) == (el)) { \ |
|||
(head) = (add); \ |
|||
(add)->next = (el)->next; \ |
|||
if ((el)->next == NULL) { \ |
|||
(add)->prev = (add); \ |
|||
} else { \ |
|||
(add)->prev = (el)->prev; \ |
|||
(add)->next->prev = (add); \ |
|||
} \ |
|||
} else { \ |
|||
(add)->next = (el)->next; \ |
|||
(add)->prev = (el)->prev; \ |
|||
(add)->prev->next = (add); \ |
|||
if ((el)->next == NULL) { \ |
|||
(head)->prev = (add); \ |
|||
} else { \ |
|||
(add)->next->prev = (add); \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define DL_PREPEND_ELEM(head, el, add) \ |
|||
do { \ |
|||
assert(head != NULL); \ |
|||
assert(el != NULL); \ |
|||
assert(add != NULL); \ |
|||
(add)->next = (el); \ |
|||
(add)->prev = (el)->prev; \ |
|||
(el)->prev = (add); \ |
|||
if ((head) == (el)) { \ |
|||
(head) = (add); \ |
|||
} else { \ |
|||
(add)->prev->next = (add); \ |
|||
} \ |
|||
} while (0) \ |
|||
|
|||
|
|||
/******************************************************************************
|
|||
* circular doubly linked list macros * |
|||
*****************************************************************************/ |
|||
#define CDL_PREPEND(head,add) \ |
|||
CDL_PREPEND2(head,add,prev,next) |
|||
|
|||
#define CDL_PREPEND2(head,add,prev,next) \ |
|||
do { \ |
|||
if (head) { \ |
|||
(add)->prev = (head)->prev; \ |
|||
(add)->next = (head); \ |
|||
(head)->prev = (add); \ |
|||
(add)->prev->next = (add); \ |
|||
} else { \ |
|||
(add)->prev = (add); \ |
|||
(add)->next = (add); \ |
|||
} \ |
|||
(head)=(add); \ |
|||
} while (0) |
|||
|
|||
#define CDL_DELETE(head,del) \ |
|||
CDL_DELETE2(head,del,prev,next) |
|||
|
|||
#define CDL_DELETE2(head,del,prev,next) \ |
|||
do { \ |
|||
if ( ((head)==(del)) && ((head)->next == (head))) { \ |
|||
(head) = 0L; \ |
|||
} else { \ |
|||
(del)->next->prev = (del)->prev; \ |
|||
(del)->prev->next = (del)->next; \ |
|||
if ((del) == (head)) (head)=(del)->next; \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define CDL_COUNT(head,el,counter) \ |
|||
CDL_COUNT2(head,el,counter,next) \ |
|||
|
|||
#define CDL_COUNT2(head, el, counter,next) \ |
|||
{ \ |
|||
counter = 0; \ |
|||
CDL_FOREACH2(head,el,next){ ++counter; } \ |
|||
} |
|||
|
|||
#define CDL_FOREACH(head,el) \ |
|||
CDL_FOREACH2(head,el,next) |
|||
|
|||
#define CDL_FOREACH2(head,el,next) \ |
|||
for(el=head;el;el=((el)->next==head ? 0L : (el)->next)) |
|||
|
|||
#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ |
|||
CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) |
|||
|
|||
#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ |
|||
for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ |
|||
(el) && ((tmp2)=(el)->next, 1); \ |
|||
((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) |
|||
|
|||
#define CDL_SEARCH_SCALAR(head,out,field,val) \ |
|||
CDL_SEARCH_SCALAR2(head,out,field,val,next) |
|||
|
|||
#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ |
|||
do { \ |
|||
CDL_FOREACH2(head,out,next) { \ |
|||
if ((out)->field == (val)) break; \ |
|||
} \ |
|||
} while(0) |
|||
|
|||
#define CDL_SEARCH(head,out,elt,cmp) \ |
|||
CDL_SEARCH2(head,out,elt,cmp,next) |
|||
|
|||
#define CDL_SEARCH2(head,out,elt,cmp,next) \ |
|||
do { \ |
|||
CDL_FOREACH2(head,out,next) { \ |
|||
if ((cmp(out,elt))==0) break; \ |
|||
} \ |
|||
} while(0) |
|||
|
|||
#define CDL_REPLACE_ELEM(head, el, add) \ |
|||
do { \ |
|||
assert(head != NULL); \ |
|||
assert(el != NULL); \ |
|||
assert(add != NULL); \ |
|||
if ((el)->next == (el)) { \ |
|||
(add)->next = (add); \ |
|||
(add)->prev = (add); \ |
|||
(head) = (add); \ |
|||
} else { \ |
|||
(add)->next = (el)->next; \ |
|||
(add)->prev = (el)->prev; \ |
|||
(add)->next->prev = (add); \ |
|||
(add)->prev->next = (add); \ |
|||
if ((head) == (el)) { \ |
|||
(head) = (add); \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
#define CDL_PREPEND_ELEM(head, el, add) \ |
|||
do { \ |
|||
assert(head != NULL); \ |
|||
assert(el != NULL); \ |
|||
assert(add != NULL); \ |
|||
(add)->next = (el); \ |
|||
(add)->prev = (el)->prev; \ |
|||
(el)->prev = (add); \ |
|||
(add)->prev->next = (add); \ |
|||
if ((head) == (el)) { \ |
|||
(head) = (add); \ |
|||
} \ |
|||
} while (0) \ |
|||
|
|||
#endif /* UTLIST_H */ |
@ -0,0 +1,919 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#include "CCOracles.h" |
|||
#include <secp256k1.h> |
|||
|
|||
/*
|
|||
An oracles CC has the purpose of converting offchain data into onchain data |
|||
simplest would just be to have a pubkey(s) that are trusted to provide such data, but this wont need to have a CC involved at all and can just be done by convention |
|||
|
|||
That begs the question, "what would an oracles CC do?" |
|||
A couple of things come to mind, ie. payments to oracles for future offchain data and maybe some sort of dispute/censoring ability |
|||
|
|||
first step is to define the data that the oracle is providing. A simple name:description tx can be created to define the name and description of the oracle data. |
|||
linked to this txid would be two types of transactions: |
|||
a) oracle providers |
|||
b) oracle data users |
|||
|
|||
In order to be resistant to sybil attacks, the feedback mechanism needs to have a cost. combining with the idea of payments for data, the oracle providers will be ranked by actual payments made to each oracle for each data type. |
|||
|
|||
Implementation notes: |
|||
In order to maintain good performance even under heavy usage, special marker utxo are used. Actually a pair of them. When a provider registers to be a data provider, a special unspendable normal output is created to allow for quick scanning. Since the marker is based on the oracletxid, it becomes a single address where all the providers can be found. |
|||
|
|||
A convention is used so that the datafee can be changed by registering again. it is assumed that there wont be too many of these datafee changes. if more than one from the same provider happens in the same block, the lower price is used. |
|||
|
|||
The other efficiency issue is finding the most recent data point. We want to create a linked list of all data points, going back to the first one. In order to make this efficient, a special and unique per provider/oracletxid baton utxo is used. This should have exactly one utxo, so the search would be a direct lookup and it is passed on from one data point to the next. There is some small chance that the baton utxo is spent in a non-data transaction, so provision is made to allow for recreating a baton utxo in case it isnt found. The baton utxo is a convenience and doesnt affect validation |
|||
|
|||
Required transactions: |
|||
0) create oracle description -> just needs to create txid for oracle data |
|||
1) register as oracle data provider with price -> become a registered oracle data provider |
|||
2) pay provider for N oracle data points -> lock funds for oracle provider |
|||
3) publish oracle data point -> publish data and collect payment |
|||
|
|||
The format string is a set of chars with the following meaning: |
|||
's' -> <256 char string |
|||
'S' -> <65536 char string |
|||
'd' -> <256 binary data |
|||
'D' -> <65536 binary data |
|||
'c' -> 1 byte signed little endian number, 'C' unsigned |
|||
't' -> 2 byte signed little endian number, 'T' unsigned |
|||
'i' -> 4 byte signed little endian number, 'I' unsigned |
|||
'l' -> 8 byte signed little endian number, 'L' unsigned |
|||
'h' -> 32 byte hash |
|||
|
|||
create: |
|||
vins.*: normal inputs |
|||
vout.0: txfee tag to oracle normal address |
|||
vout.1: change, if any |
|||
vout.n-1: opreturn with name and description and format for data |
|||
|
|||
register: |
|||
vins.*: normal inputs |
|||
vout.0: txfee tag to normal marker address |
|||
vout.1: baton CC utxo |
|||
vout.2: change, if any |
|||
vout.n-1: opreturn with oracletxid, pubkey and price per data point |
|||
|
|||
subscribe: |
|||
vins.*: normal inputs |
|||
vout.0: subscription fee to publishers CC address |
|||
vout.1: change, if any |
|||
vout.n-1: opreturn with oracletxid, registered provider's pubkey, amount |
|||
|
|||
data: |
|||
vin.0: normal input |
|||
vin.1: baton CC utxo (most of the time) |
|||
vin.2+: subscription or data vout.0 |
|||
vout.0: change to publishers CC address |
|||
vout.1: baton CC utxo |
|||
vout.2: payment for dataprovider |
|||
vout.3: change, if any |
|||
vout.n-1: opreturn with oracletxid, prevbatontxid and data in proper format |
|||
|
|||
data (without payment) this is not needed as publisher can pay themselves! |
|||
vin.0: normal input |
|||
vin.1: baton CC utxo |
|||
vout.0: txfee to publishers normal address |
|||
vout.1: baton CC utxo |
|||
vout.2: change, if any |
|||
vout.n-1: opreturn with oracletxid, prevbatontxid and data in proper format |
|||
|
|||
*/ |
|||
|
|||
// start of consensus code
|
|||
|
|||
|
|||
CScript EncodeOraclesCreateOpRet(uint8_t funcid,std::string name,std::string description,std::string format) |
|||
{ |
|||
CScript opret; uint8_t evalcode = EVAL_ORACLES; |
|||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << name << format << description); |
|||
return(opret); |
|||
} |
|||
|
|||
uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,std::string &description,std::string &format) |
|||
{ |
|||
std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; |
|||
GetOpReturnData(scriptPubKey,vopret); |
|||
script = (uint8_t *)vopret.data(); |
|||
if ( script[0] == EVAL_ORACLES ) |
|||
{ |
|||
if ( script[1] == 'C' ) |
|||
{ |
|||
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> name; ss >> format; ss >> description) != 0 ) |
|||
{ |
|||
return(script[1]); |
|||
} else fprintf(stderr,"DecodeOraclesCreateOpRet unmarshal error for C\n"); |
|||
} |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
CScript EncodeOraclesOpRet(uint8_t funcid,uint256 oracletxid,CPubKey pk,int64_t num) |
|||
{ |
|||
CScript opret; uint8_t evalcode = EVAL_ORACLES; |
|||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << oracletxid << pk << num); |
|||
return(opret); |
|||
} |
|||
|
|||
uint8_t DecodeOraclesOpRet(const CScript &scriptPubKey,uint256 &oracletxid,CPubKey &pk,int64_t &num) |
|||
{ |
|||
std::vector<uint8_t> vopret; uint8_t *script,e,f; |
|||
GetOpReturnData(scriptPubKey,vopret); |
|||
script = (uint8_t *)vopret.data(); |
|||
if ( vopret.size() > 1 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> oracletxid; ss >> pk; ss >> num) != 0 ) |
|||
{ |
|||
if ( e == EVAL_ORACLES && (f == 'R' || f == 'S') ) |
|||
return(f); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
CScript EncodeOraclesData(uint8_t funcid,uint256 oracletxid,uint256 batontxid,CPubKey pk,std::vector <uint8_t>data) |
|||
{ |
|||
CScript opret; uint8_t evalcode = EVAL_ORACLES; |
|||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << oracletxid << batontxid << pk << data); |
|||
return(opret); |
|||
} |
|||
|
|||
uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector <uint8_t>&data) |
|||
{ |
|||
std::vector<uint8_t> vopret; uint8_t *script,e,f; |
|||
GetOpReturnData(scriptPubKey,vopret); |
|||
script = (uint8_t *)vopret.data(); |
|||
if ( vopret.size() > 1 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> oracletxid; ss >> batontxid; ss >> pk; ss >> data) != 0 ) |
|||
{ |
|||
if ( e == EVAL_ORACLES && f == 'D' ) |
|||
return(f); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
CPubKey OracleBatonPk(char *batonaddr,struct CCcontract_info *cp) |
|||
{ |
|||
static secp256k1_context *ctx; |
|||
size_t clen = CPubKey::PUBLIC_KEY_SIZE; |
|||
secp256k1_pubkey pubkey; CPubKey batonpk; uint8_t priv[32]; int32_t i; |
|||
if ( ctx == 0 ) |
|||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); |
|||
Myprivkey(priv); |
|||
cp->evalcode2 = EVAL_ORACLES; |
|||
for (i=0; i<32; i++) |
|||
cp->unspendablepriv2[i] = (priv[i] ^ cp->CCpriv[i]); |
|||
while ( secp256k1_ec_seckey_verify(ctx,cp->unspendablepriv2) == 0 ) |
|||
{ |
|||
for (i=0; i<32; i++) |
|||
fprintf(stderr,"%02x",cp->unspendablepriv2[i]); |
|||
fprintf(stderr," invalid privkey\n"); |
|||
if ( secp256k1_ec_privkey_tweak_add(ctx,cp->unspendablepriv2,priv) != 0 ) |
|||
break; |
|||
} |
|||
if ( secp256k1_ec_pubkey_create(ctx,&pubkey,cp->unspendablepriv2) != 0 ) |
|||
{ |
|||
secp256k1_ec_pubkey_serialize(ctx,(unsigned char*)batonpk.begin(),&clen,&pubkey,SECP256K1_EC_COMPRESSED); |
|||
cp->unspendablepk2 = batonpk; |
|||
Getscriptaddress(batonaddr,MakeCC1vout(cp->evalcode,0,batonpk).scriptPubKey); |
|||
//fprintf(stderr,"batonpk.(%s) -> %s\n",(char *)HexStr(batonpk).c_str(),batonaddr);
|
|||
strcpy(cp->unspendableaddr2,batonaddr); |
|||
} else fprintf(stderr,"error creating pubkey\n"); |
|||
return(batonpk); |
|||
} |
|||
|
|||
int64_t OracleCurrentDatafee(uint256 reforacletxid,char *markeraddr,CPubKey publisher) |
|||
{ |
|||
uint256 txid,oracletxid,hashBlock; int64_t datafee=0,dfee; int32_t dheight=0,vout,height,numvouts; CTransaction tx; CPubKey pk; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
SetCCunspents(unspentOutputs,markeraddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
height = (int32_t)it->second.blockHeight; |
|||
if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) |
|||
{ |
|||
if ( DecodeOraclesOpRet(tx.vout[numvouts-1].scriptPubKey,oracletxid,pk,dfee) == 'R' ) |
|||
{ |
|||
if ( oracletxid == reforacletxid && pk == publisher ) |
|||
{ |
|||
if ( height > dheight || (height == dheight && dfee < datafee) ) |
|||
{ |
|||
dheight = height; |
|||
datafee = dfee; |
|||
if ( 0 && dheight != 0 ) |
|||
fprintf(stderr,"set datafee %.8f height.%d\n",(double)datafee/COIN,height); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return(datafee); |
|||
} |
|||
|
|||
int64_t OracleDatafee(CScript &scriptPubKey,uint256 oracletxid,CPubKey publisher) |
|||
{ |
|||
CTransaction oracletx; char markeraddr[64]; uint256 hashBlock; std::string name,description,format; int32_t numvouts; int64_t datafee = 0; |
|||
if ( myGetTransaction(oracletxid,oracletx,hashBlock) != 0 && (numvouts= oracletx.vout.size()) > 0 ) |
|||
{ |
|||
if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) == 'C' ) |
|||
{ |
|||
CCtxidaddr(markeraddr,oracletxid); |
|||
datafee = OracleCurrentDatafee(oracletxid,markeraddr,publisher); |
|||
} |
|||
} |
|||
return(datafee); |
|||
} |
|||
|
|||
static uint256 myIs_baton_spentinmempool(uint256 batontxid,int32_t batonvout) |
|||
{ |
|||
BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) |
|||
{ |
|||
const CTransaction &tx = e.GetTx(); |
|||
if ( tx.vout.size() > 0 && tx.vin.size() > 1 && batontxid == tx.vin[1].prevout.hash && batonvout == tx.vin[1].prevout.n ) |
|||
{ |
|||
const uint256 &txid = tx.GetHash(); |
|||
//char str[65]; fprintf(stderr,"found baton spent in mempool %s\n",uint256_str(str,txid));
|
|||
return(txid); |
|||
} |
|||
} |
|||
return(batontxid); |
|||
} |
|||
|
|||
uint256 OracleBatonUtxo(uint64_t txfee,struct CCcontract_info *cp,uint256 reforacletxid,char *batonaddr,CPubKey publisher,std::vector <uint8_t> &dataarg) |
|||
{ |
|||
uint256 txid,oracletxid,hashBlock,btxid,batontxid = zeroid; int64_t dfee; int32_t dheight=0,vout,height,numvouts; CTransaction tx; CPubKey pk; uint8_t *ptr; std::vector<uint8_t> vopret,data; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
SetCCunspents(unspentOutputs,batonaddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
height = (int32_t)it->second.blockHeight; |
|||
if ( it->second.satoshis != txfee ) |
|||
{ |
|||
fprintf(stderr,"it->second.satoshis %llu != %llu txfee\n",(long long)it->second.satoshis,(long long)txfee); |
|||
continue; |
|||
} |
|||
if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) |
|||
{ |
|||
GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); |
|||
if ( vopret.size() > 2 ) |
|||
{ |
|||
ptr = (uint8_t *)vopret.data(); |
|||
if ( (ptr[1] == 'D' && DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D') || (ptr[1] == 'R' && DecodeOraclesOpRet(tx.vout[numvouts-1].scriptPubKey,oracletxid,pk,dfee) == 'R') ) |
|||
{ |
|||
if ( oracletxid == reforacletxid && pk == publisher ) |
|||
{ |
|||
if ( height > dheight ) |
|||
{ |
|||
dheight = height; |
|||
batontxid = txid; |
|||
if ( ptr[1] == 'D' ) |
|||
dataarg = data; |
|||
//char str[65]; fprintf(stderr,"set batontxid %s height.%d\n",uint256_str(str,batontxid),height);
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
while ( myIsutxo_spentinmempool(batontxid,1) != 0 ) |
|||
batontxid = myIs_baton_spentinmempool(batontxid,1); |
|||
return(batontxid); |
|||
} |
|||
|
|||
uint256 OraclesBatontxid(uint256 reforacletxid,CPubKey refpk) |
|||
{ |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
CTransaction regtx; uint256 hash,txid,batontxid,oracletxid; CPubKey pk; int32_t numvouts,height,maxheight=0; int64_t datafee; char markeraddr[64],batonaddr[64]; std::vector <uint8_t> data; struct CCcontract_info *cp,C; |
|||
batontxid = zeroid; |
|||
cp = CCinit(&C,EVAL_ORACLES); |
|||
CCtxidaddr(markeraddr,reforacletxid); |
|||
SetCCunspents(unspentOutputs,markeraddr); |
|||
//char str[67]; fprintf(stderr,"markeraddr.(%s) %s\n",markeraddr,pubkey33_str(str,(uint8_t *)&refpk));
|
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
//fprintf(stderr,"check %s\n",uint256_str(str,txid));
|
|||
height = (int32_t)it->second.blockHeight; |
|||
if ( myGetTransaction(txid,regtx,hash) != 0 ) |
|||
{ |
|||
if ( regtx.vout.size() > 0 && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R' && oracletxid == reforacletxid && pk == refpk ) |
|||
{ |
|||
Getscriptaddress(batonaddr,regtx.vout[1].scriptPubKey); |
|||
batontxid = OracleBatonUtxo(10000,cp,oracletxid,batonaddr,pk,data); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return(batontxid); |
|||
} |
|||
|
|||
int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t *data,int32_t offset,int32_t datalen) |
|||
{ |
|||
char _str[65]; int32_t sflag = 0,i,val32,len = 0,slen = 0,dlen = 0; uint32_t uval32; uint16_t uval16; int16_t val16; int64_t val = 0; uint64_t uval = 0; |
|||
*valp = 0; |
|||
*hashp = zeroid; |
|||
if ( str != 0 ) |
|||
str[0] = 0; |
|||
switch ( fmt ) |
|||
{ |
|||
case 's': slen = data[offset++]; break; |
|||
case 'S': slen = data[offset++]; slen |= ((int32_t)data[offset++] << 8); break; |
|||
case 'd': dlen = data[offset++]; break; |
|||
case 'D': dlen = data[offset++]; dlen |= ((int32_t)data[offset++] << 8); break; |
|||
case 'c': len = 1; sflag = 1; break; |
|||
case 'C': len = 1; break; |
|||
case 't': len = 2; sflag = 1; break; |
|||
case 'T': len = 2; break; |
|||
case 'i': len = 4; sflag = 1; break; |
|||
case 'I': len = 4; break; |
|||
case 'l': len = 8; sflag = 1; break; |
|||
case 'L': len = 8; break; |
|||
case 'h': len = 32; break; |
|||
default: return(-1); break; |
|||
} |
|||
if ( slen != 0 ) |
|||
{ |
|||
if ( str != 0 ) |
|||
{ |
|||
if ( slen < IGUANA_MAXSCRIPTSIZE && offset+slen <= datalen ) |
|||
{ |
|||
for (i=0; i<slen; i++) |
|||
str[i] = data[offset++]; |
|||
str[i] = 0; |
|||
} else return(-1); |
|||
} |
|||
} |
|||
else if ( dlen != 0 ) |
|||
{ |
|||
if ( str != 0 ) |
|||
{ |
|||
if ( dlen < IGUANA_MAXSCRIPTSIZE && offset+dlen <= datalen ) |
|||
{ |
|||
for (i=0; i<dlen; i++) |
|||
sprintf(&str[i<<1],"%02x",data[offset++]); |
|||
str[i] = 0; |
|||
} else return(-1); |
|||
} |
|||
} |
|||
else if ( len != 0 && len+offset <= datalen ) |
|||
{ |
|||
if ( len == 32 ) |
|||
{ |
|||
iguana_rwbignum(0,&data[offset],len,(uint8_t *)hashp); |
|||
if ( str != 0 ) |
|||
sprintf(str,"%s",uint256_str(_str,*hashp)); |
|||
} |
|||
else |
|||
{ |
|||
if ( sflag != 0 ) |
|||
{ |
|||
switch ( len ) |
|||
{ |
|||
case 1: val = (int8_t)data[offset]; break; |
|||
case 2: iguana_rwnum(0,&data[offset],len,(void *)&val16); val = val16; break; |
|||
case 4: iguana_rwnum(0,&data[offset],len,(void *)&val32); val = val32; break; |
|||
case 8: iguana_rwnum(0,&data[offset],len,(void *)&val); break; |
|||
} |
|||
if ( str != 0 ) |
|||
sprintf(str,"%lld",(long long)val); |
|||
*valp = val; |
|||
} |
|||
else |
|||
{ |
|||
switch ( len ) |
|||
{ |
|||
case 1: uval = data[offset]; break; |
|||
case 2: iguana_rwnum(0,&data[offset],len,(void *)&uval16); uval = uval16; break; |
|||
case 4: iguana_rwnum(0,&data[offset],len,(void *)&uval32); uval = uval32; break; |
|||
case 8: iguana_rwnum(0,&data[offset],len,(void *)&uval); break; |
|||
} |
|||
if ( str != 0 ) |
|||
sprintf(str,"%llu",(long long)uval); |
|||
*valp = (int64_t)uval; |
|||
} |
|||
} |
|||
offset += len; |
|||
} else return(-1); |
|||
return(offset); |
|||
} |
|||
|
|||
int64_t _correlate_price(int64_t *prices,int32_t n,int64_t price) |
|||
{ |
|||
int32_t i,count = 0; int64_t diff,threshold = (price >> 8); |
|||
for (i=0; i<n; i++) |
|||
{ |
|||
if ( (diff= (price - prices[i])) < 0 ) |
|||
diff = -diff; |
|||
if ( diff <= threshold ) |
|||
count++; |
|||
} |
|||
if ( count < (n >> 1) ) |
|||
return(0); |
|||
else return(price); |
|||
} |
|||
|
|||
int64_t correlate_price(int32_t height,int64_t *prices,int32_t n) |
|||
{ |
|||
int32_t i,j; int64_t price = 0; |
|||
if ( n == 1 ) |
|||
return(prices[0]); |
|||
for (i=0; i<n; i++) |
|||
{ |
|||
j = (height + i) % n; |
|||
if ( prices[j] != 0 && (price= _correlate_price(prices,n,prices[j])) != 0 ) |
|||
break; |
|||
} |
|||
for (i=0; i<n; i++) |
|||
fprintf(stderr,"%llu ",(long long)prices[i]); |
|||
fprintf(stderr,"-> %llu ht.%d\n",(long long)price,height); |
|||
} |
|||
|
|||
int64_t OracleCorrelatedPrice(int32_t height,std::vector <int64_t> origprices) |
|||
{ |
|||
std::vector <int64_t> sorted; int32_t i,n; int64_t *prices,price; |
|||
if ( (n= origprices.size()) == 1 ) |
|||
return(origprices[0]); |
|||
std::sort(origprices.begin(), origprices.end()); |
|||
prices = (int64_t *)calloc(n,sizeof(*prices)); |
|||
i = 0; |
|||
for (std::vector<int64_t>::const_iterator it=sorted.begin(); it!=sorted.end(); it++) |
|||
prices[i++] = *it; |
|||
price = correlate_price(height,prices,i); |
|||
free(prices); |
|||
return(price); |
|||
} |
|||
|
|||
int32_t oracleprice_add(std::vector<struct oracleprice_info> &publishers,CPubKey pk,int32_t height,std::vector <uint8_t> data,int32_t maxheight) |
|||
{ |
|||
struct oracleprice_info item; int32_t flag = 0; |
|||
for (std::vector<struct oracleprice_info>::iterator it=publishers.begin(); it!=publishers.end(); it++) |
|||
{ |
|||
if ( pk == it->pk ) |
|||
{ |
|||
flag = 1; |
|||
if ( height > it->height ) |
|||
{ |
|||
it->height = height; |
|||
it->data = data; |
|||
return(height); |
|||
} |
|||
} |
|||
} |
|||
if ( flag == 0 ) |
|||
{ |
|||
item.pk = pk; |
|||
item.data = data; |
|||
item.height = height; |
|||
publishers.push_back(item); |
|||
return(height); |
|||
} else return(0); |
|||
} |
|||
|
|||
int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format) |
|||
{ |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
CTransaction regtx; uint256 hash,txid,oracletxid,batontxid; CPubKey pk; int32_t i,ht,maxheight=0; int64_t datafee,price; char batonaddr[64]; std::vector <uint8_t> data; struct CCcontract_info *cp,C; std::vector <struct oracleprice_info> publishers; std::vector <int64_t> prices; |
|||
if ( format[0] != 'L' ) |
|||
return(0); |
|||
cp = CCinit(&C,EVAL_ORACLES); |
|||
SetCCunspents(unspentOutputs,markeraddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
ht = (int32_t)it->second.blockHeight; |
|||
if ( myGetTransaction(txid,regtx,hash) != 0 ) |
|||
{ |
|||
if ( regtx.vout.size() > 0 && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R' && oracletxid == reforacletxid ) |
|||
{ |
|||
Getscriptaddress(batonaddr,regtx.vout[1].scriptPubKey); |
|||
batontxid = OracleBatonUtxo(10000,cp,oracletxid,batonaddr,pk,data); |
|||
if ( batontxid != zeroid && (ht= oracleprice_add(publishers,pk,ht,data,maxheight)) > maxheight ) |
|||
maxheight = ht; |
|||
} |
|||
} |
|||
} |
|||
if ( maxheight > 10 ) |
|||
{ |
|||
for (std::vector<struct oracleprice_info>::const_iterator it=publishers.begin(); it!=publishers.end(); it++) |
|||
{ |
|||
if ( it->height >= maxheight-10 ) |
|||
{ |
|||
oracle_format(&hash,&price,0,'L',(uint8_t *)it->data.data(),0,(int32_t)it->data.size()); |
|||
if ( price != 0 ) |
|||
prices.push_back(price); |
|||
} |
|||
} |
|||
return(OracleCorrelatedPrice(height,prices)); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
int64_t IsOraclesvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) |
|||
{ |
|||
//char destaddr[64];
|
|||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) |
|||
{ |
|||
//if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
|
|||
return(tx.vout[v].nValue); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
bool OraclesDataValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,uint256 oracletxid,CPubKey publisher,int64_t datafee) |
|||
{ |
|||
static uint256 zerohash; |
|||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; CScript scriptPubKey; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
if ( OracleDatafee(scriptPubKey,oracletxid,publisher) != datafee ) |
|||
return eval->Invalid("mismatched datafee"); |
|||
scriptPubKey = MakeCC1vout(cp->evalcode,0,publisher).scriptPubKey; |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
//fprintf(stderr,"vini.%d\n",i);
|
|||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) |
|||
{ |
|||
if ( i == 0 ) |
|||
return eval->Invalid("unexpected vin.0 is CC"); |
|||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
|||
else if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) |
|||
return eval->Invalid("cant find vinTx"); |
|||
else |
|||
{ |
|||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
|||
//if ( hashBlock == zerohash )
|
|||
// return eval->Invalid("cant Oracles from mempool");
|
|||
if ( (assetoshis= IsOraclesvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) |
|||
{ |
|||
if ( i == 1 && vinTx.vout[1].scriptPubKey != tx.vout[1].scriptPubKey ) |
|||
return eval->Invalid("baton violation"); |
|||
else if ( i != 1 && scriptPubKey == vinTx.vout[tx.vin[i].prevout.n].scriptPubKey ) |
|||
inputs += assetoshis; |
|||
} |
|||
} |
|||
} |
|||
else if ( i != 0 ) |
|||
return eval->Invalid("vin0 not normal"); |
|||
|
|||
} |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
|||
if ( (assetoshis= IsOraclesvout(cp,tx,i)) != 0 ) |
|||
{ |
|||
if ( i < 2 ) |
|||
{ |
|||
if ( i == 0 ) |
|||
{ |
|||
if ( tx.vout[0].scriptPubKey == scriptPubKey ) |
|||
outputs += assetoshis; |
|||
else return eval->Invalid("invalid CC vout CC destination"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
if ( inputs != outputs+datafee ) |
|||
{ |
|||
fprintf(stderr,"inputs %llu vs outputs %llu + datafee %llu\n",(long long)inputs,(long long)outputs,(long long)datafee); |
|||
return eval->Invalid("mismatched inputs != outputs + datafee"); |
|||
} |
|||
else return(true); |
|||
} |
|||
|
|||
bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) |
|||
{ |
|||
uint256 txid,oracletxid,batontxid; uint64_t txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts; uint8_t *script; std::vector<uint8_t> vopret,data; CScript scriptPubKey; CPubKey publisher; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
preventCCvins = preventCCvouts = -1; |
|||
if ( numvouts < 1 ) |
|||
return eval->Invalid("no vouts"); |
|||
else |
|||
{ |
|||
txid = tx.GetHash(); |
|||
GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); |
|||
if ( vopret.size() > 2 ) |
|||
{ |
|||
script = (uint8_t *)vopret.data(); |
|||
switch ( script[1] ) |
|||
{ |
|||
case 'C': // create
|
|||
// vins.*: normal inputs
|
|||
// vout.0: txfee tag to oracle normal address
|
|||
// vout.1: change, if any
|
|||
// vout.n-1: opreturn with name and description and format for data
|
|||
return eval->Invalid("unexpected OraclesValidate for create"); |
|||
break; |
|||
case 'R': // register
|
|||
// vins.*: normal inputs
|
|||
// vout.0: txfee tag to normal marker address
|
|||
// vout.1: baton CC utxo
|
|||
// vout.2: change, if any
|
|||
// vout.n-1: opreturn with createtxid, pubkey and price per data point
|
|||
return eval->Invalid("unexpected OraclesValidate for register"); |
|||
break; |
|||
case 'S': // subscribe
|
|||
// vins.*: normal inputs
|
|||
// vout.0: subscription fee to publishers CC address
|
|||
// vout.1: change, if any
|
|||
// vout.n-1: opreturn with createtxid, registered provider's pubkey, amount
|
|||
return eval->Invalid("unexpected OraclesValidate for subscribe"); |
|||
break; |
|||
case 'D': // data
|
|||
// vin.0: normal input
|
|||
// vin.1: baton CC utxo (most of the time)
|
|||
// vin.2+: subscription vout.0
|
|||
// vout.0: change to publishers CC address
|
|||
// vout.1: baton CC utxo
|
|||
// vout.2: payment for dataprovider
|
|||
// vout.3: change, if any
|
|||
if ( numvins >= 2 && numvouts >= 3 && DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,batontxid,publisher,data) == 'D' ) |
|||
{ |
|||
if ( OraclesDataValidate(cp,eval,tx,oracletxid,publisher,tx.vout[2].nValue) != 0 ) |
|||
{ |
|||
return(true); |
|||
} else return(false); |
|||
} |
|||
return eval->Invalid("unexpected OraclesValidate 'D' tx invalid"); |
|||
break; |
|||
} |
|||
} |
|||
return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts)); |
|||
} |
|||
return(true); |
|||
} |
|||
// end of consensus code
|
|||
|
|||
// helper functions for rpc calls in rpcwallet.cpp
|
|||
|
|||
int64_t AddOracleInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) |
|||
{ |
|||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
GetCCaddress(cp,coinaddr,pk); |
|||
SetCCunspents(unspentOutputs,coinaddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
//char str[65]; fprintf(stderr,"oracle check %s/v%d\n",uint256_str(str,txid),vout);
|
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
// get valid CC payments
|
|||
if ( (nValue= IsOraclesvout(cp,vintx,vout)) >= 10000 && myIsutxo_spentinmempool(txid,vout) == 0 ) |
|||
{ |
|||
if ( total != 0 && maxinputs != 0 ) |
|||
mtx.vin.push_back(CTxIn(txid,vout,CScript())); |
|||
nValue = it->second.satoshis; |
|||
totalinputs += nValue; |
|||
n++; |
|||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) |
|||
break; |
|||
} //else fprintf(stderr,"nValue %.8f or utxo memspent\n",(double)nValue/COIN);
|
|||
} else fprintf(stderr,"couldnt find transaction\n"); |
|||
} |
|||
return(totalinputs); |
|||
} |
|||
|
|||
int64_t LifetimeOraclesFunds(struct CCcontract_info *cp,uint256 oracletxid,CPubKey publisher) |
|||
{ |
|||
char coinaddr[64]; CPubKey pk; int64_t total=0,num; uint256 txid,hashBlock,subtxid; CTransaction subtx; |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; |
|||
GetCCaddress(cp,coinaddr,publisher); |
|||
SetCCtxids(addressIndex,coinaddr); |
|||
//fprintf(stderr,"scan lifetime of %s\n",coinaddr);
|
|||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
if ( GetTransaction(txid,subtx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( subtx.vout.size() > 0 && DecodeOraclesOpRet(subtx.vout[subtx.vout.size()-1].scriptPubKey,subtxid,pk,num) == 'S' && subtxid == oracletxid && pk == publisher ) |
|||
{ |
|||
total += subtx.vout[0].nValue; |
|||
} |
|||
} |
|||
} |
|||
return(total); |
|||
} |
|||
|
|||
std::string OracleCreate(int64_t txfee,std::string name,std::string description,std::string format) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk,Oraclespk; struct CCcontract_info *cp,C; |
|||
cp = CCinit(&C,EVAL_ORACLES); |
|||
if ( name.size() > 32 || description.size() > 4096 || format.size() > 4096 ) |
|||
{ |
|||
fprintf(stderr,"name.%d or description.%d is too big\n",(int32_t)name.size(),(int32_t)description.size()); |
|||
return(""); |
|||
} |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
Oraclespk = GetUnspendable(cp,0); |
|||
if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(Oraclespk)) << OP_CHECKSIG)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesCreateOpRet('C',name,description,format))); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
std::string OracleRegister(int64_t txfee,uint256 oracletxid,int64_t datafee) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk,markerpubkey,batonpk; struct CCcontract_info *cp,C; char markeraddr[64],batonaddr[64]; |
|||
cp = CCinit(&C,EVAL_ORACLES); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
if ( datafee < txfee ) |
|||
{ |
|||
fprintf(stderr,"datafee must be txfee or more\n"); |
|||
return(""); |
|||
} |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
batonpk = OracleBatonPk(batonaddr,cp); |
|||
markerpubkey = CCtxidaddr(markeraddr,oracletxid); |
|||
if ( AddNormalinputs(mtx,mypk,3*txfee,4) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,batonpk)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('R',oracletxid,mypk,datafee))); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
std::string OracleSubscribe(int64_t txfee,uint256 oracletxid,CPubKey publisher,int64_t amount) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk,markerpubkey; struct CCcontract_info *cp,C; char markeraddr[64]; |
|||
cp = CCinit(&C,EVAL_ORACLES); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
markerpubkey = CCtxidaddr(markeraddr,oracletxid); |
|||
if ( AddNormalinputs(mtx,mypk,amount + 2*txfee,1) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,publisher)); |
|||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('S',oracletxid,mypk,amount))); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
std::string OracleData(int64_t txfee,uint256 oracletxid,std::vector <uint8_t> data) |
|||
{ |
|||
CMutableTransaction mtx; CScript pubKey; CPubKey mypk,batonpk; int64_t datafee,inputs,CCchange = 0; struct CCcontract_info *cp,C; uint256 batontxid; char coinaddr[64],batonaddr[64]; std::vector <uint8_t> prevdata; |
|||
cp = CCinit(&C,EVAL_ORACLES); |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if ( data.size() > 8192 ) |
|||
{ |
|||
fprintf(stderr,"datasize %d is too big\n",(int32_t)data.size()); |
|||
return(""); |
|||
} |
|||
if ( (datafee= OracleDatafee(pubKey,oracletxid,mypk)) <= 0 ) |
|||
{ |
|||
fprintf(stderr,"datafee %.8f is illegal\n",(double)datafee/COIN); |
|||
return(""); |
|||
} |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
GetCCaddress(cp,coinaddr,mypk); |
|||
if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) // have enough funds even if baton utxo not there
|
|||
{ |
|||
batonpk = OracleBatonPk(batonaddr,cp); |
|||
batontxid = OracleBatonUtxo(txfee,cp,oracletxid,batonaddr,mypk,prevdata); |
|||
if ( batontxid != zeroid ) // not impossible to fail, but hopefully a very rare event
|
|||
mtx.vin.push_back(CTxIn(batontxid,1,CScript())); |
|||
else fprintf(stderr,"warning: couldnt find baton utxo %s\n",batonaddr); |
|||
if ( (inputs= AddOracleInputs(cp,mtx,mypk,datafee,60)) > 0 ) |
|||
{ |
|||
if ( inputs > datafee ) |
|||
CCchange = (inputs - datafee); |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,batonpk)); |
|||
mtx.vout.push_back(CTxOut(datafee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesData('D',oracletxid,batontxid,mypk,data))); |
|||
} else fprintf(stderr,"couldnt find enough oracle inputs, limit 1 per utxo\n"); |
|||
} else fprintf(stderr,"couldnt add normal inputs\n"); |
|||
return(""); |
|||
} |
|||
|
|||
UniValue OracleFormat(uint8_t *data,int32_t datalen,char *format,int32_t formatlen) |
|||
{ |
|||
UniValue obj(UniValue::VARR); uint256 hash; int32_t i,j=0; int64_t val; char str[IGUANA_MAXSCRIPTSIZE*2+1]; |
|||
for (i=0; i<formatlen && j<datalen; i++) |
|||
{ |
|||
str[0] = 0; |
|||
j = oracle_format(&hash,&val,str,format[i],data,j,datalen); |
|||
if ( j < 0 ) |
|||
break; |
|||
obj.push_back(str); |
|||
if ( j >= datalen ) |
|||
break; |
|||
} |
|||
return(obj); |
|||
} |
|||
|
|||
UniValue OracleDataSamples(uint256 reforacletxid,uint256 batontxid,int32_t num) |
|||
{ |
|||
UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,oracletx; uint256 hashBlock,btxid,oracletxid; CPubKey pk; std::string name,description,format; int32_t numvouts,n=0; std::vector<uint8_t> data; char *formatstr = 0; |
|||
result.push_back(Pair("result","success")); |
|||
if ( GetTransaction(reforacletxid,oracletx,hashBlock,false) != 0 && (numvouts=oracletx.vout.size()) > 0 ) |
|||
{ |
|||
if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) == 'C' ) |
|||
{ |
|||
while ( GetTransaction(batontxid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size()) > 0 ) |
|||
{ |
|||
if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D' && reforacletxid == oracletxid ) |
|||
{ |
|||
if ( (formatstr= (char *)format.c_str()) == 0 ) |
|||
formatstr = (char *)""; |
|||
a.push_back(OracleFormat((uint8_t *)data.data(),(int32_t)data.size(),formatstr,(int32_t)format.size())); |
|||
batontxid = btxid; |
|||
if ( ++n >= num ) |
|||
break; |
|||
} else break; |
|||
} |
|||
} |
|||
} |
|||
result.push_back(Pair("samples",a)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue OracleInfo(uint256 origtxid) |
|||
{ |
|||
UniValue result(UniValue::VOBJ),a(UniValue::VARR),obj(UniValue::VOBJ); |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
CMutableTransaction mtx; CTransaction regtx,tx; std::string name,description,format; uint256 hashBlock,txid,oracletxid,batontxid; CPubKey pk; struct CCcontract_info *cp,C; int64_t datafee,funding; char str[67],markeraddr[64],numstr[64],batonaddr[64]; std::vector <uint8_t> data; |
|||
cp = CCinit(&C,EVAL_ORACLES); |
|||
CCtxidaddr(markeraddr,origtxid); |
|||
if ( GetTransaction(origtxid,tx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( tx.vout.size() > 0 && DecodeOraclesCreateOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,name,description,format) == 'C' ) |
|||
{ |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("txid",uint256_str(str,origtxid))); |
|||
result.push_back(Pair("name",name)); |
|||
result.push_back(Pair("description",description)); |
|||
result.push_back(Pair("format",format)); |
|||
result.push_back(Pair("marker",markeraddr)); |
|||
SetCCunspents(unspentOutputs,markeraddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
if ( GetTransaction(txid,regtx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( regtx.vout.size() > 0 && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R' && oracletxid == origtxid ) |
|||
{ |
|||
obj.push_back(Pair("publisher",pubkey33_str(str,(uint8_t *)pk.begin()))); |
|||
Getscriptaddress(batonaddr,regtx.vout[1].scriptPubKey); |
|||
batontxid = OracleBatonUtxo(10000,cp,oracletxid,batonaddr,pk,data); |
|||
obj.push_back(Pair("baton",batonaddr)); |
|||
obj.push_back(Pair("batontxid",uint256_str(str,batontxid))); |
|||
funding = LifetimeOraclesFunds(cp,oracletxid,pk); |
|||
sprintf(numstr,"%.8f",(double)funding/COIN); |
|||
obj.push_back(Pair("lifetime",numstr)); |
|||
funding = AddOracleInputs(cp,mtx,pk,0,0); |
|||
sprintf(numstr,"%.8f",(double)funding/COIN); |
|||
obj.push_back(Pair("funds",numstr)); |
|||
sprintf(numstr,"%.8f",(double)datafee/COIN); |
|||
obj.push_back(Pair("datafee",numstr)); |
|||
a.push_back(obj); |
|||
} |
|||
} |
|||
} |
|||
result.push_back(Pair("registered",a)); |
|||
} |
|||
} |
|||
return(result); |
|||
} |
|||
|
|||
UniValue OraclesList() |
|||
{ |
|||
UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction createtx; std::string name,description,format; char str[65]; |
|||
cp = CCinit(&C,EVAL_ORACLES); |
|||
SetCCtxids(addressIndex,cp->normaladdr); |
|||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
if ( GetTransaction(txid,createtx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( createtx.vout.size() > 0 && DecodeOraclesCreateOpRet(createtx.vout[createtx.vout.size()-1].scriptPubKey,name,description,format) == 'C' ) |
|||
{ |
|||
result.push_back(uint256_str(str,txid)); |
|||
} |
|||
} |
|||
} |
|||
return(result); |
|||
} |
|||
|
@ -0,0 +1,213 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#include "CCPayments.h" |
|||
|
|||
/*
|
|||
Payments CC is a catchall CC, supported invoices, zpayments, automated funds allocation, including token based revshare |
|||
|
|||
|
|||
*/ |
|||
|
|||
// start of consensus code
|
|||
|
|||
int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) |
|||
{ |
|||
char destaddr[64]; |
|||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) |
|||
{ |
|||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) |
|||
return(tx.vout[v].nValue); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
bool PaymentsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) |
|||
{ |
|||
static uint256 zerohash; |
|||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
//fprintf(stderr,"vini.%d\n",i);
|
|||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
|||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) |
|||
return eval->Invalid("cant find vinTx"); |
|||
else |
|||
{ |
|||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
|||
if ( hashBlock == zerohash ) |
|||
return eval->Invalid("cant Payments from mempool"); |
|||
if ( (assetoshis= IsPaymentsvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) |
|||
inputs += assetoshis; |
|||
} |
|||
} |
|||
} |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
|||
if ( (assetoshis= IsPaymentsvout(cp,tx,i)) != 0 ) |
|||
outputs += assetoshis; |
|||
} |
|||
if ( inputs != outputs+txfee ) |
|||
{ |
|||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); |
|||
return eval->Invalid("mismatched inputs != outputs + txfee"); |
|||
} |
|||
else return(true); |
|||
} |
|||
|
|||
bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) |
|||
{ |
|||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; |
|||
return(false); |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
preventCCvins = preventCCvouts = -1; |
|||
if ( numvouts < 1 ) |
|||
return eval->Invalid("no vouts"); |
|||
else |
|||
{ |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) |
|||
{ |
|||
return eval->Invalid("illegal normal vini"); |
|||
} |
|||
} |
|||
//fprintf(stderr,"check amounts\n");
|
|||
if ( PaymentsExactAmounts(cp,eval,tx,1,10000) == false ) |
|||
{ |
|||
fprintf(stderr,"Paymentsget invalid amount\n"); |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
txid = tx.GetHash(); |
|||
memcpy(hash,&txid,sizeof(hash)); |
|||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); |
|||
if ( retval != 0 ) |
|||
fprintf(stderr,"Paymentsget validated\n"); |
|||
else fprintf(stderr,"Paymentsget invalid\n"); |
|||
return(retval); |
|||
} |
|||
} |
|||
} |
|||
// end of consensus code
|
|||
|
|||
// helper functions for rpc calls in rpcwallet.cpp
|
|||
|
|||
int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) |
|||
{ |
|||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
GetCCaddress(cp,coinaddr,pk); |
|||
SetCCunspents(unspentOutputs,coinaddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
// no need to prevent dup
|
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( (nValue= IsPaymentsvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) |
|||
{ |
|||
if ( total != 0 && maxinputs != 0 ) |
|||
mtx.vin.push_back(CTxIn(txid,vout,CScript())); |
|||
nValue = it->second.satoshis; |
|||
totalinputs += nValue; |
|||
n++; |
|||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return(totalinputs); |
|||
} |
|||
|
|||
std::string PaymentsGet(uint64_t txfee,int64_t nValue) |
|||
{ |
|||
CMutableTransaction mtx,tmpmtx; CPubKey mypk,Paymentspk; int64_t inputs,CCchange=0; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; |
|||
cp = CCinit(&C,EVAL_PAYMENTS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
Paymentspk = GetUnspendable(cp,0); |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if ( (inputs= AddPaymentsInputs(cp,mtx,Paymentspk,nValue+txfee,60)) > 0 ) |
|||
{ |
|||
if ( inputs > nValue ) |
|||
CCchange = (inputs - nValue - txfee); |
|||
if ( CCchange != 0 ) |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,CCchange,Paymentspk)); |
|||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
|||
fprintf(stderr,"start at %u\n",(uint32_t)time(NULL)); |
|||
j = rand() & 0xfffffff; |
|||
for (i=0; i<1000000; i++,j++) |
|||
{ |
|||
tmpmtx = mtx; |
|||
rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_PAYMENTS << (uint8_t)'G' << j)); |
|||
if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) |
|||
{ |
|||
len >>= 1; |
|||
decode_hex(buf,len,(char *)rawhex.c_str()); |
|||
hash = bits256_doublesha256(0,buf,len); |
|||
if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 ) |
|||
{ |
|||
fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL)); |
|||
return(rawhex); |
|||
} |
|||
//fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]);
|
|||
} |
|||
} |
|||
fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); |
|||
return(""); |
|||
} else fprintf(stderr,"cant find Payments inputs\n"); |
|||
return(""); |
|||
} |
|||
|
|||
std::string PaymentsFund(uint64_t txfee,int64_t funds) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk,Paymentspk; CScript opret; struct CCcontract_info *cp,C; |
|||
cp = CCinit(&C,EVAL_PAYMENTS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
Paymentspk = GetUnspendable(cp,0); |
|||
if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,funds,Paymentspk)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
UniValue PaymentsInfo() |
|||
{ |
|||
UniValue result(UniValue::VOBJ); char numstr[64]; |
|||
CMutableTransaction mtx; CPubKey Paymentspk; struct CCcontract_info *cp,C; int64_t funding; |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("name","Payments")); |
|||
cp = CCinit(&C,EVAL_PAYMENTS); |
|||
Paymentspk = GetUnspendable(cp,0); |
|||
funding = AddPaymentsInputs(cp,mtx,Paymentspk,0,0); |
|||
sprintf(numstr,"%.8f",(double)funding/COIN); |
|||
result.push_back(Pair("funding",numstr)); |
|||
return(result); |
|||
} |
|||
|
@ -0,0 +1,220 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#include "CCPegs.h" |
|||
|
|||
/*
|
|||
Pegs CC builds on top of Prices CC and would bind a pricefeed to a token bid/ask automated marketmaking. |
|||
|
|||
Funds deposited into CC address for a specific peg would then be used to fund the bid/ask as the pricefeed changes the price. Profits/losses would accumulate in the associated address. |
|||
|
|||
In the event funds exceed a specified level, it can be spent into a collection address. The idea is that the collection address can further be used for revshares. |
|||
|
|||
int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); |
|||
|
|||
OraclePrice is very useful for pegs. |
|||
|
|||
*/ |
|||
|
|||
// start of consensus code
|
|||
|
|||
int64_t IsPegsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) |
|||
{ |
|||
char destaddr[64]; |
|||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) |
|||
{ |
|||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) |
|||
return(tx.vout[v].nValue); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
bool PegsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) |
|||
{ |
|||
static uint256 zerohash; |
|||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
//fprintf(stderr,"vini.%d\n",i);
|
|||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
|||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) |
|||
return eval->Invalid("cant find vinTx"); |
|||
else |
|||
{ |
|||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
|||
if ( hashBlock == zerohash ) |
|||
return eval->Invalid("cant Pegs from mempool"); |
|||
if ( (assetoshis= IsPegsvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) |
|||
inputs += assetoshis; |
|||
} |
|||
} |
|||
} |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
|||
if ( (assetoshis= IsPegsvout(cp,tx,i)) != 0 ) |
|||
outputs += assetoshis; |
|||
} |
|||
if ( inputs != outputs+txfee ) |
|||
{ |
|||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); |
|||
return eval->Invalid("mismatched inputs != outputs + txfee"); |
|||
} |
|||
else return(true); |
|||
} |
|||
|
|||
bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) |
|||
{ |
|||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; |
|||
return(false); |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
preventCCvins = preventCCvouts = -1; |
|||
if ( numvouts < 1 ) |
|||
return eval->Invalid("no vouts"); |
|||
else |
|||
{ |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) |
|||
{ |
|||
return eval->Invalid("illegal normal vini"); |
|||
} |
|||
} |
|||
//fprintf(stderr,"check amounts\n");
|
|||
if ( PegsExactAmounts(cp,eval,tx,1,10000) == false ) |
|||
{ |
|||
fprintf(stderr,"Pegsget invalid amount\n"); |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
txid = tx.GetHash(); |
|||
memcpy(hash,&txid,sizeof(hash)); |
|||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); |
|||
if ( retval != 0 ) |
|||
fprintf(stderr,"Pegsget validated\n"); |
|||
else fprintf(stderr,"Pegsget invalid\n"); |
|||
return(retval); |
|||
} |
|||
} |
|||
} |
|||
// end of consensus code
|
|||
|
|||
// helper functions for rpc calls in rpcwallet.cpp
|
|||
|
|||
int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) |
|||
{ |
|||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
GetCCaddress(cp,coinaddr,pk); |
|||
SetCCunspents(unspentOutputs,coinaddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
// no need to prevent dup
|
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( (nValue= IsPegsvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) |
|||
{ |
|||
if ( total != 0 && maxinputs != 0 ) |
|||
mtx.vin.push_back(CTxIn(txid,vout,CScript())); |
|||
nValue = it->second.satoshis; |
|||
totalinputs += nValue; |
|||
n++; |
|||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return(totalinputs); |
|||
} |
|||
|
|||
std::string PegsGet(uint64_t txfee,int64_t nValue) |
|||
{ |
|||
CMutableTransaction mtx,tmpmtx; CPubKey mypk,Pegspk; int64_t inputs,CCchange=0; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; |
|||
cp = CCinit(&C,EVAL_PEGS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
Pegspk = GetUnspendable(cp,0); |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if ( (inputs= AddPegsInputs(cp,mtx,Pegspk,nValue+txfee,60)) > 0 ) |
|||
{ |
|||
if ( inputs > nValue ) |
|||
CCchange = (inputs - nValue - txfee); |
|||
if ( CCchange != 0 ) |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,CCchange,Pegspk)); |
|||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
|||
fprintf(stderr,"start at %u\n",(uint32_t)time(NULL)); |
|||
j = rand() & 0xfffffff; |
|||
for (i=0; i<1000000; i++,j++) |
|||
{ |
|||
tmpmtx = mtx; |
|||
rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_PEGS << (uint8_t)'G' << j)); |
|||
if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) |
|||
{ |
|||
len >>= 1; |
|||
decode_hex(buf,len,(char *)rawhex.c_str()); |
|||
hash = bits256_doublesha256(0,buf,len); |
|||
if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 ) |
|||
{ |
|||
fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL)); |
|||
return(rawhex); |
|||
} |
|||
//fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]);
|
|||
} |
|||
} |
|||
fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); |
|||
return(""); |
|||
} else fprintf(stderr,"cant find Pegs inputs\n"); |
|||
return(""); |
|||
} |
|||
|
|||
std::string PegsFund(uint64_t txfee,int64_t funds) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk,Pegspk; CScript opret; struct CCcontract_info *cp,C; |
|||
cp = CCinit(&C,EVAL_PEGS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
Pegspk = GetUnspendable(cp,0); |
|||
if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,funds,Pegspk)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
UniValue PegsInfo() |
|||
{ |
|||
UniValue result(UniValue::VOBJ); char numstr[64]; |
|||
CMutableTransaction mtx; CPubKey Pegspk; struct CCcontract_info *cp,C; int64_t funding; |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("name","Pegs")); |
|||
cp = CCinit(&C,EVAL_PEGS); |
|||
Pegspk = GetUnspendable(cp,0); |
|||
funding = AddPegsInputs(cp,mtx,Pegspk,0,0); |
|||
sprintf(numstr,"%.8f",(double)funding/COIN); |
|||
result.push_back(Pair("funding",numstr)); |
|||
return(result); |
|||
} |
|||
|
@ -0,0 +1,336 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#include "CCPrices.h" |
|||
|
|||
/*
|
|||
Prices CC would best build on top of the oracles CC, ie. to combine payments for multiple oracles and to calculate a 51% protected price feed. |
|||
|
|||
We need to assume there is an oracle for a specific price. In the event there are more than one provider, the majority need to be within correlation distance to update a pricepoint. |
|||
|
|||
int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); |
|||
|
|||
Using the above function, a consensus price can be obtained for a datasource. |
|||
|
|||
given an oracletxid, the marketaddr and format can be extracted to be used for future calls to OraclePrice. This allows to set a starting price and that in turn allows cash settled leveraged trading! |
|||
|
|||
Funds work like with dice, ie. there is a Prices plan that traders bet against. |
|||
|
|||
PricesOpen -> oracletxid start with 'L' price, leverage, amount |
|||
funds are locked into global CC address |
|||
it can be closed at anytime by the trader for cash settlement |
|||
the house account can close it if rekt |
|||
|
|||
Implementation Notes: |
|||
In order to eliminate the need for worrying about sybil attacks, each prices plan would be able to specific pubkey(s?) for whitelisted publishers. It would be possible to have a non-whitelisted plan that would use 50% correlation between publishers. |
|||
|
|||
delta neutral balancing of risk exposure |
|||
|
|||
*/ |
|||
|
|||
// start of consensus code
|
|||
|
|||
int64_t IsPricesvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) |
|||
{ |
|||
char destaddr[64]; |
|||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) |
|||
{ |
|||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) |
|||
return(tx.vout[v].nValue); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
bool PricesExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) |
|||
{ |
|||
static uint256 zerohash; |
|||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
//fprintf(stderr,"vini.%d\n",i);
|
|||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
|||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) |
|||
return eval->Invalid("cant find vinTx"); |
|||
else |
|||
{ |
|||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
|||
if ( hashBlock == zerohash ) |
|||
return eval->Invalid("cant Prices from mempool"); |
|||
if ( (assetoshis= IsPricesvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) |
|||
inputs += assetoshis; |
|||
} |
|||
} |
|||
} |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
|||
if ( (assetoshis= IsPricesvout(cp,tx,i)) != 0 ) |
|||
outputs += assetoshis; |
|||
} |
|||
if ( inputs != outputs+txfee ) |
|||
{ |
|||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); |
|||
return eval->Invalid("mismatched inputs != outputs + txfee"); |
|||
} |
|||
else return(true); |
|||
} |
|||
|
|||
bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) |
|||
{ |
|||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; |
|||
return(false); |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
preventCCvins = preventCCvouts = -1; |
|||
if ( numvouts < 1 ) |
|||
return eval->Invalid("no vouts"); |
|||
else |
|||
{ |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) |
|||
{ |
|||
return eval->Invalid("illegal normal vini"); |
|||
} |
|||
} |
|||
//fprintf(stderr,"check amounts\n");
|
|||
if ( PricesExactAmounts(cp,eval,tx,1,10000) == false ) |
|||
{ |
|||
fprintf(stderr,"Pricesget invalid amount\n"); |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
txid = tx.GetHash(); |
|||
memcpy(hash,&txid,sizeof(hash)); |
|||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); |
|||
if ( retval != 0 ) |
|||
fprintf(stderr,"Pricesget validated\n"); |
|||
else fprintf(stderr,"Pricesget invalid\n"); |
|||
return(retval); |
|||
} |
|||
} |
|||
} |
|||
// end of consensus code
|
|||
|
|||
// helper functions for rpc calls in rpcwallet.cpp
|
|||
|
|||
int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) |
|||
{ |
|||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
GetCCaddress(cp,coinaddr,pk); |
|||
SetCCunspents(unspentOutputs,coinaddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
// no need to prevent dup
|
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( (nValue= IsPricesvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) |
|||
{ |
|||
if ( total != 0 && maxinputs != 0 ) |
|||
mtx.vin.push_back(CTxIn(txid,vout,CScript())); |
|||
nValue = it->second.satoshis; |
|||
totalinputs += nValue; |
|||
n++; |
|||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return(totalinputs); |
|||
} |
|||
|
|||
#ifdef later |
|||
UniValue PricesInfo(uint256 pricesid) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); CPubKey pricepk; uint256 hashBlock,oracletxid; CTransaction vintx; int64_t minbet,maxbet,maxodds; uint64_t funding; char numstr[65]; struct CCcontract_info *cp,C; |
|||
if ( GetTransaction(pricesid,vintx,hashBlock,false) == 0 ) |
|||
{ |
|||
fprintf(stderr,"cant find fundingtxid\n"); |
|||
ERR_RESULT("cant find fundingtxid"); |
|||
return(result); |
|||
} |
|||
if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,oracletxid,minbet,maxbet,maxodds) == 0 ) |
|||
{ |
|||
fprintf(stderr,"fundingtxid isnt price creation txid\n"); |
|||
ERR_RESULT("fundingtxid isnt price creation txid"); |
|||
return(result); |
|||
} |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("pricesid",uint256_str(str,pricesid))); |
|||
result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); |
|||
sprintf(numstr,"%.8f",(double)minbet/COIN); |
|||
result.push_back(Pair("minbet",numstr)); |
|||
sprintf(numstr,"%.8f",(double)maxbet/COIN); |
|||
result.push_back(Pair("maxbet",numstr)); |
|||
result.push_back(Pair("maxodds",maxodds)); |
|||
cp = CCinit(&C,EVAL_PRICES); |
|||
pricepk = GetUnspendable(cp,0); |
|||
funding = PricePlanFunds(cp,pricepk,pricesid); |
|||
sprintf(numstr,"%.8f",(double)funding/COIN); |
|||
result.push_back(Pair("funding",numstr)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue PricesList() |
|||
{ |
|||
UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction vintx; int64_t minbet,maxbet,maxodds; char str[65]; |
|||
cp = CCinit(&C,EVAL_PRICES); |
|||
SetCCtxids(addressIndex,cp->normaladdr); |
|||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,oracletxid,minbet,maxbet,maxodds) != 0 ) |
|||
{ |
|||
result.push_back(uint256_str(str,txid)); |
|||
} |
|||
} |
|||
} |
|||
return(result); |
|||
} |
|||
|
|||
std::string PricesCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks) |
|||
{ |
|||
CMutableTransaction mtx; uint256 zero; CScript fundingPubKey; CPubKey mypk,pricepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C; |
|||
if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || maxodds > 9999 || timeoutblocks < 0 || timeoutblocks > 1440 ) |
|||
{ |
|||
CCerror = "invalid parameter error"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
if ( funds < 100*COIN ) |
|||
{ |
|||
CCerror = "price plan needs at least 100 coins"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
memset(&zero,0,sizeof(zero)); |
|||
if ( (cp= Pricesinit(fundingPubKey,zero,&C,planstr,txfee,mypk,pricepk,sbits,a,b,c,d)) == 0 ) |
|||
{ |
|||
CCerror = "Priceinit error in create funding"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
if ( AddNormalinputs(mtx,mypk,funds+3*txfee,60) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,pricepk)); |
|||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
|||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(pricepk)) << OP_CHECKSIG)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks))); |
|||
} |
|||
CCerror = "cant find enough inputs"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
|
|||
std::string PricesAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount) |
|||
{ |
|||
CMutableTransaction mtx; CScript fundingPubKey,scriptPubKey; CPubKey mypk,pricepk; struct CCcontract_info *cp,C; int64_t minbet,maxbet,maxodds; |
|||
if ( amount < 0 ) |
|||
{ |
|||
CCerror = "amount must be positive"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
if ( (cp= Pricesinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,pricepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) |
|||
return(""); |
|||
scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG; |
|||
if ( scriptPubKey == fundingPubKey ) |
|||
{ |
|||
if ( AddNormalinputs(mtx,mypk,amount+2*txfee,60) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,pricepk)); |
|||
mtx.vout.push_back(CTxOut(txfee,fundingPubKey)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesOpRet('E',sbits,fundingtxid,hentropy,zeroid))); |
|||
} |
|||
else |
|||
{ |
|||
CCerror = "cant find enough inputs"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
CCerror = "only fund creator can add more funds (entropy)"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
std::string PricesBet(uint64_t txfee,uint256 pricesid,int64_t bet,int32_t odds) |
|||
{ |
|||
CMutableTransaction mtx; CScript fundingPubKey; CPubKey mypk,pricepk; int64_t funding,minbet,maxbet,maxodds; struct CCcontract_info *cp,C; |
|||
if ( bet < 0 ) |
|||
{ |
|||
CCerror = "bet must be positive"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
if ( odds < 1 || odds > 9999 ) |
|||
{ |
|||
CCerror = "odds must be between 1 and 9999"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
if ( (cp= Pricesinit(fundingPubKey,pricesid,&C,txfee,mypk,pricepk,minbet,maxbet,maxodds)) == 0 ) |
|||
return(""); |
|||
if ( bet < minbet || bet > maxbet || odds > maxodds ) |
|||
{ |
|||
CCerror = strprintf("Price plan %s illegal bet %.8f: minbet %.8f maxbet %.8f or odds %d vs max.%d\n",planstr,(double)bet/COIN,(double)minbet/COIN,(double)maxbet/COIN,(int32_t)odds,(int32_t)maxodds); |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
if ( (funding= PricesPlanFunds(cp,pricepk,pricesid)) >= 2*bet*odds+txfee ) |
|||
{ |
|||
if ( myIsutxo_spentinmempool(entropytxid,0) != 0 ) |
|||
{ |
|||
CCerror = "entropy txid is spent"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,pricepk)); |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,pricepk)); |
|||
mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesOpRet('B',pricesid))); |
|||
} else fprintf(stderr,"cant find enough normal inputs for %.8f, plan funding %.8f\n",(double)bet/COIN,(double)funding/COIN); |
|||
} |
|||
if ( entropyval == 0 && funding != 0 ) |
|||
CCerror = "cant find price entropy inputs"; |
|||
else CCerror = "cant find price input"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
|
|||
std::string PricesBetFinish(int32_t *resultp,uint64_t txfee,uint256 pricesid,uint256 bettxid) |
|||
{ |
|||
*resultp = -1; |
|||
CCerror = "couldnt find bettx or entropytx"; |
|||
fprintf(stderr,"%s\n", CCerror.c_str() ); |
|||
return(""); |
|||
} |
|||
#endif |
|||
|
@ -0,0 +1,212 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#include "CCTriggers.h" |
|||
|
|||
/*
|
|||
Triggers CC is a building block CC that allows creation of event -> action processing, where events are defined during trigger creation and actions to be mostly done via payments, but by making payments to other CC contracts, it can be used to invoke other CC contracts |
|||
|
|||
*/ |
|||
|
|||
// start of consensus code
|
|||
|
|||
int64_t IsTriggersvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) |
|||
{ |
|||
char destaddr[64]; |
|||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) |
|||
{ |
|||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) |
|||
return(tx.vout[v].nValue); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
bool TriggersExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) |
|||
{ |
|||
static uint256 zerohash; |
|||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
//fprintf(stderr,"vini.%d\n",i);
|
|||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
|||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) |
|||
return eval->Invalid("cant find vinTx"); |
|||
else |
|||
{ |
|||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
|||
if ( hashBlock == zerohash ) |
|||
return eval->Invalid("cant Triggers from mempool"); |
|||
if ( (assetoshis= IsTriggersvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) |
|||
inputs += assetoshis; |
|||
} |
|||
} |
|||
} |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
|||
if ( (assetoshis= IsTriggersvout(cp,tx,i)) != 0 ) |
|||
outputs += assetoshis; |
|||
} |
|||
if ( inputs != outputs+txfee ) |
|||
{ |
|||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); |
|||
return eval->Invalid("mismatched inputs != outputs + txfee"); |
|||
} |
|||
else return(true); |
|||
} |
|||
|
|||
bool TriggersValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) |
|||
{ |
|||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; |
|||
return(false); |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
preventCCvins = preventCCvouts = -1; |
|||
if ( numvouts < 1 ) |
|||
return eval->Invalid("no vouts"); |
|||
else |
|||
{ |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) |
|||
{ |
|||
return eval->Invalid("illegal normal vini"); |
|||
} |
|||
} |
|||
//fprintf(stderr,"check amounts\n");
|
|||
if ( TriggersExactAmounts(cp,eval,tx,1,10000) == false ) |
|||
{ |
|||
fprintf(stderr,"Triggersget invalid amount\n"); |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
txid = tx.GetHash(); |
|||
memcpy(hash,&txid,sizeof(hash)); |
|||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); |
|||
if ( retval != 0 ) |
|||
fprintf(stderr,"Triggersget validated\n"); |
|||
else fprintf(stderr,"Triggersget invalid\n"); |
|||
return(retval); |
|||
} |
|||
} |
|||
} |
|||
// end of consensus code
|
|||
|
|||
// helper functions for rpc calls in rpcwallet.cpp
|
|||
|
|||
int64_t AddTriggersInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) |
|||
{ |
|||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
GetCCaddress(cp,coinaddr,pk); |
|||
SetCCunspents(unspentOutputs,coinaddr); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
// no need to prevent dup
|
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( (nValue= IsTriggersvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) |
|||
{ |
|||
if ( total != 0 && maxinputs != 0 ) |
|||
mtx.vin.push_back(CTxIn(txid,vout,CScript())); |
|||
nValue = it->second.satoshis; |
|||
totalinputs += nValue; |
|||
n++; |
|||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return(totalinputs); |
|||
} |
|||
|
|||
std::string TriggersGet(uint64_t txfee,int64_t nValue) |
|||
{ |
|||
CMutableTransaction mtx,tmpmtx; CPubKey mypk,Triggerspk; int64_t inputs,CCchange=0; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; |
|||
cp = CCinit(&C,EVAL_TRIGGERS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
Triggerspk = GetUnspendable(cp,0); |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if ( (inputs= AddTriggersInputs(cp,mtx,Triggerspk,nValue+txfee,60)) > 0 ) |
|||
{ |
|||
if ( inputs > nValue ) |
|||
CCchange = (inputs - nValue - txfee); |
|||
if ( CCchange != 0 ) |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_TRIGGERS,CCchange,Triggerspk)); |
|||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
|||
fprintf(stderr,"start at %u\n",(uint32_t)time(NULL)); |
|||
j = rand() & 0xfffffff; |
|||
for (i=0; i<1000000; i++,j++) |
|||
{ |
|||
tmpmtx = mtx; |
|||
rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_TRIGGERS << (uint8_t)'G' << j)); |
|||
if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) |
|||
{ |
|||
len >>= 1; |
|||
decode_hex(buf,len,(char *)rawhex.c_str()); |
|||
hash = bits256_doublesha256(0,buf,len); |
|||
if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 ) |
|||
{ |
|||
fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL)); |
|||
return(rawhex); |
|||
} |
|||
//fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]);
|
|||
} |
|||
} |
|||
fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); |
|||
return(""); |
|||
} else fprintf(stderr,"cant find Triggers inputs\n"); |
|||
return(""); |
|||
} |
|||
|
|||
std::string TriggersFund(uint64_t txfee,int64_t funds) |
|||
{ |
|||
CMutableTransaction mtx; CPubKey mypk,Triggerspk; CScript opret; struct CCcontract_info *cp,C; |
|||
cp = CCinit(&C,EVAL_TRIGGERS); |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
Triggerspk = GetUnspendable(cp,0); |
|||
if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_TRIGGERS,funds,Triggerspk)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
UniValue TriggersInfo() |
|||
{ |
|||
UniValue result(UniValue::VOBJ); char numstr[64]; |
|||
CMutableTransaction mtx; CPubKey Triggerspk; struct CCcontract_info *cp,C; int64_t funding; |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("name","Triggers")); |
|||
cp = CCinit(&C,EVAL_TRIGGERS); |
|||
Triggerspk = GetUnspendable(cp,0); |
|||
funding = AddTriggersInputs(cp,mtx,Triggerspk,0,0); |
|||
sprintf(numstr,"%.8f",(double)funding/COIN); |
|||
result.push_back(Pair("funding",numstr)); |
|||
return(result); |
|||
} |
|||
|
@ -0,0 +1,2 @@ |
|||
#!/bin/bash |
|||
./komodo-cli -ac_name=CCL $1 $2 $3 $4 $5 $6 |
@ -0,0 +1,2 @@ |
|||
#!/bin/bash |
|||
./komodo-cli -ac_name=VOTE2018 $1 $2 $3 $4 $5 $6 |
File diff suppressed because it is too large
Loading…
Reference in new issue